#pragma once #ifdef USE_ESP32 #include "esphome/components/audio/audio.h" #include "esphome/components/audio/audio_transfer_buffer.h" #include "esphome/components/speaker/speaker.h" #include "esphome/core/component.h" #include "esphome/core/static_task.h" #include namespace esphome { namespace resampler { class ResamplerSpeaker : public Component, public speaker::Speaker { public: float get_setup_priority() const override { return esphome::setup_priority::DATA; } void dump_config() override; void setup() override; void loop() override; size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override; size_t play(const uint8_t *data, size_t length) override { return this->play(data, length, 0); } void start() override; void stop() override; void finish() override; void set_pause_state(bool pause_state) override { this->output_speaker_->set_pause_state(pause_state); } bool get_pause_state() const override { return this->output_speaker_->get_pause_state(); } bool has_buffered_data() const override; /// @brief Mute state changes are passed to the parent's output speaker void set_mute_state(bool mute_state) override; bool get_mute_state() override { return this->output_speaker_->get_mute_state(); } /// @brief Volume state changes are passed to the parent's output speaker void set_volume(float volume) override; float get_volume() override { return this->output_speaker_->get_volume(); } void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; } void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; } void set_target_bits_per_sample(uint8_t target_bits_per_sample) { this->target_bits_per_sample_ = target_bits_per_sample; } void set_target_sample_rate(uint32_t target_sample_rate) { this->target_sample_rate_ = target_sample_rate; } void set_filters(uint16_t filters) { this->filters_ = filters; } void set_taps(uint16_t taps) { this->taps_ = taps; } void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } protected: /// @brief Starts the output speaker after setting the resampled stream info. If resampling is required, it starts the /// task. /// @return ESP_OK if successful, /// ESP_ERR_NO_MEM if the resampler task couldn't be created esp_err_t start_(); /// @brief Transitions to STATE_STOPPING, records the stopping timestamp, sends the task stop command if the task is /// running, and stops the output speaker. void enter_stopping_state_(); /// @brief Sets the appropriate status error based on the start failure reason. void set_start_error_(esp_err_t err); /// @brief Sends a command via event group bits, enables the loop, and optionally wakes the main loop. void send_command_(uint32_t command_bit, bool wake_loop = false); inline bool requires_resampling_() const; static void resample_task(void *params); EventGroupHandle_t event_group_{nullptr}; std::weak_ptr ring_buffer_; speaker::Speaker *output_speaker_{nullptr}; bool task_stack_in_psram_{false}; bool waiting_for_output_{false}; StaticTask task_; audio::AudioStreamInfo target_stream_info_; uint16_t taps_; uint16_t filters_; uint8_t target_bits_per_sample_; uint32_t target_sample_rate_; uint32_t buffer_duration_ms_; uint32_t state_start_ms_{0}; uint64_t callback_remainder_{0}; }; } // namespace resampler } // namespace esphome #endif