From 3511852f39cd5023ec8e6d0b94cc69f34e9201ed Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 3 Aug 2023 15:32:28 +1000 Subject: Add very limited resampling (it's slow as shit) --- src/audio/audio_task.cpp | 81 +++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 28 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 7e0fb207..c3498965 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -34,6 +34,7 @@ #include "freertos/queue.h" #include "freertos/ringbuf.h" #include "pipeline.hpp" +#include "sink_mixer.hpp" #include "span.hpp" #include "arena.hpp" @@ -115,14 +116,12 @@ AudioTask::AudioTask(IAudioSource* source, IAudioSink* sink) : source_(source), sink_(sink), codec_(), + mixer_(new SinkMixer(sink->stream())), timer_(), has_begun_decoding_(false), current_input_format_(), current_output_format_(), - sample_buffer_(reinterpret_cast( - heap_caps_malloc(kSampleBufferSize, - MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))), - sample_buffer_len_(kSampleBufferSize) {} + codec_buffer_(new RawStream(kSampleBufferSize)) {} void AudioTask::Main() { for (;;) { @@ -246,13 +245,17 @@ auto AudioTask::BeginDecoding(InputStream& stream) -> bool { return false; } + OutputStream writer{codec_buffer_.get()}; + writer.prepare(new_format, {}); + return true; } auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { while (!stream.data().empty()) { - auto res = codec_->ContinueStream(stream.data(), - {sample_buffer_, sample_buffer_len_}); + OutputStream writer{codec_buffer_.get()}; + + auto res = codec_->ContinueStream(stream.data(), writer.data()); stream.consume(res.first); @@ -263,9 +266,10 @@ auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { return false; } } else { - xStreamBufferSend(sink_->stream(), sample_buffer_, - res.second->bytes_written, portMAX_DELAY); - timer_->AddBytes(res.second->bytes_written); + writer.add(res.second->bytes_written); + + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); } } return true; @@ -284,21 +288,22 @@ auto AudioTask::FinishDecoding(InputStream& stream) -> void { std::unique_ptr mad_buffer; mad_buffer.reset(new RawStream(stream.data().size_bytes() + 8)); - OutputStream writer{mad_buffer.get()}; + OutputStream mad_writer{mad_buffer.get()}; std::copy(stream.data().begin(), stream.data().end(), - writer.data().begin()); - std::fill(writer.data().begin(), writer.data().end(), std::byte{0}); + mad_writer.data().begin()); + std::fill(mad_writer.data().begin(), mad_writer.data().end(), std::byte{0}); InputStream padded_stream{mad_buffer.get()}; - auto res = codec_->ContinueStream(stream.data(), - {sample_buffer_, sample_buffer_len_}); + OutputStream writer{codec_buffer_.get()}; + auto res = codec_->ContinueStream(stream.data(), writer.data()); if (res.second.has_error()) { return; } - xStreamBufferSend(sink_->stream(), sample_buffer_, - res.second->bytes_written, portMAX_DELAY); - timer_->AddBytes(res.second->bytes_written); + writer.add(res.second->bytes_written); + + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); } } @@ -319,24 +324,31 @@ auto AudioTask::ForwardPcmStream(StreamInfo::Pcm& format, xStreamBufferSend(sink_->stream(), samples.data(), samples.size_bytes(), portMAX_DELAY); timer_->AddBytes(samples.size_bytes()); + InputStream reader{codec_buffer_.get()}; + SendToSink(reader); + return true; } auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format, const Duration& duration) -> bool { if (format != current_output_format_) { - // The new format is different to the old one. Wait for the sink to drain - // before continuing. - while (!xStreamBufferIsEmpty(sink_->stream())) { - ESP_LOGI(kTag, "waiting for sink stream to drain..."); - // TODO(jacqueline): Get the sink drain ISR to notify us of this - // via semaphore instead of busy-ish waiting. - vTaskDelay(pdMS_TO_TICKS(100)); - } + current_output_format_ = format; + StreamInfo::Pcm new_sink_format = sink_->PrepareFormat(format); + if (new_sink_format != current_sink_format_) { + current_sink_format_ = new_sink_format; + + // The new format is different to the old one. Wait for the sink to drain + // before continuing. + while (!xStreamBufferIsEmpty(sink_->stream())) { + ESP_LOGI(kTag, "waiting for sink stream to drain..."); + // TODO(jacqueline): Get the sink drain ISR to notify us of this + // via semaphore instead of busy-ish waiting. + vTaskDelay(pdMS_TO_TICKS(10)); + } - ESP_LOGI(kTag, "configuring sink"); - if (!sink_->Configure(format)) { - return false; + ESP_LOGI(kTag, "configuring sink"); + sink_->Configure(new_sink_format); } } @@ -345,4 +357,17 @@ auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format, return true; } +auto AudioTask::SendToSink(InputStream& stream) -> void { + std::size_t bytes_to_send = stream.data().size_bytes(); + std::size_t bytes_sent; + if (stream.info().format_as() == current_sink_format_) { + bytes_sent = xStreamBufferSend(sink_->stream(), stream.data().data(), + bytes_to_send, portMAX_DELAY); + stream.consume(bytes_sent); + } else { + bytes_sent = mixer_->MixAndSend(stream, current_sink_format_.value()); + } + timer_->AddBytes(bytes_sent); +} + } // namespace audio -- cgit v1.2.3 From 60f767713227b5405b855e6e6e2a0475ecd96bcc Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 4 Aug 2023 20:07:44 +1000 Subject: Do our own resampling --- src/audio/audio_task.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index c3498965..d7c0c209 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -34,6 +34,7 @@ #include "freertos/queue.h" #include "freertos/ringbuf.h" #include "pipeline.hpp" +#include "sample.hpp" #include "sink_mixer.hpp" #include "span.hpp" @@ -225,7 +226,7 @@ auto AudioTask::BeginDecoding(InputStream& stream) -> bool { codecs::ICodec::OutputFormat format = res.second.value(); StreamInfo::Pcm new_format{ .channels = format.num_channels, - .bits_per_sample = format.bits_per_sample, + .bits_per_sample = 32, .sample_rate = format.sample_rate_hz, }; @@ -255,7 +256,8 @@ auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { while (!stream.data().empty()) { OutputStream writer{codec_buffer_.get()}; - auto res = codec_->ContinueStream(stream.data(), writer.data()); + auto res = + codec_->ContinueStream(stream.data(), writer.data_as()); stream.consume(res.first); @@ -266,7 +268,7 @@ auto AudioTask::ContinueDecoding(InputStream& stream) -> bool { return false; } } else { - writer.add(res.second->bytes_written); + writer.add(res.second->samples_written * sizeof(sample::Sample)); InputStream reader{codec_buffer_.get()}; SendToSink(reader); @@ -295,12 +297,12 @@ auto AudioTask::FinishDecoding(InputStream& stream) -> void { InputStream padded_stream{mad_buffer.get()}; OutputStream writer{codec_buffer_.get()}; - auto res = codec_->ContinueStream(stream.data(), writer.data()); + auto res = codec_->ContinueStream(stream.data(), writer.data_as()); if (res.second.has_error()) { return; } - writer.add(res.second->bytes_written); + writer.add(res.second->samples_written * sizeof(sample::Sample)); InputStream reader{codec_buffer_.get()}; SendToSink(reader); -- cgit v1.2.3 From c38754401b95642b5e61fd273c2adf7d76a829fe Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 7 Aug 2023 18:22:15 +1000 Subject: Downscaling working! --- src/audio/audio_task.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index d7c0c209..7c038730 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -297,7 +297,8 @@ auto AudioTask::FinishDecoding(InputStream& stream) -> void { InputStream padded_stream{mad_buffer.get()}; OutputStream writer{codec_buffer_.get()}; - auto res = codec_->ContinueStream(stream.data(), writer.data_as()); + auto res = + codec_->ContinueStream(stream.data(), writer.data_as()); if (res.second.has_error()) { return; } -- cgit v1.2.3 From 93ccf11fc506b95221ce0c5eddaed9e0e6c8b3b5 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 8 Aug 2023 13:47:08 +1000 Subject: Investigate and improve core pinning for resampler --- src/audio/audio_task.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 7c038730..75b44594 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -109,7 +109,10 @@ auto Timer::bytes_to_samples(uint32_t bytes) -> uint32_t { auto AudioTask::Start(IAudioSource* source, IAudioSink* sink) -> AudioTask* { AudioTask* task = new AudioTask(source, sink); - tasks::StartPersistent([=]() { task->Main(); }); + // Pin to CORE1 because codecs should be fixed point anyway, and being on + // the opposite core to the mixer maximises throughput in the worst case + // (some heavy codec like opus + resampling for bluetooth). + tasks::StartPersistent(1, [=]() { task->Main(); }); return task; } -- cgit v1.2.3