diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index f6e074cc11..d9daaf65fc 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -26,6 +26,7 @@ static const char *const TAG = "i2s_audio.speaker"; enum SpeakerEventGroupBits : uint32_t { COMMAND_START = (1 << 0), // Starts the main task purpose COMMAND_STOP = (1 << 1), // stops the main task + COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the task once all data has been written MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE = (1 << 5), // Locks the ring buffer when not set STATE_STARTING = (1 << 10), STATE_RUNNING = (1 << 11), @@ -194,8 +195,6 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup"); return 0; } - if (this->state_ == speaker::STATE_STOPPING) - return 0; if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { this->start(); @@ -229,14 +228,15 @@ bool I2SAudioSpeaker::has_buffered_data() const { void I2SAudioSpeaker::speaker_task(void *params) { I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; - uint32_t event_group_bits = xEventGroupWaitBits( - this_speaker->event_group_, - SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP, // Bit message to read - pdTRUE, // Clear the bits on exit - pdFALSE, // Don't wait for all the bits, - portMAX_DELAY); // Block indefinitely until a bit is set + uint32_t event_group_bits = + xEventGroupWaitBits(this_speaker->event_group_, + SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP | + SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read + pdTRUE, // Clear the bits on exit + pdFALSE, // Don't wait for all the bits, + portMAX_DELAY); // Block indefinitely until a bit is set - if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP)) { + if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP | SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY)) { // Received a stop signal before the task was requested to start this_speaker->delete_task_(0); } @@ -269,6 +269,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING); + bool stop_gracefully = false; uint32_t last_data_received_time = millis(); while ((millis() - last_data_received_time) <= this_speaker->timeout_) { @@ -278,11 +279,16 @@ void I2SAudioSpeaker::speaker_task(void *params) { break; } + if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) { + stop_gracefully = true; + } + size_t bytes_to_read = dma_buffers_size; size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read, pdMS_TO_TICKS(TASK_DELAY_MS)); if (bytes_read > 0) { + last_data_received_time = millis(); size_t bytes_written = 0; if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { @@ -304,6 +310,15 @@ void I2SAudioSpeaker::speaker_task(void *params) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); continue; } + + } else { + // No data received + + if (stop_gracefully) { + break; + } + + i2s_zero_dma_buffer(this_speaker->parent_->get_port()); last_data_received_time = millis(); } } @@ -311,15 +326,12 @@ void I2SAudioSpeaker::speaker_task(void *params) { // Couldn't configure the I2S port to be compatible with the incoming audio xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT); } - uint64_t tmp = 0; - size_t bytes_written = 0; - i2s_write(this_speaker->parent_->get_port(), &tmp, 8, &bytes_written, portMAX_DELAY); i2s_zero_dma_buffer(this_speaker->parent_->get_port()); xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); - // i2s_stop(this_speaker->parent_->get_port()); + i2s_stop(this_speaker->parent_->get_port()); i2s_driver_uninstall(this_speaker->parent_->get_port()); this_speaker->parent_->unlock(); @@ -347,8 +359,7 @@ void I2SAudioSpeaker::start() { void I2SAudioSpeaker::stop() { this->stop_(false); } -void I2SAudioSpeaker::finish() { /*do notting. */ -} +void I2SAudioSpeaker::finish() { this->stop_(true); } void I2SAudioSpeaker::stop_(bool wait_on_empty) { if (this->is_failed()) @@ -357,7 +368,7 @@ void I2SAudioSpeaker::stop_(bool wait_on_empty) { return; if (wait_on_empty) { - this->state_ = speaker::STATE_STOPPING; + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY); } else { xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP); }