diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino deleted file mode 100644 index c49798fa1..000000000 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino +++ /dev/null @@ -1,772 +0,0 @@ -/* - 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 ESP8266 || (ESP_IDF_VERSION_MAJOR < 5) -#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 -//#define USE_I2S_RTTTL // Add support for Rtttl playback -//#define USE_LSB // Add support for LSBJ chips, e.g. TM8211/PT8211 -// Microphone support -//#define USE_I2S_MIC // Add support for I2S microphone - //#define MIC_CHANNELS 1 // 1 = mono (I2S_CHANNEL_FMT_ONLY_RIGHT), 2 = stereo (I2S_CHANNEL_FMT_RIGHT_LEFT) - //#define MICSRATE 32000 // Set sample rate - //#define USE_INMP441 // Add support for INMP441 MEMS microphone - //#define MIC_PDM // Set microphone as PDM (only on ESP32) -//#define USE_SHINE // Use MP3 encoding (only on ESP32 with PSRAM) -//#define MP3_MIC_STREAM // Add support for streaming microphone via http (only on ESP32 with PSRAM) - //#define MP3_STREAM_PORT 81 // Choose MP3 stream port (default = 81) -//#define I2S_BRIDGE // Add support for UDP PCM audio bridge - //#define I2S_BRIDGE_PORT 6970 // Set bridge port (default = 6970) - -#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 - -#define MODE_MIC 1 -#define MODE_SPK 2 - -#ifndef MICSRATE -#define MICSRATE 32000 -#endif - - -typedef union { - uint8_t data; - struct { - uint8_t master : 1; - uint8_t enabled : 1; - uint8_t swap_mic : 1; - uint8_t mode : 2; - }; -} BRIDGE_MODE; - - -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; -#endif // ESP32 - 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; - uint8_t mic_gain = 1; - bool use_stream = false; - i2s_port_t mic_port; - - -#ifdef USE_SHINE - uint32_t recdur; - uint8_t stream_active; - uint8_t stream_enable; - WiFiClient client; - ESP8266WebServer *MP3Server; -#endif - - uint8_t mode; - -#ifdef I2S_BRIDGE - BRIDGE_MODE bridge_mode; - WiFiUDP i2s_bridge_udp; - WiFiUDP i2s_bridgec_udp; - IPAddress i2s_bridge_ip; - TaskHandle_t i2s_bridge_h; - int8_t ptt_pin = -1; -#endif - - -} audio_i2s; - -#ifndef MIC_CHANNELS -#define MIC_CHANNELS 1 -#endif - -#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); -#ifdef USE_I2S_RTTTL -void Rtttl(char *buffer); -void Cmd_I2SRtttl(void); -#endif - -void copy_micpars(uint32_t port) { - 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)port; -} - -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; - - copy_micpars(0); - -#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); - - copy_micpars(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); - - copy_micpars(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); - } - -#ifdef USE_I2S_LSB - audio_i2s.out->SetLsbJustified(true); -#endif // Allow supporting LSBJ chips, e.g. TM8211/PT8211 - -#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 - - audio_i2s.mode = MODE_SPK; - - return 0; -} - -void I2S_Init(void) { - - if (I2S_Init_0()) { - return; - } - -#if defined(ESP32) && defined(ESP32S3_BOX) - S3boxInit(); -#endif - -#ifdef USE_W8960 - W8960_Init1(); -#endif - - audio_i2s.is2_volume = 10; - audio_i2s.out->SetGain(((float)audio_i2s.is2_volume / 100.0) * 4.0); - audio_i2s.out->begin(); - 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; - -#ifdef USE_I2S_COMMON_IO - i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); -#endif - -#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 { -#define MP3_TIMEOUT 30000 - uint32_t tout = millis(); - while (audio_i2s.mp3->isRunning()) { - if (!audio_i2s.mp3->loop()) { - audio_i2s.mp3->stop(); - break; - } - OsWatchLoop(); - if (millis()-tout > MP3_TIMEOUT) { - break; - } - } - 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 USE_I2S_RTTTL - "|Rtttl" -#endif -#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" - "|MGain" -#if defined(USE_SHINE) && defined(MP3_MIC_STREAM) - "|STREAM" -#endif // MP3_MIC_STREAM -#ifdef I2S_BRIDGE - "|BRIDGE" -#endif // I2S_BRIDGE -#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 USE_I2S_RTTTL - ,&Cmd_I2SRtttl -#endif -#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 - ,&Cmd_MicGain -#if defined(USE_SHINE) && defined(MP3_MIC_STREAM) - ,&Cmd_MP3Stream -#endif // MP3_MIC_STREAM -#ifdef I2S_BRIDGE - ,&Cmd_I2SBridge -#endif // I2S_BRIDGE -#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); -} - -#ifdef USE_I2S_RTTTL -void Cmd_I2SRtttl(void) { - if (XdrvMailbox.data_len > 0) { - Rtttl(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); -} -#endif - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -void i2s_mp3_loop(void); -void i2s_mp3_init(void); -void MP3ShowStream(void); - -bool Xdrv42(uint32_t function) { - bool result = false; - - switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); - break; - case FUNC_INIT: - I2S_Init(); - break; - case FUNC_WEB_ADD_MAIN_BUTTON: - //MP3ShowStream(); - break; - case FUNC_LOOP: -#if defined(USE_SHINE) && defined(MP3_MIC_STREAM) - i2s_mp3_loop(); -#endif -#if defined(I2S_BRIDGE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) - i2s_bridge_loop(); -#endif - break; - case FUNC_WEB_ADD_HANDLER: -#if defined(USE_SHINE) && defined(MP3_MIC_STREAM) - audio_i2s.stream_enable = 1; - i2s_mp3_init(1); -#endif -#if defined(I2S_BRIDGE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) - I2SBridgeInit(); -#endif - 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 - - case FUNC_ACTIVE: - result = true; - break; - } - return result; -} - -#endif // USE_I2S_AUDIO -#endif // ESP8266 || (ESP_IDF_VERSION_MAJOR < 5) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino deleted file mode 100644 index 17974cc6e..000000000 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino +++ /dev/null @@ -1,335 +0,0 @@ -/* - 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(ESP32) && (ESP_IDF_VERSION_MAJOR < 5) -#if ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) - -uint32_t SpeakerMic(uint8_t spkr) { -esp_err_t err = ESP_OK; - -#ifndef USE_I2S_COMMON_IO - - if (audio_i2s.mode == spkr) { - return 0; - } - if (spkr == MODE_SPK) { - - if (audio_i2s.i2s_port == audio_i2s.mic_port) { - if (audio_i2s.mode != MODE_SPK) { - i2s_driver_uninstall(audio_i2s.mic_port); - } - I2S_Init_0(); - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume - 2) / 100.0) * 4.0); - audio_i2s.out->stop(); - } - audio_i2s.mode = spkr; - return 0; - } - - // set micro - if (audio_i2s.i2s_port == audio_i2s.mic_port) { - // close audio out - if (audio_i2s.out) { - audio_i2s.out->stop(); - delete audio_i2s.out; - audio_i2s.out = nullptr; - } - if (audio_i2s.mode == MODE_SPK) { - 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 = 8, - .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 = 12000000, - .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 - .bits_per_chan = I2S_BITS_PER_CHAN_16BIT - }; - - 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; - } - -#ifdef USE_I2S_MIC - // Mic L/R to GND - #ifdef USE_INMP441 - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX ); - i2s_config.communication_format = I2S_COMM_FORMAT_I2S; - i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT; - #elif MIC_PDM - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); - #else - 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 -#endif - - -#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_M5STACK_CORE2 - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); -#endif - - - 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); - -#endif // USE_I2S_COMMON_IO - - audio_i2s.mode = spkr; - - return err; -} - -#ifdef USE_SHINE - -#include -#include - -// 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"); - - // Webserver->send(200, "application/octet-stream", ""); - //"Content-Type: audio/mp3;\r\n\r\n"); - } - - 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)); - - if (audio_i2s.mic_gain > 1) { - // set gain - for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) { - buffer[cnt] *= audio_i2s.mic_gain; - } - } - 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); - - 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); - } - - -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(); - } - - SpeakerMic(MODE_SPK); - audio_i2s.mic_stop = 0; - audio_i2s.mic_error = error; - AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %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 - - -// mic gain in factor not percent -void Cmd_MicGain(void) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 256)) { - audio_i2s.mic_gain = XdrvMailbox.payload; - } - ResponseCmndNumber(audio_i2s.mic_gain); -} - -#endif // USE_I2S_AUDIO -#endif // ESP32 && (ESP_IDF_VERSION_MAJOR < 5) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino deleted file mode 100644 index 1fc06477f..000000000 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino +++ /dev/null @@ -1,64 +0,0 @@ - -#if defined(ESP32) && (ESP_IDF_VERSION_MAJOR < 5) -#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) - -#ifdef MP3_MIC_STREAM - -#ifndef MP3_STREAM_PORT -#define MP3_STREAM_PORT 81 -#endif - - -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(MP3_STREAM_PORT); - audio_i2s.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3); - audio_i2s.MP3Server->on(PSTR("/stream.m3a"), Stream_mp3); - audio_i2s.MP3Server->begin(); - AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created on port: %d "), MP3_STREAM_PORT); - } - } 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 // defined(ESP32) && (ESP_IDF_VERSION_MAJOR < 5)