More audio refactoring for core3 (#19749)

This commit is contained in:
s-hadinger 2023-10-15 11:22:20 +02:00 committed by GitHub
parent baef3eed91
commit 16307bc4b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 597 additions and 185 deletions

View File

@ -101,6 +101,7 @@ const be_const_member_t lv_gpio_constants[] = {
{ "HX711_SCK", (int32_t) GPIO_HX711_SCK }, { "HX711_SCK", (int32_t) GPIO_HX711_SCK },
{ "I2C_SCL", (int32_t) GPIO_I2C_SCL }, { "I2C_SCL", (int32_t) GPIO_I2C_SCL },
{ "I2C_SDA", (int32_t) GPIO_I2C_SDA }, { "I2C_SDA", (int32_t) GPIO_I2C_SDA },
{ "I2S_DAC", (int32_t) GPIO_I2S_DAC },
{ "I2S_IN_CLK", (int32_t) GPIO_I2S_BCLK_IN }, { "I2S_IN_CLK", (int32_t) GPIO_I2S_BCLK_IN },
{ "I2S_IN_DATA", (int32_t) GPIO_I2S_DIN }, { "I2S_IN_DATA", (int32_t) GPIO_I2S_DIN },
{ "I2S_IN_SLCT", (int32_t) GPIO_I2S_WS_IN }, { "I2S_IN_SLCT", (int32_t) GPIO_I2S_WS_IN },

View File

@ -213,6 +213,7 @@ enum UserSelectablePins {
GPIO_DINGTIAN_OE, // New version of Dingtian relay board where PL is not shared with OE GPIO_DINGTIAN_OE, // New version of Dingtian relay board where PL is not shared with OE
GPIO_HDMI_CEC, // Support for HDMI CEC GPIO_HDMI_CEC, // Support for HDMI CEC
GPIO_HC8_RXD, // HC8 Serial interface GPIO_HC8_RXD, // HC8 Serial interface
GPIO_I2S_DAC, // Audio DAC support for ESP32 and ESP32S2
GPIO_SENSOR_END }; GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045 // Error as warning to rethink GPIO usage with max 2045
@ -473,6 +474,7 @@ const char kSensorNames[] PROGMEM =
D_GPIO_DINGTIAN_OE "|" D_GPIO_DINGTIAN_OE "|"
D_SENSOR_HDMI_CEC "|" D_SENSOR_HDMI_CEC "|"
D_SENSOR_HC8_RX "|" D_SENSOR_HC8_RX "|"
D_SENSOR_I2S_DAC "|"
; ;
const char kSensorNamesFixed[] PROGMEM = const char kSensorNamesFixed[] PROGMEM =
@ -578,9 +580,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#if defined(USE_I2S_AUDIO) || defined (USE_I2S) #if defined(USE_I2S_AUDIO) || defined (USE_I2S)
AGPIO(GPIO_I2S_MCLK) + MAX_I2S, // I2S master clock AGPIO(GPIO_I2S_MCLK) + MAX_I2S, // I2S master clock
AGPIO(GPIO_I2S_BCLK) + MAX_I2S, // I2S bit clock AGPIO(GPIO_I2S_BCLK) + MAX_I2S, // I2S bit clock
AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data
AGPIO(GPIO_I2S_DAC) + 2, // I2S DAC Output
AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select
AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data
AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data
#endif #endif
#ifdef USE_I2S #ifdef USE_I2S
AGPIO(GPIO_I2S_BCLK_IN) + MAX_I2S, // I2S bit clock in AGPIO(GPIO_I2S_BCLK_IN) + MAX_I2S, // I2S bit clock in

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562 "MP3 Speler"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "Reproductor MP3" #define D_SENSOR_DFR562 "Reproductor MP3"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS In" #define D_SENSOR_I2S_BCLK_IN "I2S WS In"
#define D_SENSOR_I2S_DIN "I2S DIn" #define D_SENSOR_I2S_DIN "I2S DIn"
#define D_SENSOR_I2S_DOUT "I2S DOut" #define D_SENSOR_I2S_DOUT "I2S DOut"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562 "MP3 Speler"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "נגן מוזיקה" #define D_SENSOR_DFR562 "נגן מוזיקה"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 lejátszó" #define D_SENSOR_DFR562 "MP3 lejátszó"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S - WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S - WS IN"
#define D_SENSOR_I2S_DIN "I2S - DIN" #define D_SENSOR_I2S_DIN "I2S - DIN"
#define D_SENSOR_I2S_DOUT "I2S - DOUT" #define D_SENSOR_I2S_DOUT "I2S - DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "Riproduttore MP3" #define D_SENSOR_DFR562 "Riproduttore MP3"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562 "MP3 Speler"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "Odtwarzacz MP3" #define D_SENSOR_DFR562 "Odtwarzacz MP3"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "Leitor de MP3" #define D_SENSOR_DFR562 "Leitor de MP3"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -658,6 +658,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 spelare" #define D_SENSOR_DFR562 "MP3 spelare"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -657,6 +657,7 @@
#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" #define D_SENSOR_I2S_BCLK_IN "I2S WS IN"
#define D_SENSOR_I2S_DIN "I2S DIN" #define D_SENSOR_I2S_DIN "I2S DIN"
#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_I2S_DOUT "I2S DOUT"
#define D_SENSOR_I2S_DAC "I2S DAC"
#define D_SENSOR_HDMI_CEC "HDMI CEC" #define D_SENSOR_HDMI_CEC "HDMI CEC"
#define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_WS2812 "WS2812"
#define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562 "MP3 Player"

View File

@ -25,20 +25,6 @@
#include "driver/gpio.h" #include "driver/gpio.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "AudioFileSourcePROGMEM.h"
#include "AudioFileSourceID3.h"
#include "AudioGeneratorMP3.h"
#include <ESP8266SAM.h>
#include "AudioFileSourceFS.h"
#include "AudioGeneratorTalkie.h"
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorAAC.h"
#include <layer3.h>
#include <types.h>
/*********************************************************************************************\ /*********************************************************************************************\
* Driver Settings in memory * Driver Settings in memory
\*********************************************************************************************/ \*********************************************************************************************/
@ -134,41 +120,8 @@ struct AUDIO_I2S_t {
tI2SSettings *Settings; tI2SSettings *Settings;
i2s_chan_handle_t rx_handle = nullptr; i2s_chan_handle_t rx_handle = nullptr;
AudioGeneratorMP3 *mp3 = nullptr;
AudioFileSourceFS *file = nullptr;
TasmotaI2S *out = nullptr; // instance used for I2S output, or `nullptr` if none TasmotaI2S *out = nullptr; // instance used for I2S output, or `nullptr` if none
TasmotaI2S *in = nullptr; // instance used for I2S input, or `nullptr` if none (it can be the same as `out` in case of full duplex) TasmotaI2S *in = nullptr; // instance used for I2S input, or `nullptr` if none (it can be the same as `out` in case of full duplex)
AudioFileSourceID3 *id3 = nullptr;
AudioGeneratorMP3 *decoder = NULL;
void *mp3ram = NULL;
TaskHandle_t mp3_task_handle;
TaskHandle_t mic_task_handle;
char mic_path[32];
uint8_t mic_stop;
int8_t mic_error;
bool use_stream = false;
// SHINE
uint32_t recdur;
uint8_t stream_active;
uint8_t stream_enable;
WiFiClient client;
ESP8266WebServer *MP3Server;
// 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;
} audio_i2s; } audio_i2s;
#endif // USE_I2S_AUDIO #endif // USE_I2S_AUDIO

View File

@ -20,6 +20,17 @@
#if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 #if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5
#ifdef USE_I2S_AUDIO #ifdef USE_I2S_AUDIO
#include "AudioOutput.h"
#include "driver/dac_continuous.h"
// If DAC is not supported, proide some placeholders
#ifndef SOC_DAC_SUPPORTED
#define dac_continuous_enable(...) (0xFF)
#define dac_continuous_disable(...) (0xFF)
#define dac_continuous_del_channels(...) (0xFF)
#define dac_continuous_write(...) (0xFF)
#endif
/*********************************************************************************************\ /*********************************************************************************************\
* Driver Settings in memory * Driver Settings in memory
\*********************************************************************************************/ \*********************************************************************************************/
@ -67,7 +78,8 @@ public:
} }
~TasmotaI2S() { ~TasmotaI2S() {
this->stopTx(); delRxHandle();
delTxHandle();
} }
/*********************************************************************************************\ /*********************************************************************************************\
@ -137,6 +149,11 @@ public:
inline uint8_t getTxChannels(void) const { return channels; } inline uint8_t getTxChannels(void) const { return channels; }
inline bool getTxRunning(void) const { return _tx_running; } inline bool getTxRunning(void) const { return _tx_running; }
inline i2s_chan_handle_t getTxHandle(void) const { return _tx_handle; } inline i2s_chan_handle_t getTxHandle(void) const { return _tx_handle; }
#ifdef SOC_DAC_SUPPORTED
inline bool isDACMode(void) const { return (_tx_mode == I2S_MODE_DAC); }
#else
inline bool isDACMode(void) const { return false; }
#endif
inline uint8_t getRxMode(void) const { return _rx_mode; } inline uint8_t getRxMode(void) const { return _rx_mode; }
inline uint8_t getRxBitsPerSample(void) const { return 16; } // TODO - hardcoded to 16 bits for recording inline uint8_t getRxBitsPerSample(void) const { return 16; } // TODO - hardcoded to 16 bits for recording
@ -151,7 +168,12 @@ public:
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
// Setters // Setters
inline void setExclusive(bool exclusive) { _exclusive = exclusive; } inline void setExclusive(bool exclusive) { _exclusive = exclusive; }
inline void setTxMode(uint8_t mode) { _tx_mode = mode; } inline void setTxMode(uint8_t mode) {
_tx_mode = mode;
if (_tx_mode == I2S_MODE_DAC) {
_tx_configured = true;
}
}
inline void setTxChannels(uint8_t channels) { SetChannels(channels); } inline void setTxChannels(uint8_t channels) { SetChannels(channels); }
inline void setTxRunning(bool running) { _tx_running = running; } inline void setTxRunning(bool running) { _tx_running = running; }
inline void setRxMode(uint8_t mode) { _rx_mode = mode; } inline void setRxMode(uint8_t mode) { _rx_mode = mode; }
@ -181,7 +203,6 @@ public:
bool stopTx(void); bool stopTx(void);
bool ConsumeSample(int16_t sample[2]); bool ConsumeSample(int16_t sample[2]);
bool startI2SChannel(bool tx, bool rx); bool startI2SChannel(bool tx, bool rx);
bool updateClockConfig(void);
int32_t readMic(uint8_t *buffer, uint32_t size, bool dc_block, bool apply_gain, bool lowpass, uint32_t *peak_ptr); int32_t readMic(uint8_t *buffer, uint32_t size, bool dc_block, bool apply_gain, bool lowpass, uint32_t *peak_ptr);
@ -203,6 +224,10 @@ public:
protected: protected:
int16_t dcFilter(int16_t pcm_in); int16_t dcFilter(int16_t pcm_in);
int16_t lowpassFilter(int16_t pcm_in); int16_t lowpassFilter(int16_t pcm_in);
bool updateClockConfig(void);
bool delTxHandle(void); // remove handle
bool delRxHandle(void);
protected: protected:
@ -289,7 +314,12 @@ bool TasmotaI2S::beginTx(void) {
} }
} }
esp_err_t err = i2s_channel_enable(_tx_handle); esp_err_t err = ESP_OK;
if (isDACMode()) {
err = dac_continuous_enable((dac_continuous_handle_t) _tx_handle);
} else {
err = i2s_channel_enable(_tx_handle);
}
AddLog(LOG_LEVEL_INFO, "I2S: Tx i2s_channel_enable err=0x%04X", err); AddLog(LOG_LEVEL_INFO, "I2S: Tx i2s_channel_enable err=0x%04X", err);
if (err != ERR_OK){ if (err != ERR_OK){
return false; return false;
@ -300,17 +330,26 @@ bool TasmotaI2S::beginTx(void) {
} }
bool TasmotaI2S::stopTx() { bool TasmotaI2S::stopTx() {
esp_err_t err = ESP_OK;
AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopTx() tx_running:%i tx_handle:%p", _tx_running, _tx_handle); AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopTx() tx_running:%i tx_handle:%p", _tx_running, _tx_handle);
if (!_tx_configured) { return false; } // invalid action if (!_tx_configured) { return false; } // invalid action
if (!_tx_handle) { return true; } // nothing to do if (!_tx_handle) { return true; } // nothing to do
if (_tx_running) { if (_tx_running) {
esp_err_t err = i2s_channel_disable(_tx_handle); if (isDACMode()) {
err = dac_continuous_disable((dac_continuous_handle_t) _tx_handle);
} else {
err = i2s_channel_disable(_tx_handle);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_channel_disable err=0x%04X", err); AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_channel_disable err=0x%04X", err);
_tx_running = false; _tx_running = false;
} }
if (_exclusive) { // exclusive mode, deregister channel if (_exclusive) { // exclusive mode, deregister channel
if (_tx_handle) { if (_tx_handle) {
esp_err_t err = i2s_del_channel(_tx_handle); if (isDACMode()) {
err = dac_continuous_del_channels((dac_continuous_handle_t) _tx_handle);
} else {
err = i2s_del_channel(_tx_handle);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_del_channel err=0x%04X", err); AddLog(LOG_LEVEL_DEBUG, "I2S: stopTx i2s_del_channel err=0x%04X", err);
_tx_handle = nullptr; _tx_handle = nullptr;
} }
@ -319,6 +358,36 @@ bool TasmotaI2S::stopTx() {
return true; return true;
} }
bool TasmotaI2S::delTxHandle(void) {
esp_err_t err = ESP_OK;
AddLog(LOG_LEVEL_DEBUG, "I2S: calling delTxHandle() tx_running:%i tx_handle:%p", _tx_running, _tx_handle);
if (!_tx_configured) { return false; } // invalid action
if (!_tx_handle) { return true; } // nothing to do
if (_tx_running) { stopTx(); }
if (isDACMode()) {
err = dac_continuous_del_channels((dac_continuous_handle_t) _tx_handle);
} else {
err = i2s_del_channel(_tx_handle);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_del_channel Tx err=0x%04X", err);
_tx_handle = nullptr;
return true;
}
bool TasmotaI2S::delRxHandle(void) {
esp_err_t err = ESP_OK;
AddLog(LOG_LEVEL_DEBUG, "I2S: calling delRxHandle() rx_running:%i rx_handle:%p", _rx_running, _rx_handle);
if (!_rx_configured) { return false; } // invalid action
if (!_rx_handle) { return true; } // nothing to do
if (_rx_running) { stopRx(); }
err = i2s_del_channel(_rx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_del_channel Rx err=0x%04X", err);
_rx_handle = nullptr;
return true;
}
bool TasmotaI2S::stopRx(void) { bool TasmotaI2S::stopRx(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopRx() rx_running:%i rx_handle:%p", _rx_running, _rx_handle); AddLog(LOG_LEVEL_DEBUG, "I2S: calling stopRx() rx_running:%i rx_handle:%p", _rx_running, _rx_handle);
if (!_rx_configured) { return false; } // nothing configured if (!_rx_configured) { return false; } // nothing configured
@ -364,7 +433,7 @@ int32_t TasmotaI2S::consumeSamples(int16_t *samples, size_t count) {
right = (((int16_t)(right & 0xff)) - 128) << 8; right = (((int16_t)(right & 0xff)) - 128) << 8;
} }
if (_tx_mode == I2S_MODE_DAC) { if (isDACMode()) {
left = Amplify(left) + 0x8000; left = Amplify(left) + 0x8000;
right = Amplify(right) + 0x8000; right = Amplify(right) + 0x8000;
} else { } else {
@ -379,7 +448,14 @@ int32_t TasmotaI2S::consumeSamples(int16_t *samples, size_t count) {
// AddLog(LOG_LEVEL_DEBUG, "I2S: consumeSamples: left=%i right=%i", ms[0], ms[1]); // AddLog(LOG_LEVEL_DEBUG, "I2S: consumeSamples: left=%i right=%i", ms[0], ms[1]);
size_t i2s_bytes_written; size_t i2s_bytes_written;
esp_err_t err = i2s_channel_write(_tx_handle, ms, sizeof(ms), &i2s_bytes_written, 0); 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);
} else {
err = i2s_channel_write(_tx_handle, ms, sizeof(ms), &i2s_bytes_written, 0);
}
if (err && err != ESP_ERR_TIMEOUT) { if (err && err != ESP_ERR_TIMEOUT) {
AddLog(LOG_LEVEL_INFO, "I2S: Could not write samples (count=%i): %i", count, err); AddLog(LOG_LEVEL_INFO, "I2S: Could not write samples (count=%i): %i", count, err);
return -1; return -1;
@ -417,15 +493,19 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
gpio_num_t _DIN = rx ? (gpio_num_t)_gpio_din : I2S_GPIO_UNUSED; // no input pin if no Rx gpio_num_t _DIN = rx ? (gpio_num_t)_gpio_din : I2S_GPIO_UNUSED; // no input pin if no Rx
if (tx) { if (tx && !isDACMode()) {
// default dma_desc_num = 6 (DMA buffers), dma_frame_num = 240 (frames per buffer) // default dma_desc_num = 6 (DMA buffers), dma_frame_num = 240 (frames per buffer)
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER); i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(_i2s_port, I2S_ROLE_MASTER);
AddLog(LOG_LEVEL_DEBUG, "I2S: tx_chan_cfg id:%i role:%i dma_desc_num:%i dma_frame_num:%i auto_clear:%i", AddLog(LOG_LEVEL_DEBUG, "I2S: tx_chan_cfg id:%i role:%i dma_desc_num:%i dma_frame_num:%i auto_clear:%i",
tx_chan_cfg.id, tx_chan_cfg.role, tx_chan_cfg.dma_desc_num, tx_chan_cfg.dma_frame_num, tx_chan_cfg.auto_clear); tx_chan_cfg.id, tx_chan_cfg.role, tx_chan_cfg.dma_desc_num, tx_chan_cfg.dma_frame_num, tx_chan_cfg.auto_clear);
err = i2s_new_channel(&tx_chan_cfg, &_tx_handle, rx ? &_rx_handle : NULL); // configure Rx only in duplex non-exclusive err = i2s_new_channel(&tx_chan_cfg, &_tx_handle, rx ? &_rx_handle : NULL); // configure Rx only in duplex non-exclusive
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_new_channel Tx err:0x%04X", err); AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_new_channel Tx err:0x%04X", err);
if (err != ERR_OK) {
_tx_handle = nullptr;
return false;
}
// by default we configure for MSB 2 slots `I2S_STD_MSB_SLOT_DEFAULT_CONFIG` // by default we configure for MSB 2 slots `I2S_STD_MSB_SLOT_DEFAULT_CONFIG`
i2s_std_config_t tx_std_cfg = { i2s_std_config_t tx_std_cfg = {
@ -452,6 +532,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
tx_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels); tx_std_cfg.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bps, (i2s_slot_mode_t)channels);
} }
if (_tx_slot_mask != I2S_SLOT_NOCHANGE) { tx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_tx_slot_mask; } if (_tx_slot_mask != I2S_SLOT_NOCHANGE) { tx_std_cfg.slot_cfg.slot_mask = (i2s_std_slot_mask_t)_tx_slot_mask; }
if (_apll) { tx_std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL; }
err = i2s_channel_init_std_mode(_tx_handle, &tx_std_cfg); err = i2s_channel_init_std_mode(_tx_handle, &tx_std_cfg);
AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_channel_init_std_mode TX channel bits:%i channels:%i hertz:%i err=0x%04X", bps, channels, hertz, err); AddLog(LOG_LEVEL_DEBUG, "I2S: i2s_channel_init_std_mode TX channel bits:%i channels:%i hertz:%i err=0x%04X", bps, channels, hertz, err);
@ -467,6 +548,29 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
} }
} // if (tx) } // if (tx)
#ifdef SOC_DAC_SUPPORTED
if (tx && isDACMode()) {
dac_continuous_config_t dac_chan_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 6,
.buf_size = 240,
.freq_hz = hertz,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_APLL, /*DAC_DIGI_CLK_SRC_DEFAULT*/
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
// 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);
err = dac_continuous_new_channels(&dac_chan_cfg, (dac_continuous_handle_t*) &_tx_handle);
AddLog(LOG_LEVEL_DEBUG, "I2S: dac_new_channel Tx err:0x%04X", err);
if (err != ERR_OK) {
_tx_handle = nullptr;
return false;
}
} // if (tx) && DAC
#endif // SOC_DAC_SUPPORTED
// configure Rx Microphone // configure Rx Microphone
if (rx) { if (rx) {
gpio_num_t clk_gpio; gpio_num_t clk_gpio;
@ -474,7 +578,7 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
i2s_slot_mode_t slot_mode = (_rx_channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO; i2s_slot_mode_t slot_mode = (_rx_channels == 1) ? I2S_SLOT_MODE_MONO : I2S_SLOT_MODE_STEREO;
AddLog(LOG_LEVEL_DEBUG, "I2S: mic init rx_channels:%i rx_running:%i rx_handle:%p", slot_mode, _rx_running, _rx_handle); AddLog(LOG_LEVEL_DEBUG, "I2S: mic init rx_channels:%i rx_running:%i rx_handle:%p", slot_mode, _rx_running, _rx_handle);
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(_i2s_port, I2S_ROLE_MASTER);
// change to 3 buffers of 512 samples // change to 3 buffers of 512 samples
rx_chan_cfg.dma_desc_num = 3; rx_chan_cfg.dma_desc_num = 3;
rx_chan_cfg.dma_frame_num = 512; rx_chan_cfg.dma_frame_num = 512;
@ -555,26 +659,39 @@ bool TasmotaI2S::startI2SChannel(bool tx, bool rx) {
return true; return true;
} }
// called only if Tx frequency is changed
bool TasmotaI2S::updateClockConfig(void) { bool TasmotaI2S::updateClockConfig(void) {
if (!_tx_handle) { return true; } if (!_tx_handle) { return true; }
if (_tx_running) {
esp_err_t err = i2s_channel_disable(_tx_handle); // I2S mode
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_disable err=0x%04X", err); if (_tx_mode != I2S_MODE_DAC) {
if (_tx_running) {
esp_err_t err = i2s_channel_disable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_disable err=0x%04X", err);
}
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(hertz);
#ifdef SOC_I2S_SUPPORTS_APLL
if (_apll) {
clk_cfg.clk_src = I2S_CLK_SRC_APLL;
}
#endif
esp_err_t result = i2s_channel_reconfig_std_clock(_tx_handle, &clk_cfg);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_reconfig_std_clock err=0x%04X", result);
if (_tx_running) {
esp_err_t err = i2s_channel_enable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_enable err=0x%04X", err);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config");
return result == ESP_OK;
} else {
// DAC mode
// It looks like you can't change the DAC frequency without removing and recreating the DAC diver
delTxHandle();
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating DAC clock config");
return startI2SChannel(true, false);
} }
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(hertz);
#ifdef SOC_I2S_SUPPORTS_APLL
if (_apll) {
clk_cfg.clk_src = I2S_CLK_SRC_APLL;
}
#endif
esp_err_t result = i2s_channel_reconfig_std_clock(_tx_handle, &clk_cfg );
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_reconfig_std_clock err=0x%04X", result);
if (_tx_running) {
esp_err_t err = i2s_channel_enable(_tx_handle);
AddLog(LOG_LEVEL_INFO, "I2S: updateClockConfig i2s_channel_enable err=0x%04X", err);
}
AddLog(LOG_LEVEL_DEBUG, "I2S: Updating clock config");
return result == ESP_OK;
} }
/*********************************************************************************************\ /*********************************************************************************************\

View File

@ -17,8 +17,22 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 #ifdef ESP32
#if defined(USE_I2S_AUDIO) #if defined(USE_I2S_AUDIO) && ESP_IDF_VERSION_MAJOR >= 5
#include <ESP8266SAM.h>
#include "AudioFileSourcePROGMEM.h"
#include "AudioFileSourceID3.h"
#include "AudioGeneratorMP3.h"
#include "AudioFileSourceFS.h"
#include "AudioGeneratorTalkie.h"
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorAAC.h"
#include <layer3.h>
#define XDRV_42 42 #define XDRV_42 42
@ -26,6 +40,7 @@
#define USE_I2S_SAY_TIME #define USE_I2S_SAY_TIME
#define USE_I2S_RTTTL #define USE_I2S_RTTTL
#define USE_I2S_WEBRADIO #define USE_I2S_WEBRADIO
#define USE_I2S_MP3
#define USE_I2S_DEBUG // remove before flight #define USE_I2S_DEBUG // remove before flight
// Macros used in audio sub-functions // Macros used in audio sub-functions
@ -51,12 +66,57 @@ void Rtttl(char *buffer);
void CmndI2SRtttl(void); void CmndI2SRtttl(void);
void I2sWebRadioStopPlaying(void); void I2sWebRadioStopPlaying(void);
/*********************************************************************************************\
* More structures
\*********************************************************************************************/
struct AUDIO_I2S_MP3_t {
#ifdef USE_I2S_MP3
AudioGeneratorMP3 *mp3 = nullptr;
AudioFileSourceFS *file = nullptr;
AudioFileSourceID3 *id3 = nullptr;
void *mp3ram = NULL;
#endif // USE_I2S_MP3
#if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
AudioGeneratorMP3 *decoder = NULL;
TaskHandle_t mp3_task_handle;
TaskHandle_t mic_task_handle;
#endif // defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
char mic_path[32];
uint8_t mic_stop;
int8_t mic_error;
bool use_stream = false;
// SHINE
uint32_t recdur;
uint8_t stream_active;
uint8_t stream_enable;
WiFiClient client;
ESP8266WebServer *MP3Server;
// 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;
} audio_i2s_mp3;
/*********************************************************************************************\ /*********************************************************************************************\
* Commands definitions * Commands definitions
\*********************************************************************************************/ \*********************************************************************************************/
const char kI2SAudio_Commands[] PROGMEM = "I2S|" const char kI2SAudio_Commands[] PROGMEM = "I2S|"
"Gain|Play|Rec|MGain|Stop|Config" "Gain|Rec|MGain|Stop|Config"
#ifdef USE_I2S_MP3
"|Play"
#endif
#ifdef USE_I2S_DEBUG #ifdef USE_I2S_DEBUG
"|Mic" // debug only "|Mic" // debug only
#endif // USE_I2S_DEBUG #endif // USE_I2S_DEBUG
@ -81,7 +141,10 @@ const char kI2SAudio_Commands[] PROGMEM = "I2S|"
; ;
void (* const I2SAudio_Command[])(void) PROGMEM = { void (* const I2SAudio_Command[])(void) PROGMEM = {
&CmndI2SGain, &CmndI2SPlay, &CmndI2SMicRec, &CmndI2SMicGain, &CmndI2SStop, &CmndI2SConfig, &CmndI2SGain, &CmndI2SMicRec, &CmndI2SMicGain, &CmndI2SStop, &CmndI2SConfig,
#ifdef USE_I2S_MP3
&CmndI2SPlay,
#endif
#ifdef USE_I2S_DEBUG #ifdef USE_I2S_DEBUG
&CmndI2SMic, &CmndI2SMic,
#endif // USE_I2S_DEBUG #endif // USE_I2S_DEBUG
@ -141,7 +204,7 @@ void CmndI2SConfig(void) {
JsonParserToken tx_tk = root["Tx"]; JsonParserToken tx_tk = root["Tx"];
if (tx_tk.isObject()) { if (tx_tk.isObject()) {
JsonParserObject tx = tx_tk.getObject(); JsonParserObject tx = tx_tk.getObject();
// cfg->tx.sample_rate = tx.getUInt(PSTR("SampleRate"), cfg->tx.sample_rate); cfg->tx.sample_rate = tx.getUInt(PSTR("SampleRate"), cfg->tx.sample_rate);
cfg->tx.gain = tx.getUInt(PSTR("Gain"), cfg->tx.gain); cfg->tx.gain = tx.getUInt(PSTR("Gain"), cfg->tx.gain);
cfg->tx.mode = tx.getUInt(PSTR("Mode"), cfg->tx.mode); cfg->tx.mode = tx.getUInt(PSTR("Mode"), cfg->tx.mode);
cfg->tx.slot_mask = tx.getUInt(PSTR("SlotMask"), cfg->tx.slot_mask); cfg->tx.slot_mask = tx.getUInt(PSTR("SlotMask"), cfg->tx.slot_mask);
@ -259,21 +322,21 @@ void I2sMicTask(void *arg){
uint32_t ctime; uint32_t ctime;
uint32_t gain = audio_i2s.Settings->rx.gain; uint32_t gain = audio_i2s.Settings->rx.gain;
if (!audio_i2s.use_stream) { if (!audio_i2s_mp3.use_stream) {
mp3_out = ufsp->open(audio_i2s.mic_path, "w"); mp3_out = ufsp->open(audio_i2s_mp3.mic_path, "w");
if (!mp3_out) { if (!mp3_out) {
error = 1; error = 1;
goto exit; goto exit;
} }
} else { } else {
if (!audio_i2s.stream_active) { if (!audio_i2s_mp3.stream_active) {
error = 2; error = 2;
audio_i2s.use_stream = 0; audio_i2s_mp3.use_stream = 0;
goto exit; goto exit;
} }
audio_i2s.client.flush(); audio_i2s_mp3.client.flush();
audio_i2s.client.setTimeout(3); audio_i2s_mp3.client.setTimeout(3);
audio_i2s.client.print("HTTP/1.1 200 OK\r\n" audio_i2s_mp3.client.print("HTTP/1.1 200 OK\r\n"
"Content-Type: audio/mpeg;\r\n\r\n"); "Content-Type: audio/mpeg;\r\n\r\n");
// Webserver->send(200, "application/octet-stream", ""); // Webserver->send(200, "application/octet-stream", "");
@ -314,7 +377,7 @@ void I2sMicTask(void *arg){
ctime = TasmotaGlobal.uptime; ctime = TasmotaGlobal.uptime;
while (!audio_i2s.mic_stop) { while (!audio_i2s_mp3.mic_stop) {
size_t bytes_read; size_t bytes_read;
i2s_channel_read(audio_i2s.rx_handle, (void*)buffer, bytesize, &bytes_read, (100 / portTICK_PERIOD_MS)); i2s_channel_read(audio_i2s.rx_handle, (void*)buffer, bytesize, &bytes_read, (100 / portTICK_PERIOD_MS));
@ -326,27 +389,27 @@ void I2sMicTask(void *arg){
} }
ucp = shine_encode_buffer_interleaved(s, buffer, &written); ucp = shine_encode_buffer_interleaved(s, buffer, &written);
if (!audio_i2s.use_stream) { if (!audio_i2s_mp3.use_stream) {
bwritten = mp3_out.write(ucp, written); bwritten = mp3_out.write(ucp, written);
if (bwritten != written) { if (bwritten != written) {
break; break;
} }
} else { } else {
audio_i2s.client.write((const char*)ucp, written); audio_i2s_mp3.client.write((const char*)ucp, written);
if (!audio_i2s.client.connected()) { if (!audio_i2s_mp3.client.connected()) {
break; break;
} }
} }
audio_i2s.recdur = TasmotaGlobal.uptime - ctime; audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
} }
ucp = shine_flush(s, &written); ucp = shine_flush(s, &written);
if (!audio_i2s.use_stream) { if (!audio_i2s_mp3.use_stream) {
mp3_out.write(ucp, written); mp3_out.write(ucp, written);
} else { } else {
audio_i2s.client.write((const char*)ucp, written); audio_i2s_mp3.client.write((const char*)ucp, written);
} }
@ -362,17 +425,17 @@ exit:
free(buffer); free(buffer);
} }
if (audio_i2s.use_stream) { if (audio_i2s_mp3.use_stream) {
audio_i2s.client.stop(); audio_i2s_mp3.client.stop();
} }
audio_i2s.out->stopRx(); audio_i2s.out->stopRx();
audio_i2s.mic_stop = 0; audio_i2s_mp3.mic_stop = 0;
audio_i2s.mic_error = error; audio_i2s_mp3.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error); AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
audio_i2s.mic_task_handle = 0; audio_i2s_mp3.mic_task_handle = 0;
audio_i2s.recdur = 0; audio_i2s_mp3.recdur = 0;
audio_i2s.stream_active = 0; audio_i2s_mp3.stream_active = 0;
vTaskDelete(NULL); vTaskDelete(NULL);
} }
@ -380,19 +443,21 @@ exit:
int32_t I2sRecordShine(char *path) { int32_t I2sRecordShine(char *path) {
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
if (audio_i2s.decoder || audio_i2s.mp3) return 0; #ifdef USE_I2S_MP3
if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return 0;
#endif
strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path)); strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path));
audio_i2s.mic_stop = 0; audio_i2s_mp3.mic_stop = 0;
uint32_t stack = 4096; uint32_t stack = 4096;
audio_i2s.use_stream = !strcmp(audio_i2s.mic_path, "stream.mp3"); audio_i2s_mp3.use_stream = !strcmp(audio_i2s_mp3.mic_path, "stream.mp3");
if (audio_i2s.use_stream) { if (audio_i2s_mp3.use_stream) {
stack = 8000; stack = 8000;
} }
audio_i2s.out->startRx(); audio_i2s.out->startRx();
err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s.mic_task_handle, 1); err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1);
return err; return err;
} }
@ -479,10 +544,12 @@ void I2sInit(void) {
int32_t gpio_dout_0 = Pin(GPIO_I2S_DOUT, 0); int32_t gpio_dout_0 = Pin(GPIO_I2S_DOUT, 0);
int32_t gpio_dout_1 = Pin(GPIO_I2S_DOUT, 1); int32_t gpio_dout_1 = Pin(GPIO_I2S_DOUT, 1);
int32_t gpio_ws_0 = Pin(GPIO_I2S_WS, 0); int32_t gpio_ws_0 = Pin(GPIO_I2S_WS, 0);
int32_t gpio_dac_0 = Pin(GPIO_I2S_DAC, 0); // DAC-1 needs to be comfigured if DAC-2 is needed as well
int32_t gpio_dac_1 = Pin(GPIO_I2S_DAC, 1); // DAC-1 needs to be comfigured if DAC-2 is needed as well
// we need at least one pin configured // we need at least one pin configured
// Note: in case of ESP32 DAC output we may have only WS_0 configured. DAC is only supported on port 0 // Note: in case of ESP32 DAC output we may have only WS_0 configured. DAC is only supported on port 0
if ((gpio_din_0 < 0) && (gpio_din_1 < 0) && (gpio_dout_0 < 0) && (gpio_dout_1 < 0) && (gpio_ws_0 < 0)) { if ((gpio_din_0 < 0) && (gpio_din_1 < 0) && (gpio_dout_0 < 0) && (gpio_dout_1 < 0) && (gpio_ws_0 < 0) && (gpio_dac_0 < 0)) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no pin configured")); AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: no pin configured"));
return; return;
} }
@ -492,6 +559,10 @@ void I2sInit(void) {
bool duplex = false; // the same ports are used for input and output bool duplex = false; // the same ports are used for input and output
bool exclusive = false; // signals that in/out have a shared GPIO and need to un/install driver before use bool exclusive = false; // signals that in/out have a shared GPIO and need to un/install driver before use
bool dac_mode = (gpio_dac_0 >= 0);
if (dac_mode) {
audio_i2s.Settings->tx.mode = I2S_MODE_DAC;
}
// AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), // AddLog(LOG_LEVEL_INFO, PSTR("I2S: init pins bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"),
// Pin(GPIO_I2S_BCLK, 0) , Pin(GPIO_I2S_WS, 0), Pin(GPIO_I2S_DOUT, 0), Pin(GPIO_I2S_MCLK, 0), Pin(GPIO_I2S_DIN, 0)); // Pin(GPIO_I2S_BCLK, 0) , Pin(GPIO_I2S_WS, 0), Pin(GPIO_I2S_DOUT, 0), Pin(GPIO_I2S_MCLK, 0), Pin(GPIO_I2S_DIN, 0));
@ -513,18 +584,17 @@ void I2sInit(void) {
AddLog(LOG_LEVEL_DEBUG, "I2S: I2S%i bclk=%i, ws=%i, dout=%i, mclk=%i, din=%i", port, bclk, ws, dout, mclk, din); AddLog(LOG_LEVEL_DEBUG, "I2S: I2S%i bclk=%i, ws=%i, dout=%i, mclk=%i, din=%i", port, bclk, ws, dout, mclk, din);
// if neither input, nor output, nor DAC/ADC skip (WS could is only needed for DAC but supports only port 0) // if neither input, nor output, nor DAC/ADC skip (WS could is only needed for DAC but supports only port 0)
if (din < 0 && dout < 0 && (ws < 0 || port !=0)) { continue; } if (din < 0 && dout < 0 && (!(dac_mode && port == 0))) { continue; }
duplex = (din >= 0) && (dout >= 0); duplex = (din >= 0) && (dout >= 0);
if (duplex) { if (duplex) {
if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){ if (audio_i2s.Settings->rx.mode == I2S_MODE_PDM || audio_i2s.Settings->tx.mode == I2S_MODE_PDM ){
exclusive = true; exclusive = true;
} }
AddLog(LOG_LEVEL_DEBUG, "I2S: enabling duplex mode, exclusive;%i", exclusive); AddLog(LOG_LEVEL_DEBUG, "I2S: enabling duplex mode, exclusive:%i", exclusive);
} }
const char *err_msg = nullptr; // to save code, we indicate an error with a message configured const char *err_msg = nullptr; // to save code, we indicate an error with a message configured
bool dac_mode = false;
if (din >= 0 || dout >= 0) { if (din >= 0 || dout >= 0) {
// we have regular I2S configuration // we have regular I2S configuration
// do multiple checks // do multiple checks
@ -566,10 +636,11 @@ void I2sInit(void) {
err_msg = "PDM Tx is not supported"; err_msg = "PDM Tx is not supported";
} }
} else { } else {
dac_mode = true;
// no DIN/DOUT, try DAC mode // no DIN/DOUT, try DAC mode
// 1. Check that tx.mode is I2S_MODE_DAC // 1. Check that tx.mode is I2S_MODE_DAC
if (audio_i2s.Settings->tx.mode != I2S_MODE_DAC) { if (dac_mode) {
AddLog(LOG_LEVEL_DEBUG, "I2S: Configuraing DAC mode DAC0=%i DAC1=%i", gpio_dac_0, gpio_dac_1);
} else {
err_msg = "DAC mode is not enabled"; err_msg = "DAC mode is not enabled";
} }
} }
@ -646,13 +717,14 @@ void I2sInit(void) {
audio_i2s.out->beginTx(); // TODO - useful? audio_i2s.out->beginTx(); // TODO - useful?
audio_i2s.out->stopTx(); audio_i2s.out->stopTx();
} }
audio_i2s.mp3ram = nullptr; #ifdef USE_I2S_MP3
audio_i2s_mp3.mp3ram = nullptr;
if (audio_i2s.Settings->sys.mp3_preallocate == 1){ if (audio_i2s.Settings->sys.mp3_preallocate == 1){
// if (UsePSRAM()) { // if (UsePSRAM()) {
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will allocate buffer for mp3 encoder")); AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will allocate buffer for mp3 encoder"));
audio_i2s.mp3ram = special_malloc(preallocateCodecSize); audio_i2s_mp3.mp3ram = special_malloc(preallocateCodecSize);
} }
#endif // USE_I2S_MP3
AddLog(LOG_LEVEL_DEBUG, "I2S: I2sInit done"); AddLog(LOG_LEVEL_DEBUG, "I2S: I2sInit done");
} }
@ -693,16 +765,17 @@ int32_t I2SPrepareRx(void) {
* Driver features and commands * Driver features and commands
\*********************************************************************************************/ \*********************************************************************************************/
#if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
void I2sMp3Task(void *arg) { void I2sMp3Task(void *arg) {
while (1) { while (1) {
while (audio_i2s.mp3->isRunning()) { while (audio_i2s_mp3.mp3->isRunning()) {
if (!audio_i2s.mp3->loop()) { if (!audio_i2s_mp3.mp3->loop()) {
audio_i2s.mp3->stop(); audio_i2s_mp3.mp3->stop();
mp3_delete(); mp3_delete();
audio_i2s.out->stop(); audio_i2s.out->stop();
if (audio_i2s.mp3_task_handle) { if (audio_i2s_mp3.mp3_task_handle) {
vTaskDelete(audio_i2s.mp3_task_handle); vTaskDelete(audio_i2s_mp3.mp3_task_handle);
audio_i2s.mp3_task_handle = 0; audio_i2s_mp3.mp3_task_handle = 0;
} }
//mp3_task_handle=nullptr; //mp3_task_handle=nullptr;
} }
@ -710,6 +783,7 @@ void I2sMp3Task(void *arg) {
} }
} }
} }
#endif // defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO)
void I2sStatusCallback(void *cbData, int code, const char *string) { void I2sStatusCallback(void *cbData, int code, const char *string) {
const char *ptr = reinterpret_cast<const char *>(cbData); const char *ptr = reinterpret_cast<const char *>(cbData);
@ -719,10 +793,11 @@ void I2sStatusCallback(void *cbData, int code, const char *string) {
//status[sizeof(status)-1] = 0; //status[sizeof(status)-1] = 0;
} }
#ifdef USE_I2S_MP3
void I2sMp3Task2(void *arg){ void I2sMp3Task2(void *arg){
while (1) { while (1) {
if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { if (audio_i2s_mp3.decoder && audio_i2s_mp3.decoder->isRunning()) {
if (!audio_i2s.decoder->loop()) { if (!audio_i2s_mp3.decoder->loop()) {
I2sStopPlaying(); I2sStopPlaying();
//retryms = millis() + 2000; //retryms = millis() + 2000;
} }
@ -730,33 +805,38 @@ void I2sMp3Task2(void *arg){
} }
} }
} }
void I2SStopMP3Play(void) {
if (audio_i2s_mp3.mp3_task_handle) {
vTaskDelete(audio_i2s_mp3.mp3_task_handle);
audio_i2s_mp3.mp3_task_handle = nullptr;
}
if (audio_i2s_mp3.decoder) {
audio_i2s_mp3.decoder->stop();
delete audio_i2s_mp3.decoder;
audio_i2s_mp3.decoder = NULL;
}
}
#endif // USE_I2S_MP3
void I2sStopPlaying() { void I2sStopPlaying() {
#ifdef USE_I2S_MP3
if (audio_i2s.mp3_task_handle) { I2SStopMP3Play();
vTaskDelete(audio_i2s.mp3_task_handle); #endif // USE_I2S_MP3
audio_i2s.mp3_task_handle = nullptr;
}
if (audio_i2s.decoder) {
audio_i2s.decoder->stop();
delete audio_i2s.decoder;
audio_i2s.decoder = NULL;
}
#ifdef USE_I2S_WEBRADIO #ifdef USE_I2S_WEBRADIO
I2sWebRadioStopPlaying(); I2sWebRadioStopPlaying();
#endif #endif
I2SAudioPower(false); I2SAudioPower(false);
} }
#ifdef USE_I2S_MP3
// Play_mp3 - Play a MP3 file from filesystem // Play_mp3 - Play a MP3 file from filesystem
// //
// Returns I2S_error_t // Returns I2S_error_t
int32_t I2SPlayMp3(const char *path) { int32_t I2SPlayMp3(const char *path) {
int32_t i2s_err = I2S_OK; int32_t i2s_err = I2S_OK;
if ((i2s_err = I2SPrepareTx()) != I2S_OK) { return i2s_err; } if ((i2s_err = I2SPrepareTx()) != I2S_OK) { return i2s_err; }
if (audio_i2s.decoder || audio_i2s.mp3) return I2S_ERR_DECODER_IN_USE; if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return I2S_ERR_DECODER_IN_USE;
// check if the filename starts with '/', if not add it // check if the filename starts with '/', if not add it
char fname[64]; char fname[64];
@ -769,44 +849,30 @@ int32_t I2SPlayMp3(const char *path) {
I2SAudioPower(true); I2SAudioPower(true);
audio_i2s.file = new AudioFileSourceFS(*ufsp, fname); audio_i2s_mp3.file = new AudioFileSourceFS(*ufsp, fname);
audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); audio_i2s_mp3.id3 = new AudioFileSourceID3(audio_i2s_mp3.file);
if (audio_i2s.mp3ram) { if (audio_i2s_mp3.mp3ram) {
audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); audio_i2s_mp3.mp3 = new AudioGeneratorMP3(audio_i2s_mp3.mp3ram, preallocateCodecSize);
} else { } else {
audio_i2s.mp3 = new AudioGeneratorMP3(); audio_i2s_mp3.mp3 = new AudioGeneratorMP3();
} }
audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); audio_i2s_mp3.mp3->begin(audio_i2s_mp3.id3, audio_i2s.out);
// Always use a task // Always use a task
xTaskCreatePinnedToCore(I2sMp3Task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_handle, 1); xTaskCreatePinnedToCore(I2sMp3Task, "MP3", 8192, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1);
return I2S_OK; return I2S_OK;
} }
void mp3_delete(void) { void mp3_delete(void) {
delete audio_i2s.file; delete audio_i2s_mp3.file;
delete audio_i2s.id3; delete audio_i2s_mp3.id3;
delete audio_i2s.mp3; delete audio_i2s_mp3.mp3;
audio_i2s.mp3=nullptr; audio_i2s_mp3.mp3=nullptr;
I2SAudioPower(false);
}
void Say(char *text) {
if (I2SPrepareTx()) { return; }
I2SAudioPower(true);
ESP8266SAM *sam = new ESP8266SAM;
sam->Say(audio_i2s.out, text);
delete sam;
audio_i2s.out->stopTx();
I2SAudioPower(false); I2SAudioPower(false);
} }
#endif // USE_I2S_MP3
/*********************************************************************************************\ /*********************************************************************************************\
* Commands * Commands
@ -819,7 +885,6 @@ void CmndI2SMic(void) {
} }
esp_err_t err = ESP_OK; esp_err_t err = ESP_OK;
if (audio_i2s.decoder || audio_i2s.mp3) return;
audio_i2s.in->startRx(); audio_i2s.in->startRx();
if (audio_i2s.in->getRxRunning()) { if (audio_i2s.in->getRxRunning()) {
uint8_t buf[128]; uint8_t buf[128];
@ -836,7 +901,7 @@ void CmndI2SMic(void) {
AddLog(LOG_LEVEL_INFO, "I2S: Mic (%i) %*_H", btr, btr, buf); AddLog(LOG_LEVEL_INFO, "I2S: Mic (%i) %*_H", btr, btr, buf);
} }
// err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s.mic_task_handle, 1); // err = xTaskCreatePinnedToCore(I2sMicTask, "MIC", stack, NULL, 3, &audio_i2s_mp3.mic_task_handle, 1);
ResponseCmndDone(); ResponseCmndDone();
} }
@ -851,6 +916,7 @@ void CmndI2SStop(void) {
ResponseCmndDone(); ResponseCmndDone();
} }
#ifdef USE_I2S_MP3
void CmndI2SPlay(void) { void CmndI2SPlay(void) {
if (I2SPrepareTx()) { if (I2SPrepareTx()) {
ResponseCmndChar("I2S output not configured"); ResponseCmndChar("I2S output not configured");
@ -883,6 +949,7 @@ void CmndI2SPlay(void) {
ResponseCmndChar("Missing filename"); ResponseCmndChar("Missing filename");
} }
} }
#endif // USE_I2S_MP3
void CmndI2SGain(void) { void CmndI2SGain(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) {
@ -895,12 +962,18 @@ void CmndI2SGain(void) {
} }
void CmndI2SSay(void) { void CmndI2SSay(void) {
if (I2SPrepareTx()) {
ResponseCmndChar("I2S output not configured");
return;
}
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
Say(XdrvMailbox.data); if (I2SPrepareTx()) {
ResponseCmndChar("I2S output not configured");
return;
} else {
I2SAudioPower(true);
ESP8266SAM sam;
sam.Say(audio_i2s.out, XdrvMailbox.data);
audio_i2s.out->stopTx();
I2SAudioPower(false);
// end of scope, ESP8266SAM is destroyed
}
} }
ResponseCmndChar(XdrvMailbox.data); ResponseCmndChar(XdrvMailbox.data);
} }
@ -920,16 +993,16 @@ void CmndI2SMicRec(void) {
if (audio_i2s.Settings->sys.mp3_preallocate == 1) { if (audio_i2s.Settings->sys.mp3_preallocate == 1) {
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
if (!strncmp(XdrvMailbox.data, "-?", 2)) { if (!strncmp(XdrvMailbox.data, "-?", 2)) {
Response_P("{\"I2SREC-duration\":%d}", audio_i2s.recdur); Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur);
} else { } else {
I2sRecordShine(XdrvMailbox.data); I2sRecordShine(XdrvMailbox.data);
ResponseCmndChar(XdrvMailbox.data); ResponseCmndChar(XdrvMailbox.data);
} }
} else { } else {
if (audio_i2s.mic_task_handle) { if (audio_i2s_mp3.mic_task_handle) {
// stop task // stop task
audio_i2s.mic_stop = 1; audio_i2s_mp3.mic_stop = 1;
while (audio_i2s.mic_stop) { while (audio_i2s_mp3.mic_stop) {
delay(1); delay(1);
} }
ResponseCmndChar_P(PSTR("Stopped")); ResponseCmndChar_P(PSTR("Stopped"));

View File

@ -18,7 +18,7 @@
*/ */
#ifdef ESP32 #if ESP32 && (ESP_IDF_VERSION_MAJOR < 5)
#if ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) #if ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) )
uint32_t SpeakerMic(uint8_t spkr) { uint32_t SpeakerMic(uint8_t spkr) {
@ -332,4 +332,4 @@ void Cmd_MicGain(void) {
} }
#endif // USE_I2S_AUDIO #endif // USE_I2S_AUDIO
#endif // ESP32 #endif // ESP32 && (ESP_IDF_VERSION_MAJOR < 5)

View File

@ -0,0 +1,238 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5
#ifdef USE_I2S_AUDIO
uint32_t SpeakerMic(uint8_t spkr) {
esp_err_t err = ESP_OK;
// audio_i2s.mode = spkr;
return err;
}
#ifdef USE_SHINE
#include <layer3.h>
#include <types.h>
// 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_mp3.use_stream) {
mp3_out = ufsp->open(audio_i2s_mp3.mic_path, "w");
if (!mp3_out) {
error = 1;
goto exit;
}
} else {
if (!audio_i2s_mp3.stream_active) {
error = 2;
audio_i2s_mp3.use_stream = 0;
goto exit;
}
audio_i2s_mp3.client.flush();
audio_i2s_mp3.client.setTimeout(3);
audio_i2s_mp3.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_mp3.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_mp3.use_stream) {
bwritten = mp3_out.write(ucp, written);
if (bwritten != written) {
break;
}
} else {
audio_i2s_mp3.client.write((const char*)ucp, written);
if (!audio_i2s_mp3.client.connected()) {
break;
}
}
audio_i2s_mp3.recdur = TasmotaGlobal.uptime - ctime;
}
ucp = shine_flush(s, &written);
if (!audio_i2s_mp3.use_stream) {
mp3_out.write(ucp, written);
} else {
audio_i2s_mp3.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_mp3.use_stream) {
audio_i2s_mp3.client.stop();
}
SpeakerMic(MODE_SPK);
audio_i2s_mp3.mic_stop = 0;
audio_i2s_mp3.mic_error = error;
AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error);
audio_i2s.mic_task_h = 0;
audio_i2s_mp3.recdur = 0;
audio_i2s_mp3.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_mp3.decoder || audio_i2s_mp3.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_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path));
audio_i2s_mp3.mic_stop = 0;
uint32_t stack = 4096;
audio_i2s_mp3.use_stream = !strcmp(audio_i2s_mp3.mic_path, "stream.mp3");
if (audio_i2s_mp3.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_mp3.recdur);
} else {
i2s_record_shine(XdrvMailbox.data);
ResponseCmndChar(XdrvMailbox.data);
}
} else {
if (audio_i2s.mic_task_h) {
// stop task
audio_i2s_mp3.mic_stop = 1;
while (audio_i2s_mp3.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)) {
if (audio_i2s.in) {
audio_i2s.in->setRxGain(XdrvMailbox.payload);
}
if (audio_i2s.Settings) {
audio_i2s.Settings->rx.gain = XdrvMailbox.payload * 16;
}
I2SSettingsSave(AUDIO_CONFIG_FILENAME);
}
ResponseCmndNumber(audio_i2s.Settings->rx.gain / 16);
}
#endif // USE_I2S_AUDIO
#endif // defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5

View File

@ -65,17 +65,17 @@ void Webradio(const char *url) {
return; return;
} }
if (audio_i2s.decoder || audio_i2s.mp3) return; // if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return;
if (!audio_i2s.out) return; if (!audio_i2s.out) return;
I2SAudioPower(true); I2SAudioPower(true);
Audio_webradio.ifile = new AudioFileSourceICYStream(url); Audio_webradio.ifile = new AudioFileSourceICYStream(url);
Audio_webradio.ifile->RegisterMetadataCB(I2sMDCallback, NULL); Audio_webradio.ifile->RegisterMetadataCB(I2sMDCallback, NULL);
Audio_webradio.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, Audio_webradio.preallocateBuffer, preallocateBufferSize); Audio_webradio.buff = new AudioFileSourceBuffer(Audio_webradio.ifile, Audio_webradio.preallocateBuffer, preallocateBufferSize);
Audio_webradio.buff->RegisterStatusCB(I2sStatusCallback, NULL); Audio_webradio.buff->RegisterStatusCB(I2sStatusCallback, NULL);
audio_i2s.decoder = new AudioGeneratorMP3(Audio_webradio.preallocateCodec, preallocateCodecSize); audio_i2s_mp3.decoder = new AudioGeneratorMP3(Audio_webradio.preallocateCodec, preallocateCodecSize);
audio_i2s.decoder->RegisterStatusCB(I2sStatusCallback, NULL); audio_i2s_mp3.decoder->RegisterStatusCB(I2sStatusCallback, NULL);
audio_i2s.decoder->begin(Audio_webradio.buff, audio_i2s.out); audio_i2s_mp3.decoder->begin(Audio_webradio.buff, audio_i2s.out);
if (!audio_i2s.decoder->isRunning()) { if (!audio_i2s_mp3.decoder->isRunning()) {
// Serial.printf_P(PSTR("Can't connect to URL")); // Serial.printf_P(PSTR("Can't connect to URL"));
I2sStopPlaying(); I2sStopPlaying();
// strcpy_P(status, PSTR("Unable to connect to URL")); // strcpy_P(status, PSTR("Unable to connect to URL"));
@ -83,7 +83,7 @@ void Webradio(const char *url) {
} }
AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will launch webradio task")); AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will launch webradio task"));
xTaskCreatePinnedToCore(I2sMp3Task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_handle, 1); xTaskCreatePinnedToCore(I2sMp3Task2, "MP3-2", 8192, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1);
} }
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
@ -91,7 +91,7 @@ const char HTTP_WEBRADIO[] PROGMEM =
"{s}" "I2S_WR-Title" "{m}%s{e}"; "{s}" "I2S_WR-Title" "{m}%s{e}";
void I2sWrShow(bool json) { void I2sWrShow(bool json) {
if (audio_i2s.decoder) { if (audio_i2s_mp3.decoder) {
if (json) { if (json) {
ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), Audio_webradio.wr_title); ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), Audio_webradio.wr_title);
} else { } else {
@ -104,7 +104,7 @@ void I2sWrShow(bool json) {
void CmndI2SWebRadio(void) { void CmndI2SWebRadio(void) {
if (!audio_i2s.out) return; if (!audio_i2s.out) return;
if (audio_i2s.decoder) { if (audio_i2s_mp3.decoder) {
I2sStopPlaying(); I2sStopPlaying();
} }
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {