diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino index 414a014ce..c9a102517 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino @@ -53,7 +53,7 @@ enum : int8_t { }; #define I2S_SLOTS 2 -#define AUDIO_SETTINGS_VERSION 1 +#define AUDIO_SETTINGS_VERSION 2 typedef struct{ struct{ @@ -75,7 +75,7 @@ typedef struct{ uint32_t sample_rate = 16000; // B00-03 uint8_t gain = 10; // B04 - was `volume` uint8_t mode = I2S_MODE_STD; // B05 - I2S mode standard, PDM, TDM, DAC - uint8_t slot_mask = I2S_SLOT_NOCHANGE;// B06 - slot mask + uint8_t slot_mask = BIT(0) | BIT(1); // B06 - slot mask - both slots aka stereo by default uint8_t slot_config = I2S_SLOT_MSB;// B07 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2 uint8_t channels = 2; // B08 - mono/stereo - 1 is added for both bool apll = 1; // B09 - will be ignored on unsupported SOC's @@ -83,11 +83,11 @@ typedef struct{ uint8_t spare[5]; // B0B-0F } tx; struct { - uint32_t sample_rate = 16000; // B00-03 + uint32_t sample_rate = 32000; // B00-03 - 32000 is compatible with MP3 encoding uint16_t gain = 30 * 16; // B04-05 - in Q12.4 uint8_t mode = I2S_MODE_PDM; // B06 - I2S mode standard, PDM, TDM, DAC - uint8_t slot_mask = I2S_SLOT_NOCHANGE;// B07 - slot mask - uint8_t slot_config = I2S_SLOT_MSB;// B08 - slot configuration MSB = 0, PCM = 1, PHILIPS = 2 + uint8_t slot_mask = BIT(0); // B07 - slot mask = left/right/both depended on mode, so BIT(0) maybe left or right + uint8_t slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT; // B08 - auto equals data_bit_width - can differ from bits per sample e.g. INMP441 uint8_t channels = 1; // B09 - mono/stereo - 1 is added for both // filters uint16_t dc_filter_alpha = 0b0111111011111111; // B0A-B0B - DC_block filter = (1-2^(-7)) Q32:1.31, or `0` if disabled @@ -99,11 +99,19 @@ typedef struct{ // alpha = (b) 0.100010100110111 = 0x4537 uint16_t lowpass_alpha = 0b0100010100110111; // B0C-B0D - lowpass filter = 3000Hz for 16000Hz sample rate bool apll = 1; // B0E - will be ignored on unsupported SOC's - uint8_t spare[1]; // B0F + uint8_t ws_width = 32; // B0F - WS signal width - can differ from bits per sample - default 32 bits for INMP441 + bool ws_pol = false; // B10 - WS signal polarity + bool bit_shift = true; // B11 - enable bit shift in Philips mode + bool left_align = true; // B12 - left alignment + bool big_endian = false; // B13 - big endian + bool bit_order_lsb = false; // B14 - lsb first + uint16_t dma_frame_num = 512; // B015/16 - DMA buffer size in samples, 512 should be okay up to ~32000 bps + uint8_t dma_desc_num = 3; // B17 - number of DMA buffers, maybe increased with smaller buffers + uint8_t spare[3]; // B018-20 - padding } rx; } tI2SSettings; -static_assert(sizeof(tI2SSettings) == 48, "tI2SSettings Size is not correct"); +static_assert(sizeof(tI2SSettings) == 60, "tI2SSettings Size is not correct"); typedef union { uint8_t data; @@ -120,7 +128,7 @@ class TasmotaI2S; struct AUDIO_I2S_t { tI2SSettings *Settings; - i2s_chan_handle_t rx_handle = nullptr; + // i2s_chan_handle_t rx_handle = nullptr; TasmotaI2S *out = nullptr; // instance used for I2S output, or `nullptr` if none TasmotaI2S *in = nullptr; // instance used for I2S input, or `nullptr` if none (it can be the same as `out` in case of full duplex) } audio_i2s; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino index 2207be6e3..59d0a959b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino @@ -103,11 +103,10 @@ public: 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); - void setSlotConfig(i2s_port_t i2s_port, uint8_t tx_slot_config, uint8_t rx_slot_config, + void setSlotConfig(i2s_port_t i2s_port, uint8_t tx_slot_config, uint8_t tx_slot_mask, uint8_t rx_slot_mask) { _i2s_port = i2s_port; _tx_slot_config = tx_slot_config; - _rx_slot_config = rx_slot_config; } void setRxFreq(uint16_t freq) { _rx_freq = freq; } @@ -237,7 +236,7 @@ protected: // TX bool _tx_configured = false; // true = configured, false = not configured uint8_t _tx_mode = I2S_MODE_STD; // I2S_MODE_STD / I2S_MODE_PDM / I2S_MODE_TDM / I2S_MODE_DAC - uint8_t _tx_slot_mask = I2S_SLOT_NOCHANGE; + uint8_t _tx_slot_mask = BIT(0)|BIT(1);// default stereo bool _tx_running = false; // true = enabled, false = disabled uint8_t _tx_channels = 2; // number of channels, 1 = mono, 2 = stereo -- `channels` i2s_chan_handle_t _tx_handle = nullptr; // I2S channel handle, automatically computed @@ -247,12 +246,12 @@ protected: // RX bool _rx_configured = false; // true = configured, false = not configured uint8_t _rx_mode = I2S_MODE_STD; // I2S_MODE_STD / I2S_MODE_PDM / I2S_MODE_TDM / I2S_MODE_DAC - uint8_t _rx_slot_mask = I2S_SLOT_NOCHANGE; + uint8_t _rx_slot_mask = BIT(0); // default mono: for PDM right, for standard left bool _rx_running = false; // true = enabled, false = disabled uint8_t _rx_channels = 2; // number of channels, 1 = mono, 2 = stereo i2s_chan_handle_t _rx_handle = nullptr; // I2S channel handle, automatically computed - uint8_t _rx_slot_config = I2S_SLOT_MSB;// I2S slot configuration - uint16_t _rx_freq = 16000; // I2S Rx sampling frequency in Hz + // uint8_t _rx_slot_config; // I2S slot configuration + uint16_t _rx_freq = 32000; // I2S Rx sampling frequency in Hz - default 32 kHz to prevent problems with Shine Encoder uint8_t _rx_bps = 16; // bits per sample, 16 or 8 uint16_t _rx_gain = 0x10; // Microphone gain in Q12.4 format (0x10 = 1.0) @@ -539,7 +538,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) { } else if (_tx_slot_config == I2S_SLOT_PHILIPS) { // PHILIPS tx_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(tx_data_bit_width, tx_slot_mode); } - if (_tx_slot_mask != I2S_SLOT_NOCHANGE) { tx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_tx_slot_mask; } + tx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_tx_slot_mask; #if SOC_I2S_SUPPORTS_APLL if (_apll) { tx_std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL; } #endif // SOC_I2S_SUPPORTS_APLL @@ -590,9 +589,9 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) { i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(_i2s_port, I2S_ROLE_MASTER); i2s_data_bit_width_t rx_data_bit_width = (_rx_bps == 8) ? I2S_DATA_BIT_WIDTH_8BIT : I2S_DATA_BIT_WIDTH_16BIT; - // change to 3 buffers of 512 samples - rx_chan_cfg.dma_desc_num = 3; - rx_chan_cfg.dma_frame_num = 512; + // defaults to 3 buffers of 512 samples + rx_chan_cfg.dma_desc_num = audio_i2s.Settings->rx.dma_desc_num; + rx_chan_cfg.dma_frame_num = audio_i2s.Settings->rx.dma_frame_num; AddLog(LOG_LEVEL_DEBUG, "I2S: rx_chan_cfg id:%i role:%i dma_desc_num:%i dma_frame_num:%i auto_clear:%i", 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); @@ -615,7 +614,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) { }, }, }; - if (_rx_slot_mask != I2S_SLOT_NOCHANGE) { rx_pdm_cfg.slot_cfg.slot_mask = (i2s_pdm_slot_mask_t)_rx_slot_mask; } + rx_pdm_cfg.slot_cfg.slot_mask = (i2s_pdm_slot_mask_t)_rx_slot_mask; // AddLog(LOG_LEVEL_DEBUG, "I2S: rx_pdm_cfg clk_cfg sample_rate_hz:%i clk_src:%i mclk_multiple:%i dn_sample_mode:%i", // rx_pdm_cfg.clk_cfg.sample_rate_hz, rx_pdm_cfg.clk_cfg.clk_src, rx_pdm_cfg.clk_cfg.mclk_multiple, rx_pdm_cfg.clk_cfg.dn_sample_mode); @@ -637,10 +636,26 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) { break; #endif // SOC_I2S_SUPPORTS_PDM_RX case I2S_MODE_STD: - { + { + i2s_std_slot_config_t _slot_cfg = { + .data_bit_width = (i2s_data_bit_width_t)bps, + .slot_bit_width = (i2s_slot_bit_width_t)audio_i2s.Settings->rx.slot_bit_width, + .slot_mode = (i2s_slot_mode_t)channels, + .slot_mask = (i2s_std_slot_mask_t)audio_i2s.Settings->rx.slot_mask, + .ws_width = audio_i2s.Settings->rx.ws_width, + .ws_pol = audio_i2s.Settings->rx.ws_pol, + .bit_shift = audio_i2s.Settings->rx.bit_shift, +#if SOC_I2S_HW_VERSION_1 // For esp32/esp32-s2 + .msb_right = audio_i2s.Settings->rx.bit_order_lsb, // Placeholder for now!! +#else + .left_align = audio_i2s.Settings->rx.left_align, + .big_endian = audio_i2s.Settings->rx.big_endian, + .bit_order_lsb = audio_i2s.Settings->rx.bit_order_lsb, +#endif + }; i2s_std_config_t rx_std_cfg = { .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_rx_freq), - .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(rx_data_bit_width, rx_slot_mode), + .slot_cfg = _slot_cfg, .gpio_cfg = { .mclk = (gpio_num_t)Pin(GPIO_I2S_MCLK), .bclk = (gpio_num_t)Pin(GPIO_I2S_BCLK), @@ -654,11 +669,10 @@ 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(_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", rx_slot_mode); + AddLog(LOG_LEVEL_DEBUG, "I2S: RX i2s_channel_init_std_mode with err:%i", err); + AddLog(LOG_LEVEL_DEBUG, "I2S: RX channel in standard mode with %u bit width on %i channel(s) initialized", bps, rx_slot_mode); if (err) { _rx_handle = nullptr; return false; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino index e772add39..32ddd4b06 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino @@ -227,11 +227,19 @@ void CmndI2SConfig(void) { // cfg->rx.gain = rx.getUInt(PSTR("Gain"), cfg->rx.gain); cfg->rx.mode = rx.getUInt(PSTR("Mode"), cfg->rx.mode); cfg->rx.slot_mask = rx.getUInt(PSTR("SlotMask"), cfg->rx.slot_mask); - cfg->rx.slot_config = rx.getUInt(PSTR("SlotConfig"), cfg->rx.slot_config); + cfg->rx.slot_bit_width = rx.getUInt(PSTR("SlotWidth"), cfg->rx.slot_bit_width); cfg->rx.channels = rx.getUInt(PSTR("Channels"), cfg->rx.channels); cfg->rx.dc_filter_alpha = rx.getUInt(PSTR("DCFilterAlpha"), cfg->rx.dc_filter_alpha); cfg->rx.lowpass_alpha = rx.getUInt(PSTR("LowpassAlpha"), cfg->rx.lowpass_alpha); cfg->rx.apll = rx.getUInt(PSTR("APLL"), cfg->rx.apll); + cfg->rx.ws_width = rx.getUInt(PSTR("WsWidth"), cfg->rx.ws_width); + cfg->rx.ws_pol = rx.getUInt(PSTR("WsPol"), cfg->rx.ws_pol); + cfg->rx.bit_shift = rx.getUInt(PSTR("BitShift"), cfg->rx.bit_shift); + cfg->rx.left_align = rx.getUInt(PSTR("LeftAlign"), cfg->rx.left_align); + cfg->rx.big_endian = rx.getUInt(PSTR("BigEndian"), cfg->rx.big_endian); + cfg->rx.bit_order_lsb = rx.getUInt(PSTR("LsbOrder"), cfg->rx.bit_order_lsb); + cfg->rx.dma_frame_num = rx.getUInt(PSTR("DMAFrame"), cfg->rx.dma_frame_num); + cfg->rx.dma_desc_num = rx.getUInt(PSTR("DMADesc"), cfg->rx.dma_desc_num); } I2SSettingsSave(AUDIO_CONFIG_FILENAME); } @@ -268,11 +276,19 @@ void CmndI2SConfig(void) { // "\"Gain\":%d," "\"Mode\":%d," "\"SlotMask\":%d," - "\"SlotConfig\":%d," + "\"SlotWidth\":%d," "\"Channels\":%d," "\"DCFilterAlpha\":%d," "\"LowpassAlpha\":%d," - "\"APLL\":%d" + "\"APLL\":%d," + "\"WsWidth\":%d," + "\"WsPol\":%d," + "\"BitShift\":%d," + "\"LeftAlign\":%d," + "\"BigEndian\":%d," + "\"LsbOrder\":%d," + "\"DMAFrame\":%d," + "\"DMADesc\":%d" "}}}", cfg->sys.version, cfg->sys.duplex, @@ -300,11 +316,19 @@ void CmndI2SConfig(void) { // cfg->rx.gain, cfg->rx.mode, cfg->rx.slot_mask, - cfg->rx.slot_config, + cfg->rx.slot_bit_width, cfg->rx.channels, cfg->rx.dc_filter_alpha, cfg->rx.lowpass_alpha, - cfg->rx.apll + cfg->rx.apll, + cfg->rx.ws_width, + cfg->rx.ws_pol, + cfg->rx.bit_shift, + cfg->rx.left_align, + cfg->rx.big_endian, + cfg->rx.bit_order_lsb, + cfg->rx.dma_frame_num, + cfg->rx.dma_desc_num ); } /*********************************************************************************************\ @@ -325,6 +349,7 @@ void I2sMicTask(void *arg){ uint16_t bwritten; uint32_t ctime; uint32_t gain = audio_i2s.Settings->rx.gain; + uint32_t timeForOneRead; if (!audio_i2s_mp3.use_stream) { mp3_out = ufsp->open(audio_i2s_mp3.mic_path, "w"); @@ -354,7 +379,7 @@ void I2sMicTask(void *arg){ } else { config.mpeg.mode = STEREO; } - config.mpeg.bitr = 128; + // config.mpeg.bitr = 128; - this is default anyway, but maybe we want to make it a variable in the future config.wave.samplerate = audio_i2s.Settings->rx.sample_rate; config.wave.channels = (channels)(audio_i2s.Settings->rx.channels); @@ -379,16 +404,25 @@ void I2sMicTask(void *arg){ } ctime = TasmotaGlobal.uptime; + timeForOneRead = 1000 / ((audio_i2s.Settings->rx.sample_rate / samples_per_pass)); + timeForOneRead -= 1; // be very in time + AddLog(LOG_LEVEL_DEBUG, PSTR("I2S: samples %u, bytesize %u, time: %u"),samples_per_pass, bytesize, timeForOneRead); while (!audio_i2s_mp3.mic_stop) { + TickType_t xLastWakeTime = xTaskGetTickCount(); size_t bytes_read; - i2s_channel_read(audio_i2s.rx_handle, (void*)buffer, bytesize, &bytes_read, (100 / portTICK_PERIOD_MS)); + + // bytes_read = audio_i2s.in->readMic((uint8_t*)buffer, bytesize, true /*dc_block*/, false /*apply_gain*/, true /*lowpass*/, nullptr /*peak_ptr*/); + i2s_channel_read(audio_i2s.in->getRxHandle(), (void*)buffer, bytesize, &bytes_read, pdMS_TO_TICKS(3)); + + if(bytes_read < bytesize) AddLog(LOG_LEVEL_DEBUG, PSTR("!! %u, %u"), bytes_read, bytesize); if (gain > 1) { - // set gain + // set gain the "old way" + int16_t _gain = gain / 16; for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) { - buffer[cnt] *= gain; + buffer[cnt] *= _gain; } } ucp = shine_encode_buffer_interleaved(s, buffer, &written); @@ -406,6 +440,8 @@ void I2sMicTask(void *arg){ } } audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime; + + vTaskDelayUntil( &xLastWakeTime, pdMS_TO_TICKS(timeForOneRead)); } ucp = shine_flush(s, &written); @@ -433,7 +469,7 @@ exit: audio_i2s_mp3.client.stop(); } - audio_i2s.out->stopRx(); + audio_i2s.in->stopRx(); audio_i2s_mp3.mic_stop = 0; audio_i2s_mp3.mic_error = error; AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error); @@ -453,13 +489,10 @@ int32_t I2sRecordShine(char *path) { strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path)); audio_i2s_mp3.mic_stop = 0; - uint32_t stack = 4096; + uint32_t stack = 8000; audio_i2s_mp3.use_stream = !strcmp(audio_i2s_mp3.mic_path, "stream.mp3"); - if (audio_i2s_mp3.use_stream) { - stack = 8000; - } - audio_i2s.out->startRx(); + audio_i2s.in->startRx(); err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1); @@ -507,9 +540,9 @@ void I2SSettingsLoad(const char * config_filename, bool erase) { else if (TfsLoadFile(config_filename, (uint8_t*)audio_i2s.Settings, sizeof(tI2SSettings))) { AddLog(LOG_LEVEL_INFO, "I2S: config loaded from file '%s'", config_filename); if ((audio_i2s.Settings->sys.version == 0) || (audio_i2s.Settings->sys.version > AUDIO_SETTINGS_VERSION)) { + AddLog(LOG_LEVEL_DEBUG, "I2S: unsupported configuration version %u, use defaults", audio_i2s.Settings->sys.version); delete audio_i2s.Settings; audio_i2s.Settings = new tI2SSettings(); - AddLog(LOG_LEVEL_DEBUG, "I2S: unsuppoted configuration version, use defaults"); I2SSettingsSave(config_filename); } } @@ -671,7 +704,7 @@ void I2sInit(void) { i2s->setPinout(bclk, ws, dout, mclk, din, audio_i2s.Settings->sys.mclk_inv[0], audio_i2s.Settings->sys.bclk_inv[0], audio_i2s.Settings->sys.ws_inv[0], audio_i2s.Settings->tx.apll); - i2s->setSlotConfig((i2s_port_t)port, audio_i2s.Settings->tx.slot_config, audio_i2s.Settings->rx.slot_config, + i2s->setSlotConfig((i2s_port_t)port, audio_i2s.Settings->tx.slot_config, audio_i2s.Settings->tx.slot_mask, audio_i2s.Settings->rx.slot_mask); if (tx) { i2s->setTxMode(audio_i2s.Settings->tx.mode); @@ -1063,8 +1096,8 @@ bool Xdrv42(uint32_t function) { break; case FUNC_WEB_ADD_HANDLER: #if defined(USE_SHINE) && defined(MP3_MIC_STREAM) - audio_i2s.Settings->tx.stream_enable = 1; - I2sMp3Init(1); + // audio_i2s.Settings->tx.stream_enable = 1; + // I2sMp3Init(1); #endif #if defined(I2S_BRIDGE) I2SBridgeInit(); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream_idf51.ino index 5a83f0d70..0fff1b0e6 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream_idf51.ino @@ -26,10 +26,6 @@ void Stream_mp3(void) { - if (!audio_i2s.Settings->tx.stream_enable) { - return; - } - if (audio_i2s_mp3.stream_active) { return; } @@ -37,7 +33,8 @@ void Stream_mp3(void) { audio_i2s_mp3.stream_active = 1; audio_i2s_mp3.client = audio_i2s_mp3.MP3Server->client(); AddLog(LOG_LEVEL_INFO, PSTR("I2S: Create client")); - i2s_record_shine((char*)"stream.mp3"); + // i2s_record_shine((char*)"stream.mp3"); + I2sRecordShine((char*)"stream.mp3"); } void I2sMp3Loop(void) { @@ -60,6 +57,7 @@ void I2sMp3Init(uint32_t on) { audio_i2s_mp3.MP3Server->stop(); delete audio_i2s_mp3.MP3Server; audio_i2s_mp3.MP3Server = nullptr; + audio_i2s_mp3.mic_stop = 1; AddLog(LOG_LEVEL_INFO, PSTR("MP3: server deleted")); } }