From a9531c86a433c8b7ae1f77ff0266c27c39eca7f4 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 10 Mar 2023 11:28:33 +1100 Subject: mostly single task pipeline --- src/audio/audio_task.cpp | 212 ++++++++++++++++++++++------------------------- 1 file changed, 97 insertions(+), 115 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index ce6d724e..542bada8 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -2,16 +2,20 @@ #include +#include +#include #include #include #include #include "cbor.h" +#include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "freertos/portmacro.h" #include "freertos/projdefs.h" #include "freertos/queue.h" +#include "pipeline.hpp" #include "span.hpp" #include "arena.hpp" @@ -25,25 +29,26 @@ namespace audio { +namespace task { + static const char* kTag = "task"; +static const std::size_t kStackSize = 24 * 1024; +static const uint8_t kAudioCore = 0; -auto StartAudioTask(const std::string& name, - std::optional core_id, - std::shared_ptr element) -> void { - auto task_handle = std::make_unique(); +auto Start(Pipeline* pipeline) -> Handle* { + auto input_queue = xQueueCreate(8, 1); // Newly created task will free this. - AudioTaskArgs* args = new AudioTaskArgs{.element = element}; - - ESP_LOGI(kTag, "starting audio task %s", name.c_str()); - if (core_id) { - xTaskCreatePinnedToCore(&AudioTaskMain, name.c_str(), - element->StackSizeBytes(), args, kTaskPriorityAudio, - task_handle.get(), *core_id); - } else { - xTaskCreate(&AudioTaskMain, name.c_str(), element->StackSizeBytes(), args, - kTaskPriorityAudio, task_handle.get()); - } + AudioTaskArgs* args = new AudioTaskArgs{ + .pipeline = pipeline, + .input = input_queue, + }; + + ESP_LOGI(kTag, "starting audio task"); + xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, + kTaskPriorityAudio, NULL, kAudioCore); + + return new Handle(input_queue); } void AudioTaskMain(void* args) { @@ -51,113 +56,88 @@ void AudioTaskMain(void* args) { // called before the task quits. { AudioTaskArgs* real_args = reinterpret_cast(args); - std::shared_ptr element = std::move(real_args->element); + std::unique_ptr pipeline(real_args->pipeline); + QueueHandle_t input; + StreamBufferHandle_t output; delete real_args; - // Queue of events that we have received on our input queue, but not yet - // processed. - std::deque> pending_events; - - // TODO(jacqueline): quit event - while (true) { - // First, we pull events from our input queue into pending_events. This - // keeps us responsive to any events that need to be handled immediately. - // Then we check if there's any events to flush downstream. - // Then we pass anything requiring processing to the element. - - bool has_work_to_do = - (!pending_events.empty() || element->HasUnflushedOutput() || - element->HasUnprocessedInput()) && - !element->IsOverBuffered(); - - if (has_work_to_do) { - ESP_LOGD(kTag, "checking for events"); - } else { - ESP_LOGD(kTag, "waiting for events"); - } - - // If we have no new events to process and the element has nothing left to - // do, then just delay forever waiting for a new event. - TickType_t ticks_to_wait = has_work_to_do ? 0 : portMAX_DELAY; - - StreamEvent* new_event = nullptr; - bool has_event = - xQueueReceive(element->InputEventQueue(), &new_event, ticks_to_wait); - - if (has_event) { - if (new_event->tag == StreamEvent::UNINITIALISED) { - ESP_LOGE(kTag, "discarding invalid event!!"); - } else if (new_event->tag == StreamEvent::CHUNK_NOTIFICATION) { - delete new_event; - } else if (new_event->tag == StreamEvent::LOG_STATUS) { - element->ProcessLogStatus(); - if (element->OutputEventQueue() != nullptr) { - xQueueSendToFront(element->OutputEventQueue(), &new_event, 0); - } else { - delete new_event; - } - } else { - // This isn't an event that needs to be actioned immediately. Add it - // to our work queue. - pending_events.emplace_back(new_event); - ESP_LOGD(kTag, "deferring event"); + std::vector elements = pipeline->GetIterationOrder(); + std::size_t max_inputs = + (*std::max_element(elements.begin(), elements.end(), + [](Pipeline const* first, Pipeline const* second) { + return first->NumInputs() < second->NumInputs(); + })) + ->NumInputs(); + + // We need to be able to simultaneously map all of an element's inputs, plus + // its output. So preallocate that many ranges. + std::vector> in_regions(max_inputs); + MappableRegion out_region; + std::for_each(in_regions.begin(), in_regions.end(), + [](const MappableRegion& region) { + assert(region.is_valid); + }); + assert(out_region.is_valid); + + // Each element has exactly one output buffer. + std::vector> buffers(elements.size()); + std::vector buffer_infos(buffers.size()); + std::for_each(buffers.begin(), buffers.end(), + [](const HimemAlloc& alloc) { + assert(alloc.is_valid); + }); + + bool playing = true; + bool quit = false; + while (!quit) { + // TODO: full event here? + Command cmd; + bool has_cmd = xQueueReceive(input, &cmd, 0); + if (has_cmd) { + switch (cmd) { + case PLAY: + playing = true; + break; + case PAUSE: + playing = false; + break; + case QUIT: + quit = true; + break; } - // Loop again, so that we service all incoming events before doing our - // possibly expensive processing. - continue; } - - if (element->HasUnflushedOutput()) { - ESP_LOGD(kTag, "flushing output"); - } - - // We have no new events. Next, see if there's anything that needs to be - // flushed. - if (element->HasUnflushedOutput() && !element->FlushBufferedOutput()) { - // We had things to flush, but couldn't send it all. This probably - // implies that the downstream element is having issues servicing its - // input queue, so hold off for a moment before retrying. - ESP_LOGW(kTag, "failed to flush buffered output"); - vTaskDelay(pdMS_TO_TICKS(100)); - continue; + if (quit) { + break; } - if (element->HasUnprocessedInput()) { - ESP_LOGD(kTag, "processing input events"); - element->Process(); - continue; - } - - // The element ran out of data, so now it's time to let it process more - // input. - while (!pending_events.empty()) { - std::unique_ptr event; - pending_events.front().swap(event); - pending_events.pop_front(); - ESP_LOGD(kTag, "processing event, tag %i", event->tag); - - if (event->tag == StreamEvent::STREAM_INFO) { - ESP_LOGD(kTag, "processing stream info"); - - element->ProcessStreamInfo(*event->stream_info); - - } else if (event->tag == StreamEvent::ARENA_CHUNK) { - ESP_LOGD(kTag, "processing arena data"); - - memory::ArenaRef ref(event->arena_chunk); - auto callback = - StreamEvent::CreateChunkNotification(element->InputEventQueue()); - if (!xQueueSend(event->source, &callback, 0)) { - ESP_LOGW(kTag, "failed to send chunk notif"); - continue; + if (playing) { + for (int i = 0; i < elements.size(); i++) { + std::vector in_streams; + elements.at(i)->InStreams(&in_regions, &in_streams); + MutableStream out_stream = elements.at(i)->OutStream(&out_region); + + // Crop the input and output streams to the ranges that are safe to + // touch. For the input streams, this is the region that contains + // data. For the output stream, this is the region that does *not* + // already contain data. + std::vector cropped_in_streams; + std::for_each(in_streams.begin(), in_streams.end(), + [&](MutableStream& s) { + cropped_in_streams.emplace_back( + s.info, s.data.first(s.info->bytes_in_stream)); + }); + + elements.at(i)->OutputElement()->Process(&cropped_in_streams, + &out_stream); + + for (int stream = 0; stream < in_streams.size(); stream++) { + MutableStream& orig_stream = in_streams.at(stream); + Stream& cropped_stream = cropped_in_streams.at(stream); + std::move(cropped_stream.data.begin(), cropped_stream.data.end(), + orig_stream.data.begin()); + orig_stream.info->bytes_in_stream = + cropped_stream.data.size_bytes(); } - - // TODO(jacqueline): Consider giving the element a full ArenaRef here, - // so that it can hang on to it and potentially save an alloc+copy. - element->ProcessChunk({ref.ptr.start, ref.ptr.used_size}); - - // TODO: think about whether to do the whole queue - break; } } } @@ -165,4 +145,6 @@ void AudioTaskMain(void* args) { vTaskDelete(NULL); } +} // namespace task + } // namespace audio -- cgit v1.2.3 From 7c6fd654f50e6665efa4226c6b927f9762734182 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Sat, 1 Apr 2023 13:22:21 +1100 Subject: New pipeline building, still needs proper control --- src/audio/audio_task.cpp | 129 +++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 54 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 542bada8..e6c7778c 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -8,7 +8,9 @@ #include #include +#include "audio_sink.hpp" #include "cbor.h" +#include "dac.hpp" #include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" @@ -33,22 +35,29 @@ namespace task { static const char* kTag = "task"; static const std::size_t kStackSize = 24 * 1024; +static const std::size_t kDrainStackSize = 1024; static const uint8_t kAudioCore = 0; -auto Start(Pipeline* pipeline) -> Handle* { - auto input_queue = xQueueCreate(8, 1); - +auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { // Newly created task will free this. - AudioTaskArgs* args = new AudioTaskArgs{ - .pipeline = pipeline, - .input = input_queue, - }; + AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink}; - ESP_LOGI(kTag, "starting audio task"); + ESP_LOGI(kTag, "starting audio pipeline task"); xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, kTaskPriorityAudio, NULL, kAudioCore); +} - return new Handle(input_queue); +auto StartDrain(IAudioSink* sink) -> void { + auto command = new std::atomic(PLAY); + // Newly created task will free this. + AudioDrainArgs* drain_args = new AudioDrainArgs{ + .sink = sink, + .command = command, + }; + + ESP_LOGI(kTag, "starting audio drain task"); + xTaskCreatePinnedToCore(&AudioDrainMain, "drain", kDrainStackSize, drain_args, + kTaskPriorityAudio, NULL, kAudioCore); } void AudioTaskMain(void* args) { @@ -57,10 +66,11 @@ void AudioTaskMain(void* args) { { AudioTaskArgs* real_args = reinterpret_cast(args); std::unique_ptr pipeline(real_args->pipeline); - QueueHandle_t input; - StreamBufferHandle_t output; + IAudioSink* sink = real_args->sink; delete real_args; + std::optional output_format; + std::vector elements = pipeline->GetIterationOrder(); std::size_t max_inputs = (*std::max_element(elements.begin(), elements.end(), @@ -74,9 +84,7 @@ void AudioTaskMain(void* args) { std::vector> in_regions(max_inputs); MappableRegion out_region; std::for_each(in_regions.begin(), in_regions.end(), - [](const MappableRegion& region) { - assert(region.is_valid); - }); + [](const auto& region) { assert(region.is_valid); }); assert(out_region.is_valid); // Each element has exactly one output buffer. @@ -90,55 +98,68 @@ void AudioTaskMain(void* args) { bool playing = true; bool quit = false; while (!quit) { - // TODO: full event here? - Command cmd; - bool has_cmd = xQueueReceive(input, &cmd, 0); - if (has_cmd) { - switch (cmd) { - case PLAY: - playing = true; - break; - case PAUSE: - playing = false; - break; - case QUIT: - quit = true; - break; - } - } - if (quit) { - break; - } - if (playing) { for (int i = 0; i < elements.size(); i++) { - std::vector in_streams; - elements.at(i)->InStreams(&in_regions, &in_streams); - MutableStream out_stream = elements.at(i)->OutStream(&out_region); + std::vector raw_in_streams; + elements.at(i)->InStreams(&in_regions, &raw_in_streams); + RawStream raw_out_stream = elements.at(i)->OutStream(&out_region); // Crop the input and output streams to the ranges that are safe to // touch. For the input streams, this is the region that contains // data. For the output stream, this is the region that does *not* // already contain data. - std::vector cropped_in_streams; - std::for_each(in_streams.begin(), in_streams.end(), - [&](MutableStream& s) { - cropped_in_streams.emplace_back( - s.info, s.data.first(s.info->bytes_in_stream)); - }); - - elements.at(i)->OutputElement()->Process(&cropped_in_streams, - &out_stream); - - for (int stream = 0; stream < in_streams.size(); stream++) { - MutableStream& orig_stream = in_streams.at(stream); - Stream& cropped_stream = cropped_in_streams.at(stream); - std::move(cropped_stream.data.begin(), cropped_stream.data.end(), - orig_stream.data.begin()); - orig_stream.info->bytes_in_stream = - cropped_stream.data.size_bytes(); + std::vector in_streams; + std::for_each(raw_in_streams.begin(), raw_in_streams.end(), + [&](RawStream& s) { in_streams.emplace_back(&s); }); + OutputStream out_stream(&raw_out_stream); + + elements.at(i)->OutputElement()->Process(in_streams, &out_stream); + } + + RawStream raw_sink_stream = elements.back()->OutStream(&out_region); + InputStream sink_stream(&raw_sink_stream); + + if (!output_format || output_format != sink_stream.info().format) { + // The format of the stream within the sink stream has changed. We + // need to reconfigure the sink, but shouldn't do so until we've fully + // drained the current buffer. + if (xStreamBufferIsEmpty(sink->buffer())) { + output_format = sink_stream.info().format; + sink->Configure(*output_format); } } + + // We've reconfigured the sink, or it was already configured correctly. + // Send through some data. + if (output_format == sink_stream.info().format) { + // TODO: tune the delay on this, as it's currently the only way to + // throttle this task's CPU time. Maybe also hold off on the pipeline + // if the buffer is already close to full? + std::size_t sent = xStreamBufferSend( + sink->buffer(), sink_stream.data().data(), + sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); + sink_stream.consume(sent); + } + } + } + } + vTaskDelete(NULL); +} + +void AudioDrainMain(void* args) { + { + AudioDrainArgs* real_args = reinterpret_cast(args); + IAudioSink* sink = real_args->sink; + std::atomic* command = real_args->command; + delete real_args; + + // TODO(jacqueline): implement PAUSE without busy-waiting. + while (*command != QUIT) { + std::byte buf[64]; + std::size_t len = + xStreamBufferReceive(sink->buffer(), buf, sizeof(buf), portMAX_DELAY); + if (len > 0) { + sink->Send({buf, len}); } } } -- cgit v1.2.3 From 3836768bb8b95188e6657ab69027d1d9e4b13a77 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 3 Apr 2023 14:06:30 +1000 Subject: new pipeline working(?), but the dac eludes me --- src/audio/audio_task.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index e6c7778c..eb33611b 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "audio_sink.hpp" #include "cbor.h" @@ -114,16 +115,29 @@ void AudioTaskMain(void* args) { OutputStream out_stream(&raw_out_stream); elements.at(i)->OutputElement()->Process(in_streams, &out_stream); + + std::for_each(in_regions.begin(), in_regions.end(), + [](auto&& r) { r.Unmap(); }); + out_region.Unmap(); } - RawStream raw_sink_stream = elements.back()->OutStream(&out_region); + RawStream raw_sink_stream = elements.front()->OutStream(&out_region); InputStream sink_stream(&raw_sink_stream); - if (!output_format || output_format != sink_stream.info().format) { + if (sink_stream.data().size_bytes() == 0) { + out_region.Unmap(); + vTaskDelay(pdMS_TO_TICKS(100)); + continue; + } + + if ((!output_format || output_format != sink_stream.info().format) && + !std::holds_alternative( + sink_stream.info().format)) { // The format of the stream within the sink stream has changed. We // need to reconfigure the sink, but shouldn't do so until we've fully // drained the current buffer. if (xStreamBufferIsEmpty(sink->buffer())) { + ESP_LOGI(kTag, "reconfiguring dac"); output_format = sink_stream.info().format; sink->Configure(*output_format); } @@ -140,6 +154,8 @@ void AudioTaskMain(void* args) { sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); sink_stream.consume(sent); } + + out_region.Unmap(); } } } -- cgit v1.2.3 From 40a9734b04c48339cfdf6ed9043aa3f6f0dda62d Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 4 Apr 2023 09:46:52 +1000 Subject: Redo pcm registers to include pages --- src/audio/audio_task.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index eb33611b..babe0a97 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -162,6 +162,8 @@ void AudioTaskMain(void* args) { vTaskDelete(NULL); } +static std::byte sDrainBuf[1024]; + void AudioDrainMain(void* args) { { AudioDrainArgs* real_args = reinterpret_cast(args); @@ -171,11 +173,10 @@ void AudioDrainMain(void* args) { // TODO(jacqueline): implement PAUSE without busy-waiting. while (*command != QUIT) { - std::byte buf[64]; - std::size_t len = - xStreamBufferReceive(sink->buffer(), buf, sizeof(buf), portMAX_DELAY); + std::size_t len = xStreamBufferReceive(sink->buffer(), sDrainBuf, + sizeof(sDrainBuf), portMAX_DELAY); if (len > 0) { - sink->Send({buf, len}); + sink->Send({sDrainBuf, len}); } } } -- cgit v1.2.3 From 7a54ff0df9c18b662e5bdc11ac2e26ff052cfa4d Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 4 Apr 2023 14:12:01 +1000 Subject: WIP track down new pipeline memory issues --- src/audio/audio_task.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index babe0a97..464879d8 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -130,9 +130,7 @@ void AudioTaskMain(void* args) { continue; } - if ((!output_format || output_format != sink_stream.info().format) && - !std::holds_alternative( - sink_stream.info().format)) { + if (!output_format || output_format != sink_stream.info().format) { // The format of the stream within the sink stream has changed. We // need to reconfigure the sink, but shouldn't do so until we've fully // drained the current buffer. @@ -145,7 +143,7 @@ void AudioTaskMain(void* args) { // We've reconfigured the sink, or it was already configured correctly. // Send through some data. - if (output_format == sink_stream.info().format) { + if (output_format == sink_stream.info().format && !std::holds_alternative(*output_format)) { // TODO: tune the delay on this, as it's currently the only way to // throttle this task's CPU time. Maybe also hold off on the pipeline // if the buffer is already close to full? -- cgit v1.2.3 From 561f9d2a07ee6ee1c2f18dc375125f87ea7b0d55 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 19 Apr 2023 13:00:42 +1000 Subject: Ensure the sink buffer is large enough to not fully drain during playback --- src/audio/audio_task.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 464879d8..3a2a5941 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -124,7 +124,7 @@ void AudioTaskMain(void* args) { RawStream raw_sink_stream = elements.front()->OutStream(&out_region); InputStream sink_stream(&raw_sink_stream); - if (sink_stream.data().size_bytes() == 0) { + if (sink_stream.info().bytes_in_stream == 0) { out_region.Unmap(); vTaskDelay(pdMS_TO_TICKS(100)); continue; @@ -143,7 +143,8 @@ void AudioTaskMain(void* args) { // We've reconfigured the sink, or it was already configured correctly. // Send through some data. - if (output_format == sink_stream.info().format && !std::holds_alternative(*output_format)) { + if (output_format == sink_stream.info().format && + !std::holds_alternative(*output_format)) { // TODO: tune the delay on this, as it's currently the only way to // throttle this task's CPU time. Maybe also hold off on the pipeline // if the buffer is already close to full? @@ -160,7 +161,7 @@ void AudioTaskMain(void* args) { vTaskDelete(NULL); } -static std::byte sDrainBuf[1024]; +static std::byte sDrainBuf[8 * 1024]; void AudioDrainMain(void* args) { { -- cgit v1.2.3 From 4c77950e702a329f3136456a932efbea36e03d42 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 19 Apr 2023 16:45:50 +1000 Subject: Pipeline working and outputting correctly, but noisy --- src/audio/audio_task.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 3a2a5941..1670f9f6 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -45,7 +45,7 @@ auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { ESP_LOGI(kTag, "starting audio pipeline task"); xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, - kTaskPriorityAudio, NULL, kAudioCore); + kTaskPriorityAudioPipeline, NULL, kAudioCore); } auto StartDrain(IAudioSink* sink) -> void { @@ -57,8 +57,8 @@ auto StartDrain(IAudioSink* sink) -> void { }; ESP_LOGI(kTag, "starting audio drain task"); - xTaskCreatePinnedToCore(&AudioDrainMain, "drain", kDrainStackSize, drain_args, - kTaskPriorityAudio, NULL, kAudioCore); + xTaskCreate(&AudioDrainMain, "drain", kDrainStackSize, drain_args, + kTaskPriorityAudioDrain, NULL); } void AudioTaskMain(void* args) { @@ -134,7 +134,7 @@ void AudioTaskMain(void* args) { // The format of the stream within the sink stream has changed. We // need to reconfigure the sink, but shouldn't do so until we've fully // drained the current buffer. - if (xStreamBufferIsEmpty(sink->buffer())) { + if (xStreamBufferIsEmpty(*sink->buffer())) { ESP_LOGI(kTag, "reconfiguring dac"); output_format = sink_stream.info().format; sink->Configure(*output_format); @@ -149,7 +149,7 @@ void AudioTaskMain(void* args) { // throttle this task's CPU time. Maybe also hold off on the pipeline // if the buffer is already close to full? std::size_t sent = xStreamBufferSend( - sink->buffer(), sink_stream.data().data(), + *sink->buffer(), sink_stream.data().data(), sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); sink_stream.consume(sent); } @@ -172,7 +172,7 @@ void AudioDrainMain(void* args) { // TODO(jacqueline): implement PAUSE without busy-waiting. while (*command != QUIT) { - std::size_t len = xStreamBufferReceive(sink->buffer(), sDrainBuf, + std::size_t len = xStreamBufferReceive(*sink->buffer(), sDrainBuf, sizeof(sDrainBuf), portMAX_DELAY); if (len > 0) { sink->Send({sDrainBuf, len}); -- cgit v1.2.3 From 731b2cfa77a063e98da8fa26acc1e7ed1de8c169 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 20 Apr 2023 11:25:43 +1000 Subject: working isr-based sink, but still grainy --- src/audio/audio_task.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 1670f9f6..45db0b60 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -58,7 +58,7 @@ auto StartDrain(IAudioSink* sink) -> void { ESP_LOGI(kTag, "starting audio drain task"); xTaskCreate(&AudioDrainMain, "drain", kDrainStackSize, drain_args, - kTaskPriorityAudioDrain, NULL); + kTaskPriorityAudioDrain, NULL); } void AudioTaskMain(void* args) { @@ -134,7 +134,7 @@ void AudioTaskMain(void* args) { // The format of the stream within the sink stream has changed. We // need to reconfigure the sink, but shouldn't do so until we've fully // drained the current buffer. - if (xStreamBufferIsEmpty(*sink->buffer())) { + if (xStreamBufferIsEmpty(sink->buffer())) { ESP_LOGI(kTag, "reconfiguring dac"); output_format = sink_stream.info().format; sink->Configure(*output_format); @@ -149,7 +149,7 @@ void AudioTaskMain(void* args) { // throttle this task's CPU time. Maybe also hold off on the pipeline // if the buffer is already close to full? std::size_t sent = xStreamBufferSend( - *sink->buffer(), sink_stream.data().data(), + sink->buffer(), sink_stream.data().data(), sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); sink_stream.consume(sent); } @@ -172,7 +172,7 @@ void AudioDrainMain(void* args) { // TODO(jacqueline): implement PAUSE without busy-waiting. while (*command != QUIT) { - std::size_t len = xStreamBufferReceive(*sink->buffer(), sDrainBuf, + std::size_t len = xStreamBufferReceive(sink->buffer(), sDrainBuf, sizeof(sDrainBuf), portMAX_DELAY); if (len > 0) { sink->Send({sDrainBuf, len}); -- cgit v1.2.3 From 27c63ebb957aa5b942939aea33431ac50d101a26 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 20 Apr 2023 21:25:22 +1000 Subject: Switch to an MVP-ready 16bit three wire DAC setup --- src/audio/audio_task.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 45db0b60..07f49085 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -37,15 +37,14 @@ namespace task { static const char* kTag = "task"; static const std::size_t kStackSize = 24 * 1024; static const std::size_t kDrainStackSize = 1024; -static const uint8_t kAudioCore = 0; auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { // Newly created task will free this. AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink}; ESP_LOGI(kTag, "starting audio pipeline task"); - xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, - kTaskPriorityAudioPipeline, NULL, kAudioCore); + xTaskCreate(&AudioTaskMain, "pipeline", kStackSize, args, + kTaskPriorityAudioPipeline, NULL); } auto StartDrain(IAudioSink* sink) -> void { @@ -151,6 +150,13 @@ void AudioTaskMain(void* args) { std::size_t sent = xStreamBufferSend( sink->buffer(), sink_stream.data().data(), sink_stream.data().size_bytes(), pdMS_TO_TICKS(10)); + if (sent > 0) { + ESP_LOGI(kTag, "sunk %u bytes out of %u (%d %%)", sent, + sink_stream.info().bytes_in_stream, + (int)(((float)sent / + (float)sink_stream.info().bytes_in_stream) * + 100)); + } sink_stream.consume(sent); } -- cgit v1.2.3 From 130009eb142a5ce8bb6cf9cbb59d69a4d96ed5f9 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 21 Apr 2023 10:35:39 +1000 Subject: Pin audio pipeline to the app core --- src/audio/audio_task.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/audio/audio_task.cpp') diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 07f49085..b2a8062e 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -43,8 +43,8 @@ auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void { AudioTaskArgs* args = new AudioTaskArgs{.pipeline = pipeline, .sink = sink}; ESP_LOGI(kTag, "starting audio pipeline task"); - xTaskCreate(&AudioTaskMain, "pipeline", kStackSize, args, - kTaskPriorityAudioPipeline, NULL); + xTaskCreatePinnedToCore(&AudioTaskMain, "pipeline", kStackSize, args, + kTaskPriorityAudioPipeline, NULL, 1); } auto StartDrain(IAudioSink* sink) -> void { -- cgit v1.2.3