diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp
index d0ec59c3b..80d7b0626 100755
--- a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp
+++ b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp
@@ -106,8 +106,16 @@ shine_global_config *shine_initialise(shine_config_t *pub_config) {
for (x = 0; x < MAX_CHANNELS; x++) {
for (y = 0; y < MAX_GRANULES; y++) {
// 2 * 2 * 576 each
- config->l3_enc[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //Significant performance hit in IRAM
- config->mdct_freq[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 1%
+ config->l3_enc[x][y] = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //Significant performance hit in IRAM
+ if (!config->l3_enc[x][y]) {
+ // error should never occur because of spiram size
+ //config->l3_enc[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE,MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT);
+ }
+ config->mdct_freq[x][y] = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 1%
+ if (!config->mdct_freq[x][y]) {
+ // error
+ //config->mdct_freq[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT);
+ }
}
}
#ifdef SHINE_DEBUG
@@ -117,8 +125,16 @@ shine_global_config *shine_initialise(shine_config_t *pub_config) {
#ifdef SHINE_DEBUG
printf("xrsq & xrabs each: %d\n", sizeof(int32_t)*GRANULE_SIZE);
#endif
- config->l3loop->xrsq = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5%
- config->l3loop->xrabs = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5%
+ config->l3loop->xrsq = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5%
+ if (!config->l3loop->xrsq) {
+ // error
+ //config->l3loop->xrsq = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5%
+ }
+
+ config->l3loop->xrabs = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5%
+ if (!config->l3loop->xrabs) {
+ //config->l3loop->xrabs = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5%
+ }
/*typedef struct {
int32_t *xr;
@@ -273,7 +289,6 @@ void shine_close(shine_global_config *config) {
}
}
- config->l3loop = (l3loop_t*)heap_caps_malloc(sizeof(l3loop_t), MALLOC_CAP_SPIRAM);
if (config->l3loop) {
free(config->l3loop);
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino
new file mode 100644
index 000000000..8dc835059
--- /dev/null
+++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino
@@ -0,0 +1,668 @@
+/*
+ xdrv_42_i2s_audio.ino - Audio dac support for Tasmota
+
+ Copyright (C) 2021 Gerhard Mutz and Theo Arends
+
+ 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(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX))
+/*********************************************************************************************\
+ * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor
+ *
+ * Uses fixed GPIOs for ESP8266:
+ * I2S Out Data GPIO03 (Rx)
+ * I2S Out Bit Clock GPIO15
+ * I2S Out Word Select GPIO02
+ * I2S In Data GPIO12
+ * I2S In Bit Clock GPIO13
+ * I2S In Word Select GPIO14
+\*********************************************************************************************/
+
+#define XDRV_42 42
+
+#define USE_I2S_EXTERNAL_DAC 1
+//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC
+//#define USE_I2S_WEBRADIO // Add support for web radio
+//#define USE_I2S_SAY_TIME // Add support for english speaking clock
+
+#include "AudioFileSourcePROGMEM.h"
+#include "AudioFileSourceID3.h"
+#include "AudioGeneratorMP3.h"
+#ifdef USE_I2S_NO_DAC
+ #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin
+#else
+ #include "AudioOutputI2S.h" // External I2S DAC IC
+#endif // USE_I2S_NO_DAC
+#include
+#include "AudioFileSourceFS.h"
+#ifdef USE_I2S_SAY_TIME
+ #include "AudioGeneratorTalkie.h"
+#endif // USE_I2S_SAY_TIME
+#include "AudioFileSourceICYStream.h"
+#include "AudioFileSourceBuffer.h"
+#include "AudioGeneratorAAC.h"
+
+#ifdef ESP32
+#include
+#endif
+
+#undef AUDIO_PWR_ON
+#undef AUDIO_PWR_OFF
+#define AUDIO_PWR_ON
+#define AUDIO_PWR_OFF
+
+#ifdef ESP8266
+#define i2s_port_t uint8_t
+#endif
+
+#ifdef ESP32
+#define MODE_MIC 0
+#define MODE_SPK 1
+#ifndef MICSRATE
+#define MICSRATE 16000
+#endif
+#endif // ESP32
+
+struct AUDIO_I2S_t {
+ uint8_t is2_volume; // should be in settings
+ i2s_port_t i2s_port;
+ int8_t mclk = -1;
+ int8_t bclk = -1;
+ int8_t ws = -1;
+ int8_t dout = -1;
+ int8_t din = -1;
+ AudioGeneratorMP3 *mp3 = nullptr;
+ AudioFileSourceFS *file;
+#ifdef USE_I2S_NO_DAC
+ AudioOutputI2SNoDAC *out;
+#else
+ AudioOutputI2S *out;
+#endif // USE_I2S_NO_DAC
+ AudioFileSourceID3 *id3;
+ AudioGeneratorMP3 *decoder = NULL;
+ void *mp3ram = NULL;
+#ifdef USE_I2S_WEBRADIO
+ AudioFileSourceICYStream *ifile = NULL;
+ AudioFileSourceBuffer *buff = NULL;
+ char wr_title[64];
+ void *preallocateBuffer = NULL;
+ void *preallocateCodec = NULL;
+ uint32_t retryms = 0;
+#endif // USE_I2S_WEBRADIO
+
+#ifdef ESP32
+ TaskHandle_t mp3_task_h;
+ TaskHandle_t mic_task_h;
+ uint32_t mic_size;
+ uint32_t mic_rate;
+ uint8_t *mic_buff;
+ char mic_path[32];
+ uint8_t mic_channels;
+ File fwp;
+ uint8_t mic_stop;
+ int8_t mic_error;
+ int8_t mic_mclk = -1;
+ int8_t mic_bclk = -1;
+ int8_t mic_ws = -1;
+ int8_t mic_din = -1;
+ int8_t mic_dout = -1;
+ bool use_stream = false;
+ i2s_port_t mic_port;
+#endif // ESP32
+
+#ifdef USE_SHINE
+ uint32_t recdur;
+ uint8_t stream_active;
+ uint8_t stream_enable;
+ WiFiClient client;
+ ESP8266WebServer *MP3Server;
+#endif
+
+} audio_i2s;
+
+
+#define MIC_CHANNELS 1
+
+#ifdef USE_TTGO_WATCH
+#undef AUDIO_PWR_ON
+#undef AUDIO_PWR_OFF
+#define AUDIO_PWR_ON TTGO_audio_power(true);
+#define AUDIO_PWR_OFF TTGO_audio_power(false);
+#endif // USE_TTGO_WATCH
+
+#ifdef USE_M5STACK_CORE2
+// leave this predefined currently
+#undef AUDIO_PWR_ON
+#undef AUDIO_PWR_OFF
+#define AUDIO_PWR_ON Core2AudioPower(true);
+#define AUDIO_PWR_OFF Core2AudioPower(false);
+#undef DAC_IIS_BCK
+#undef DAC_IIS_WS
+#undef DAC_IIS_DOUT
+#undef DAC_IIS_DIN
+#define DAC_IIS_BCK 12
+#define DAC_IIS_WS 0
+#define DAC_IIS_DOUT 2
+#define DAC_IIS_DIN 34
+#undef MICSRATE
+#define MICSRATE 32000
+#undef MIC_CHANNELS
+#define MIC_CHANNELS 1
+#endif // USE_M5STACK_CORE2
+
+
+#ifdef ESP32S3_BOX
+#undef AUDIO_PWR_ON
+#undef AUDIO_PWR_OFF
+#define AUDIO_PWR_ON S3boxAudioPower(true);
+#define AUDIO_PWR_OFF S3boxAudioPower(false);
+#undef MIC_CHANNELS
+#define MIC_CHANNELS 2
+#endif // ESP32S3_BOX
+
+extern FS *ufsp;
+extern FS *ffsp;
+
+#ifdef ESP8266
+const int preallocateBufferSize = 5*1024;
+const int preallocateCodecSize = 29192; // MP3 codec max mem needed
+#endif // ESP8266
+
+#ifdef ESP32
+const int preallocateBufferSize = 16*1024;
+const int preallocateCodecSize = 29192; // MP3 codec max mem needed
+//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed
+#endif // ESP32
+
+enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
+enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
+
+#ifdef ESP8266
+#define I2S_MCLK_MULTIPLE_128 0
+#endif
+
+void sayTime(int hour, int minutes);
+void Cmd_MicRec(void);
+void Cmd_wav2mp3(void);
+void Cmd_Time(void);
+
+int32_t I2S_Init_0(void) {
+
+ audio_i2s.i2s_port = (i2s_port_t)0;
+ audio_i2s.mic_port = (i2s_port_t)0;
+
+#if USE_I2S_EXTERNAL_DAC
+ // use i2s
+#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT))
+ audio_i2s.i2s_port = (i2s_port_t)0;
+#ifdef USE_I2S_NO_DAC
+ audio_i2s.out = new AudioOutputI2SNoDAC();
+#else
+ audio_i2s.out = new AudioOutputI2S();
+#endif
+ audio_i2s.bclk = DAC_IIS_BCK;
+ audio_i2s.ws = DAC_IIS_WS;
+ audio_i2s.dout = DAC_IIS_DOUT;
+ audio_i2s.din = DAC_IIS_DIN;
+#else
+#ifdef USE_I2S_NO_DAC
+ if (PinUsed(GPIO_I2S_DOUT)) {
+#else
+ if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) {
+#endif // USE_I2S_NO_DAC
+ audio_i2s.i2s_port = (i2s_port_t)0;
+#ifdef USE_I2S_NO_DAC
+ audio_i2s.out = new AudioOutputI2SNoDAC();
+#else
+ //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
+ audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
+#endif // USE_I2S_NO_DAC
+ audio_i2s.mclk = Pin(GPIO_I2S_MCLK);
+ audio_i2s.bclk = Pin(GPIO_I2S_BCLK);
+ audio_i2s.ws = Pin(GPIO_I2S_WS);
+ audio_i2s.dout = Pin(GPIO_I2S_DOUT);
+ audio_i2s.din = Pin(GPIO_I2S_DIN);
+
+ audio_i2s.mic_mclk = audio_i2s.mclk;
+ audio_i2s.mic_bclk = audio_i2s.bclk;
+ audio_i2s.mic_ws = audio_i2s.ws;
+ audio_i2s.mic_dout = audio_i2s.dout;
+ audio_i2s.mic_din = audio_i2s.din;
+ audio_i2s.mic_port = (i2s_port_t)0;
+
+ // check if 2 ports used, use second for micro
+ if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DIN, 1)) {
+ audio_i2s.mic_bclk = -1;
+ audio_i2s.mic_bclk = Pin(GPIO_I2S_BCLK, 1);
+ audio_i2s.mic_ws = Pin(GPIO_I2S_WS, 1);
+ audio_i2s.mic_dout = -1;
+ audio_i2s.mic_din = Pin(GPIO_I2S_DIN, 1);
+ audio_i2s.mic_port = (i2s_port_t)1;
+ }
+ } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT, 1)) {
+ audio_i2s.i2s_port = (i2s_port_t)1;
+#ifdef USE_I2S_NO_DAC
+ audio_i2s.out = new AudioOutputI2SNoDAC();
+#else
+ //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
+ audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
+#endif // USE_I2S_NO_DAC
+ audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1);
+ audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1);
+ audio_i2s.ws = Pin(GPIO_I2S_WS, 1);
+ audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1);
+ audio_i2s.din = Pin(GPIO_I2S_DIN, 1);
+
+ audio_i2s.mic_mclk = audio_i2s.mclk;
+ audio_i2s.mic_bclk = audio_i2s.bclk;
+ audio_i2s.mic_ws = audio_i2s.ws;
+ audio_i2s.mic_dout = audio_i2s.dout;
+ audio_i2s.mic_din = audio_i2s.din;
+ audio_i2s.mic_port = (i2s_port_t)1;
+
+ } else {
+ return -1;
+ }
+#ifdef ESP8266
+ // esp8266 have fixed pins
+ if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) {
+ return -2;
+ }
+#endif // ESP8266
+#endif // defined(DAC_IIS_BCK)
+
+ audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
+
+ AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
+ if (audio_i2s.mic_port != 0) {
+ AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S mic: port=%d, bclk=%d, ws=%d, din=%d"), audio_i2s.mic_port, audio_i2s.mic_bclk, audio_i2s.mic_ws, audio_i2s.mic_din);
+ }
+#else
+
+#ifdef USE_I2S_NO_DAC
+ audio_i2s.out = new AudioOutputI2SNoDAC();
+#else
+ audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0
+#endif // USE_I2S_NO_DAC
+
+#endif // USE_I2S_EXTERNAL_DAC
+
+ return 0;
+}
+
+void I2S_Init(void) {
+
+ #if defined(ESP32) && defined(ESP32S3_BOX)
+ S3boxInit();
+ #endif
+
+ if (I2S_Init_0()) {
+ return;
+ }
+
+ audio_i2s.is2_volume=10;
+ audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0);
+ audio_i2s.out->stop();
+ audio_i2s.mp3ram = nullptr;
+
+#ifdef ESP32
+ if (UsePSRAM()) {
+ audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ }
+
+#ifdef USE_I2S_WEBRADIO
+ if (UsePSRAM()) {
+ audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ } else {
+ audio_i2s.preallocateBuffer = malloc(preallocateBufferSize);
+ audio_i2s.preallocateCodec = malloc(preallocateCodecSize);
+ }
+ if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) {
+ //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
+ }
+#endif // USE_I2S_WEBRADIO
+
+ audio_i2s.mic_channels = MIC_CHANNELS;
+ audio_i2s.mic_rate = MICSRATE;
+
+#endif // ESP32
+}
+
+#ifdef ESP32
+void mp3_task(void *arg) {
+ while (1) {
+ while (audio_i2s.mp3->isRunning()) {
+ if (!audio_i2s.mp3->loop()) {
+ audio_i2s.mp3->stop();
+ mp3_delete();
+ audio_i2s.out->stop();
+ if (audio_i2s.mp3_task_h) {
+ vTaskDelete(audio_i2s.mp3_task_h);
+ audio_i2s.mp3_task_h = 0;
+ }
+ //mp3_task_h=nullptr;
+ }
+ delay(1);
+ }
+ }
+}
+#endif // ESP32
+
+#ifdef USE_I2S_WEBRADIO
+void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) {
+ const char *ptr = reinterpret_cast(cbData);
+ (void) isUnicode; // Punt this ball for now
+ (void) ptr;
+ if (strstr_P(type, PSTR("Title"))) {
+ strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title));
+ audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0;
+ //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title);
+ } else {
+ // Who knows what to do? Not me!
+ }
+}
+
+void StatusCallback(void *cbData, int code, const char *string) {
+ const char *ptr = reinterpret_cast(cbData);
+ (void) code;
+ (void) ptr;
+ //strncpy_P(status, string, sizeof(status)-1);
+ //status[sizeof(status)-1] = 0;
+}
+
+void Webradio(const char *url) {
+ if (audio_i2s.decoder || audio_i2s.mp3) return;
+ if (!audio_i2s.out) return;
+ AUDIO_PWR_ON
+ audio_i2s.ifile = new AudioFileSourceICYStream(url);
+ audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL);
+ audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize);
+ audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL);
+ audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize);
+ audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL);
+ audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out);
+ if (!audio_i2s.decoder->isRunning()) {
+ // Serial.printf_P(PSTR("Can't connect to URL"));
+ StopPlaying();
+ // strcpy_P(status, PSTR("Unable to connect to URL"));
+ audio_i2s.retryms = millis() + 2000;
+ }
+
+ xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
+}
+
+void mp3_task2(void *arg){
+ while (1) {
+ if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) {
+ if (!audio_i2s.decoder->loop()) {
+ StopPlaying();
+ //retryms = millis() + 2000;
+ }
+ delay(1);
+ }
+ }
+}
+
+void StopPlaying() {
+
+ if (audio_i2s.mp3_task_h) {
+ vTaskDelete(audio_i2s.mp3_task_h);
+ audio_i2s.mp3_task_h = nullptr;
+ }
+
+ if (audio_i2s.decoder) {
+ audio_i2s.decoder->stop();
+ delete audio_i2s.decoder;
+ audio_i2s.decoder = NULL;
+ }
+
+ if (audio_i2s.buff) {
+ audio_i2s.buff->close();
+ delete audio_i2s.buff;
+ audio_i2s.buff = NULL;
+ }
+
+ if (audio_i2s.ifile) {
+ audio_i2s.ifile->close();
+ delete audio_i2s.ifile;
+ audio_i2s.ifile = NULL;
+ }
+ AUDIO_PWR_OFF
+}
+
+void Cmd_WebRadio(void) {
+ if (!audio_i2s.out) return;
+
+ if (audio_i2s.decoder) {
+ StopPlaying();
+ }
+ if (XdrvMailbox.data_len > 0) {
+ Webradio(XdrvMailbox.data);
+ ResponseCmndChar(XdrvMailbox.data);
+ } else {
+ ResponseCmndChar_P(PSTR("Stopped"));
+ }
+}
+
+#ifdef USE_WEBSERVER
+const char HTTP_WEBRADIO[] PROGMEM =
+ "{s}" "I2S_WR-Title" "{m}%s{e}";
+
+void I2S_WR_Show(bool json) {
+ if (audio_i2s.decoder) {
+ if (json) {
+ ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title);
+ } else {
+ WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title);
+ }
+ }
+}
+#endif // USE_WEBSERVER
+
+#endif // USE_I2S_WEBRADIO
+
+
+#ifdef ESP32
+void Play_mp3(const char *path) {
+#ifdef USE_UFILESYS
+ if (audio_i2s.decoder || audio_i2s.mp3) return;
+ if (!audio_i2s.out) return;
+
+ bool I2S_Task;
+
+ if (*path=='+') {
+ I2S_Task = true;
+ path++;
+ } else {
+ I2S_Task = false;
+ }
+
+ FS *mp3fsp = ufsp;
+
+ if (!strncmp(path, "/ffs", 4)) {
+ path += 4;
+ mp3fsp = ffsp;
+ }
+
+ if (!mp3fsp->exists(path)) {
+ return;
+ }
+
+ AUDIO_PWR_ON
+
+ audio_i2s.file = new AudioFileSourceFS(*mp3fsp, path);
+
+ audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file);
+
+ if (audio_i2s.mp3ram) {
+ audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize);
+ } else {
+ audio_i2s.mp3 = new AudioGeneratorMP3();
+ }
+ audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out);
+
+ if (I2S_Task) {
+ xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
+ } else {
+ while (audio_i2s.mp3->isRunning()) {
+ if (!audio_i2s.mp3->loop()) {
+ audio_i2s.mp3->stop();
+ break;
+ }
+ OsWatchLoop();
+ }
+ audio_i2s.out->stop();
+ mp3_delete();
+ }
+
+#endif // USE_UFILESYS
+}
+
+void mp3_delete(void) {
+ delete audio_i2s.file;
+ delete audio_i2s.id3;
+ delete audio_i2s.mp3;
+ audio_i2s.mp3=nullptr;
+ AUDIO_PWR_OFF
+}
+#endif // ESP32
+
+void Say(char *text) {
+
+ if (!audio_i2s.out) return;
+
+ AUDIO_PWR_ON
+
+ audio_i2s.out->begin();
+ ESP8266SAM *sam = new ESP8266SAM;
+ sam->Say(audio_i2s.out, text);
+ delete sam;
+ audio_i2s.out->stop();
+
+ AUDIO_PWR_OFF
+}
+
+
+const char kI2SAudio_Commands[] PROGMEM = "I2S|"
+ "Say|Gain"
+#ifdef USE_I2S_SAY_TIME
+ "|Time"
+#endif // USE_I2S_SAY_TIME
+#ifdef ESP32
+ "|Play"
+#ifdef USE_I2S_WEBRADIO
+ "|WR"
+#endif // USE_I2S_WEBRADIO
+#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
+ "|REC"
+#ifdef MP3_MIC_STREAM
+ "|STREAM"
+#endif // MP3_MIC_STREAM
+#endif // USE_SHINE
+#endif // ESP32
+;
+
+void (* const I2SAudio_Command[])(void) PROGMEM = {
+ &Cmd_Say, &Cmd_Gain
+#ifdef USE_I2S_SAY_TIME
+ ,&Cmd_Time
+#endif // USE_I2S_SAY_TIME
+#ifdef ESP32
+ ,&Cmd_Play
+#ifdef USE_I2S_WEBRADIO
+ ,&Cmd_WebRadio
+#endif // USE_I2S_WEBRADIO
+#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
+ ,&Cmd_MicRec
+#ifdef MP3_MIC_STREAM
+ ,&Cmd_MP3Stream
+#endif // MP3_MIC_STREAM
+#endif // USE_SHINE
+#endif // ESP32
+};
+
+void Cmd_Play(void) {
+ if (XdrvMailbox.data_len > 0) {
+ Play_mp3(XdrvMailbox.data);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+void Cmd_Gain(void) {
+ if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
+ if (audio_i2s.out) {
+ audio_i2s.is2_volume=XdrvMailbox.payload;
+ audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0);
+ }
+ }
+ ResponseCmndNumber(audio_i2s.is2_volume);
+}
+
+void Cmd_Say(void) {
+ if (XdrvMailbox.data_len > 0) {
+ Say(XdrvMailbox.data);
+ }
+ ResponseCmndChar(XdrvMailbox.data);
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+void i2s_mp3_loop(void);
+void i2s_mp3_init(void);
+void MP3ShowStream(void);
+
+bool Xdrv42(uint8_t function) {
+ bool result = false;
+
+ switch (function) {
+ case FUNC_COMMAND:
+ result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
+ break;
+ case FUNC_INIT:
+ I2S_Init();
+ break;
+#if defined(MP3_MIC_STREAM)
+//#if defined(USE_SHINE) && defined(MP3_MIC_STREAM)
+ case FUNC_WEB_ADD_MAIN_BUTTON:
+ //MP3ShowStream();
+ break;
+ case FUNC_LOOP:
+ i2s_mp3_loop();
+ break;
+ case FUNC_WEB_ADD_HANDLER:
+ audio_i2s.stream_enable = 1;
+ i2s_mp3_init(1);
+ break;
+#endif
+#ifdef USE_WEBSERVER
+#ifdef USE_I2S_WEBRADIO
+ case FUNC_WEB_SENSOR:
+ I2S_WR_Show(false);
+ break;
+#endif // USE_I2S_WEBRADIO
+#endif // USE_WEBSERVER
+#ifdef USE_I2S_WEBRADIO
+ case FUNC_JSON_APPEND:
+ I2S_WR_Show(true);
+ break;
+#endif // USE_I2S_WEBRADIO
+ }
+ return result;
+}
+
+#endif // USE_I2S_AUDIO
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino
new file mode 100644
index 000000000..53550300c
--- /dev/null
+++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino
@@ -0,0 +1,297 @@
+/*
+ xdrv_42_i2s_audio.ino - Audio dac support for Tasmota
+
+ Copyright (C) 2021 Gerhard Mutz and Theo Arends
+
+ 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 .
+*/
+
+
+#ifdef ESP32
+#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
+
+#define MP3_BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e"
+
+uint32_t SpeakerMic(uint8_t spkr) {
+esp_err_t err = ESP_OK;
+
+
+ if (spkr == MODE_SPK) {
+ if (audio_i2s.mic_port == 0) {
+ I2S_Init_0();
+ audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0);
+ audio_i2s.out->stop();
+ }
+ return 0;
+ }
+
+ // set micro
+ if (audio_i2s.mic_port == 0) {
+ // close audio out
+ if (audio_i2s.out) {
+ audio_i2s.out->stop();
+ delete audio_i2s.out;
+ audio_i2s.out = nullptr;
+ }
+ i2s_driver_uninstall(audio_i2s.i2s_port);
+ }
+
+ // config mic
+ i2s_config_t i2s_config = {
+ .mode = (i2s_mode_t)(I2S_MODE_MASTER),
+ .sample_rate = audio_i2s.mic_rate,
+ .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
+ .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
+ .communication_format = I2S_COMM_FORMAT_I2S,
+ .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
+ .dma_buf_count = 2,
+ //.dma_buf_len = 128,
+ .dma_buf_len = 1024,
+ .use_apll = 0, // Use audio PLL
+ .tx_desc_auto_clear = true,
+ .fixed_mclk = 0,
+ .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128
+ .bits_per_chan = I2S_BITS_PER_CHAN_16BIT
+ };
+
+#ifdef ESP32S3_BOX
+ i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX);
+ i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
+#endif
+
+#ifdef USE_I2S_MIC
+ // mic select to GND
+ i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX);
+ i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
+#endif
+
+#ifdef USE_M5STACK_CORE2
+ i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
+#endif
+
+ if (audio_i2s.mic_channels == 1) {
+ i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
+ } else {
+ i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
+ }
+
+ err += i2s_driver_install(audio_i2s.mic_port, &i2s_config, 0, NULL);
+
+ i2s_pin_config_t tx_pin_config;
+
+ tx_pin_config.mck_io_num = audio_i2s.mic_mclk;
+ tx_pin_config.bck_io_num = audio_i2s.mic_bclk;
+ tx_pin_config.ws_io_num = audio_i2s.mic_ws;
+ tx_pin_config.data_out_num = audio_i2s.mic_dout;
+ tx_pin_config.data_in_num = audio_i2s.mic_din;
+
+ err += i2s_set_pin(audio_i2s.mic_port, &tx_pin_config);
+
+ i2s_channel_t mode = I2S_CHANNEL_MONO;
+ if (audio_i2s.mic_channels > 1) {
+ mode = I2S_CHANNEL_STEREO;
+ }
+ err += i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, mode);
+
+ return err;
+}
+
+
+#ifdef USE_SHINE
+#include
+#include
+
+#define MP3HANDLECLIENT audio_i2s.MP3Server->handleClient();
+
+// micro to mp3 file or stream
+void mic_task(void *arg){
+ int8_t error = 0;
+ uint8_t *ucp;
+ int written;
+ shine_config_t config;
+ shine_t s = nullptr;
+ uint16_t samples_per_pass;
+ File mp3_out = (File)nullptr;
+ int16_t *buffer = nullptr;
+ uint16_t bytesize;
+ uint16_t bwritten;
+ uint32_t ctime;
+
+ if (!audio_i2s.use_stream) {
+ mp3_out = ufsp->open(audio_i2s.mic_path, "w");
+ if (!mp3_out) {
+ error = 1;
+ goto exit;
+ }
+ } else {
+ if (!audio_i2s.stream_active) {
+ error = 2;
+ audio_i2s.use_stream = 0;
+ goto exit;
+ }
+ audio_i2s.client.flush();
+ audio_i2s.client.setTimeout(3);
+ audio_i2s.client.print("HTTP/1.1 200 OK\r\n"
+ "Content-Type: audio/mpeg;\r\n\r\n");
+ MP3HANDLECLIENT
+ }
+
+ shine_set_config_mpeg_defaults(&config.mpeg);
+
+ if (audio_i2s.mic_channels == 1) {
+ config.mpeg.mode = MONO;
+ } else {
+ config.mpeg.mode = STEREO;
+ }
+ config.mpeg.bitr = 128;
+ config.wave.samplerate = audio_i2s.mic_rate;
+ config.wave.channels = (channels)audio_i2s.mic_channels;
+
+ if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
+ error = 3;
+ goto exit;
+ }
+
+ s = shine_initialise(&config);
+ if (!s) {
+ error = 4;
+ goto exit;
+ }
+
+ samples_per_pass = shine_samples_per_pass(s);
+ bytesize = samples_per_pass * 2 * audio_i2s.mic_channels;
+
+ buffer = (int16_t*)malloc(bytesize);
+ if (!buffer) {
+ error = 5;
+ goto exit;
+ }
+
+ ctime = TasmotaGlobal.uptime;
+
+ while (!audio_i2s.mic_stop) {
+ uint32_t bytes_read;
+ i2s_read(audio_i2s.mic_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS));
+ ucp = shine_encode_buffer_interleaved(s, buffer, &written);
+
+ if (!audio_i2s.use_stream) {
+ bwritten = mp3_out.write(ucp, written);
+ if (bwritten != written) {
+ break;
+ }
+ } else {
+ audio_i2s.client.write((const char*)ucp, written);
+ MP3HANDLECLIENT
+ if (!audio_i2s.client.connected()) {
+ break;
+ }
+ }
+ audio_i2s.recdur = TasmotaGlobal.uptime - ctime;
+ }
+
+ ucp = shine_flush(s, &written);
+
+ if (!audio_i2s.use_stream) {
+ mp3_out.write(ucp, written);
+ } else {
+ audio_i2s.client.write((const char*)ucp, written);
+ MP3HANDLECLIENT
+ }
+
+
+exit:
+ if (s) {
+ shine_close(s);
+ }
+ if (mp3_out) {
+ mp3_out.close();
+ }
+ if (buffer) {
+ free(buffer);
+ }
+
+ if (audio_i2s.use_stream) {
+ audio_i2s.client.stop();
+ MP3HANDLECLIENT
+ }
+
+ SpeakerMic(MODE_SPK);
+ audio_i2s.mic_stop = 0;
+ audio_i2s.mic_error = error;
+ AddLog(LOG_LEVEL_INFO, PSTR("mp3task error: %d"), error);
+ audio_i2s.mic_task_h = 0;
+ audio_i2s.recdur = 0;
+ audio_i2s.stream_active = 0;
+ vTaskDelete(NULL);
+
+}
+
+int32_t i2s_record_shine(char *path) {
+esp_err_t err = ESP_OK;
+
+ if (audio_i2s.mic_port == 0) {
+ if (audio_i2s.decoder || audio_i2s.mp3) return 0;
+ }
+
+ err = SpeakerMic(MODE_MIC);
+ if (err) {
+ if (audio_i2s.mic_port == 0) {
+ SpeakerMic(MODE_SPK);
+ }
+ AddLog(LOG_LEVEL_INFO, PSTR("mic init error: %d"), err);
+ return err;
+ }
+
+ strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path));
+
+ audio_i2s.mic_stop = 0;
+
+ uint32_t stack = 4096;
+
+ audio_i2s.use_stream = !strcmp(audio_i2s.mic_path, "stream.mp3");
+
+ if (audio_i2s.use_stream) {
+ stack = 8000;
+ }
+
+ err = xTaskCreatePinnedToCore(mic_task, "MIC", stack, NULL, 3, &audio_i2s.mic_task_h, 1);
+
+ return err;
+}
+
+void Cmd_MicRec(void) {
+
+ if (XdrvMailbox.data_len > 0) {
+ if (!strncmp(XdrvMailbox.data, "-?", 2)) {
+ Response_P("{\"I2SREC-duration\":%d}", audio_i2s.recdur);
+ } else {
+ i2s_record_shine(XdrvMailbox.data);
+ ResponseCmndChar(XdrvMailbox.data);
+ }
+ } else {
+ if (audio_i2s.mic_task_h) {
+ // stop task
+ audio_i2s.mic_stop = 1;
+ while (audio_i2s.mic_stop) {
+ delay(1);
+ }
+ ResponseCmndChar_P(PSTR("Stopped"));
+ }
+ }
+
+}
+
+#endif // USE_SHINE
+#endif // USE_I2S_AUDIO
+#endif // ESP32
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino
new file mode 100644
index 000000000..72495edb9
--- /dev/null
+++ b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino
@@ -0,0 +1,58 @@
+
+#ifdef ESP32
+#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
+
+#ifdef MP3_MIC_STREAM
+
+void Stream_mp3(void) {
+ if (!audio_i2s.stream_enable) {
+ return;
+ }
+
+ if (audio_i2s.stream_active) {
+ return;
+ }
+ AddLog(LOG_LEVEL_INFO, PSTR("I2S: Handle mp3server"));
+ audio_i2s.stream_active = 1;
+ audio_i2s.client = audio_i2s.MP3Server->client();
+ AddLog(LOG_LEVEL_INFO, PSTR("I2S: Create client"));
+ i2s_record_shine((char*)"stream.mp3");
+}
+
+void i2s_mp3_loop(void) {
+ if (audio_i2s.MP3Server) {
+ audio_i2s.MP3Server->handleClient();
+ }
+}
+
+void i2s_mp3_init(uint32_t on) {
+ if (on) {
+ if (!audio_i2s.MP3Server) {
+ audio_i2s.MP3Server = new ESP8266WebServer(81);
+ audio_i2s.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3);
+ audio_i2s.MP3Server->begin();
+ AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created"));
+ }
+ } else {
+ if (audio_i2s.MP3Server) {
+ audio_i2s.MP3Server->stop();
+ delete audio_i2s.MP3Server;
+ audio_i2s.MP3Server = nullptr;
+ AddLog(LOG_LEVEL_INFO, PSTR("MP3: server deleted"));
+ }
+ }
+}
+
+
+void Cmd_MP3Stream(void) {
+ if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
+ audio_i2s.stream_enable = XdrvMailbox.payload;
+ }
+ i2s_mp3_init(audio_i2s.stream_enable);
+ ResponseCmndNumber(audio_i2s.stream_enable);
+}
+#endif // MP3_MIC_STREAM
+
+
+#endif // USE_SHINE
+#endif // ESP32
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino
similarity index 50%
rename from tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino
rename to tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino
index 853b0d90e..91583effa 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino
@@ -1,7 +1,8 @@
-/*
- xdrv_42_i2s_audio.ino - Audio dac support for Tasmota
- Copyright (C) 2021 Gerhard Mutz and Theo Arends
+/*
+ audio is2 say time
+
+ Copyright (C) 2022 Gerhard Mutz and Theo Arends
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
@@ -16,164 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-
#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX))
-/*********************************************************************************************\
- * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor
- *
- * Uses fixed GPIOs for ESP8266:
- * I2S Out Data GPIO03 (Rx)
- * I2S Out Bit Clock GPIO15
- * I2S Out Word Select GPIO02
- * I2S In Data GPIO12
- * I2S In Bit Clock GPIO13
- * I2S In Word Select GPIO14
-\*********************************************************************************************/
-
-#define XDRV_42 42
-
-#define USE_I2S_EXTERNAL_DAC 1
-//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC
-//#define USE_I2S_WEBRADIO // Add support for web radio
-//#define USE_I2S_SAY_TIME // Add support for english speaking clock
-
-#include "AudioFileSourcePROGMEM.h"
-#include "AudioFileSourceID3.h"
-#include "AudioGeneratorMP3.h"
-#ifdef USE_I2S_NO_DAC
- #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin
-#else
- #include "AudioOutputI2S.h" // External I2S DAC IC
-#endif // USE_I2S_NO_DAC
-#include
-#include "AudioFileSourceFS.h"
-#ifdef USE_I2S_SAY_TIME
- #include "AudioGeneratorTalkie.h"
-#endif // USE_I2S_SAY_TIME
-#include "AudioFileSourceICYStream.h"
-#include "AudioFileSourceBuffer.h"
-#include "AudioGeneratorAAC.h"
-
-#ifdef ESP32
-#include
-#endif
-
-#undef AUDIO_PWR_ON
-#undef AUDIO_PWR_OFF
-#define AUDIO_PWR_ON
-#define AUDIO_PWR_OFF
-
-#ifdef ESP8266
-#define i2s_port_t uint8_t
-#endif
-
-#ifdef ESP32
-#define MODE_MIC 0
-#define MODE_SPK 1
-#ifndef MICSRATE
-#define MICSRATE 16000
-#endif
-#endif // ESP32
-
-struct AUDIO_I2S_t {
- uint8_t is2_volume; // should be in settings
- i2s_port_t i2s_port;
- int8_t mclk = -1;
- int8_t bclk = -1;
- int8_t ws = -1;
- int8_t dout = -1;
- int8_t din = -1;
- AudioGeneratorMP3 *mp3 = nullptr;
- AudioFileSourceFS *file;
-#ifdef USE_I2S_NO_DAC
- AudioOutputI2SNoDAC *out;
-#else
- AudioOutputI2S *out;
-#endif // USE_I2S_NO_DAC
- AudioFileSourceID3 *id3;
- AudioGeneratorMP3 *decoder = NULL;
- void *mp3ram = NULL;
-#ifdef USE_I2S_WEBRADIO
- AudioFileSourceICYStream *ifile = NULL;
- AudioFileSourceBuffer *buff = NULL;
- char wr_title[64];
- void *preallocateBuffer = NULL;
- void *preallocateCodec = NULL;
- uint32_t retryms = 0;
-#endif // USE_I2S_WEBRADIO
-
-#ifdef ESP32
- TaskHandle_t mp3_task_h;
- TaskHandle_t mic_task_h;
- uint32_t mic_size;
- uint32_t mic_rate;
- uint8_t *mic_buff;
- char mic_path[32];
- uint8_t mic_channels;
- File fwp;
- uint8_t mic_stop;
- int8_t mic_error;
-#endif // ESP32
-} audio_i2s;
-
-
-// because S3 box mclk severly disturbs WLAN
-// we must slow down after each sound
-#ifdef ESP32S3_BOX
-#undef DOWNRATE
-#define DOWNRATE audio_i2s.out->SetRate(1000);
-#else
-#undef DOWNRATE
-#define DOWNRATE
-#endif
-
-#define MIC_CHANNELS 1
-
-#ifdef USE_TTGO_WATCH
-#undef AUDIO_PWR_ON
-#undef AUDIO_PWR_OFF
-#define AUDIO_PWR_ON TTGO_audio_power(true);
-#define AUDIO_PWR_OFF TTGO_audio_power(false);
-#endif // USE_TTGO_WATCH
-
-#ifdef USE_M5STACK_CORE2
-// leave this predefined currently
-#undef AUDIO_PWR_ON
-#undef AUDIO_PWR_OFF
-#define AUDIO_PWR_ON Core2AudioPower(true);
-#define AUDIO_PWR_OFF Core2AudioPower(false);
-#undef DAC_IIS_BCK
-#undef DAC_IIS_WS
-#undef DAC_IIS_DOUT
-#define DAC_IIS_BCK 12
-#define DAC_IIS_WS 0
-#define DAC_IIS_DOUT 2
-#endif // USE_M5STACK_CORE2
-
-
-#ifdef ESP32S3_BOX
-#undef AUDIO_PWR_ON
-#undef AUDIO_PWR_OFF
-#define AUDIO_PWR_ON S3boxAudioPower(true);
-#define AUDIO_PWR_OFF S3boxAudioPower(false);
-#undef MIC_CHANNELS
-#define MIC_CHANNELS 2
-#endif // ESP32S3_BOX
-
-extern FS *ufsp;
-
-#ifdef ESP8266
-const int preallocateBufferSize = 5*1024;
-const int preallocateCodecSize = 29192; // MP3 codec max mem needed
-#endif // ESP8266
-
-#ifdef ESP32
-const int preallocateBufferSize = 16*1024;
-const int preallocateCodecSize = 29192; // MP3 codec max mem needed
-//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed
-#endif // ESP32
-
-
#ifdef USE_I2S_SAY_TIME
long timezone = 2;
byte daysavetime = 1;
@@ -272,778 +116,13 @@ AudioGeneratorTalkie *talkie = nullptr;
}
delete talkie;
audio_i2s.out->stop();
- DOWNRATE
AUDIO_PWR_OFF
}
-#endif // USE_I2S_SAY_TIME
-
-
-enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 };
-enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 };
-
-#ifdef ESP8266
-#define I2S_MCLK_MULTIPLE_128 0
-#endif
-
-int32_t I2S_Init_0(void) {
-
- audio_i2s.i2s_port = (i2s_port_t)0;
-
-#if USE_I2S_EXTERNAL_DAC
- // use i2s
-#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT))
- audio_i2s.i2s_port = (i2s_port_t)0;
-#ifdef USE_I2S_NO_DAC
- audio_i2s.out = new AudioOutputI2SNoDAC();
-#else
- audio_i2s.out = new AudioOutputI2S();
-#endif
- audio_i2s.bclk = DAC_IIS_BCK;
- audio_i2s.ws = DAC_IIS_WS;
- audio_i2s.dout = DAC_IIS_DOUT;
-#else
-#ifdef USE_I2S_NO_DAC
- if (PinUsed(GPIO_I2S_DOUT)) {
-#else
- if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) {
-#endif // USE_I2S_NO_DAC
- audio_i2s.i2s_port = (i2s_port_t)0;
-#ifdef USE_I2S_NO_DAC
- audio_i2s.out = new AudioOutputI2SNoDAC();
-#else
- //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
- audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
-#endif // USE_I2S_NO_DAC
- audio_i2s.mclk = Pin(GPIO_I2S_MCLK);
- audio_i2s.bclk = Pin(GPIO_I2S_BCLK);
- audio_i2s.ws = Pin(GPIO_I2S_WS);
- audio_i2s.dout = Pin(GPIO_I2S_DOUT);
- audio_i2s.din = Pin(GPIO_I2S_DIN);
- } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT), 1) {
- audio_i2s.i2s_port = (i2s_port_t)1;
-#ifdef USE_I2S_NO_DAC
- audio_i2s.out = new AudioOutputI2SNoDAC();
-#else
- //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port);
- audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000);
-#endif // USE_I2S_NO_DAC
- audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1);
- audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1);
- audio_i2s.ws = Pin(GPIO_I2S_WS, 1);
- audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1);
- audio_i2s.din = Pin(GPIO_I2S_DIN, 1);
- } else {
- return -1;
- }
-#ifdef ESP8266
- // esp8266 have fixed pins
- if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) {
- return -2;
- }
-#endif // ESP8266
-#endif // defined(DAC_IIS_BCK)
-
- audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
-
- AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din);
-
-
-#else
-
-#ifdef USE_I2S_NO_DAC
- audio_i2s.out = new AudioOutputI2SNoDAC();
-#else
- audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0
-#endif // USE_I2S_NO_DAC
-
-#endif // USE_I2S_EXTERNAL_DAC
-
- return 0;
-}
-
-
-
-void I2S_Init(void) {
-
- #if defined(ESP32) && defined(ESP32S3_BOX)
- S3boxInit();
- #endif
-
- if (I2S_Init_0()) {
- return;
- }
-
- DOWNRATE
- audio_i2s.is2_volume=10;
- audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0);
- audio_i2s.out->stop();
- audio_i2s.mp3ram = nullptr;
-
-#ifdef ESP32
- if (UsePSRAM()) {
- audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- }
-
-#ifdef USE_I2S_WEBRADIO
- if (UsePSRAM()) {
- audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- } else {
- audio_i2s.preallocateBuffer = malloc(preallocateBufferSize);
- audio_i2s.preallocateCodec = malloc(preallocateCodecSize);
- }
- if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) {
- //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
- }
-#endif // USE_I2S_WEBRADIO
-
- audio_i2s.mic_channels = MIC_CHANNELS;
- audio_i2s.mic_rate = MICSRATE;
-
-#endif // ESP32
-}
-
-#ifdef ESP32
-uint32_t SpeakerMic(uint8_t spkr) {
- esp_err_t err = ESP_OK;
-
- if (audio_i2s.out) {
- audio_i2s.out->stop();
- delete audio_i2s.out;
- audio_i2s.out = nullptr;
- }
-
- i2s_driver_uninstall(audio_i2s.i2s_port);
-
- if (spkr == MODE_SPK) {
- I2S_Init_0();
- audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0);
- audio_i2s.out->stop();
- DOWNRATE
- } else {
- // config mic
- i2s_config_t i2s_config = {
- .mode = (i2s_mode_t)(I2S_MODE_MASTER),
- .sample_rate = audio_i2s.mic_rate,
- .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
- .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
- .communication_format = I2S_COMM_FORMAT_I2S,
- .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
- .dma_buf_count = 2,
- //.dma_buf_len = 128,
- .dma_buf_len = 1024,
- .use_apll = 0, // Use audio PLL
- .tx_desc_auto_clear = true,
- .fixed_mclk = 0,
- .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128
- .bits_per_chan = I2S_BITS_PER_CHAN_16BIT
- };
-
-#ifdef ESP32S3_BOX
- i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
- i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX);
- i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
-#endif
-
-#ifdef USE_I2S_MIC
- i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX);
- // mic select to GND
- i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
- i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
-#endif
-
-#ifdef USE_M5STACK_CORE2
- i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM);
-#endif
-
- err += i2s_driver_install(audio_i2s.i2s_port, &i2s_config, 0, NULL);
-
- i2s_pin_config_t tx_pin_config;
-#ifdef ESP32S3_BOX
- tx_pin_config.mck_io_num = audio_i2s.mclk;
-#else
- tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE;
-#endif
- tx_pin_config.bck_io_num = audio_i2s.bclk;
- tx_pin_config.ws_io_num = audio_i2s.ws;
- tx_pin_config.data_out_num = audio_i2s.dout;
- tx_pin_config.data_in_num = audio_i2s.din;
-
- err += i2s_set_pin(audio_i2s.i2s_port, &tx_pin_config);
-#ifdef ESP32S3_BOX
- err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
-#else
- err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
-#endif
- }
- return err;
-}
-#endif //ESP32
-
-#ifdef ESP32
-
-#ifdef USE_SHINE
-#include
-#include
-
-// micro to mp3 file
-void mic_task(void *arg){
- int8_t error = 0;
- uint8_t *ucp;
- int written;
- shine_config_t config;
- shine_t s = nullptr;
- uint16_t samples_per_pass;
- File mp3_out = (File)nullptr;
- int16_t *buffer = nullptr;
- uint16_t bytesize;
- uint16_t bwritten;
-
- mp3_out = ufsp->open(audio_i2s.mic_path, "w");
- if (!mp3_out) {
- error = -1;
- goto exit;
- }
-
- shine_set_config_mpeg_defaults(&config.mpeg);
-
- if (audio_i2s.mic_channels == 1) {
- config.mpeg.mode = MONO;
- } else {
- config.mpeg.mode = STEREO;
- }
- config.mpeg.bitr = 128;
- config.wave.samplerate = audio_i2s.mic_rate;
- config.wave.channels = (channels)audio_i2s.mic_channels;
-
- if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
- error = -3;
- goto exit;
- }
-
- s = shine_initialise(&config);
- if (!s) {
- error = -4;
- goto exit;
- }
-
- samples_per_pass = shine_samples_per_pass(s);
- bytesize = samples_per_pass * 2 * audio_i2s.mic_channels;
-
- buffer = (int16_t*)malloc(bytesize);
- if (!buffer) {
- error = -5;
- goto exit;
- }
-
- while (!audio_i2s.mic_stop) {
- uint32_t bytes_read;
- i2s_read(audio_i2s.i2s_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS));
- ucp = shine_encode_buffer_interleaved(s, buffer, &written);
- bwritten = mp3_out.write(ucp, written);
- if (bwritten != written) {
- break;
- }
- }
- ucp = shine_flush(s, &written);
- mp3_out.write(ucp, written);
-
-exit:
- if (s) {
- shine_close(s);
- }
- if (mp3_out) {
- mp3_out.close();
- }
- if (buffer) {
- free(buffer);
- }
-
- SpeakerMic(MODE_SPK);
- audio_i2s.mic_stop = 0;
- audio_i2s.mic_error = error;
- AddLog(LOG_LEVEL_INFO, PSTR("task error: %d"), error);
- audio_i2s.mic_task_h = 0;
- vTaskDelete(NULL);
-
-}
-
-int32_t i2s_record_shine(char *path) {
-esp_err_t err = ESP_OK;
-
- if (audio_i2s.decoder || audio_i2s.mp3) return 0;
-
- err = SpeakerMic(MODE_MIC);
- if (err) {
- SpeakerMic(MODE_SPK);
- return err;
- }
-
- strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path));
-
- audio_i2s.mic_stop = 0;
- err = xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1);
-
- return err;
-}
-
-#else
-// micro to wav file
-
-#define DATA_SIZE 1024
-
-void mic_task(void *arg){
- uint32_t data_offset = 0;
- while (1) {
- uint32_t bytes_read;
- i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS));
- if (bytes_read != DATA_SIZE) break;
- data_offset += DATA_SIZE;
- if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break;
- }
- SpeakerMic(MODE_SPK);
- SaveWav(audio_i2s.mic_path, audio_i2s.mic_buff, audio_i2s.mic_size);
- free(audio_i2s.mic_buff);
- vTaskDelete(audio_i2s.mic_task_h);
-}
-
-uint32_t i2s_record(char *path, uint32_t secs) {
- esp_err_t err = ESP_OK;
-
- if (audio_i2s.decoder || audio_i2s.mp3) return 0;
-
- err = SpeakerMic(MODE_MIC);
- if (err) {
- SpeakerMic(MODE_SPK);
- return err;
- }
-
- audio_i2s.mic_size = secs * audio_i2s.mic_rate * 2 * audio_i2s.mic_channels;
-
- audio_i2s.mic_buff = (uint8_t*)heap_caps_malloc(audio_i2s.mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
- if (!audio_i2s.mic_buff) return 2;
-
- if (*path=='+') {
- path++;
- strlcpy(audio_i2s.mic_path, path , sizeof(audio_i2s.mic_path));
- xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1);
- return 0;
- }
-
- uint32_t data_offset = 0;
- uint32_t stime=millis();
- while (1) {
- uint32_t bytes_read;
- i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS));
- if (bytes_read != DATA_SIZE) break;
- data_offset += DATA_SIZE;
- if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break;
- delay(0);
- }
- //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime);
- SpeakerMic(MODE_SPK);
- // save to path
- SaveWav(path, audio_i2s.mic_buff, audio_i2s.mic_size);
- free(audio_i2s.mic_buff);
- return 0;
-}
-
-static const uint8_t wavHTemplate[] PROGMEM = { // Hardcoded simple WAV header with 0xffffffff lengths all around
- 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45,
- 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00,
- 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff };
-
-bool SaveWav(char *path, uint8_t *buff, uint32_t size) {
- File fwp = ufsp->open(path, "w");
- if (!fwp) return false;
- uint8_t wavHeader[sizeof(wavHTemplate)];
- memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate));
-
- uint8_t channels = audio_i2s.mic_channels;
- uint32_t hertz = audio_i2s.mic_rate;
- uint8_t bps = 16;
-
- wavHeader[22] = channels & 0xff;
- wavHeader[23] = 0;
- wavHeader[24] = hertz & 0xff;
- wavHeader[25] = (hertz >> 8) & 0xff;
- wavHeader[26] = (hertz >> 16) & 0xff;
- wavHeader[27] = (hertz >> 24) & 0xff;
- int byteRate = hertz * bps * channels / 8;
- wavHeader[28] = byteRate & 0xff;
- wavHeader[29] = (byteRate >> 8) & 0xff;
- wavHeader[30] = (byteRate >> 16) & 0xff;
- wavHeader[31] = (byteRate >> 24) & 0xff;
- wavHeader[32] = channels * bps / 8;
- wavHeader[33] = 0;
- wavHeader[34] = bps;
- wavHeader[35] = 0;
-
- fwp.write(wavHeader, sizeof(wavHeader));
-
- fwp.write(buff, size);
- fwp.close();
-
- return true;
-}
-#endif // USE_SHINE
-
-#endif // ESP32
-
-#ifdef ESP32
-void mp3_task(void *arg) {
- while (1) {
- while (audio_i2s.mp3->isRunning()) {
- if (!audio_i2s.mp3->loop()) {
- audio_i2s.mp3->stop();
- mp3_delete();
- audio_i2s.out->stop();
- if (audio_i2s.mp3_task_h) {
- vTaskDelete(audio_i2s.mp3_task_h);
- audio_i2s.mp3_task_h = 0;
- }
- //mp3_task_h=nullptr;
- }
- delay(1);
- }
- }
-}
-#endif // ESP32
-
-#ifdef USE_I2S_WEBRADIO
-void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) {
- const char *ptr = reinterpret_cast(cbData);
- (void) isUnicode; // Punt this ball for now
- (void) ptr;
- if (strstr_P(type, PSTR("Title"))) {
- strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title));
- audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0;
- //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title);
- } else {
- // Who knows what to do? Not me!
- }
-}
-
-void StatusCallback(void *cbData, int code, const char *string) {
- const char *ptr = reinterpret_cast(cbData);
- (void) code;
- (void) ptr;
- //strncpy_P(status, string, sizeof(status)-1);
- //status[sizeof(status)-1] = 0;
-}
-
-void Webradio(const char *url) {
- if (audio_i2s.decoder || audio_i2s.mp3) return;
- if (!audio_i2s.out) return;
- AUDIO_PWR_ON
- audio_i2s.ifile = new AudioFileSourceICYStream(url);
- audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL);
- audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize);
- audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL);
- audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize);
- audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL);
- audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out);
- if (!audio_i2s.decoder->isRunning()) {
- // Serial.printf_P(PSTR("Can't connect to URL"));
- StopPlaying();
- // strcpy_P(status, PSTR("Unable to connect to URL"));
- audio_i2s.retryms = millis() + 2000;
- }
-
- xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
-}
-
-void mp3_task2(void *arg){
- while (1) {
- if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) {
- if (!audio_i2s.decoder->loop()) {
- StopPlaying();
- //retryms = millis() + 2000;
- }
- delay(1);
- }
- }
-}
-
-void StopPlaying() {
-
- if (audio_i2s.mp3_task_h) {
- vTaskDelete(audio_i2s.mp3_task_h);
- audio_i2s.mp3_task_h = nullptr;
- }
-
- if (audio_i2s.decoder) {
- audio_i2s.decoder->stop();
- delete audio_i2s.decoder;
- audio_i2s.decoder = NULL;
- }
-
- if (audio_i2s.buff) {
- audio_i2s.buff->close();
- delete audio_i2s.buff;
- audio_i2s.buff = NULL;
- }
-
- if (audio_i2s.ifile) {
- audio_i2s.ifile->close();
- delete audio_i2s.ifile;
- audio_i2s.ifile = NULL;
- }
- DOWNRATE
- AUDIO_PWR_OFF
-}
-
-void Cmd_WebRadio(void) {
- if (audio_i2s.decoder) {
- StopPlaying();
- }
- if (XdrvMailbox.data_len > 0) {
- Webradio(XdrvMailbox.data);
- ResponseCmndChar(XdrvMailbox.data);
- } else {
- ResponseCmndChar_P(PSTR("Stopped"));
- }
-}
-
-#ifdef USE_WEBSERVER
-const char HTTP_WEBRADIO[] PROGMEM =
- "{s}" "I2S_WR-Title" "{m}%s{e}";
-
-void I2S_WR_Show(bool json) {
- if (audio_i2s.decoder) {
- if (json) {
- ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title);
- } else {
- WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title);
- }
- }
-}
-#endif // USE_WEBSERVER
-
-#endif // USE_I2S_WEBRADIO
-
-#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC)
-#ifdef USE_SHINE
-void Cmd_MicRec(void) {
-
- if (audio_i2s.mic_task_h) {
- // stop task
- audio_i2s.mic_stop = 1;
- while (audio_i2s.mic_stop) {
- delay(1);
- }
- ResponseCmndChar_P(PSTR("Stopped"));
- }
- if (XdrvMailbox.data_len > 0) {
- i2s_record_shine(XdrvMailbox.data);
- ResponseCmndChar(XdrvMailbox.data);
- }
-}
-#else
-void Cmd_MicRec(void) {
-
- if (audio_i2s.mic_task_h) {
- // stop task
- vTaskDelete(audio_i2s.mic_task_h);
- audio_i2s.mic_task_h = nullptr;
- ResponseCmndChar_P(PSTR("Stopped"));
- }
-
- if (XdrvMailbox.data_len > 0) {
- uint16 time = 10;
- char *cp = strchr(XdrvMailbox.data, ':');
- if (cp) {
- time = atoi(cp + 1);
- *cp = 0;
- }
- if (time<10) time = 10;
- if (time>30) time = 30;
- i2s_record(XdrvMailbox.data, time);
- ResponseCmndChar(XdrvMailbox.data);
- }
-}
-#endif // USE_SHINE
-#endif // USE_M5STACK_CORE2
-
-#ifdef ESP32
-void Play_mp3(const char *path) {
-#ifdef USE_UFILESYS
- if (audio_i2s.decoder || audio_i2s.mp3) return;
- if (!audio_i2s.out) return;
-
- bool I2S_Task;
-
- if (*path=='+') {
- I2S_Task = true;
- path++;
- } else {
- I2S_Task = false;
- }
-
- if (!ufsp->exists(path)) {
- return;
- }
-
- AUDIO_PWR_ON
-
- audio_i2s.file = new AudioFileSourceFS(*ufsp, path);
-
- audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file);
-
- if (audio_i2s.mp3ram) {
- audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize);
- } else {
- audio_i2s.mp3 = new AudioGeneratorMP3();
- }
- audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out);
-
- if (I2S_Task) {
- xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1);
- } else {
- while (audio_i2s.mp3->isRunning()) {
- if (!audio_i2s.mp3->loop()) {
- audio_i2s.mp3->stop();
- break;
- }
- OsWatchLoop();
- }
- audio_i2s.out->stop();
- mp3_delete();
- }
-
-#endif // USE_UFILESYS
-}
-
-void mp3_delete(void) {
- delete audio_i2s.file;
- delete audio_i2s.id3;
- delete audio_i2s.mp3;
- audio_i2s.mp3=nullptr;
- DOWNRATE
- AUDIO_PWR_OFF
-}
-#endif // ESP32
-
-void Say(char *text) {
-
- if (!audio_i2s.out) return;
-
- AUDIO_PWR_ON
-
- audio_i2s.out->begin();
- ESP8266SAM *sam = new ESP8266SAM;
- sam->Say(audio_i2s.out, text);
- delete sam;
- audio_i2s.out->stop();
-
- DOWNRATE
- AUDIO_PWR_OFF
-}
-
-
-const char kI2SAudio_Commands[] PROGMEM = "I2S|"
- "Say|Gain|Time"
-#ifdef ESP32
- "|Play"
-#ifdef USE_I2S_WEBRADIO
- "|WR"
-#endif // USE_I2S_WEBRADIO
-#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC)
- "|REC"
-#ifdef WAV2MP3
- "|W2M"
-#endif
-#endif // USE_M5STACK_CORE2
-#endif // ESP32
- ;
-
-void (* const I2SAudio_Command[])(void) PROGMEM = {
- &Cmd_Say, &Cmd_Gain, &Cmd_Time
-#ifdef ESP32
- ,&Cmd_Play
-#ifdef USE_I2S_WEBRADIO
- ,&Cmd_WebRadio
-#endif // USE_I2S_WEBRADIO
-#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC)
- ,&Cmd_MicRec
-#ifdef WAV2MP3
- ,&Cmd_wav2mp3
-#endif
-#endif // USE_M5STACK_CORE2
-
-#endif // ESP32
-};
-
-void Cmd_Play(void) {
- if (XdrvMailbox.data_len > 0) {
- Play_mp3(XdrvMailbox.data);
- }
- ResponseCmndChar(XdrvMailbox.data);
-}
-
-void Cmd_Gain(void) {
- if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
- if (audio_i2s.out) {
- audio_i2s.is2_volume=XdrvMailbox.payload;
- audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0);
- }
- }
- ResponseCmndNumber(audio_i2s.is2_volume);
-}
-
-#ifdef WAV2MP3
-void Cmd_wav2mp3(void) {
- if (XdrvMailbox.data_len > 0) {
-#ifdef USE_SHINE
- wav2mp3(XdrvMailbox.data);
-#endif // USE_SHINE
- }
- ResponseCmndChar(XdrvMailbox.data);
-}
-#endif // WAV2MP3
-
-void Cmd_Say(void) {
- if (XdrvMailbox.data_len > 0) {
- Say(XdrvMailbox.data);
- }
- ResponseCmndChar(XdrvMailbox.data);
-}
void Cmd_Time(void) {
-#ifdef USE_I2S_SAY_TIME
+ if (!audio_i2s.out) return;
sayTime(RtcTime.hour, RtcTime.minute);
-#endif // USE_I2S_SAY_TIME
ResponseCmndDone();
}
-
-/*********************************************************************************************\
- * Interface
-\*********************************************************************************************/
-
-bool Xdrv42(uint8_t function) {
- bool result = false;
-
- switch (function) {
- case FUNC_COMMAND:
- result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command);
- break;
- case FUNC_INIT:
- I2S_Init();
- break;
-#ifdef USE_WEBSERVER
-#ifdef USE_I2S_WEBRADIO
- case FUNC_WEB_SENSOR:
- I2S_WR_Show(false);
- break;
-#endif // USE_I2S_WEBRADIO
-#endif // USE_WEBSERVER
-#ifdef USE_I2S_WEBRADIO
- case FUNC_JSON_APPEND:
- I2S_WR_Show(true);
- break;
-#endif // USE_I2S_WEBRADIO
- }
- return result;
-}
-
-#endif // USE_I2S_AUDIO
+#endif // USE_I2S_SAY_TIME
+#endif // is2audio
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino
similarity index 56%
rename from tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino
rename to tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino
index 46e000b5e..8bf927fc2 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino
@@ -1,4 +1,23 @@
+/*
+ audio is2 support for ESP32-S3 box and box lite
+
+ Copyright (C) 2022 Gerhard Mutz and Theo Arends
+
+ 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 .
+*/
+
#ifdef ESP32
#ifdef ESP32S3_BOX
#include
@@ -7,9 +26,11 @@
#include
#include
+
+#define S3BOX_APWR_GPIO 46
+
void S3boxAudioPower(uint8_t pwr) {
- pinMode(46 , OUTPUT);
- digitalWrite(46, pwr);
+ digitalWrite(S3BOX_APWR_GPIO, pwr);
}
// box lite dac init
@@ -111,130 +132,9 @@ void S3boxInit() {
// box full
ES8311_init();
es7210_init();
+
+ pinMode(S3BOX_APWR_GPIO , OUTPUT);
}
}
#endif // ESP32S3_BOX
-
-#ifdef USE_SHINE
-#ifdef WAV2MP3
-
-#include
-#include
-
-typedef uint8_t mp3buf_t;
-
-
-// min freq = 16 KHz Stereo or 32 KHz Mono
-int32_t wav2mp3(char *path) {
- int32_t error = 0;
- shine_config_t config;
- shine_t s = nullptr;
- File wav_in = (File)nullptr;
- File mp3_out = (File)nullptr;
- uint8_t *ucp;
- int written;
- int16_t *buffer = nullptr;
- uint32_t bread;
- uint16_t samples_per_pass;
- char mpath[64];
- char *cp;
- uint8_t chans = 1;
- uint32_t sfreq = 16000;
-
- strlcpy(mpath, path, sizeof(mpath));
-
- wav_in = ufsp->open(mpath, FS_FILE_READ);
- if (!wav_in) {
- error = -1;
- goto exit;
- }
-
- // script>wav2mp3("/test2.wav")
- uint8_t wavHeader[sizeof(wavHTemplate)];
- wav_in.read((uint8_t*)wavHeader, sizeof(wavHTemplate));
- chans = wavHeader[22];
- sfreq = wavHeader[24]|(wavHeader[25]<<8)|(wavHeader[26]<<16)|(wavHeader[27]<<24);
-
- cp = strchr(mpath, '.');
- if (!cp) {
- error = -6;
- goto exit;
- }
-
- strcpy(cp, ".mp3");
-
- mp3_out = ufsp->open(mpath, FS_FILE_WRITE);
- if (!mp3_out) {
- error = -2;
- goto exit;
- }
-
- shine_set_config_mpeg_defaults(&config.mpeg);
-
- if (chans == 1) {
- config.mpeg.mode = MONO;
- } else {
- config.mpeg.mode = STEREO;
- }
- config.mpeg.bitr = 128;
- config.wave.samplerate = sfreq;
- config.wave.channels = (channels)chans;
-
-
- if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) {
- error = -3;
- goto exit;
- }
-
- s = shine_initialise(&config);
- if (!s) {
- error = -4;
- goto exit;
- }
-
- samples_per_pass = shine_samples_per_pass(s);
-
-
- buffer = (int16_t*)malloc(samples_per_pass * 2 * chans);
- if (!buffer) {
- error = -5;
- goto exit;
- }
-
- AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding %d channels with freq %d Hz"), chans, sfreq);
-
- while (1) {
- bread = wav_in.read((uint8_t*)buffer, samples_per_pass * 2 * chans);
- if (!bread) {
- break;
- }
- ucp = shine_encode_buffer_interleaved(s, buffer, &written);
- mp3_out.write(ucp, written);
- }
- ucp = shine_flush(s, &written);
- mp3_out.write(ucp, written);
-
-exit:
- if (s) {
- shine_close(s);
- }
- if (wav_in) {
- wav_in.close();
- }
- if (mp3_out) {
- mp3_out.close();
- }
-
- if (buffer) {
- free(buffer);
- }
-
- AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding exit with code: %d"), error);
-
- return error;
-}
-#endif // WAV2MP3
-#endif // USE_SHINE
-
-
-#endif
+#endif // ESP32