422 lines
11 KiB
C++
422 lines
11 KiB
C++
#pragma once
|
|
|
|
#include "AudioToolsConfig.h"
|
|
#include "AudioTools/AudioLibs/I2SCodecStream.h"
|
|
#include "AudioTools/CoreAudio/AudioActions.h"
|
|
|
|
namespace audio_tools {
|
|
|
|
/**
|
|
* @brief New functionality which replaces the AudioKitStream that is based on
|
|
* the legacy AudioKit library. This functionality uses the new
|
|
* arduino-audio-driver library! It is the same as I2SCodecStream extended by
|
|
* some AudioActions and some method calls to determine defined pin values.
|
|
* See https://github.com/pschatzmann/arduino-audio-driver
|
|
* @ingroup io
|
|
* @author Phil Schatzmann
|
|
* @copyright GPLv3
|
|
*/
|
|
class AudioBoardStream : public I2SCodecStream {
|
|
struct AudioBoardAction : public AudioActions::Action {
|
|
AudioBoardAction(AudioBoard &board, AudioDriverKey key) {
|
|
this->key = key;
|
|
this->p_board = &board;
|
|
}
|
|
AudioDriverKey key;
|
|
AudioBoard *p_board;
|
|
int id() override { return key | 0x400; }
|
|
bool readValue() override { return p_board->isKeyPressed(key); }
|
|
};
|
|
|
|
public:
|
|
/**
|
|
* @brief Default constructor: for available AudioBoard values check
|
|
* the audioboard variables in
|
|
* https://pschatzmann.github.io/arduino-audio-driver/html/group__audio__driver.html
|
|
* Further information can be found in
|
|
* https://github.com/pschatzmann/arduino-audio-driver/wiki
|
|
*/
|
|
AudioBoardStream(audio_driver::AudioBoard &board) : I2SCodecStream(board) {
|
|
// pin mode already set up by driver library
|
|
actions.setPinMode(false);
|
|
}
|
|
|
|
bool begin() override { return I2SCodecStream::begin(); }
|
|
|
|
bool begin(I2SCodecConfig cfg) override { return I2SCodecStream::begin(cfg); }
|
|
|
|
/**
|
|
* @brief Process input keys and pins
|
|
*
|
|
*/
|
|
void processActions() {
|
|
// TRACED();
|
|
actions.processActions();
|
|
delay(1);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Defines a new action that is executed when the Button is pressed
|
|
*/
|
|
void addAction(AudioDriverKey key, void (*action)(bool, int, void *),
|
|
void *ref = nullptr) {
|
|
AudioBoardAction *abo = new AudioBoardAction(board(), key);
|
|
abo->actionOn = action;
|
|
abo->ref = (ref == nullptr) ? this : ref;
|
|
actions.add(*abo);
|
|
}
|
|
|
|
/**
|
|
* @brief Defines a new action that is executed when the Button is pressed and released
|
|
*/
|
|
void addAction(AudioDriverKey key, void (*actionOn)(bool, int, void *),
|
|
void (*actionOff)(bool, int, void *),
|
|
void *ref = nullptr) {
|
|
|
|
AudioBoardAction *abo = new AudioBoardAction(board(), key);
|
|
abo->actionOn = actionOn;
|
|
abo->actionOn = actionOff;
|
|
abo->ref = (ref == nullptr) ? this : ref;
|
|
actions.add(*abo);
|
|
}
|
|
|
|
/**
|
|
* @brief Defines a new action that is executed when the indicated pin is
|
|
* active
|
|
*
|
|
* @param pin
|
|
* @param action
|
|
* @param ref
|
|
*/
|
|
void addAction(int pin, void (*action)(bool, int, void *),
|
|
void *ref = nullptr) {
|
|
TRACEI();
|
|
// determine logic from config
|
|
AudioActions::ActiveLogic activeLogic = getActionLogic(pin);
|
|
actions.add(pin, action, activeLogic, ref == nullptr ? this : ref);
|
|
}
|
|
|
|
/**
|
|
* @brief Defines a new action that is executed when the indicated pin is
|
|
* active
|
|
*
|
|
* @param pin
|
|
* @param action
|
|
* @param activeLogic
|
|
* @param ref
|
|
*/
|
|
void addAction(int pin, void (*action)(bool, int, void *),
|
|
AudioActions::ActiveLogic activeLogic, void *ref = nullptr) {
|
|
TRACEI();
|
|
actions.add(pin, action, activeLogic, ref == nullptr ? this : ref);
|
|
}
|
|
|
|
/// Provides access to the AudioActions
|
|
AudioActions &audioActions() { return actions; }
|
|
|
|
AudioActions &getActions() { return actions; }
|
|
|
|
/**
|
|
* @brief Relative volume control
|
|
*
|
|
* @param vol
|
|
*/
|
|
void incrementVolume(float inc) {
|
|
float current_volume = getVolume();
|
|
float new_volume = current_volume + inc;
|
|
LOGI("incrementVolume: %f -> %f", current_volume, new_volume);
|
|
setVolume(new_volume);
|
|
}
|
|
|
|
/**
|
|
* @brief Increase the volume
|
|
*
|
|
*/
|
|
static void actionVolumeUp(bool, int, void *ref) {
|
|
TRACEI();
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
self->incrementVolume(+self->actionVolumeIncrementValue());
|
|
}
|
|
|
|
/**
|
|
* @brief Decrease the volume
|
|
*
|
|
*/
|
|
static void actionVolumeDown(bool, int, void *ref) {
|
|
TRACEI();
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
self->incrementVolume(-self->actionVolumeIncrementValue());
|
|
}
|
|
|
|
/**
|
|
* @brief Toggle start stop
|
|
*
|
|
*/
|
|
static void actionStartStop(bool, int, void *ref) {
|
|
TRACEI();
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
self->active = !self->active;
|
|
self->setActive(self->active);
|
|
}
|
|
|
|
/**
|
|
* @brief Start
|
|
*
|
|
*/
|
|
static void actionStart(bool, int, void *ref) {
|
|
TRACEI();
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
self->active = true;
|
|
self->setActive(self->active);
|
|
}
|
|
|
|
/**
|
|
* @brief Stop
|
|
*/
|
|
static void actionStop(bool, int, void *ref) {
|
|
TRACEI();
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
self->active = false;
|
|
self->setActive(self->active);
|
|
}
|
|
|
|
/**
|
|
* @brief Switch off the PA if the headphone in plugged in
|
|
* and switch it on again if the headphone is unplugged.
|
|
* This method complies with the
|
|
*/
|
|
static void actionHeadphoneDetection(bool, int, void *ref) {
|
|
AudioBoardStream *self = (AudioBoardStream *)ref;
|
|
if (self->pinHeadphoneDetect() >= 0) {
|
|
// detect changes
|
|
bool isConnected = self->headphoneStatus();
|
|
if (self->headphoneIsConnected != isConnected) {
|
|
self->headphoneIsConnected = isConnected;
|
|
|
|
// update if things have stabilized
|
|
bool powerActive = !isConnected;
|
|
LOGW("Headphone jack has been %s",
|
|
isConnected ? "inserted" : "removed");
|
|
self->setSpeakerActive(powerActive);
|
|
}
|
|
}
|
|
delay(1);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the gpio number for auxin detection
|
|
*
|
|
* @return -1 non-existent
|
|
* Others gpio number
|
|
*/
|
|
GpioPin pinAuxin() { return getPinID(PinFunction::AUXIN_DETECT); }
|
|
|
|
/**
|
|
* @brief Get the gpio number for headphone detection
|
|
*
|
|
* @return -1 non-existent
|
|
* Others gpio number
|
|
*/
|
|
GpioPin pinHeadphoneDetect() {
|
|
return getPinID(PinFunction::HEADPHONE_DETECT);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the gpio number for PA enable
|
|
*
|
|
* @return -1 non-existent
|
|
* Others gpio number
|
|
*/
|
|
GpioPin pinPaEnable() { return getPinID(PinFunction::PA); }
|
|
|
|
// /**
|
|
// * @brief Get the gpio number for adc detection
|
|
// *
|
|
// * @return -1 non-existent
|
|
// * Others gpio number
|
|
// */
|
|
// GpioPin pinAdcDetect() { return getPin(AUXIN_DETECT); }
|
|
|
|
/**
|
|
* @brief Get the record-button id for adc-button
|
|
*
|
|
* @return -1 non-existent
|
|
* Others button id
|
|
*/
|
|
GpioPin pinInputRec() { return getPinID(PinFunction::KEY, 1); }
|
|
|
|
/**
|
|
* @brief Get the number for mode-button
|
|
*
|
|
* @return -1 non-existent
|
|
* Others number
|
|
*/
|
|
GpioPin pinInputMode() { return getPinID(PinFunction::KEY, 2); }
|
|
|
|
/**
|
|
* @brief Get number for set function
|
|
*
|
|
* @return -1 non-existent
|
|
* Others number
|
|
*/
|
|
GpioPin pinInputSet() { return getPinID(PinFunction::KEY, 4); }
|
|
|
|
/**
|
|
* @brief Get number for play function
|
|
*
|
|
* @return -1 non-existent
|
|
* Others number
|
|
*/
|
|
GpioPin pinInputPlay() { return getPinID(PinFunction::KEY, 3); }
|
|
|
|
/**
|
|
* @brief number for volume up function
|
|
*
|
|
* @return -1 non-existent
|
|
* Others number
|
|
*/
|
|
GpioPin pinVolumeUp() { return getPinID(PinFunction::KEY, 6); }
|
|
|
|
/**
|
|
* @brief Get number for volume down function
|
|
*
|
|
* @return -1 non-existent
|
|
* Others number
|
|
*/
|
|
GpioPin pinVolumeDown() { return getPinID(PinFunction::KEY, 5); }
|
|
|
|
/**
|
|
* @brief Get LED pin
|
|
*
|
|
* @return -1 non-existent
|
|
* Others gpio number
|
|
*/
|
|
GpioPin pinLed(int idx) { return getPinID(PinFunction::LED, idx); }
|
|
|
|
/// the same as setPAPower()
|
|
void setSpeakerActive(bool active) { setPAPower(active); }
|
|
|
|
/**
|
|
* @brief Returns true if the headphone was detected
|
|
*
|
|
* @return true
|
|
* @return false
|
|
*/
|
|
bool headphoneStatus() {
|
|
int headphoneGpioPin = pinHeadphoneDetect();
|
|
return headphoneGpioPin > 0 ? !digitalRead(headphoneGpioPin) : false;
|
|
}
|
|
|
|
/**
|
|
* @brief The oposite of setMute(): setActive(true) calls setMute(false)
|
|
*/
|
|
void setActive(bool active) { setMute(!active); }
|
|
|
|
/// add start/stop on inputMode
|
|
void addStartStopAction() {
|
|
// pin conflicts for pinInputMode() with the SD CS pin for AIThinker and
|
|
// buttons
|
|
int sd_cs = getSdCsPin();
|
|
int input_mode = pinInputMode();
|
|
if (input_mode != -1 && (input_mode != sd_cs || !cfg.sd_active)) {
|
|
LOGD("actionInputMode")
|
|
addAction(input_mode, actionStartStop);
|
|
}
|
|
}
|
|
|
|
/// add volume up and volume down action
|
|
void addVolumeActions() {
|
|
// pin conflicts with SD Lyrat SD CS GpioPin and buttons / Conflict on
|
|
// Audiokit V. 2957
|
|
int sd_cs = getSdCsPin();
|
|
int vol_up = pinVolumeUp();
|
|
int vol_down = pinVolumeDown();
|
|
if ((vol_up != -1 && vol_down != -1) &&
|
|
(!cfg.sd_active || (vol_down != sd_cs && vol_up != sd_cs))) {
|
|
LOGD("actionVolumeDown")
|
|
addAction(vol_down, actionVolumeDown);
|
|
LOGD("actionVolumeUp")
|
|
addAction(vol_up, actionVolumeUp);
|
|
} else {
|
|
LOGW("Volume Buttons ignored because of conflict: %d ", pinVolumeDown());
|
|
}
|
|
}
|
|
|
|
/// Adds headphone determination
|
|
void addHeadphoneDetectionAction() {
|
|
// pin conflicts with AIThinker A101: key6 and headphone detection
|
|
int head_phone = pinHeadphoneDetect();
|
|
if (head_phone != -1 && (getPinID(PinFunction::KEY, 6) != head_phone)) {
|
|
actions.add(head_phone, actionHeadphoneDetection,
|
|
AudioActions::ActiveChange, this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Setup the supported default actions (volume, start/stop, headphone
|
|
* detection)
|
|
*/
|
|
void addDefaultActions() {
|
|
TRACEI();
|
|
addHeadphoneDetectionAction();
|
|
addStartStopAction();
|
|
addVolumeActions();
|
|
}
|
|
|
|
/// Defines the increment value used by actionVolumeDown/actionVolumeUp
|
|
void setActionVolumeIncrementValue(float value) {
|
|
action_increment_value = value;
|
|
}
|
|
|
|
float actionVolumeIncrementValue() { return action_increment_value; }
|
|
|
|
bool isKeyPressed(int key) {
|
|
if (!board()) return false;
|
|
return board().isKeyPressed(key);
|
|
}
|
|
|
|
protected:
|
|
AudioActions actions;
|
|
bool headphoneIsConnected = false;
|
|
bool active = true;
|
|
float action_increment_value = 0.02;
|
|
|
|
int getSdCsPin() {
|
|
static GpioPin sd_cs = -2;
|
|
// execute only once
|
|
if (sd_cs != -2) return sd_cs;
|
|
|
|
auto sd_opt = getPins().getSPIPins(PinFunction::SD);
|
|
if (sd_opt) {
|
|
sd_cs = sd_opt.value().cs;
|
|
} else {
|
|
// no spi -> no sd
|
|
LOGI("No sd defined -> sd_active=false")
|
|
cfg.sd_active = false;
|
|
sd_cs = -1;
|
|
}
|
|
return sd_cs;
|
|
}
|
|
|
|
/// Determines the action logic (ActiveLow or ActiveTouch) for the pin
|
|
AudioActions::ActiveLogic getActionLogic(int pin) {
|
|
auto opt = board().getPins().getPin(pin);
|
|
PinLogic logic = PinLogic::Input;
|
|
if (opt) logic = opt.value().pin_logic;
|
|
switch (logic) {
|
|
case PinLogic::Input:
|
|
case PinLogic::InputActiveLow:
|
|
return AudioActions::ActiveLow;
|
|
case PinLogic::InputActiveHigh:
|
|
return AudioActions::ActiveHigh;
|
|
case PinLogic::InputActiveTouch:
|
|
return AudioActions::ActiveTouch;
|
|
default:
|
|
return AudioActions::ActiveLow;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace audio_tools
|