snapshot
This commit is contained in:
@@ -0,0 +1,421 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user