79 const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT;
87 const size_t ring_buffer_size =
94 const bool narrowing = input_bytes_per_sample != output_bytes_per_sample;
101 size_t dma_buffer_bytes;
102 i2s_chan_info_t chan_info;
103 if (i2s_channel_get_info(this->
tx_handle_, &chan_info) == ESP_OK && chan_info.total_dma_buf_size > 0) {
105 dma_buffer_bytes = chan_info.total_dma_buf_size / DMA_BUFFERS_COUNT;
115 bool successful_setup =
false;
117 std::unique_ptr<audio::RingBufferAudioSource> audio_source;
121 uint8_t *silence_buffer = silence_allocator.
allocate(dma_buffer_bytes);
123 if (silence_buffer !=
nullptr) {
124 memset(silence_buffer, 0, dma_buffer_bytes);
128 static_cast<uint8_t
>(bytes_per_frame));
130 if (audio_source !=
nullptr) {
133 successful_setup =
true;
137 if (successful_setup) {
141 for (
size_t i = 0; i < DMA_BUFFERS_COUNT; i++) {
142 size_t bytes_loaded = 0;
143 esp_err_t err = i2s_channel_preload_data(this->
tx_handle_, silence_buffer, dma_buffer_bytes, &bytes_loaded);
144 if (err != ESP_OK || bytes_loaded != dma_buffer_bytes) {
145 ESP_LOGV(TAG,
"Failed to preload silence into DMA buffer %u (err=%d, loaded=%u)", (
unsigned) i, (
int) err,
146 (
unsigned) bytes_loaded);
147 successful_setup =
false;
153 ESP_LOGV(TAG,
"Failed to push preload write record");
154 successful_setup =
false;
160 if (successful_setup) {
163 const i2s_event_callbacks_t callbacks = {.on_sent =
i2s_on_sent_cb};
164 i2s_channel_register_event_callback(this->
tx_handle_, &callbacks,
this);
166 if (i2s_channel_enable(this->
tx_handle_) != ESP_OK) {
167 ESP_LOGV(TAG,
"Failed to enable I2S channel");
168 successful_setup =
false;
172 if (!successful_setup) {
175 bool stop_gracefully =
false;
202 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_STOP);
203 ESP_LOGV(TAG,
"Exiting: COMMAND_STOP received");
207 xEventGroupClearBits(this->
event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
208 stop_gracefully =
true;
213 ESP_LOGV(TAG,
"Exiting: stream info changed");
220 int64_t write_timestamp;
221 bool lockstep_broken =
false;
226 ESP_LOGV(TAG,
"Event without matching write record");
228 lockstep_broken =
true;
231 if (real_frames > 0) {
232 pending_real_buffers--;
236 const uint32_t silence_frames = frames_per_dma_buffer - real_frames;
237 const int64_t adjusted_ts =
242 if (lockstep_broken) {
250 if (stop_gracefully && audio_source->available() == 0 && !this->has_buffered_data() &&
251 pending_real_buffers == 0) {
252 ESP_LOGV(TAG,
"Exiting: graceful stop complete");
261 size_t bytes_written_total = 0;
263 bool partial_write_failure =
false;
266 while (bytes_written_total < dma_buffer_bytes) {
267 size_t bytes_read = audio_source->fill(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS) / 2,
false);
268 if (bytes_read > 0) {
270 uint8_t *
new_data = audio_source->mutable_data() + audio_source->available() - bytes_read;
279 const uint32_t frames_to_write = std::min(frames_available, frames_room);
280 if (frames_to_write == 0) {
289 uint8_t *chunk = audio_source->mutable_data();
294 esp_audio_libs::pcm_convert::copy_frames(chunk, chunk, input_bytes_per_sample, channels,
295 output_bytes_per_sample, channels, frames_to_write);
300 i2s_channel_write(this->
tx_handle_, chunk, output_bytes, &bw, WRITE_TIMEOUT_TICKS);
301 if (bw != output_bytes) {
304 ESP_LOGV(TAG,
"Partial real audio write: %u of %u bytes", (
unsigned) bw, (
unsigned) output_bytes);
306 partial_write_failure =
true;
309 audio_source->consume(input_bytes);
310 bytes_written_total += output_bytes;
311 real_frames_total += frames_to_write;
313 if (real_frames_total > 0) {
314 last_data_received_time =
millis();
318 if (partial_write_failure) {
322 const size_t silence_bytes = dma_buffer_bytes - bytes_written_total;
323 if (silence_bytes > 0) {
325 i2s_channel_write(this->
tx_handle_, silence_buffer, silence_bytes, &bw, WRITE_TIMEOUT_TICKS);
326 if (bw != silence_bytes) {
328 ESP_LOGV(TAG,
"Partial silence write: %u of %u bytes", (
unsigned) bw, (
unsigned) silence_bytes);
338 ESP_LOGV(TAG,
"Exiting: write records queue full");
342 if (real_frames_total > 0) {
343 pending_real_buffers++;
350 audio_source.reset();
352 if (silence_buffer !=
nullptr) {
353 silence_allocator.
deallocate(silence_buffer, dma_buffer_bytes);
354 silence_buffer =
nullptr;
361 vTaskDelay(pdMS_TO_TICKS(10));
370 ESP_LOGE(TAG,
"Incompatible stream settings");
371 return ESP_ERR_NOT_SUPPORTED;
380 const uint8_t configured_bits =
static_cast<uint8_t
>(this->
slot_bit_width_);
381 if (output_bits_per_sample > configured_bits) {
382 output_bits_per_sample = configured_bits;
388#ifdef USE_ESP32_VARIANT_ESP32
394 if (output_bits_per_sample % 16 != 0) {
395 ESP_LOGE(TAG,
"ESP32 supports only 16- or 32-bit output, got %u-bit", (
unsigned) output_bits_per_sample);
396 return ESP_ERR_NOT_SUPPORTED;
400 if (!this->
parent_->try_lock()) {
401 ESP_LOGE(TAG,
"Parent bus is busy");
402 return ESP_ERR_INVALID_STATE;
409 i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
411#if SOC_CLK_APLL_SUPPORTED
413 clk_src = i2s_clock_src_t::I2S_CLK_SRC_APLL;
418 ESP_LOGV(TAG,
"I2S DMA config: %zu buffers x %lu frames", (
size_t) DMA_BUFFERS_COUNT,
419 (
unsigned long) dma_buffer_length);
421 i2s_chan_config_t chan_cfg = {
422 .id = this->
parent_->get_port(),
424 .dma_desc_num = DMA_BUFFERS_COUNT,
425 .dma_frame_num = dma_buffer_length,
431 i2s_std_clk_config_t clk_cfg = {
440 slot_mode = I2S_SLOT_MODE_MONO;
442 slot_mode = I2S_SLOT_MODE_STEREO;
443 slot_mask = I2S_STD_SLOT_BOTH;
448 i2s_std_slot_config_t slot_cfg;
451 slot_cfg = I2S_STD_PCM_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
454 slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
457 slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(data_bit_width, slot_mode);
461#ifdef USE_ESP32_VARIANT_ESP32
467 slot_cfg.ws_width = configured_bit_width;
468 if (configured_bit_width > 16) {
469 slot_cfg.msb_right =
false;
478 slot_cfg.slot_mask = slot_mask;
480 i2s_std_gpio_config_t gpio_cfg = this->
parent_->get_pin_config();
483 i2s_std_config_t std_cfg = {
485 .slot_cfg = slot_cfg,
486 .gpio_cfg = gpio_cfg,
489 esp_err_t err = this->
init_i2s_channel_(chan_cfg, std_cfg, I2S_EVENT_QUEUE_COUNT);