mirror of
https://github.com/arendst/Tasmota.git
synced 2025-04-24 23:07:17 +00:00
Audio exclusive mode (#19698)
This commit is contained in:
parent
6bde92eef8
commit
8c6788c6bc
@ -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 */);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user