diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_2_dacbuffer_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_2_dacbuffer_idf51.ino new file mode 100644 index 000000000..cc0b55ad5 --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_2_dacbuffer_idf51.ino @@ -0,0 +1,163 @@ +/* + xdrv_42_0_i2s_2_dacbuffer_idf51.ino - Simplified Audio library, core class + + Copyright (C) 2021 Gerhard Mutz, Theo Arends, Staars, Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#ifdef USE_I2S_AUDIO +#ifdef SOC_DAC_SUPPORTED + +#include "driver/dac_continuous.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "driver/dac_continuous.h" + +// #define DAC_BUFFER_SIZE (960 * 2) +#define DAC_BUFFER_SIZE (240 * 2) + +SemaphoreHandle_t dac_buffer_Mutex; +size_t dac_buffer_len = 0; +TaskHandle_t dac_task_handle = NULL; +dac_continuous_handle_t dac_handle = NULL; +uint8_t *dac_buffer = NULL; + +int32_t dac_submit_buffer(uint8_t* data, size_t length) { +static int cnt = 0; +// if (cnt++ < 10) { AddLog(LOG_LEVEL_DEBUG, "dac_submit_buffer: %*_H", length, data); } + // send to dac audio + size_t i2s_bytes_written = 0; + if (length == 0) { return 0; } + +// Serial.printf("%i\n", length); + esp_err_t err = dac_continuous_write(dac_handle, data, length, &i2s_bytes_written, 0); + if (err && err != ESP_ERR_TIMEOUT) { + // AddLog(LOG_LEVEL_INFO, "I2S: Could not write samples (count=%i): %i", length, err); + return -1; + } else if (err == ESP_OK) { + // AddLog(LOG_LEVEL_DEBUG, "I2S: dac_continuous_write bytes_written=%i buf=%*_H", i2s_bytes_written, length, data); + } + + + // Submit buffer and return bytes consumed + return i2s_bytes_written; +} + +void dac_buffer_filling_task(void*) { + size_t bytesCopied; + while (true) { + // Wait for data + if (dac_buffer_Mutex != NULL) { + xSemaphoreTake(dac_buffer_Mutex, portMAX_DELAY); + // bytesCopied = fillBuffer(data, length); + if (dac_buffer_len > 0) { + int32_t bytes_consumed = dac_submit_buffer(dac_buffer, dac_buffer_len); + if (bytes_consumed > 0) { + if (bytes_consumed >= dac_buffer_len) { + dac_buffer_len = 0; + } else { + dac_buffer_len -= bytes_consumed; + memmove(dac_buffer, dac_buffer + bytes_consumed, dac_buffer_len); // shift what's left + } + } + } + xSemaphoreGive(dac_buffer_Mutex); + } + + // Wait 1ms + vTaskDelay(1); + } +} + +int32_t send_dac_data(uint8_t* buf, size_t len_in_bytes) { +// static int cnt = 11; +// cnt++; +// if (cnt < 10) { AddLog(LOG_LEVEL_DEBUG, "send_dac_data: %*_H", len_in_bytes, buf); } + if (dac_buffer_Mutex != NULL && len_in_bytes > 0 && buf != NULL) { + xSemaphoreTake(dac_buffer_Mutex, portMAX_DELAY); +// if (cnt < 10) { AddLog(LOG_LEVEL_DEBUG, "mutex gained"); } + + // Calculate available space + int32_t space_left = DAC_BUFFER_SIZE - dac_buffer_len; + int32_t bytes_sent = 0; + + if (space_left > 0) { + if (len_in_bytes > space_left) { + // send only the portion to fill the buffer + bytes_sent = space_left; + } else { + // send all of it + bytes_sent = len_in_bytes; + } +// if (cnt < 10) { AddLog(LOG_LEVEL_DEBUG, "memmove buf:%p from:%p len:%i", dac_buffer + dac_buffer_len, buf, bytes_sent); } + memmove(dac_buffer + dac_buffer_len, buf, bytes_sent); + dac_buffer_len += bytes_sent; +// if (cnt < 10) { AddLog(LOG_LEVEL_DEBUG, "dac_buffer_len:%i", dac_buffer_len); } + } + xSemaphoreGive(dac_buffer_Mutex); + return bytes_sent; + } else { + return -1; + } +} + +// returns `true` if successful +bool dac_task_start(void* _dac_handle) { + dac_handle = (dac_continuous_handle_t)_dac_handle; + + // allocate buffer TODO + if (dac_buffer == NULL) { + dac_buffer = (uint8_t*)malloc(DAC_BUFFER_SIZE); + if (dac_buffer == NULL) { + return false; + } + } + + // create mutex + if (dac_buffer_Mutex == NULL) { + dac_buffer_Mutex = xSemaphoreCreateMutex(); + if (dac_buffer_Mutex == NULL) { + return false; + } + } + + // create task + if (dac_task_handle == NULL) { + // Create task + xTaskCreatePinnedToCore( + dac_buffer_filling_task, /* Function to implement the task */ + "dac_buffer_fill", /* Name of the task */ + 1024, /* Stack size in words */ + NULL, /* Task input parameter */ + 1, /* Priority of the task */ + &dac_task_handle, /* Task handle. */ + 0); /* Core where the task should run */ + } + return true; +} + +bool dac_task_stop(void) { + if (dac_task_handle != NULL) { + vTaskDelete(dac_task_handle); + dac_task_handle = NULL; + } + return true; +} + +#endif // SOC_DAC_SUPPORTED +#endif // USE_I2S_AUDIO +#endif // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_lib_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino similarity index 97% rename from tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_lib_idf51.ino rename to tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino index 4090d4a58..3b6987986 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_lib_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_3_lib_idf51.ino @@ -1,5 +1,5 @@ /* - xdrv_42_0_i2s_0_lib_idf51.ino - Simplified Audio library, core class + xdrv_42_0_i2s_3_lib_idf51.ino - Simplified Audio library, core class Copyright (C) 2021 Gerhard Mutz, Theo Arends, Staars, Stephan Hadinger @@ -21,9 +21,8 @@ #ifdef USE_I2S_AUDIO #include "AudioOutput.h" -#include "driver/dac_continuous.h" -// If DAC is not supported, proide some placeholders +// If DAC is not supported, provide some placeholders #ifndef SOC_DAC_SUPPORTED #define dac_continuous_enable(...) (0xFF) #define dac_continuous_disable(...) (0xFF) @@ -319,6 +318,7 @@ bool TasmotaI2S::beginTx(void) { esp_err_t err = ESP_OK; if (isDACMode()) { err = dac_continuous_enable((dac_continuous_handle_t) _tx_handle); + dac_task_start((dac_continuous_handle_t) _tx_handle); } else { err = i2s_channel_enable(_tx_handle); } @@ -338,6 +338,7 @@ bool TasmotaI2S::stopTx() { if (!_tx_handle) { return true; } // nothing to do if (_tx_running) { if (isDACMode()) { + dac_task_stop(); err = dac_continuous_disable((dac_continuous_handle_t) _tx_handle); } else { err = i2s_channel_disable(_tx_handle); @@ -435,16 +436,18 @@ int32_t TasmotaI2S::consumeSamples(int16_t *samples, size_t count) { right = (((int16_t)(right & 0xff)) - 128) << 8; } + // apply gain + left = Amplify(left); + right = Amplify(right); + if (isDACMode()) { - left = Amplify(left) + 0x8000; - right = Amplify(right) + 0x8000; + ms[i*2 + LEFTCHANNEL] = left + 0x8000; + ms[i*2 + RIGHTCHANNEL] = right + 0x8000; } else { - left = Amplify(left); - right = Amplify(right); + ms[i*2 + LEFTCHANNEL] = left; + ms[i*2 + RIGHTCHANNEL] = right; } - ms[i*2 + LEFTCHANNEL] = left; - ms[i*2 + RIGHTCHANNEL] = right; } // AddLog(LOG_LEVEL_DEBUG, "I2S: consumeSamples: left=%i right=%i", ms[0], ms[1]); @@ -452,9 +455,7 @@ int32_t TasmotaI2S::consumeSamples(int16_t *samples, size_t count) { size_t i2s_bytes_written; esp_err_t err = ESP_OK; if (isDACMode()) { - err = dac_continuous_write((dac_continuous_handle_t) _tx_handle, (uint8_t*) ms, sizeof(ms), &i2s_bytes_written, -1); - // Serial.printf("."); Serial.flush(); - // AddLog(LOG_LEVEL_DEBUG, "I2S: dac_continuous_write err=0x%04X bytes_written=%i buf=%*_H", err, i2s_bytes_written, sizeof(ms), (uint8_t*) ms); + i2s_bytes_written = send_dac_data((uint8_t*)ms, sizeof(ms)); } else { err = i2s_channel_write(_tx_handle, ms, sizeof(ms), &i2s_bytes_written, 0); } @@ -563,7 +564,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) { .freq_hz = hertz, .offset = 0, .clk_src = DAC_DIGI_CLK_SRC_APLL, /*DAC_DIGI_CLK_SRC_DEFAULT*/ - .chan_mode = DAC_CHANNEL_MODE_SIMUL, + .chan_mode = DAC_CHANNEL_MODE_ALTER, }; // AddLog(LOG_LEVEL_DEBUG, "I2S: dac_chan_cfg chan_mask:%i clk_src:%i chan_mode:%i", // dac_chan_cfg.chan_mask, dac_chan_cfg.clk_src, dac_chan_cfg.chan_mode);