ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
i2s_audio_speaker.cpp
Go to the documentation of this file.
1#include "i2s_audio_speaker.h"
2
3#ifdef USE_ESP32
4
5#include <driver/i2s_std.h>
6
9
11#include "esphome/core/hal.h"
12#include "esphome/core/log.h"
13
14#include "esp_timer.h"
15
16namespace esphome {
17namespace i2s_audio {
18
19static const uint32_t DMA_BUFFER_DURATION_MS = 15;
20static const size_t DMA_BUFFERS_COUNT = 4;
21
22static const size_t TASK_STACK_SIZE = 4096;
23static const ssize_t TASK_PRIORITY = 19;
24
25static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1;
26
27static const char *const TAG = "i2s_audio.speaker";
28
30 COMMAND_START = (1 << 0), // indicates loop should start speaker task
31 COMMAND_STOP = (1 << 1), // stops the speaker task
32 COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written
33
34 TASK_STARTING = (1 << 10),
35 TASK_RUNNING = (1 << 11),
36 TASK_STOPPING = (1 << 12),
37 TASK_STOPPED = (1 << 13),
38
39 ERR_ESP_NO_MEM = (1 << 19),
40
41 WARN_DROPPED_EVENT = (1 << 20),
42
43 ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
44};
45
46// Lists the Q15 fixed point scaling factor for volume reduction.
47// Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB.
48// dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014)
49// float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15)
50static const std::vector<int16_t> Q15_VOLUME_SCALING_FACTORS = {
51 0, 116, 122, 130, 137, 146, 154, 163, 173, 183, 194, 206, 218, 231, 244,
52 259, 274, 291, 308, 326, 345, 366, 388, 411, 435, 461, 488, 517, 548, 580,
53 615, 651, 690, 731, 774, 820, 868, 920, 974, 1032, 1094, 1158, 1227, 1300, 1377,
54 1459, 1545, 1637, 1734, 1837, 1946, 2061, 2184, 2313, 2450, 2596, 2750, 2913, 3085, 3269,
55 3462, 3668, 3885, 4116, 4360, 4619, 4893, 5183, 5490, 5816, 6161, 6527, 6914, 7324, 7758,
56 8218, 8706, 9222, 9770, 10349, 10963, 11613, 12302, 13032, 13805, 14624, 15491, 16410, 17384, 18415,
57 19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767};
58
60 this->event_group_ = xEventGroupCreate();
61
62 if (this->event_group_ == nullptr) {
63 ESP_LOGE(TAG, "Failed to create event group");
64 this->mark_failed();
65 return;
66 }
67}
68
70 ESP_LOGCONFIG(TAG,
71 "Speaker:\n"
72 " Pin: %d\n"
73 " Buffer duration: %" PRIu32,
74 static_cast<int8_t>(this->dout_pin_), this->buffer_duration_ms_);
75 if (this->timeout_.has_value()) {
76 ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value());
77 }
78 ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str());
79}
80
82 uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
83
84 if ((event_group_bits & SpeakerEventGroupBits::COMMAND_START) && (this->state_ == speaker::STATE_STOPPED)) {
86 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
87 }
88
89 // Handle the task's state
90 if (event_group_bits & SpeakerEventGroupBits::TASK_STARTING) {
91 ESP_LOGD(TAG, "Starting");
92 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STARTING);
93 }
94 if (event_group_bits & SpeakerEventGroupBits::TASK_RUNNING) {
95 ESP_LOGD(TAG, "Started");
96 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_RUNNING);
98 }
99 if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPING) {
100 ESP_LOGD(TAG, "Stopping");
101 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STOPPING);
103 }
104 if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPED) {
105 ESP_LOGD(TAG, "Stopped");
106
107 vTaskDelete(this->speaker_task_handle_);
108 this->speaker_task_handle_ = nullptr;
109
110 this->stop_i2s_driver_();
111 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
112 this->status_clear_error();
113
115 }
116
117 // Log any errors encounted by the task
118 if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NO_MEM) {
119 ESP_LOGE(TAG, "Not enough memory");
120 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
121 }
122
123 // Warn if any playback timestamp events are dropped, which drastically reduces synced playback accuracy
124 if (event_group_bits & SpeakerEventGroupBits::WARN_DROPPED_EVENT) {
125 ESP_LOGW(TAG, "Event dropped, synchronized playback accuracy is reduced");
126 xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT);
127 }
128
129 // Handle the speaker's state
130 switch (this->state_) {
132 if (this->status_has_error()) {
133 break;
134 }
135
136 if (this->start_i2s_driver_(this->audio_stream_info_) != ESP_OK) {
137 ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second");
138 this->status_momentary_error("driver-faiure", 1000);
139 break;
140 }
141
142 if (this->speaker_task_handle_ == nullptr) {
143 xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
144 &this->speaker_task_handle_);
145
146 if (this->speaker_task_handle_ == nullptr) {
147 ESP_LOGE(TAG, "Task failed to start, retrying in 1 second");
148 this->status_momentary_error("task-failure", 1000);
149 this->stop_i2s_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
150 }
151 }
152 break;
153 case speaker::STATE_RUNNING: // Intentional fallthrough
154 case speaker::STATE_STOPPING: // Intentional fallthrough
156 break;
157 }
158}
159
160void I2SAudioSpeaker::set_volume(float volume) {
161 this->volume_ = volume;
162#ifdef USE_AUDIO_DAC
163 if (this->audio_dac_ != nullptr) {
164 if (volume > 0.0) {
165 this->audio_dac_->set_mute_off();
166 }
167 this->audio_dac_->set_volume(volume);
168 } else
169#endif
170 {
171 // Fallback to software volume control by using a Q15 fixed point scaling factor
172 ssize_t decibel_index = remap<ssize_t, float>(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1);
173 this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index];
174 }
175}
176
177void I2SAudioSpeaker::set_mute_state(bool mute_state) {
178 this->mute_state_ = mute_state;
179#ifdef USE_AUDIO_DAC
180 if (this->audio_dac_) {
181 if (mute_state) {
182 this->audio_dac_->set_mute_on();
183 } else {
184 this->audio_dac_->set_mute_off();
185 }
186 } else
187#endif
188 {
189 if (mute_state) {
190 // Fallback to software volume control and scale by 0
191 this->q15_volume_factor_ = 0;
192 } else {
193 // Revert to previous volume when unmuting
194 this->set_volume(this->volume_);
195 }
196 }
197}
198
199size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
200 if (this->is_failed()) {
201 ESP_LOGE(TAG, "Setup failed; cannot play audio");
202 return 0;
203 }
205 this->start();
206 }
207
208 if (this->state_ != speaker::STATE_RUNNING) {
209 // Unable to write data to a running speaker, so delay the max amount of time so it can get ready
210 vTaskDelay(ticks_to_wait);
211 ticks_to_wait = 0;
212 }
213
214 size_t bytes_written = 0;
215 if (this->state_ == speaker::STATE_RUNNING) {
216 std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
217 if (temp_ring_buffer.use_count() == 2) {
218 // Only the speaker task and this temp_ring_buffer own the ring buffer, so its safe to write to
219 bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
220 }
221 }
222
223 return bytes_written;
224}
225
227 if (this->audio_ring_buffer_.use_count() > 0) {
228 std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
229 return temp_ring_buffer->available() > 0;
230 }
231 return false;
232}
233
234void I2SAudioSpeaker::speaker_task(void *params) {
235 I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
236
237 xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STARTING);
238
239 const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT;
240 // Ensure ring buffer duration is at least the duration of all DMA buffers
241 const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this_speaker->buffer_duration_ms_);
242
243 // The DMA buffers may have more bits per sample, so calculate buffer sizes based in the input audio stream info
244 const size_t ring_buffer_size = this_speaker->current_stream_info_.ms_to_bytes(ring_buffer_duration);
245
246 const uint32_t frames_to_fill_single_dma_buffer =
247 this_speaker->current_stream_info_.ms_to_frames(DMA_BUFFER_DURATION_MS);
248 const size_t bytes_to_fill_single_dma_buffer =
249 this_speaker->current_stream_info_.frames_to_bytes(frames_to_fill_single_dma_buffer);
250
251 bool successful_setup = false;
252 std::unique_ptr<audio::AudioSourceTransferBuffer> transfer_buffer =
253 audio::AudioSourceTransferBuffer::create(bytes_to_fill_single_dma_buffer);
254
255 if (transfer_buffer != nullptr) {
256 std::shared_ptr<RingBuffer> temp_ring_buffer = RingBuffer::create(ring_buffer_size);
257 if (temp_ring_buffer.use_count() == 1) {
258 transfer_buffer->set_source(temp_ring_buffer);
259 this_speaker->audio_ring_buffer_ = temp_ring_buffer;
260 successful_setup = true;
261 }
262 }
263
264 if (!successful_setup) {
265 xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
266 } else {
267 bool stop_gracefully = false;
268 bool tx_dma_underflow = true;
269
270 uint32_t frames_written = 0;
271 uint32_t last_data_received_time = millis();
272
273 xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_RUNNING);
274
275 while (this_speaker->pause_state_ || !this_speaker->timeout_.has_value() ||
276 (millis() - last_data_received_time) <= this_speaker->timeout_.value()) {
277 uint32_t event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
278
279 if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) {
280 xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
281 break;
282 }
283 if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) {
284 xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
285 stop_gracefully = true;
286 }
287
288 if (this_speaker->audio_stream_info_ != this_speaker->current_stream_info_) {
289 // Audio stream info changed, stop the speaker task so it will restart with the proper settings.
290 break;
291 }
292 int64_t write_timestamp;
293 while (xQueueReceive(this_speaker->i2s_event_queue_, &write_timestamp, 0)) {
294 // Receives timing events from the I2S on_sent callback. If actual audio data was sent in this event, it passes
295 // on the timing info via the audio_output_callback.
296 uint32_t frames_sent = frames_to_fill_single_dma_buffer;
297 if (frames_to_fill_single_dma_buffer > frames_written) {
298 tx_dma_underflow = true;
299 frames_sent = frames_written;
300 const uint32_t frames_zeroed = frames_to_fill_single_dma_buffer - frames_written;
301 write_timestamp -= this_speaker->current_stream_info_.frames_to_microseconds(frames_zeroed);
302 } else {
303 tx_dma_underflow = false;
304 }
305 frames_written -= frames_sent;
306 if (frames_sent > 0) {
307 this_speaker->audio_output_callback_(frames_sent, write_timestamp);
308 }
309 }
310
311 if (this_speaker->pause_state_) {
312 // Pause state is accessed atomically, so thread safe
313 // Delay so the task yields, then skip transferring audio data
314 vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS));
315 continue;
316 }
317
318 // Wait half the duration of the data already written to the DMA buffers for new audio data
319 // The millisecond helper modifies the frames_written variable, so use the microsecond helper and divide by 1000
320 const uint32_t read_delay =
321 (this_speaker->current_stream_info_.frames_to_microseconds(frames_written) / 1000) / 2;
322
323 size_t bytes_read = transfer_buffer->transfer_data_from_source(pdMS_TO_TICKS(read_delay));
324 uint8_t *new_data = transfer_buffer->get_buffer_end() - bytes_read;
325
326 if (bytes_read > 0) {
327 if (this_speaker->q15_volume_factor_ < INT16_MAX) {
328 // Apply the software volume adjustment by unpacking the sample into a Q31 fixed-point number, shifting it,
329 // multiplying by the volume factor, and packing the sample back into the original bytes per sample.
330
331 const size_t bytes_per_sample = this_speaker->current_stream_info_.samples_to_bytes(1);
332 const uint32_t len = bytes_read / bytes_per_sample;
333
334 // Use Q16 for samples with 1 or 2 bytes: shifted_sample * gain_factor is Q16 * Q15 -> Q31
335 int32_t shift = 15; // Q31 -> Q16
336 int32_t gain_factor = this_speaker->q15_volume_factor_; // Q15
337
338 if (bytes_per_sample >= 3) {
339 // Use Q23 for samples with 3 or 4 bytes: shifted_sample * gain_factor is Q23 * Q8 -> Q31
340
341 shift = 8; // Q31 -> Q23
342 gain_factor >>= 7; // Q15 -> Q8
343 }
344
345 for (uint32_t i = 0; i < len; ++i) {
346 int32_t sample =
347 audio::unpack_audio_sample_to_q31(&new_data[i * bytes_per_sample], bytes_per_sample); // Q31
348 sample >>= shift;
349 sample *= gain_factor; // Q31
350 audio::pack_q31_as_audio_sample(sample, &new_data[i * bytes_per_sample], bytes_per_sample);
351 }
352 }
353
354#ifdef USE_ESP32_VARIANT_ESP32
355 // For ESP32 16-bit mono mode, adjacent samples need to be swapped.
356 if (this_speaker->current_stream_info_.get_channels() == 1 &&
357 this_speaker->current_stream_info_.get_bits_per_sample() == 16) {
358 int16_t *samples = reinterpret_cast<int16_t *>(new_data);
359 size_t sample_count = bytes_read / sizeof(int16_t);
360 for (size_t i = 0; i + 1 < sample_count; i += 2) {
361 int16_t tmp = samples[i];
362 samples[i] = samples[i + 1];
363 samples[i + 1] = tmp;
364 }
365 }
366#endif
367 }
368
369 if (transfer_buffer->available() == 0) {
370 if (stop_gracefully && tx_dma_underflow) {
371 break;
372 }
373 vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS / 2));
374 } else {
375 size_t bytes_written = 0;
376 if (tx_dma_underflow) {
377 // Temporarily disable channel and callback to reset the I2S driver's internal DMA buffer queue so timing
378 // callbacks are accurate. Preload the data.
379 i2s_channel_disable(this_speaker->tx_handle_);
380 const i2s_event_callbacks_t callbacks = {
381 .on_sent = nullptr,
382 };
383
384 i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker);
385 i2s_channel_preload_data(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(),
386 transfer_buffer->available(), &bytes_written);
387 } else {
388 // Audio is already playing, use regular I2S write to add to the DMA buffers
389 i2s_channel_write(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(), transfer_buffer->available(),
390 &bytes_written, DMA_BUFFER_DURATION_MS);
391 }
392 if (bytes_written > 0) {
393 last_data_received_time = millis();
394 frames_written += this_speaker->current_stream_info_.bytes_to_frames(bytes_written);
395 transfer_buffer->decrease_buffer_length(bytes_written);
396 if (tx_dma_underflow) {
397 tx_dma_underflow = false;
398 // Reset the event queue timestamps
399 // Enable the on_sent callback to accurately track the timestamps of played audio
400 // Enable the I2S channel to start sending the preloaded audio
401
402 xQueueReset(this_speaker->i2s_event_queue_);
403
404 const i2s_event_callbacks_t callbacks = {
405 .on_sent = i2s_on_sent_cb,
406 };
407 i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker);
408
409 i2s_channel_enable(this_speaker->tx_handle_);
410 }
411 }
412 }
413 }
414 }
415
416 xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPING);
417
418 if (transfer_buffer != nullptr) {
419 transfer_buffer.reset();
420 }
421
422 xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPED);
423
424 while (true) {
425 // Continuously delay until the loop method deletes the task
426 vTaskDelay(pdMS_TO_TICKS(10));
427 }
428}
429
431 if (!this->is_ready() || this->is_failed() || this->status_has_error())
432 return;
433 if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING))
434 return;
435
436 xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
437}
438
439void I2SAudioSpeaker::stop() { this->stop_(false); }
440
441void I2SAudioSpeaker::finish() { this->stop_(true); }
442
443void I2SAudioSpeaker::stop_(bool wait_on_empty) {
444 if (this->is_failed())
445 return;
446 if (this->state_ == speaker::STATE_STOPPED)
447 return;
448
449 if (wait_on_empty) {
450 xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
451 } else {
452 xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
453 }
454}
455
457 this->current_stream_info_ = audio_stream_info; // store the stream info settings the driver will use
458
459 if ((this->i2s_role_ & I2S_ROLE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
460 // Can't reconfigure I2S bus, so the sample rate must match the configured value
461 ESP_LOGE(TAG, "Audio stream settings are not compatible with this I2S configuration");
462 return ESP_ERR_NOT_SUPPORTED;
463 }
464
465 if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO &&
466 (i2s_slot_bit_width_t) audio_stream_info.get_bits_per_sample() > this->slot_bit_width_) {
467 // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
468 ESP_LOGE(TAG, "Audio streams with more bits per sample than the I2S speaker's configuration is not supported");
469 return ESP_ERR_NOT_SUPPORTED;
470 }
471
472 if (!this->parent_->try_lock()) {
473 ESP_LOGE(TAG, "Parent I2S bus not free");
474 return ESP_ERR_INVALID_STATE;
475 }
476
477 uint32_t dma_buffer_length = audio_stream_info.ms_to_frames(DMA_BUFFER_DURATION_MS);
478
479 i2s_chan_config_t chan_cfg = {
480 .id = this->parent_->get_port(),
481 .role = this->i2s_role_,
482 .dma_desc_num = DMA_BUFFERS_COUNT,
483 .dma_frame_num = dma_buffer_length,
484 .auto_clear = true,
485 .intr_priority = 3,
486 };
487 /* Allocate a new TX channel and get the handle of this channel */
488 esp_err_t err = i2s_new_channel(&chan_cfg, &this->tx_handle_, NULL);
489 if (err != ESP_OK) {
490 ESP_LOGE(TAG, "Failed to allocate new I2S channel");
491 this->parent_->unlock();
492 return err;
493 }
494
495 i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
496#ifdef I2S_CLK_SRC_APLL
497 if (this->use_apll_) {
498 clk_src = I2S_CLK_SRC_APLL;
499 }
500#endif
501 i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
502
503 i2s_std_clk_config_t clk_cfg = {
504 .sample_rate_hz = audio_stream_info.get_sample_rate(),
505 .clk_src = clk_src,
506 .mclk_multiple = this->mclk_multiple_,
507 };
508
509 i2s_slot_mode_t slot_mode = this->slot_mode_;
510 i2s_std_slot_mask_t slot_mask = this->std_slot_mask_;
511 if (audio_stream_info.get_channels() == 1) {
512 slot_mode = I2S_SLOT_MODE_MONO;
513 } else if (audio_stream_info.get_channels() == 2) {
514 slot_mode = I2S_SLOT_MODE_STEREO;
515 slot_mask = I2S_STD_SLOT_BOTH;
516 }
517
518 i2s_std_slot_config_t std_slot_cfg;
519 if (this->i2s_comm_fmt_ == "std") {
520 std_slot_cfg =
521 I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
522 } else if (this->i2s_comm_fmt_ == "pcm") {
523 std_slot_cfg =
524 I2S_STD_PCM_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
525 } else {
526 std_slot_cfg =
527 I2S_STD_MSB_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
528 }
529#ifdef USE_ESP32_VARIANT_ESP32
530 // There seems to be a bug on the ESP32 (non-variant) platform where setting the slot bit width higher then the bits
531 // per sample causes the audio to play too fast. Setting the ws_width to the configured slot bit width seems to
532 // make it play at the correct speed while sending more bits per slot.
533 if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) {
534 uint32_t configured_bit_width = static_cast<uint32_t>(this->slot_bit_width_);
535 std_slot_cfg.ws_width = configured_bit_width;
536 if (configured_bit_width > 16) {
537 std_slot_cfg.msb_right = false;
538 }
539 }
540#else
541 std_slot_cfg.slot_bit_width = this->slot_bit_width_;
542#endif
543 std_slot_cfg.slot_mask = slot_mask;
544
545 pin_config.dout = this->dout_pin_;
546
547 i2s_std_config_t std_cfg = {
548 .clk_cfg = clk_cfg,
549 .slot_cfg = std_slot_cfg,
550 .gpio_cfg = pin_config,
551 };
552 /* Initialize the channel */
553 err = i2s_channel_init_std_mode(this->tx_handle_, &std_cfg);
554
555 if (err != ESP_OK) {
556 ESP_LOGE(TAG, "Failed to initialize channel");
557 i2s_del_channel(this->tx_handle_);
558 this->tx_handle_ = nullptr;
559 this->parent_->unlock();
560 return err;
561 }
562 if (this->i2s_event_queue_ == nullptr) {
563 this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(int64_t));
564 }
565
566 i2s_channel_enable(this->tx_handle_);
567
568 return err;
569}
570
571bool IRAM_ATTR I2SAudioSpeaker::i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
572 int64_t now = esp_timer_get_time();
573
574 BaseType_t need_yield1 = pdFALSE;
575 BaseType_t need_yield2 = pdFALSE;
576 BaseType_t need_yield3 = pdFALSE;
577
578 I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx;
579
580 if (xQueueIsQueueFullFromISR(this_speaker->i2s_event_queue_)) {
581 // Queue is full, so discard the oldest event and set the warning flag to inform the user
582 int64_t dummy;
583 xQueueReceiveFromISR(this_speaker->i2s_event_queue_, &dummy, &need_yield1);
584 xEventGroupSetBitsFromISR(this_speaker->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT, &need_yield2);
585 }
586
587 xQueueSendToBackFromISR(this_speaker->i2s_event_queue_, &now, &need_yield3);
588
589 return need_yield1 | need_yield2 | need_yield3;
590}
591
593 i2s_channel_disable(this->tx_handle_);
594 i2s_del_channel(this->tx_handle_);
595 this->tx_handle_ = nullptr;
596 this->parent_->unlock();
597}
598
599} // namespace i2s_audio
600} // namespace esphome
601
602#endif // USE_ESP32
void mark_failed()
Mark this component as failed.
void status_momentary_error(const char *name, uint32_t length=5000)
Set error status flag and automatically clear it after a timeout.
bool is_failed() const
Definition component.h:284
void status_clear_error()
Definition component.h:312
bool is_ready() const
bool status_has_error() const
Definition component.h:292
static std::unique_ptr< RingBuffer > create(size_t len)
static std::unique_ptr< AudioSourceTransferBuffer > create(size_t buffer_size)
Creates a new source transfer buffer.
uint8_t get_bits_per_sample() const
Definition audio.h:28
uint8_t get_channels() const
Definition audio.h:29
uint32_t ms_to_frames(uint32_t ms) const
Converts duration to frames.
Definition audio.h:63
uint32_t get_sample_rate() const
Definition audio.h:30
virtual bool set_mute_off()=0
virtual bool set_volume(float volume)=0
virtual bool set_mute_on()=0
i2s_std_slot_mask_t std_slot_mask_
Definition i2s_audio.h:29
i2s_slot_bit_width_t slot_bit_width_
Definition i2s_audio.h:30
i2s_mclk_multiple_t mclk_multiple_
Definition i2s_audio.h:33
esp_err_t start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info)
Starts the ESP32 I2S driver.
static bool i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
Callback function used to send playback timestamps the to the speaker task.
void stop_(bool wait_on_empty)
Plays the provided audio data.
void stop_i2s_driver_()
Stops the I2S driver and unlocks the I2S port.
std::weak_ptr< RingBuffer > audio_ring_buffer_
audio::AudioStreamInfo current_stream_info_
virtual size_t play(const uint8_t *data, size_t length)=0
Plays the provided audio data.
virtual void set_volume(float volume)
Definition speaker.h:71
audio_dac::AudioDac * audio_dac_
Definition speaker.h:120
virtual void set_mute_state(bool mute_state)
Definition speaker.h:81
virtual bool has_buffered_data() const =0
audio::AudioStreamInfo audio_stream_info_
Definition speaker.h:115
__int64 ssize_t
Definition httplib.h:178
int32_t unpack_audio_sample_to_q31(const uint8_t *data, size_t bytes_per_sample)
Unpacks a quantized audio sample into a Q31 fixed-point number.
Definition audio.h:152
void pack_q31_as_audio_sample(int32_t sample, uint8_t *data, size_t bytes_per_sample)
Packs a Q31 fixed-point number as an audio sample with the specified number of bytes per sample.
Definition audio.h:178
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:1045
auto * new_data
Definition helpers.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
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).
Definition helpers.h:753
static void uint32_t
uint16_t length
Definition tt21100.cpp:0