Files
klubhaus-doorbell/libraries/audio-tools/src/AudioTools/AudioLibs/AudioBoardStream.h
2026-02-12 21:00:02 -08:00

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