5#include <driver/gpio.h>
6#include <driver/i2s_std.h>
25static constexpr float SOFTWARE_VOLUME_MIN_DB = -49.0f;
28static constexpr uint32_t GAIN_RAMP_MS_PER_DB = 1;
34 ESP_LOGE(TAG,
"Event group creation failed");
44#ifdef USE_I2S_AUDIO_FULL_DUPLEX
47 if (this->
parent_->is_full_duplex()) {
48 if (this->
parent_->is_failed()) {
49 this->
mark_failed(LOG_STR(
"Full duplex bus setup failed"));
57 this->park_dout_pin_();
60void I2SAudioSpeakerBase::park_dout_pin_() {
62 gpio_set_direction(this->
dout_pin_, GPIO_MODE_OUTPUT);
70 " Buffer duration: %" PRIu32
"\n"
74 ESP_LOGCONFIG(TAG,
" Timeout: %" PRIu32
" ms", this->
timeout_.value());
90 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_START);
95 ESP_LOGD(TAG,
"Starting");
96 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::TASK_STARTING);
99 ESP_LOGV(TAG,
"Started");
100 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::TASK_RUNNING);
110 ESP_LOGV(TAG,
"Stopping");
111 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::TASK_STOPPING);
115 ESP_LOGD(TAG,
"Stopped");
124 ESP_LOGE(TAG,
"Speaker task setup failed (allocation, preload, or channel enable)");
126 this->stop_i2s_driver_(this->
keep_alive_ && !task_failed);
132 ESP_LOGD(TAG,
"Start requested while stopping; keeping the request");
133 xEventGroupSetBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_START);
137 this->on_task_stopped();
154#ifdef USE_I2S_AUDIO_FULL_DUPLEX
155 if (this->
parent_->is_tx_in_use()) {
161 if (err == ESP_ERR_NOT_SUPPORTED) {
163 ESP_LOGE(TAG,
"Stream format not supported; not starting");
169 ESP_LOGE(TAG,
"Driver failed to start; retrying in 1 second");
177 this->post_software_gain_(0);
178 xTaskCreate(I2SAudioSpeakerBase::speaker_task,
"speaker_task", TASK_STACK_SIZE, (
void *)
this, TASK_PRIORITY,
182 ESP_LOGE(TAG,
"Task failed to start, retrying in 1 second");
184 this->stop_i2s_driver_(
false);
205void I2SAudioSpeakerBase::post_software_gain_(
uint32_t rate_samples) {
214 target_db = -INFINITY;
215 }
else if (this->
volume_ >= 1.0f) {
220 this->
gain_ramp_.set_target_db_at_rate(target_db, rate_samples);
225 ESP_LOGE(TAG,
"Setup failed; cannot play audio");
235 vTaskDelay(ticks_to_wait);
239 size_t bytes_written = 0;
241 std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->
audio_ring_buffer_.lock();
242 if (temp_ring_buffer !=
nullptr) {
244 bytes_written = temp_ring_buffer->write_without_replacement((
void *) data,
length, ticks_to_wait);
248 return bytes_written;
252 std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->
audio_ring_buffer_.lock();
253 if (temp_ring_buffer !=
nullptr) {
254 return temp_ring_buffer->available() > 0;
259void I2SAudioSpeakerBase::speaker_task(
void *params) {
260 I2SAudioSpeakerBase *this_speaker = (I2SAudioSpeakerBase *) params;
261 this_speaker->run_speaker_task();
270 if (this->
rejected_stream_info_.has_value() && this->rejected_stream_info_.value() == this->audio_stream_info_)
280void I2SAudioSpeakerBase::stop_(
bool wait_on_empty) {
292esp_err_t I2SAudioSpeakerBase::init_i2s_channel_(
const i2s_chan_config_t &chan_cfg,
const i2s_std_config_t &std_cfg,
293 size_t event_queue_size) {
296 ESP_LOGE(TAG,
"I2S channel allocation failed: %s",
esp_err_to_name(err));
301 err = i2s_channel_init_std_mode(this->
tx_handle_, &std_cfg);
303 ESP_LOGE(TAG,
"Failed to initialize I2S channel");
304 this->delete_channel_();
309 err = this->prepare_event_queues_(event_queue_size);
311 this->delete_channel_();
317esp_err_t I2SAudioSpeakerBase::prepare_event_queues_(
size_t event_queue_size) {
330 ESP_LOGE(TAG,
"Failed to allocate I2S event queue(s)");
331 return ESP_ERR_NO_MEM;
337#ifdef USE_I2S_AUDIO_FULL_DUPLEX
338esp_err_t I2SAudioSpeakerBase::acquire_full_duplex_channel_(
size_t event_queue_size) {
341 ESP_LOGE(TAG,
"Full duplex channel unavailable");
342 return ESP_ERR_INVALID_STATE;
345 esp_err_t err = this->prepare_event_queues_(event_queue_size);
347 this->
parent_->release_tx_channel();
354void I2SAudioSpeakerBase::stop_i2s_driver_(
bool keep_channel) {
355#ifdef USE_I2S_AUDIO_FULL_DUPLEX
356 if (this->
parent_->is_full_duplex()) {
359 this->
parent_->release_tx_channel();
371 this->delete_channel_();
377void I2SAudioSpeakerBase::delete_channel_() {
386 this->park_dout_pin_();
389bool IRAM_ATTR I2SAudioSpeakerBase::i2s_on_sent_cb(i2s_chan_handle_t
handle, i2s_event_data_t *event,
void *user_ctx) {
392 BaseType_t need_yield1 = pdFALSE;
393 BaseType_t need_yield2 = pdFALSE;
394 BaseType_t need_yield3 = pdFALSE;
396 I2SAudioSpeakerBase *this_speaker = (I2SAudioSpeakerBase *) user_ctx;
398 if (xQueueIsQueueFullFromISR(this_speaker->i2s_event_queue_)) {
401 xQueueReceiveFromISR(this_speaker->i2s_event_queue_, &dummy, &need_yield1);
405 xQueueSendToBackFromISR(this_speaker->i2s_event_queue_, &now, &need_yield3);
407 return need_yield1 | need_yield2 | need_yield3;
410void I2SAudioSpeakerBase::drain_lockstep_(
uint32_t extra_frames) {
418 frames += record_frames;
423 ESP_LOGV(TAG,
"Crediting %" PRIu32
" dropped frames as played", frames);
428void I2SAudioSpeakerBase::apply_software_volume_(uint8_t *data,
size_t bytes_read) {
435 this->
gain_ramp_.process(data,
static_cast<uint8_t
>(bytes_per_sample),
439bool I2SAudioSpeakerBase::esp32_mono_swap_needed(
const audio::AudioStreamInfo &stream_info) {
440#ifdef USE_ESP32_VARIANT_ESP32
441 return stream_info.get_channels() == 1 && stream_info.get_bits_per_sample() == 16;
448#ifdef USE_ESP32_VARIANT_ESP32
450 if (this->esp32_mono_swap_needed_()) {
451 int16_t *samples =
reinterpret_cast<int16_t *
>(data);
452 size_t sample_count = bytes_read /
sizeof(int16_t);
453 for (
size_t i = 0; i + 1 < sample_count; i += 2) {
454 int16_t tmp = samples[i];
455 samples[i] = samples[i + 1];
456 samples[i + 1] = tmp;
void mark_failed()
Mark this component as failed.
void status_clear_error()
bool status_has_error() const
void status_momentary_error(uint32_t length=5000)
Set error status flag and clear it after length ms.
I2SAudioComponent * parent_
uint32_t ms_to_samples(uint32_t ms) const
Converts duration to samples.
size_t samples_to_bytes(uint32_t samples) const
Converts samples to bytes.
uint32_t bytes_to_samples(size_t bytes) const
Convert bytes to samples.
virtual void on_audio_started()
Called from the main loop by the speaker driving this DAC once its audio clocks are running.
uint32_t buffer_duration_ms_
esp_audio_libs::gain::GainRamp gain_ramp_
i2s_chan_handle_t tx_handle_
std::weak_ptr< ring_buffer::RingBuffer > audio_ring_buffer_
optional< audio::AudioStreamInfo > rejected_stream_info_
TaskHandle_t speaker_task_handle_
void swap_esp32_mono_samples_(uint8_t *data, size_t bytes_read)
Plays the provided audio data.
audio::AudioStreamInfo current_stream_info_
void dump_config() override
optional< uint32_t > timeout_
QueueHandle_t write_records_queue_
QueueHandle_t i2s_event_queue_
EventGroupHandle_t event_group_
virtual size_t play(const uint8_t *data, size_t length)=0
Plays the provided audio data.
virtual void set_volume(float volume)
audio_dac::AudioDac * audio_dac_
CallbackManager< void(uint32_t, int64_t)> audio_output_callback_
virtual void set_mute_state(bool mute_state)
virtual bool has_buffered_data() const =0
audio::AudioStreamInfo audio_stream_info_
bool is_silent_() const
Whether the output should be silent: muted, or the volume is effectively zero.
const char * esp_err_to_name(esp_err_t err)
@ COMMAND_STOP_GRACEFULLY
ESPHOME_LOG_TAG(TAG, "i2s_audio")
T remap(U value, U min, U max, T min_out, T max_out)
Remap value from the range (min, max) to (min_out, max_out).
int64_t esp_timer_get_time(void)
spi_device_handle_t handle