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 3cefc554c..20b328d53 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 @@ -98,8 +98,12 @@ struct AUDIO_I2S_MP3_t { bool mic_stop = false; bool use_stream = false; bool task_running = false; + bool file_play_pausing = false; bool task_has_ended = false; + bool file_has_paused = false; bool task_loop_mode = false; + uint8_t current_file_type = 0; // for resumed playback + uint32_t paused_position = 0; // position in the file for paused audio file // RECORD/STREAM/ENCODING uint32_t recdur; @@ -119,6 +123,10 @@ struct AUDIO_I2S_MP3_t { } audio_i2s_mp3; +struct AUDIO_I2S_WEBRADIO_t { + AudioFileSourceICYStream *ifile = NULL; +} Audio_webradio; + #define I2S_AUDIO_MODE_MIC 1 #define I2S_AUDIO_MODE_SPK 2 @@ -148,7 +156,7 @@ void I2sWrShow(bool json) { const char kI2SAudio_Commands[] PROGMEM = "I2S|" "Gain|Rec|Stop|Config" #ifdef USE_I2S_MP3 - "|Play|Loop" + "|Play|Loop|Pause" #endif #ifdef USE_I2S_DEBUG "|Mic" // debug only @@ -178,6 +186,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { #ifdef USE_I2S_MP3 &CmndI2SPlay, &CmndI2SLoop, + &CmndI2SPause, #endif #ifdef USE_I2S_DEBUG &CmndI2SMic, @@ -678,10 +687,16 @@ int32_t I2SPrepareRx(void) { #if defined(USE_I2S_MP3) || defined(USE_I2S_WEBRADIO) void I2sMp3Task(void *arg) { audio_i2s_mp3.task_running = true; + audio_i2s_mp3.file_play_pausing = false; while (audio_i2s_mp3.decoder->isRunning() && audio_i2s_mp3.task_running) { if (!audio_i2s_mp3.decoder->loop()) { audio_i2s_mp3.task_running = false; } + if (audio_i2s_mp3.file_play_pausing == true) { + audio_i2s_mp3.paused_position = audio_i2s_mp3.file->getPos(); + audio_i2s_mp3.decoder->stop(); + audio_i2s_mp3.file_has_paused = true; + } vTaskDelay(pdMS_TO_TICKS(1)); } audio_i2s.out->flush(); @@ -797,6 +812,7 @@ int32_t I2SPlayFile(const char *path, uint32_t decoder_type) { strncpy(audio_i2s_mp3.audio_title, fname, sizeof(audio_i2s_mp3.audio_title)); audio_i2s_mp3.audio_title[sizeof(audio_i2s_mp3.audio_title)-1] = 0; + audio_i2s_mp3.current_file_type = decoder_type; //save for i2spause I2SAudioPower(true); @@ -815,6 +831,7 @@ int32_t I2SPlayFile(const char *path, uint32_t decoder_type) { if(I2SinitDecoder(decoder_type)){ audio_i2s_mp3.decoder->begin(audio_i2s_mp3.id3, audio_i2s.out); + audio_i2s_mp3.file->seek(audio_i2s_mp3.paused_position, 1); // seek to the position where we paused or have it set to 0, 1 = SEEK_CUR } else { return I2S_ERR_DECODER_FAILED_TO_INIT; } @@ -835,8 +852,14 @@ void mp3_delete(void) { delete audio_i2s_mp3.id3; delete audio_i2s_mp3.decoder; audio_i2s_mp3.decoder = nullptr; - } + +void i2s_clean_pause_data(void) { + audio_i2s_mp3.current_file_type = MP3_DECODER; + audio_i2s_mp3.paused_position = 0; + audio_i2s_mp3.file_play_pausing = false; +} + #endif // USE_I2S_MP3 /*********************************************************************************************\ @@ -878,6 +901,7 @@ void CmndI2SStop(void) { return; } audio_i2s.out->setGain(0); + i2s_clean_pause_data(); ResponseCmndDone(); } @@ -887,8 +911,25 @@ void CmndI2SLoop(void) { CmndI2SPlay(); } +void CmndI2SPause(void) { + if(audio_i2s_mp3.task_running +#ifdef MP3_MIC_STREAM + && !audio_i2s_mp3.stream_active +#endif //MP3_MIC_STREAM +#ifdef USE_I2S_WEBRADIO + && Audio_webradio.ifile == nullptr +#endif // USE_I2S_WEBRADIO + ){ + audio_i2s_mp3.file_play_pausing = true; + ResponseCmndChar("Player Paused"); + } else { + ResponseCmndChar("Player not running"); // or webradio is using decoder + } +} + void CmndI2SPlay(void) { if (XdrvMailbox.data_len > 0) { + i2s_clean_pause_data(); // clean up any previous pause data, set start to 0 int32_t err = I2SPlayFile(XdrvMailbox.data, XdrvMailbox.index); // display return message switch (err) { @@ -915,7 +956,13 @@ void CmndI2SPlay(void) { break; } } else { - ResponseCmndChar("Missing filename"); + if(audio_i2s_mp3.file_play_pausing == true){ + int32_t err = I2SPlayFile((const char *)audio_i2s_mp3.audio_title, (uint32_t)audio_i2s_mp3.current_file_type); // the line above should rule out basically any error, but we'll see ... + AddLog(LOG_LEVEL_DEBUG, "I2S: Resume: %s, type: %i at %i , err: %i", audio_i2s_mp3.audio_title, audio_i2s_mp3.current_file_type, audio_i2s_mp3.paused_position, err); + ResponseCmndChar("Player resumed"); + } else { + ResponseCmndChar("Missing filename"); + } } } #endif // USE_I2S_MP3 @@ -993,8 +1040,17 @@ void CmndI2SMicRec(void) { } void I2sEventHandler(){ + if(audio_i2s_mp3.file_has_paused == true){ + audio_i2s_mp3.task_has_ended = false; //do not send ended event + audio_i2s_mp3.file_has_paused = false; + audio_i2s_mp3.task_running = false; + MqttPublishPayloadPrefixTopicRulesProcess_P(RESULT_OR_STAT,PSTR(""),PSTR("{\"Event\":{\"I2SPlay\":\"Paused\"}}")); + // Rule1 ON event#i2splay=paused DO ENDON + I2SAudioPower(false); + } if(audio_i2s_mp3.task_has_ended == true){ audio_i2s_mp3.task_has_ended = false; + audio_i2s_mp3.task_running = false; MqttPublishPayloadPrefixTopicRulesProcess_P(RESULT_OR_STAT,PSTR(""),PSTR("{\"Event\":{\"I2SPlay\":\"Ended\"}}")); // Rule1 ON event#i2splay=ended DO ENDON I2SAudioPower(false); 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 3654ca465..bddb7d12f 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 @@ -20,10 +20,6 @@ #if defined(ESP32) && ESP_IDF_VERSION_MAJOR >= 5 #if defined(USE_I2S_AUDIO) && defined(USE_I2S_WEBRADIO) -struct AUDIO_I2S_WEBRADIO_t { - AudioFileSourceICYStream *ifile = NULL; -} Audio_webradio; - void I2sMDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { const char *ptr = reinterpret_cast(cbData); (void) isUnicode; // Punt this ball for now @@ -43,7 +39,7 @@ void I2SWrStatusCB(void *cbData, int code, const char *str){ 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 + if(decoder_type == OPUS_DECODER){ // opus needs a ton of stack wr_tasksize = 26000; }