Use an RingBufferAudioSource in the SPDIF speaker

This commit is contained in:
Kevin Ahrendt
2026-05-08 21:33:04 +00:00
parent b0f07a4e3c
commit d6800a5f6f
@@ -143,7 +143,11 @@ void I2SAudioSpeakerSPDIF::run_speaker_task() {
const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this->buffer_duration_ms_);
// The DMA buffers may have more bits per sample, so calculate buffer sizes based on the input audio stream info
const size_t ring_buffer_size = this->current_stream_info_.ms_to_bytes(ring_buffer_duration);
const size_t bytes_per_frame = this->current_stream_info_.frames_to_bytes(1);
// Round the ring buffer size down to a multiple of bytes_per_frame so the wrap boundary stays frame-aligned and
// avoids unnecessary single-frame splices.
const size_t ring_buffer_size =
(this->current_stream_info_.ms_to_bytes(ring_buffer_duration) / bytes_per_frame) * bytes_per_frame;
// For SPDIF mode, one DMA buffer = one SPDIF block = 192 PCM frames
const uint32_t frames_to_fill_single_dma_buffer = SPDIF_BLOCK_SAMPLES;
@@ -151,13 +155,13 @@ void I2SAudioSpeakerSPDIF::run_speaker_task() {
this->current_stream_info_.frames_to_bytes(frames_to_fill_single_dma_buffer);
bool successful_setup = false;
std::unique_ptr<audio::AudioSourceTransferBuffer> transfer_buffer =
audio::AudioSourceTransferBuffer::create(bytes_to_fill_single_dma_buffer);
std::unique_ptr<audio::RingBufferAudioSource> audio_source;
if (transfer_buffer != nullptr) {
{
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(ring_buffer_size);
if (temp_ring_buffer.use_count() == 1) {
transfer_buffer->set_source(temp_ring_buffer);
audio_source = audio::RingBufferAudioSource::create(temp_ring_buffer, bytes_to_fill_single_dma_buffer,
static_cast<uint8_t>(bytes_per_frame));
if (audio_source != nullptr) {
this->audio_ring_buffer_ = temp_ring_buffer;
successful_setup = true;
}
@@ -297,24 +301,24 @@ void I2SAudioSpeakerSPDIF::run_speaker_task() {
if (!this->pause_state_) {
while (real_frames_in_block < SPDIF_BLOCK_SAMPLES) {
if (transfer_buffer->available() == 0) {
size_t bytes_read = transfer_buffer->transfer_data_from_source(read_timeout_ticks);
if (audio_source->available() == 0) {
size_t bytes_read = audio_source->fill(read_timeout_ticks, false);
if (bytes_read == 0) {
break; // No upstream data within the read budget; silence-pad the remainder.
}
uint8_t *new_data = transfer_buffer->get_buffer_end() - bytes_read;
uint8_t *new_data = audio_source->mutable_data();
this->apply_software_volume_(new_data, bytes_read);
this->swap_esp32_mono_samples_(new_data, bytes_read);
}
const uint32_t frames_still_needed = SPDIF_BLOCK_SAMPLES - real_frames_in_block;
const size_t bytes_still_needed = this->current_stream_info_.frames_to_bytes(frames_still_needed);
const size_t bytes_to_feed = std::min(transfer_buffer->available(), bytes_still_needed);
const size_t bytes_to_feed = std::min(audio_source->available(), bytes_still_needed);
uint32_t blocks_sent = 0;
size_t pcm_consumed = 0;
esp_err_t err = this->spdif_encoder_->write(transfer_buffer->get_buffer_start(), bytes_to_feed,
write_timeout_ticks, &blocks_sent, &pcm_consumed);
esp_err_t err = this->spdif_encoder_->write(audio_source->data(), bytes_to_feed, write_timeout_ticks,
&blocks_sent, &pcm_consumed);
if (err != ESP_OK) {
// A failed (or timed-out) send leaves an unsent block in the encoder's stitch buffer;
// resuming would credit the next iteration's bytes against an old block. Bail and
@@ -325,7 +329,7 @@ void I2SAudioSpeakerSPDIF::run_speaker_task() {
}
if (pcm_consumed > 0) {
transfer_buffer->decrease_buffer_length(pcm_consumed);
audio_source->consume(pcm_consumed);
real_frames_in_block += this->current_stream_info_.bytes_to_frames(pcm_consumed);
}
if (blocks_sent > 0) {
@@ -387,9 +391,7 @@ void I2SAudioSpeakerSPDIF::run_speaker_task() {
this->spdif_encoder_->reset();
}
if (transfer_buffer != nullptr) {
transfer_buffer.reset();
}
audio_source.reset();
xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::TASK_STOPPED);