82 const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT;
90 const bool pair_aligned = source_alignment > bytes_per_frame;
92 const size_t ring_buffer_size =
99 const bool narrowing = input_bytes_per_sample != output_bytes_per_sample;
106 size_t dma_buffer_bytes;
107 i2s_chan_info_t chan_info;
108 if (i2s_channel_get_info(this->
tx_handle_, &chan_info) == ESP_OK && chan_info.total_dma_buf_size > 0) {
110 dma_buffer_bytes = chan_info.total_dma_buf_size / DMA_BUFFERS_COUNT;
121 bool successful_setup =
false;
123 std::unique_ptr<audio::RingBufferAudioSource> audio_source;
127 uint8_t *silence_buffer = silence_allocator.
allocate(dma_buffer_bytes);
129 if (silence_buffer !=
nullptr) {
130 memset(silence_buffer, 0, dma_buffer_bytes);
134 static_cast<uint8_t
>(source_alignment));
136 if (audio_source !=
nullptr) {
139 successful_setup =
true;
145 auto preload_silence = [&]() ->
bool {
146 for (
size_t i = 0; i < DMA_BUFFERS_COUNT; i++) {
147 size_t bytes_loaded = 0;
148 esp_err_t err = i2s_channel_preload_data(this->
tx_handle_, silence_buffer, dma_buffer_bytes, &bytes_loaded);
149 if (err != ESP_OK || bytes_loaded != dma_buffer_bytes) {
150 ESP_LOGV(TAG,
"Failed to preload silence into DMA buffer %u (err=%d, loaded=%u)", (
unsigned) i, (
int) err,
151 (
unsigned) bytes_loaded);
157 ESP_LOGV(TAG,
"Failed to push preload write record");
164 if (successful_setup) {
165 successful_setup = preload_silence();
168 if (successful_setup) {
171 const i2s_event_callbacks_t callbacks = {.on_sent = i2s_on_sent_cb};
172 i2s_channel_register_event_callback(this->
tx_handle_, &callbacks,
this);
174 if (i2s_channel_enable(this->
tx_handle_) != ESP_OK) {
175 ESP_LOGV(TAG,
"Failed to enable I2S channel");
176 successful_setup =
false;
180 if (!successful_setup) {
183 bool stop_gracefully =
false;
188 bool resync_needed =
false;
211 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_STOP);
212 ESP_LOGV(TAG,
"Exiting: COMMAND_STOP received");
216 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
217 stop_gracefully =
true;
222 ESP_LOGV(TAG,
"Exiting: stream info changed");
227 ESP_LOGE(TAG,
"ISR event queue overflow, resyncing DMA lockstep");
228 resync_needed =
true;
232 const bool resynced = this->resync_lockstep_(unrecorded_frames, preload_silence);
233 unrecorded_frames = 0;
234 pending_real_buffers = 0;
235 resync_needed =
false;
237 ESP_LOGE(TAG,
"DMA lockstep resync failed, restarting speaker task");
245 int64_t write_timestamp;
246 bool lockstep_broken =
false;
251 ESP_LOGE(TAG,
"Event without matching write record, resyncing DMA lockstep");
252 lockstep_broken =
true;
255 if (real_frames > 0) {
256 pending_real_buffers--;
260 const uint32_t silence_frames = frames_per_dma_buffer - real_frames;
261 const int64_t adjusted_ts =
266 if (lockstep_broken) {
267 resync_needed =
true;
275 if (stop_gracefully && audio_source->available() == 0 && !this->has_buffered_data() &&
276 pending_real_buffers == 0) {
277 ESP_LOGV(TAG,
"Exiting: graceful stop complete");
286 size_t bytes_written_total = 0;
288 bool partial_write_failure =
false;
291 while (bytes_written_total < dma_buffer_bytes) {
292 size_t bytes_read = audio_source->fill(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS) / 2);
293 if (bytes_read > 0) {
295 uint8_t *
new_data = audio_source->mutable_data() + audio_source->available() - bytes_read;
296 this->apply_software_volume_(
new_data, bytes_read);
305 if (pair_aligned && frames_available == 0 && frames_room >= 2 && !audio_source->has_buffered_data()) {
308 const size_t real_bytes = audio_source->expose_padded_partial(bytes_per_frame);
309 if (real_bytes > 0) {
310 this->apply_software_volume_(audio_source->mutable_data(), real_bytes);
315 const uint32_t frames_to_write = std::min(frames_available, frames_room);
316 if (frames_to_write == 0) {
325 uint8_t *chunk = audio_source->mutable_data();
330 esp_audio_libs::pcm_convert::copy_frames(chunk, chunk, input_bytes_per_sample, channels,
331 output_bytes_per_sample, channels, frames_to_write);
336 i2s_channel_write(this->
tx_handle_, chunk, output_bytes, &bw, WRITE_TIMEOUT_TICKS);
337 if (bw != output_bytes) {
340 ESP_LOGE(TAG,
"Partial DMA write (%u of %u bytes), resyncing DMA lockstep", (
unsigned) bw,
341 (
unsigned) output_bytes);
342 audio_source->consume(input_bytes);
343 real_frames_total += frames_to_write - padding_frames;
344 partial_write_failure =
true;
347 audio_source->consume(input_bytes);
348 bytes_written_total += output_bytes;
349 real_frames_total += frames_to_write - padding_frames;
350 if (padding_frames > 0) {
355 if (real_frames_total > 0) {
356 last_data_received_time =
millis();
360 if (partial_write_failure) {
361 unrecorded_frames += real_frames_total;
362 resync_needed =
true;
366 const size_t silence_bytes = dma_buffer_bytes - bytes_written_total;
367 if (silence_bytes > 0) {
369 i2s_channel_write(this->
tx_handle_, silence_buffer, silence_bytes, &bw, WRITE_TIMEOUT_TICKS);
370 if (bw != silence_bytes) {
372 ESP_LOGE(TAG,
"Partial DMA write (%u of %u bytes), resyncing DMA lockstep", (
unsigned) bw,
373 (
unsigned) silence_bytes);
374 unrecorded_frames += real_frames_total;
375 resync_needed =
true;
384 ESP_LOGE(TAG,
"Write records queue full, resyncing DMA lockstep");
385 unrecorded_frames += real_frames_total;
386 resync_needed =
true;
389 if (real_frames_total > 0) {
390 pending_real_buffers++;
397 audio_source.reset();
399 if (silence_buffer !=
nullptr) {
400 silence_allocator.
deallocate(silence_buffer, dma_buffer_bytes);
401 silence_buffer =
nullptr;
408 vTaskDelay(pdMS_TO_TICKS(10));
499 i2s_std_config_t &std_cfg)
const {
504 ESP_LOGV(TAG,
"I2S DMA config: %zu buffers x %lu frames", (
size_t) DMA_BUFFERS_COUNT,
505 (
unsigned long) dma_buffer_length);
508 .id = this->
parent_->get_port(),
510 .dma_desc_num = DMA_BUFFERS_COUNT,
511 .dma_frame_num = dma_buffer_length,
517 i2s_std_clk_config_t clk_cfg = {
526 slot_mode = I2S_SLOT_MODE_MONO;
528 slot_mode = I2S_SLOT_MODE_STEREO;
529 slot_mask = I2S_STD_SLOT_BOTH;
533 const i2s_data_bit_width_t data_bit_width = (i2s_data_bit_width_t) output_stream_info.
get_bits_per_sample();
534 i2s_std_slot_config_t slot_cfg;
537 slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
540 slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
543 slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
547#ifdef USE_ESP32_VARIANT_ESP32
553 slot_cfg.ws_width = configured_bit_width;
554 if (configured_bit_width > 16) {
555 slot_cfg.msb_right =
false;
564 slot_cfg.slot_mask = slot_mask;
566 i2s_std_gpio_config_t gpio_cfg = this->
parent_->get_pin_config();
571 .slot_cfg = slot_cfg,
572 .gpio_cfg = gpio_cfg,