Audio exclusive mode (#19698)

This commit is contained in:
s-hadinger 2023-10-07 22:11:37 +02:00 committed by GitHub
parent 6bde92eef8
commit 8c6788c6bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 198 additions and 145 deletions

View File

@ -67,9 +67,27 @@ public:
}
~TasmotaI2S() {
this->stop();
this->stopTx();
}
/*********************************************************************************************\
* Global state for this class
*
* _exclusive: true if Tx and Rx are sharing a common GPIO, so only Tx or Rx can be active at the same time
* _is_dupleix: (virtual) true if both Rx and Tx are configured, i.e. the same instance handles both Rx/Tx
* Note: there are some restrictions on protocols, for ex. PDM is not supported in duplex,
* and will trigger 'exclusive'
*
* _t/rx_configured: true if Tx/Rx is configured, hence should be possible to start
* _r/tx_handle: handle for I2S driver, or nullptr if driver not stared
* _t/rx_running: true if Tx/Rx is actively running and sending/receiving data
*
\*********************************************************************************************/
// ------------------------------------------------------------------------------------------
// Setters for configuration parameters
//
// ------------------------------------------------------------------------------------------
// Settings
void setPinout(int32_t bclk, int32_t ws, int32_t dout, int32_t mclk, int32_t din,
bool mclk_inv = false, bool bclk_inv = false, bool ws_inv = false, bool apll = false);
@ -82,20 +100,16 @@ public:
}
void setRxFreq(uint16_t freq) { _rx_freq = freq; }
// ------------------------------------------------------------------------------------------
// Setters for configuration parameters
//
// TODO: not sure we still need them since all this should be set at initialiation
// ------------------------------------------------------------------------------------------
// Settings from superclass
virtual bool SetBitsPerSample(int bits) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetBitsPerSample: %i"), bits);
AddLog(LOG_LEVEL_DEBUG,"I2S: SetBitsPerSample: %i", bits);
if ( (bits != 16) && (bits != 8) ) { return false; }
this->bps = bits;
return true;
}
virtual bool SetChannels(int channels) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetChannels: %i"), channels);
AddLog(LOG_LEVEL_DEBUG,"I2S: SetChannels: %i", channels);
if ((channels < 1) || (channels > 2)) { return false; }
if (channels == (int)this->channels) { return true; }
this->channels = channels;
@ -103,17 +117,14 @@ public:
}
virtual bool SetRate(int hz) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetRate: %i was %i on=%i"), hz, this->hertz, _i2s_on);
AddLog(LOG_LEVEL_DEBUG,"I2S: SetRate: %i was %i tx_running=%i", hz, this->hertz, _tx_running);
if (hz == (int) this->hertz) { return true; }
this->hertz = hz;
if (_i2s_on) {
int result = updateClockConfig();
}
return true;
return updateClockConfig();
}
virtual bool SetGain(float f) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: SetGain: %_f"), &f);
AddLog(LOG_LEVEL_DEBUG, "I2S: SetGain: %_f", &f);
return AudioOutput::SetGain(f);
}
@ -163,11 +174,14 @@ public:
inline void setChannels(int chan) { SetChannels(chan); }
inline void setGain(float f) { SetGain(f); }
bool begin(void);
bool stop(void);
// Tx
virtual bool begin(void) { return beginTx(); }; // the name `begin()`is inherited from superclass, prefer `beginTx()` which is more explicit
virtual bool stop(void) { return stopTx(); }; // the name `stop()`is inherited from superclass, prefer `stopTx()` which is more explicit
bool beginTx(void);
bool stopTx(void);
bool ConsumeSample(int16_t sample[2]);
bool startI2SChannel(bool tx, bool rx);
int updateClockConfig(void);
bool updateClockConfig(void);
int32_t readMic(uint8_t *buffer, uint32_t size, bool dc_block, bool apply_gain, bool lowpass, uint32_t *peak_ptr);
@ -183,8 +197,8 @@ public:
// ------------------------------------------------------------------------------------------
// Microphone related methods
uint32_t micInit(void);
void micDeinit(void);
uint32_t startRx(void);
bool stopRx(void);
protected:
int16_t dcFilter(int16_t pcm_in);
@ -192,7 +206,6 @@ protected:
protected:
bool _i2s_on = false; // is I2S audio active
bool _exclusive = false; // in exclusive mode, stopping this instance needs to uninstall driver, and reinstall for next use
i2s_port_t _i2s_port = I2S_NUM_AUTO; // I2S port, I2S_NUM_0/I2S_NUM_1/I2S_NUM_AUTO
@ -266,32 +279,63 @@ void TasmotaI2S::setPinout(int32_t bclk, int32_t ws, int32_t dout, int32_t mclk,
_tx_configured, _rx_configured);
}
bool TasmotaI2S::begin() {
AddLog(LOG_LEVEL_DEBUG, "I2S: begin _tx_running:%i _i2s_on:%i", _tx_running, _i2s_on);
bool TasmotaI2S::beginTx(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: Calling beginTX (tx_handle:%i already_running:%i)", _tx_handle, _tx_running);
if (_tx_running) { return true; }
// if (!_i2s_on) {
// if ((!_rx_configured || !_tx_configured) && _rx_configured) { // not duplex -- TODO ?
// this->startI2SChannel();
// }
// }
int result = i2s_channel_enable(_tx_handle);
if (result != 0){
AddLog(LOG_LEVEL_INFO, "I2S: Could not enable i2s_channel: %i", result);
if (!_tx_handle){
if (!startI2SChannel(true, false)) {
return false;
}
}
esp_err_t err = i2s_channel_enable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: Tx i2s_channel_enable err=0x%04X", err);
if (err != ERR_OK){
return false;
}
_tx_running = true;
AddLog(LOG_LEVEL_DEBUG, "I2S: begin _tx_running succeeded");
return true;
}
bool TasmotaI2S::stop() {
i2s_channel_disable(_tx_handle);
if ((!_rx_configured || !_tx_configured) && _rx_configured) { // not duplex -- TODO ?
i2s_del_channel(_tx_handle);
_i2s_on = false;
bool TasmotaI2S::stopTx() {
AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopTx() tx_running:%i tx_handle:%p", _tx_running, _tx_handle);
if (!_tx_configured) { return false; } // invalid action
if (!_tx_handle) { return true; } // nothing to do
if (_tx_running) {
esp_err_t err = i2s_channel_disable(_tx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_channel_disable err=0x%04X", err);
_tx_running = false;
}
if (_exclusive) { // exclusive mode, deregister channel
if (_tx_handle) {
esp_err_t err = i2s_del_channel(_tx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_del_channel err=0x%04X", err);
_tx_handle = nullptr;
}
AddLog(LOG_LEVEL_INFO, "I2S: stop: I2S channel disabled");
}
_tx_running = false;
return true;
}
bool TasmotaI2S::stopRx(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopRx() rx_running:%i rx_handle:%p", _rx_running, _rx_handle);
if (!_rx_configured) { return false; } // nothing configured
if (!_rx_handle) { return true; } // bothing to do
if (_rx_running) {
esp_err_t err = i2s_channel_disable(_rx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: stopRx i2s_channel_disable err=0x%04X", err);
_rx_running = false;
}
if (_exclusive) {
if (_rx_handle) {
esp_err_t err = i2s_del_channel(_rx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: stopRx i2s_del_channel err=0x%04X", err);
_rx_handle = nullptr;
}
}
return true;
}
@ -346,28 +390,42 @@ int32_t TasmotaI2S::consumeSamples(int16_t *samples, size_t count) {
// Initialize I2S channel
// return `true` if successful, `false` otherwise
bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
if (!tx) { _tx_configured = false; }
if (!rx) { _rx_configured = false; }
if (!_tx_configured && !_rx_configured) { return false; } // nothing configured
AddLog(LOG_LEVEL_DEBUG, "I2S: startI2SChannel: tx:%i rx:%i tx_configured:%i rx_configured:%i", tx, rx, _tx_configured, _rx_configured);
if (tx && !_tx_configured) { return false; }
if (rx && !_rx_configured) { return false; }
if (rx && tx && _exclusive) { return false; } // cannot initialize full-duplex with exclusive
if (!tx && !rx) { return false; } // nothing to do
// check if already configured
if (tx && rx && _tx_handle && _rx_handle) { return true; }
if (tx && _tx_handle) { return true; }
if (rx && _rx_handle) { return true; }
if (_exclusive) {
// in exclusive mode, we may need to remove exisiting driver
if (tx && _rx_handle) {
AddLog(LOG_LEVEL_DEBUG, "I2S: (exclusive mode) forcing stopRx");
stopRx();
}
if (rx && _tx_handle) {
AddLog(LOG_LEVEL_DEBUG, "I2S: (exclusive mode) forcing stopTx");
stopTx();
}
}
esp_err_t err = ESP_OK;
gpio_num_t _DIN = I2S_GPIO_UNUSED; // no input pin by default
gpio_num_t _DIN = rx ? (gpio_num_t)_gpio_din : I2S_GPIO_UNUSED; // no input pin if no Rx
if (_tx_configured) {
if (tx) {
// default dma_desc_num = 6 (DMA buffers), dma_frame_num = 240 (frames per buffer)
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
AddLog(LOG_LEVEL_DEBUG, "I2S: tx_chan_cfg id:%i role:%i dma_desc_num:%i dma_frame_num:%i auto_clear:%i",
tx_chan_cfg.id, tx_chan_cfg.role, tx_chan_cfg.dma_desc_num, tx_chan_cfg.dma_frame_num, tx_chan_cfg.auto_clear);
if (_tx_configured && _rx_configured) {
_DIN = (gpio_num_t)_gpio_din;
err = i2s_new_channel(&tx_chan_cfg, &_tx_handle, &audio_i2s.out->_rx_handle);
} else{
err = i2s_new_channel(&tx_chan_cfg, &_tx_handle, NULL);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_new_channel err:%i", err);
err = i2s_new_channel(&tx_chan_cfg, &_tx_handle, rx ? &_rx_handle : NULL); // configure Rx only in duplex non-exclusive
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_new_channel Tx err:0x%04X", err);
// by default we configure for MSB 2 slots `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`
i2s_std_config_t tx_std_cfg = {
@ -395,36 +453,27 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
}
if (_tx_slot_mask != I2S_SLOT_NOCHANGE) { tx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_tx_slot_mask; }
// AddLog(LOG_LEVEL_INFO, ">>>: I2S tx_chan_cfg=%*_H", sizeof(tx_chan_cfg), &tx_chan_cfg);
// AddLog(LOG_LEVEL_INFO, ">>>: I2S tx_std_cfg=%*_H", sizeof(tx_std_cfg), &tx_std_cfg);
err = i2s_channel_init_std_mode(_tx_handle, &tx_std_cfg);
AddLog(LOG_LEVEL_DEBUG, "I2S: TX channel bits:%i channels:%i hertz:%i initialized err=0x%04X", bps, channels, hertz, err);
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_channel_init_std_mode TX channel bits:%i channels:%i hertz:%i err=0x%04X", bps, channels, hertz, err);
if (err != ERR_OK) {
_i2s_on = false;
_tx_handle = nullptr;
return false;
}
_i2s_on = true;
if (_rx_configured) { // full duplex mode
err = i2s_channel_init_std_mode(audio_i2s.out->_rx_handle, &tx_std_cfg);
if (rx) { // full duplex mode
err = i2s_channel_init_std_mode(_rx_handle, &tx_std_cfg);
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_channel_init_std_mode err:%i", err);
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel added in full duplex mode");
}
} // if (tx)
// configure Rx Microphone
if (_rx_configured && !_tx_configured) { // if Microphone and not duplex
if (rx) {
gpio_num_t clk_gpio;
i2s_slot_mode_t slot_mode = (_rx_channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init rx_channels:%i rx_running:%i rx_handle:%p", slot_mode, _rx_running, _rx_handle);
if (_tx_configured && _rx_running) { // duplex mode, mic was already initialized
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init exit Rx already enabled");
return 0; // no need to en- or disable when in full duplex mode and already initialized
}
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
// change to 3 buffers of 512 samples
rx_chan_cfg.dma_desc_num = 3;
@ -434,7 +483,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
rx_chan_cfg.id, rx_chan_cfg.role, rx_chan_cfg.dma_desc_num, rx_chan_cfg.dma_frame_num, rx_chan_cfg.auto_clear);
err = i2s_new_channel(&rx_chan_cfg, NULL, &_rx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init i2s_new_channel err=%i", err);
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_new_channel Rx err:%i", err);
switch (_rx_mode){
case I2S_MODE_PDM:
{
@ -462,9 +511,12 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
// rx_pdm_cfg.gpio_cfg.clk, rx_pdm_cfg.gpio_cfg.din, rx_pdm_cfg.gpio_cfg.invert_flags.clk_inv);
err = i2s_channel_init_pdm_rx_mode(_rx_handle, &rx_pdm_cfg);
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: 0x%04X"),
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel in PDM mode, CLK: %i, DIN: %i, 16 bit width, %i channel(s), err code: 0x%04X",
_gpio_ws, _gpio_din, _rx_channels, err);
_i2s_on = true;
if (err) {
_rx_handle = nullptr;
return false;
}
}
break;
case I2S_MODE_STD:
@ -487,10 +539,13 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
};
if (_rx_slot_mask != I2S_SLOT_NOCHANGE) { rx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_rx_slot_mask; }
err = i2s_channel_init_std_mode(audio_i2s.rx_handle, &rx_std_cfg);
err = i2s_channel_init_std_mode(_rx_handle, &rx_std_cfg);
AddLog(LOG_LEVEL_DEBUG, "I2S: RX i2s_channel_init_std_mode err:%i", err);
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel in standard mode with 16 bit width on %i channel(s) initialized", slot_mode);
_i2s_on = true;
if (err) {
_rx_handle = nullptr;
return false;
}
}
break;
default:
@ -500,27 +555,40 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
return true;
}
int TasmotaI2S::updateClockConfig(void) {
i2s_channel_disable(_tx_handle);
bool TasmotaI2S::updateClockConfig(void) {
if (!_tx_handle) { return true; }
if (_tx_running) {
esp_err_t err = i2s_channel_disable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_disable err=0x%04X", err);
}
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(hertz);
#ifdef SOC_I2S_SUPPORTS_APLL
if (_apll) {
clk_cfg.clk_src = I2S_CLK_SRC_APLL;
}
#endif
int result = i2s_channel_reconfig_std_clock(_tx_handle, &clk_cfg );
if (_tx_running) { i2s_channel_enable(_tx_handle); }
esp_err_t result = i2s_channel_reconfig_std_clock(_tx_handle, &clk_cfg );
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_reconfig_std_clock err=0x%04X", result);
if (_tx_running) {
esp_err_t err = i2s_channel_enable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_enable err=0x%04X", err);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config");
return result;
return result == ESP_OK;
}
/*********************************************************************************************\
* microphone related functions
\*********************************************************************************************/
uint32_t TasmotaI2S::micInit(void) {
uint32_t TasmotaI2S::startRx(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: startRx called");
if (!_rx_configured) { return 0; } // nothing configured
if (!_rx_handle){
startI2SChannel(false, true);
}
esp_err_t err = ESP_OK;
gpio_num_t clk_gpio;
@ -529,29 +597,12 @@ uint32_t TasmotaI2S::micInit(void) {
if (!_rx_running) {
err = i2s_channel_enable(_rx_handle);
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: RX channel enable err:0x%04X"),err);
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel enable err:0x%04X", err);
_rx_running = true;
}
return err;
}
void TasmotaI2S::micDeinit(void) {
esp_err_t err = ESP_OK;
gpio_num_t clk_gpio;
AddLog(LOG_LEVEL_DEBUG, "I2S: mic deinit rx_running:%i rx_handle:%p", _rx_running, _rx_handle);
if (!_rx_handle) { return; }
if (!_tx_configured || !_rx_configured) { // if duplex mode, there is no mic channel - TODO check this
int err = i2s_channel_disable(_rx_handle);
i2s_del_channel(_rx_handle);
_rx_handle = nullptr;
_rx_running = false;
AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel disable: %i", err);
}
}
// Read data into buffer of uint16_t[]
//
// Returns:
@ -563,7 +614,7 @@ int32_t TasmotaI2S::readMic(uint8_t *buffer, uint32_t size, bool dc_block, bool
uint32_t peak = 0;
if (peak_ptr) { *peak_ptr = peak; }
if (!audio_i2s.in->getRxRunning()) { return -1; }
if (!getRxRunning()) { return -1; }
size_t btr = 0;
esp_err_t err = i2s_channel_read(_rx_handle, buffer, size, &btr, 0 /* do not wait */);

View File

@ -366,7 +366,7 @@ exit:
audio_i2s.client.stop();
}
audio_i2s.out->micDeinit();
audio_i2s.out->stopRx();
audio_i2s.mic_stop = 0;
audio_i2s.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
@ -390,7 +390,7 @@ int32_t I2sRecordShine(char *path) {
if (audio_i2s.use_stream) {
stack = 8000;
}
audio_i2s.out->micInit();
audio_i2s.out->startRx();
err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s.mic_task_handle, 1);
@ -407,6 +407,7 @@ enum {
I2S_ERR_INPUT_NOT_CONFIGURED,
I2S_ERR_DECODER_IN_USE,
I2S_ERR_FILE_NOT_FOUND,
I2S_ERR_TX_FAILED,
};
// signal to an external Berry driver that we turn audio power on or off
@ -489,18 +490,8 @@ void I2sInit(void) {
I2SSettingsLoad(AUDIO_CONFIG_FILENAME, false); // load configuration (no-erase)
if (!audio_i2s.Settings) { return; } // fatal error, could not allocate memory for configuration
// detect if we need full-duplex on port 0
bool duplex = false;
if ((gpio_din_0 >= 0) && (gpio_dout_0 >= 0)) {
// conditions are potentially favorable for duplex
if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: PDM forbids full duplex mode, ignoring 'I2S DIN 1'"));
gpio_din_0 = -1; // hence deconfigure DIN_0 which can't be used
} else {
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: will try to use full duplex mode"));
duplex = true;
}
}
bool duplex = false; // the same ports are used for input and output
bool exclusive = false; // signals that in/out have a shared GPIO and need to un/install driver before use
// AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"),
// Pin(GPIO_I2S_BCLK, 0) , Pin(GPIO_I2S_WS, 0), Pin(GPIO_I2S_DOUT, 0), Pin(GPIO_I2S_MCLK, 0), Pin(GPIO_I2S_DIN, 0));
@ -509,7 +500,6 @@ void I2sInit(void) {
audio_i2s.Settings->sys.tx = false;
audio_i2s.Settings->sys.rx = false;
audio_i2s.Settings->sys.exclusive = false;
bool exclusive = false; // signals that in/out have a shared GPIO and need to un/install driver before use
for (uint32_t port = 0; port < SOC_I2S_NUM; port++) {
int32_t bclk = Pin(GPIO_I2S_BCLK, port);
@ -525,8 +515,15 @@ void I2sInit(void) {
// if neither input, nor output, nor DAC/ADC skip (WS could is only needed for DAC but supports only port 0)
if (din < 0 && dout < 0 && (ws < 0 || port !=0)) { continue; }
duplex = (din >= 0) && (dout >= 0);
if (duplex) {
if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){
exclusive = true;
}
AddLog(LOG_LEVEL_DEBUG, "I2S: enabling duplex mode, exclusive;%i", exclusive);
}
const char *err_msg = nullptr; // to save code, we indicate an error with a message configured
bool duplex = (din >= 0) && (dout >= 0);
bool dac_mode = false;
if (din >= 0 || dout >= 0) {
// we have regular I2S configuration
@ -568,12 +565,6 @@ void I2sInit(void) {
else if (port != 0 && dout >= 0 && audio_i2s.Settings->tx.mode == I2S_MODE_PDM) {
err_msg = "PDM Tx is not supported";
}
// 8. check that we don't try full-duplex with PDM in either direction
else if (duplex && (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM )) {
err_msg = "PDM forbids full duplex mode";
duplex = false;
din = -1; // deconfigure DIN_0 which can't be used
}
} else {
dac_mode = true;
// no DIN/DOUT, try DAC mode
@ -592,8 +583,13 @@ void I2sInit(void) {
tx = (dout >= 0) || dac_mode;
rx = (din >= 0);
if (duplex) {
AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: will try to use full duplex mode"));
if (tx && audio_i2s.out) {
AddLog(LOG_LEVEL_DEBUG, "I2S: Warning: Tx already configured, skipping superfluous Tx configuration");
tx = false;
}
if (rx && audio_i2s.in) {
AddLog(LOG_LEVEL_DEBUG, "I2S: Warning: Rx already configured, skipping superfluous Rx configuration");
rx = false;
}
TasmotaI2S * i2s = new TasmotaI2S;
@ -611,35 +607,44 @@ void I2sInit(void) {
i2s->setRxMode(audio_i2s.Settings->rx.mode);
i2s->setRxFreq(audio_i2s.Settings->rx.sample_rate);
i2s->setRxChannels(audio_i2s.Settings->rx.channels);
i2s->setRate(audio_i2s.Settings->rx.sample_rate);
i2s->setRxGain(audio_i2s.Settings->rx.gain);
}
if (i2s->startI2SChannel(tx, rx)) {
// succesful, register handlers
if (tx) {
audio_i2s.out = i2s;
audio_i2s.Settings->sys.tx = true;
}
if (rx) {
audio_i2s.in = i2s;
audio_i2s.Settings->sys.rx = true;
}
if (duplex) {
audio_i2s.Settings->sys.duplex = true;
}
bool init_tx_ok = false;
bool init_rx_ok = false;
if (tx && rx && exclusive) {
i2s->setExclusive(true);
audio_i2s.Settings->sys.exclusive = exclusive;
// in exclusive mode, we need to intialize in sequence Tx and Rx
init_tx_ok = i2s->startI2SChannel(true, false);
init_rx_ok = i2s->startI2SChannel(false, true);
} else if (tx && rx) {
init_tx_ok = init_rx_ok = i2s->startI2SChannel(true, true);
} else {
if (tx) { init_tx_ok = i2s->startI2SChannel(true, false); }
if (rx) { init_rx_ok = i2s->startI2SChannel(false, true); }
}
if (init_tx_ok) { audio_i2s.out = i2s; }
if (init_rx_ok) { audio_i2s.in = i2s; }
audio_i2s.Settings->sys.tx = init_tx_ok;
audio_i2s.Settings->sys.rx = init_rx_ok;
if (init_tx_ok && init_rx_ok) { audio_i2s.Settings->sys.duplex = true; }
// if intput and output are configured, don't proceed with other IS2 ports
if (audio_i2s.out && audio_i2s.in) {
break;
}
}
// do we have exclusive mode?
audio_i2s.Settings->sys.exclusive = exclusive;
if (audio_i2s.out) { audio_i2s.out->setExclusive(exclusive); }
if (audio_i2s.in) { audio_i2s.in->setExclusive(exclusive); }
if(audio_i2s.out != nullptr){
audio_i2s.out->SetGain(((float)audio_i2s.Settings->tx.gain / 100.0) * 4.0);
audio_i2s.out->begin();
audio_i2s.out->stop();
audio_i2s.out->beginTx(); // TODO - useful?
audio_i2s.out->stopTx();
}
audio_i2s.mp3ram = nullptr;
@ -647,10 +652,6 @@ void I2sInit(void) {
// if (UsePSRAM()) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will allocate buffer for mp3 encoder"));
audio_i2s.mp3ram = special_malloc(preallocateCodecSize);
// }
// else{
// audio_i2s.Settings->sys.mp3_preallocate = 0; // no PS-RAM -> no MP3 encoding
// }
}
AddLog(LOG_LEVEL_DEBUG, "I2S: I2sInit done");
}
@ -669,9 +670,7 @@ int32_t I2SPrepareTx(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: I2SPrepareTx out=%p", audio_i2s.out);
if (!audio_i2s.out) { return I2S_ERR_OUTPUT_NOT_CONFIGURED; }
if (audio_i2s.Settings->sys.exclusive) {
// TODO - deconfigure input driver
}
if (!audio_i2s.out->beginTx()) { return I2S_ERR_TX_FAILED; }
return I2S_OK;
}
@ -804,7 +803,7 @@ void Say(char *text) {
sam->Say(audio_i2s.out, text);
delete sam;
audio_i2s.out->stop();
audio_i2s.out->stopTx();
I2SAudioPower(false);
}
@ -821,7 +820,7 @@ void CmndI2SMic(void) {
esp_err_t err = ESP_OK;
if (audio_i2s.decoder || audio_i2s.mp3) return;
audio_i2s.in->micInit();
audio_i2s.in->startRx();
if (audio_i2s.in->getRxRunning()) {
uint8_t buf[128];
@ -873,6 +872,9 @@ void CmndI2SPlay(void) {
case I2S_ERR_FILE_NOT_FOUND:
ResponseCmndChar("File not found");
break;
case I2S_ERR_TX_FAILED:
ResponseCmndChar("Unable to open sound output");
break;
default:
ResponseCmndChar("Unknown error");
break;

View File

@ -323,13 +323,13 @@ extern "C" {
// AudioInputI2S.begin() -> bool
int be_audio_input_i2s_begin(bvm *vm, TasmotaI2S* in) {
if (I2SPrepareRx()) { be_raisef(vm, "internal_error", "I2SPrepareRx() failed"); be_return_nil(vm); }
in->micInit();
in->startRx();
return in->getRxRunning();
}
// AudioInputI2S.stop() -> bool
int be_audio_input_i2s_stop(TasmotaI2S* in) {
in->micDeinit();
in->stopRx();
return true;
}