mirror of https://github.com/arendst/Tasmota.git
More audio refactoring for core3 (#19749)
This commit is contained in:
parent
baef3eed91
commit
16307bc4b8
|
@ -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 },
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 "נגן מוזיקה"
|
||||||
|
|
|
@ -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ó"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue