From 59da00684992fa98aa4d4031b7a2a0d8eb931358 Mon Sep 17 00:00:00 2001 From: Staars Date: Thu, 9 Jan 2025 21:24:13 +0100 Subject: [PATCH] add opus stream and file support for opus/aac --- tasmota/my_user_config.h | 1 + .../xdrv_42_0_i2s_0_config_idf51.ino | 1 + .../xdrv_42_0_i2s_audio_idf51.ino | 102 ++++++++++++------ .../xdrv_42_7_i2s_webradio_idf51.ino | 41 +++---- 4 files changed, 84 insertions(+), 61 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 920ea0f1503c..bee454c7bcc0 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1412,6 +1412,7 @@ #define MP3_MIC_STREAM #define USE_I2S_AUDIO_BERRY #define USE_I2S_AAC + #define USE_I2S_OPUS #endif // USE_I2S_ALL #endif // _MY_USER_CONFIG_H_ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino index 24223ee0d30d..1b7be962963f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_0_config_idf51.ino @@ -56,6 +56,7 @@ enum : int8_t { enum : uint32_t { AAC_DECODER = 0, MP3_DECODER = 1, + OPUS_DECODER = 2, }; #define I2S_SLOTS 2 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino index fc4269ea593d..0ccd69e6e3b9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio_idf51.ino @@ -33,6 +33,9 @@ #ifdef USE_I2S_AAC #include "AudioGeneratorAAC.h" #endif // USE_I2S_AAC +#ifdef USE_I2S_OPUS +#include "AudioGeneratorOpus.h" +#endif // USE_I2S_OPUS #include @@ -75,15 +78,14 @@ void CmndI2SMP3Stream(void); struct AUDIO_I2S_MP3_t { #ifdef USE_I2S_MP3 - AudioGeneratorMP3 *mp3 = nullptr; + AudioGenerator *decoder = nullptr; AudioFileSourceFS *file = nullptr; AudioFileSourceID3 *id3 = nullptr; - void *mp3ram = NULL; + void *preallocateCodec = NULL; #endif // USE_I2S_MP3 #if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO) || defined(USE_SHINE) || defined(MP3_MIC_STREAM) - AudioGenerator *decoder = nullptr; TaskHandle_t mp3_task_handle; TaskHandle_t mic_task_handle; #endif // defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO) @@ -500,7 +502,7 @@ int32_t I2sRecordShine(char *path) { AddLog(LOG_LEVEL_INFO, PSTR("I2S: accepted sample rate for MP3 encoding: %d"), audio_i2s.Settings->rx.sample_rate); #ifdef USE_I2S_MP3 - if (audio_i2s_mp3.decoder || audio_i2s_mp3.mp3) return 0; + if (audio_i2s_mp3.decoder) return 0; #endif strlcpy(audio_i2s_mp3.mic_path, path, sizeof(audio_i2s_mp3.mic_path)); @@ -524,6 +526,7 @@ enum { I2S_ERR_OUTPUT_NOT_CONFIGURED, I2S_ERR_INPUT_NOT_CONFIGURED, I2S_ERR_DECODER_IN_USE, + I2S_ERR_DECODER_FAILED_TO_INIT, I2S_ERR_FILE_NOT_FOUND, I2S_ERR_TX_FAILED, }; @@ -771,11 +774,11 @@ void I2sInit(void) { // audio_i2s.out->stopTx(); // } #ifdef USE_I2S_MP3 - audio_i2s_mp3.mp3ram = nullptr; + audio_i2s_mp3.preallocateCodec = nullptr; if (audio_i2s.Settings->sys.mp3_preallocate == 1){ // if (UsePSRAM()) { AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will allocate buffer for mp3 encoder")); - audio_i2s_mp3.mp3ram = special_malloc(preallocateCodecSize); + audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize); } #endif // USE_I2S_MP3 AddLog(LOG_LEVEL_DEBUG, "I2S: I2sInit done"); @@ -826,14 +829,14 @@ int32_t I2SPrepareRx(void) { #if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO) void I2sMp3Task(void *arg) { audio_i2s_mp3.task_running = true; - while (audio_i2s_mp3.mp3->isRunning() && audio_i2s_mp3.task_running) { - if (!audio_i2s_mp3.mp3->loop()) { + while (audio_i2s_mp3.decoder->isRunning() && audio_i2s_mp3.task_running) { + if (!audio_i2s_mp3.decoder->loop()) { audio_i2s_mp3.task_running = false; } vTaskDelay(pdMS_TO_TICKS(1)); } audio_i2s.out->flush(); - audio_i2s_mp3.mp3->stop(); + audio_i2s_mp3.decoder->stop(); mp3_delete(); audio_i2s_mp3.mp3_task_handle = nullptr; audio_i2s_mp3.task_has_ended = true; @@ -877,7 +880,7 @@ void I2sStopPlaying() { while(audio_i2s_mp3.task_has_ended == false){ delay(10); } - while(audio_i2s_mp3.mp3){ + while(audio_i2s_mp3.decoder){ delay(10); } } @@ -890,13 +893,47 @@ void I2sStopPlaying() { } #ifdef USE_I2S_MP3 -// Play_mp3 - Play a MP3 file from filesystem + +bool I2SinitDecoder(uint32_t decoder_type){ + switch(decoder_type){ + case MP3_DECODER: + if (audio_i2s_mp3.preallocateCodec) { + audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorMP3(audio_i2s_mp3.preallocateCodec, preallocateCodecSize)); + } else { + audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorMP3()); + } + break; +#ifdef USE_I2S_AAC + case AAC_DECODER: + audio_i2s_mp3.preallocateCodec = special_realloc(audio_i2s_mp3.preallocateCodec, preallocateCodecSizeAAC); + if(audio_i2s_mp3.preallocateCodec == nullptr){ + AddLog(LOG_LEVEL_ERROR, "I2S: could not alloc heap for AAC"); + return false; + } + audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorAAC(audio_i2s_mp3.preallocateCodec, preallocateCodecSizeAAC)); + break; +#endif //USE_I2S_AAC +#ifdef USE_I2S_OPUS + case OPUS_DECODER: + free(audio_i2s_mp3.preallocateCodec); + audio_i2s_mp3.preallocateCodec = nullptr; + audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorOpus()); + break; +#endif //USE_I2S_OPUS + } + if(audio_i2s_mp3.decoder == nullptr){ + return false; + } + return true; +} + +// Play a audio file from filesystem // // Returns I2S_error_t -int32_t I2SPlayMp3(const char *path) { +int32_t I2SPlayFile(const char *path, uint32_t decoder_type) { int32_t i2s_err = I2SPrepareTx(); if ((i2s_err) != I2S_OK) { return i2s_err; } - if (audio_i2s_mp3.mp3) return I2S_ERR_DECODER_IN_USE; + if (audio_i2s_mp3.decoder) return I2S_ERR_DECODER_IN_USE; // check if the filename starts with '/', if not add it char fname[64]; @@ -913,30 +950,28 @@ int32_t I2SPlayMp3(const char *path) { audio_i2s_mp3.id3 = new AudioFileSourceID3(audio_i2s_mp3.file); - if (audio_i2s_mp3.mp3ram) { - audio_i2s_mp3.mp3 = new AudioGeneratorMP3(audio_i2s_mp3.mp3ram, preallocateCodecSize); + if(I2SinitDecoder(decoder_type)){ + audio_i2s_mp3.decoder->begin(audio_i2s_mp3.id3, audio_i2s.out); } else { - audio_i2s_mp3.mp3 = new AudioGeneratorMP3(); + return I2S_ERR_DECODER_FAILED_TO_INIT; + } + + size_t wr_tasksize = 8000; // suitable for ACC and MP3 + if(decoder_type == 2){ // opus needs a ton of stack + wr_tasksize = 26000; } - audio_i2s_mp3.mp3->begin(audio_i2s_mp3.id3, audio_i2s.out); // Always use a task - xTaskCreatePinnedToCore(I2sMp3Task, "MP3", 8192, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1); + xTaskCreatePinnedToCore(I2sMp3Task, "PLAYFILE", wr_tasksize, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1); return I2S_OK; } void mp3_delete(void) { delete audio_i2s_mp3.file; delete audio_i2s_mp3.id3; - delete audio_i2s_mp3.mp3; - audio_i2s_mp3.mp3 = nullptr; - - // if (audio_i2s_mp3.decoder) { - // audio_i2s_mp3.decoder->stop(); - // delete audio_i2s_mp3.decoder; - // audio_i2s_mp3.decoder = nullptr; - // AddLog(LOG_LEVEL_DEBUG, "I2S: audio_i2s_mp3.decoder = nullptr"); - // } + delete audio_i2s_mp3.decoder; + audio_i2s_mp3.decoder = nullptr; + } #endif // USE_I2S_MP3 @@ -985,7 +1020,7 @@ void CmndI2SStop(void) { #ifdef USE_I2S_MP3 void CmndI2SPlay(void) { if (XdrvMailbox.data_len > 0) { - int32_t err = I2SPlayMp3(XdrvMailbox.data); + int32_t err = I2SPlayFile(XdrvMailbox.data, XdrvMailbox.index); // display return message switch (err) { case I2S_OK: @@ -997,6 +1032,9 @@ void CmndI2SPlay(void) { case I2S_ERR_DECODER_IN_USE: ResponseCmndChar("Decoder already in use"); break; + case I2S_ERR_DECODER_FAILED_TO_INIT: + ResponseCmndChar("Decoder failed to init"); + break; case I2S_ERR_FILE_NOT_FOUND: ResponseCmndChar("File not found"); break; @@ -1056,11 +1094,11 @@ void CmndI2SMicRec(void) { ResponseCmndChar("I2S Mic not configured"); return; } - if (audio_i2s_mp3.mp3ram == nullptr){ - AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: try late buffer allocation for mp3 encoder")); - audio_i2s_mp3.mp3ram = special_malloc(preallocateCodecSize); + if (audio_i2s_mp3.preallocateCodec == nullptr){ + AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: try late codec buffer allocation")); + audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize); } - if (audio_i2s_mp3.mp3ram != nullptr) { + if (audio_i2s_mp3.preallocateCodec != nullptr) { if (XdrvMailbox.data_len > 0) { if (!strncmp(XdrvMailbox.data, "-?", 2)) { Response_P("{\"I2SREC-duration\":%d}", audio_i2s_mp3.recdur); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino index 5f482a7de8e7..c19fe7cb2709 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_7_i2s_webradio_idf51.ino @@ -26,7 +26,6 @@ struct AUDIO_I2S_WEBRADIO_t { AudioFileSourceBuffer *buff = NULL; char wr_title[64]; void *preallocateBuffer = NULL; - void *preallocateCodec = NULL; } Audio_webradio; void I2sMDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { @@ -46,46 +45,30 @@ void I2SWrStatusCB(void *cbData, int code, const char *str){ AddLog(LOG_LEVEL_INFO, "I2S: status: %s",str); } -bool I2SinitDecoder(uint32_t decoder_type){ - switch(decoder_type){ - case MP3_DECODER: - audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorMP3(Audio_webradio.preallocateCodec, preallocateCodecSize)); - break; -#ifdef USE_I2S_AAC - case AAC_DECODER: - Audio_webradio.preallocateCodec = special_realloc(Audio_webradio.preallocateCodec, preallocateCodecSizeAAC); - if(Audio_webradio.preallocateCodec == nullptr){ - AddLog(LOG_LEVEL_ERROR, "I2S: could not alloc heap for AAC"); - return false; - } - audio_i2s_mp3.decoder = dynamic_cast(new AudioGeneratorAAC(Audio_webradio.preallocateCodec, preallocateCodecSizeAAC)); - break; -#endif //USE_I2S_AAC - } - if(audio_i2s_mp3.decoder == nullptr){ - return false; +bool I2SWebradio(const char *url, uint32_t decoder_type) { + + size_t wr_tasksize = 8000; // suitable for ACC and MP3 + if(decoder_type == 2){ // opus needs a ton of stack + wr_tasksize = 26000; } - return true; -} -bool I2SWebradio(const char *url, uint32_t decoder_type) { // allocate buffers if not already done if (Audio_webradio.preallocateBuffer == NULL) { Audio_webradio.preallocateBuffer = special_malloc(preallocateBufferSize); } - if (Audio_webradio.preallocateCodec == NULL) { - Audio_webradio.preallocateCodec = special_malloc(preallocateCodecSize); + if (audio_i2s_mp3.preallocateCodec == NULL) { + audio_i2s_mp3.preallocateCodec = special_malloc(preallocateCodecSize); } // check if we have buffers - if (Audio_webradio.preallocateBuffer == NULL || Audio_webradio.preallocateCodec == NULL) { + if (Audio_webradio.preallocateBuffer == NULL || audio_i2s_mp3.preallocateCodec == NULL) { AddLog(LOG_LEVEL_INFO, "I2S: cannot allocate buffers"); if (Audio_webradio.preallocateBuffer != NULL) { free(Audio_webradio.preallocateBuffer); Audio_webradio.preallocateBuffer = NULL; } - if (Audio_webradio.preallocateCodec != NULL) { - free(Audio_webradio.preallocateCodec); - Audio_webradio.preallocateCodec = NULL; + if (audio_i2s_mp3.preallocateCodec != NULL) { + free(audio_i2s_mp3.preallocateCodec); + audio_i2s_mp3.preallocateCodec = NULL; } return false; } @@ -118,7 +101,7 @@ bool I2SWebradio(const char *url, uint32_t decoder_type) { } AddLog(LOG_LEVEL_DEBUG,PSTR("I2S: will launch webradio task with decoder type %u"), decoder_type); - xTaskCreatePinnedToCore(I2sMp3WrTask, "MP3-WR", 8192, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1); + xTaskCreatePinnedToCore(I2sMp3WrTask, "MP3-WR", wr_tasksize, NULL, 3, &audio_i2s_mp3.mp3_task_handle, 1); return true; i2swr_fail: