diff --git a/esphome/components/adf_pipeline/adf_audio_element.h b/esphome/components/adf_pipeline/adf_audio_element.h index ae321f7..d5450f8 100644 --- a/esphome/components/adf_pipeline/adf_audio_element.h +++ b/esphome/components/adf_pipeline/adf_audio_element.h @@ -40,6 +40,8 @@ class AudioPipelineSettingsRequest { int final_number_of_channels{-1}; float final_volume{-1.}; + int finish_on_timeout{0}; + bool failed{false}; int error_code{0}; ADFPipelineElement *requested_by{nullptr}; diff --git a/esphome/components/adf_pipeline/adf_audio_sources.cpp b/esphome/components/adf_pipeline/adf_audio_sources.cpp index 8442714..7753fa2 100644 --- a/esphome/components/adf_pipeline/adf_audio_sources.cpp +++ b/esphome/components/adf_pipeline/adf_audio_sources.cpp @@ -306,10 +306,12 @@ bool PCMSource::init_adf_elements_() { } int PCMSource::stream_write(char *buffer, int len) { + if( !this->adf_raw_stream_writer_ ){ + ESP_LOGE(TAG, "Attempt writing to PCM stream buffer which has not been created."); + return 0; + } int ret = audio_element_output(this->adf_raw_stream_writer_, buffer, len); - if (ret == AEL_IO_TIMEOUT) { - audio_element_report_status(this->adf_raw_stream_writer_, AEL_STATUS_STATE_FINISHED); - } else if (ret < 0) { + if (ret < 0) { return 0; } return ret; @@ -320,6 +322,8 @@ bool PCMSource::has_buffered_data() const { return rb_bytes_filled(rb) > 0; } + + } // namespace esp_adf } // namespace esphome diff --git a/esphome/components/adf_pipeline/adf_audio_sources.h b/esphome/components/adf_pipeline/adf_audio_sources.h index ac2cbc3..a9fbe7a 100644 --- a/esphome/components/adf_pipeline/adf_audio_sources.h +++ b/esphome/components/adf_pipeline/adf_audio_sources.h @@ -88,6 +88,7 @@ class PCMSource : public ADFPipelineSourceElement { const std::string get_name() override { return "PCMSource"; } int stream_write(char *buffer, int len); bool has_buffered_data() const; + bool elements_have_stopped() override { return true; } protected: bool init_adf_elements_() override; diff --git a/esphome/components/adf_pipeline/adf_pipeline.cpp b/esphome/components/adf_pipeline/adf_pipeline.cpp index b42a14e..4941231 100644 --- a/esphome/components/adf_pipeline/adf_pipeline.cpp +++ b/esphome/components/adf_pipeline/adf_pipeline.cpp @@ -371,7 +371,7 @@ void ADFPipeline::watch_() { if(this->requested_ == PipelineRequest::STOPPED){ set_state_(PipelineState::STOPPED); } - else if ( millis() - this->finish_timeout_invoke_ > 16000){ + else if ( millis() - this->finish_timeout_invoke_ > this->wait_for_finish_timeout_ms_){ this->requested_ = PipelineRequest::STOPPED; set_state_(PipelineState::ABORTING); } diff --git a/esphome/components/adf_pipeline/adf_pipeline.h b/esphome/components/adf_pipeline/adf_pipeline.h index f3f8fc5..bc6bbdf 100644 --- a/esphome/components/adf_pipeline/adf_pipeline.h +++ b/esphome/components/adf_pipeline/adf_pipeline.h @@ -78,6 +78,7 @@ class ADFPipeline { void loop() { this->watch_(); } void set_destroy_on_stop(bool value){ this->destroy_on_stop_ = value; } + void set_finish_timeout_ms(int timeout){ this->wait_for_finish_timeout_ms_ = timeout; } void append_element(ADFPipelineElement *element); int get_number_of_elements() { return pipeline_elements_.size(); } @@ -99,6 +100,7 @@ class ADFPipeline { bool check_all_finished_(); bool check_all_destroyed_(); uint32_t finish_timeout_invoke_{0}; + int wait_for_finish_timeout_ms_{16000}; enum CheckState { CHECK_PREPARED, CHECK_PAUSED, CHECK_RESUMED, CHECK_STOPPED, NUM_STATE_CHECKS }; std::vector check_state_name = {"PREPARING", "PAUSING", "RESUMING", "STOPPING","WRONG_IDX"}; @@ -116,6 +118,8 @@ class ADFPipeline { bool build_adf_pipeline_(); void deinit_all_(); + + audio_pipeline_handle_t adf_pipeline_{}; audio_event_iface_handle_t adf_pipeline_event_{}; diff --git a/esphome/components/adf_pipeline/speaker/esp_adf_speaker.cpp b/esphome/components/adf_pipeline/speaker/esp_adf_speaker.cpp index 6b20dc5..09cded6 100644 --- a/esphome/components/adf_pipeline/speaker/esp_adf_speaker.cpp +++ b/esphome/components/adf_pipeline/speaker/esp_adf_speaker.cpp @@ -13,6 +13,7 @@ static const char *const TAG = "esp_adf.speaker"; void ADFSpeaker::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP ADF Speaker..."); + pipeline.set_finish_timeout_ms(10000); } void ADFSpeaker::dump_config() { @@ -67,11 +68,12 @@ size_t ADFSpeaker::play(const uint8_t *data, size_t length) { ESP_LOGE(TAG, "Failed to play audio, speaker is in failed state."); return 0; } - /* - if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { - this->start(); + + if (this->state_ != speaker::STATE_RUNNING) { + ESP_LOGV(TAG, "Trying to play audio while speaker not running."); + return 0; } - */ + size_t remaining = length; size_t index = 0; while (remaining > 0) { @@ -92,6 +94,8 @@ void ADFSpeaker::request_pipeline_settings_(){ request.sampling_rate = 16000; request.bit_depth = 16; request.number_of_channels = 1; + request.finish_on_timeout = 1000; //ms + request.target_volume = 1.; if (!this->pipeline.request_settings(request)) { esph_log_e(TAG, "Requested audio settings, didn't get accepted"); this->pipeline.on_settings_request_failed(request); diff --git a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_in.cpp b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_in.cpp index ba7f927..638834b 100644 --- a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_in.cpp +++ b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_in.cpp @@ -44,6 +44,7 @@ bool ADFElementI2SIn::init_adf_elements_() { .uninstall_drv = false, .need_expand = false, .expand_src_bits = I2S_BITS_PER_SAMPLE_16BIT, + .finish_on_timeout = false, }; this->adf_i2s_stream_reader_ = i2s_stream_init(&i2s_stream_cfg); diff --git a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.cpp b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.cpp index c182dfc..0285d2c 100644 --- a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.cpp +++ b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.cpp @@ -40,7 +40,7 @@ bool ADFElementI2SOut::init_adf_elements_() { .i2s_port = this->parent_->get_port(), .use_alc = this->use_adf_alc_, .volume = 0, - .out_rb_size = (4 * 1024), + .out_rb_size = (20 * 1024), .task_stack = I2S_STREAM_TASK_STACK, .task_core = I2S_STREAM_TASK_CORE, .task_prio = I2S_STREAM_TASK_PRIO, @@ -49,12 +49,15 @@ bool ADFElementI2SOut::init_adf_elements_() { .uninstall_drv = false, .need_expand = i2s_config.bits_per_sample != I2S_BITS_PER_SAMPLE_16BIT, .expand_src_bits = I2S_BITS_PER_SAMPLE_16BIT, + .finish_on_timeout = false, //don't set it yet, set it in the preparation phase instead }; this->adf_i2s_stream_writer_ = i2s_stream_init(&i2s_cfg); this->adf_i2s_stream_writer_->buf_size = 1 * 1024; this->install_i2s_driver(i2s_config); + audio_element_set_input_timeout(this->adf_i2s_stream_writer_, 1000 / portTICK_PERIOD_MS); + this->finish_on_timeout_ms_ = 0; #ifdef I2S_EXTERNAL_DAC if (this->external_dac_ != nullptr){ @@ -112,6 +115,8 @@ void ADFElementI2SOut::on_settings_request(AudioPipelineSettingsRequest &request audio_element_set_music_info(this->adf_i2s_stream_writer_,this->sample_rate_, this->num_of_channels(), this->bits_per_sample_ ); esph_log_d(TAG, "update i2s clk settings: rate:%d bits:%d ch:%d",this->sample_rate_, this->bits_per_sample_, this->num_of_channels()); + i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(this->adf_i2s_stream_writer_); + i2s->config.i2s_config.bits_per_sample = this->bits_per_sample_; if (i2s_stream_set_clk(this->adf_i2s_stream_writer_, this->sample_rate_, this->bits_per_sample_, this->num_of_channels()) != ESP_OK) { esph_log_e(TAG, "error while setting sample rate and bit depth,"); @@ -122,6 +127,15 @@ void ADFElementI2SOut::on_settings_request(AudioPipelineSettingsRequest &request } } + if ( request.finish_on_timeout != this->finish_on_timeout_ms_ ){ + esph_log_d(TAG, "Setting finish_on_timout to (ms): %d", request.finish_on_timeout); + this->finish_on_timeout_ms_ = request.finish_on_timeout; + i2s_stream_t *i2s = (i2s_stream_t *) audio_element_getdata(this->adf_i2s_stream_writer_); + esph_log_d(TAG, "finish on timeout was: %s",i2s->finish_on_timeout ? "true":"false"); + i2s->finish_on_timeout = this->finish_on_timeout_ms_ > 0; + audio_element_set_input_timeout(this->adf_i2s_stream_writer_, this->finish_on_timeout_ms_ / portTICK_PERIOD_MS); + } + // final pipeline settings are unset if (request.final_sampling_rate == -1) { esph_log_d(TAG, "Set final i2s settings: %d", this->sample_rate_); diff --git a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.h b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.h index 12052c3..4a37913 100644 --- a/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.h +++ b/esphome/components/i2s_audio/adf_pipeline/adf_i2s_out.h @@ -24,15 +24,17 @@ class ADFElementI2SOut : public I2SWriter, public ADFPipelineSinkElement, public void set_use_adf_alc(bool use_alc){ this->use_adf_alc_ = use_alc; } void set_alc_max_val(float max_val){ this->max_alc_val_ = max_val;} - + void set_finish_on_timeout_ms(int timeout){this->finish_on_timeout_ms_ = timeout;} protected: void on_settings_request(AudioPipelineSettingsRequest &request) override; bool use_adf_alc_{false}; float max_alc_val_{1.}; bool adjustable_{false}; + int finish_on_timeout_ms_{0}; bool init_adf_elements_() override; void clear_adf_elements_() override; + audio_element_handle_t adf_i2s_stream_writer_; }; diff --git a/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.c b/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.c index f937632..8825ef3 100644 --- a/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.c +++ b/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.c @@ -37,7 +37,7 @@ #include "audio_common.h" #include "audio_mem.h" #include "audio_element.h" -#include "i2s_stream.h" +#include "i2s_stream_mod.h" #include "esp_alc.h" #include "board_pins_config.h" #include "audio_idf_version.h" @@ -53,15 +53,6 @@ static const char *TAG = "I2S_STREAM"; #endif #endif -typedef struct i2s_stream { - audio_stream_type_t type; - i2s_stream_cfg_t config; - bool is_open; - bool use_alc; - void *volume_handle; - int volume; - bool uninstall_drv; -} i2s_stream_t; #ifdef SOC_I2S_SUPPORTS_ADC_DAC static esp_err_t i2s_mono_fix(int bits, uint8_t *sbuff, uint32_t len) { @@ -161,10 +152,12 @@ static esp_err_t _i2s_open(audio_element_handle_t self) return ESP_OK; } + /* if (i2s->type == AUDIO_STREAM_WRITER) { audio_element_set_input_timeout(self, 10 / portTICK_RATE_MS); ESP_LOGI(TAG, "AUDIO_STREAM_WRITER"); } + */ i2s->is_open = true; if (i2s->use_alc) { i2s->volume_handle = alc_volume_setup_open(); @@ -283,7 +276,14 @@ static int _i2s_process(audio_element_handle_t self, char *in_buffer, int in_len } else #endif { - memset(in_buffer, 0x00, in_len); + if( i2s->finish_on_timeout ){ + ESP_LOGI(TAG, "Sending finish status after time out occurred." ); + audio_element_report_status(self, AEL_STATUS_STATE_FINISHED); + return 0; + } + else { + memset(in_buffer, 0x00, in_len); + } } r_size = in_len; audio_element_multi_output(self, in_buffer, r_size, 0); @@ -375,6 +375,7 @@ audio_element_handle_t i2s_stream_init(i2s_stream_cfg_t *config) i2s->use_alc = config->use_alc; i2s->volume = config->volume; i2s->uninstall_drv = config->uninstall_drv; + i2s->finish_on_timeout = config->finish_on_timeout; if (config->type == AUDIO_STREAM_READER) { cfg.read = _i2s_read; diff --git a/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.h b/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.h index 3fb2445..04aca21 100644 --- a/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.h +++ b/esphome/components/i2s_audio/adf_pipeline/i2s_stream_mod.h @@ -53,8 +53,21 @@ typedef struct { bool uninstall_drv; /*!< whether uninstall the i2s driver when stream destroyed*/ bool need_expand; /*!< whether to expand i2s data */ i2s_bits_per_sample_t expand_src_bits; /*!< The source bits per sample when data expand */ + bool finish_on_timeout; /*!< send finish message when input data timeout occurs */ } i2s_stream_cfg_t; +typedef struct i2s_stream { + audio_stream_type_t type; + i2s_stream_cfg_t config; + bool is_open; + bool use_alc; + void *volume_handle; + int volume; + bool uninstall_drv; + bool finish_on_timeout; +} i2s_stream_t; + + #define I2S_STREAM_TASK_STACK (3072+512) #define I2S_STREAM_BUF_SIZE (2048) #define I2S_STREAM_TASK_PRIO (23) diff --git a/examples/m5stack-atom-echo-adf-spk.yaml b/examples/m5stack-atom-echo-adf-spk.yaml index 897e64a..7413cfc 100644 --- a/examples/m5stack-atom-echo-adf-spk.yaml +++ b/examples/m5stack-atom-echo-adf-spk.yaml @@ -26,6 +26,7 @@ esp32: logger: api: ota: + platform: esphome packages: wifi: !include common/wifi.yaml @@ -49,6 +50,12 @@ adf_pipeline: id: adf_i2s_out i2s_audio_id: i2s_dplx i2s_dout_pin: GPIO22 + adf_alc: true + alc_max: 0.6 + + sample_rate: 16000 + bits_per_sample: 16bit + channel: right fixed_settings: false - platform: i2s_audio