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/include/audio_decoder.hpp | 19 ++++------ src/audio/include/audio_element.hpp | 58 ++---------------------------- src/audio/include/audio_playback.hpp | 13 +++---- src/audio/include/audio_task.hpp | 31 +++++++++++++--- src/audio/include/fatfs_audio_input.hpp | 17 ++++----- src/audio/include/i2s_audio_output.hpp | 17 ++++----- src/audio/include/pipeline.hpp | 42 ++++++++++++++++++++++ src/audio/include/stream_info.hpp | 63 +++++++++++++++++++++++++++++---- 8 files changed, 153 insertions(+), 107 deletions(-) create mode 100644 src/audio/include/pipeline.hpp (limited to 'src/audio/include') diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index 47642469..be8daf99 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "chunk.hpp" #include "ff.h" @@ -10,6 +11,7 @@ #include "audio_element.hpp" #include "codec.hpp" +#include "stream_info.hpp" namespace audio { @@ -22,28 +24,21 @@ class AudioDecoder : public IAudioElement { AudioDecoder(); ~AudioDecoder(); - auto StackSizeBytes() const -> std::size_t override { return 10 * 1024; }; - - auto HasUnprocessedInput() -> bool override; - auto IsOverBuffered() -> bool override; - - auto ProcessStreamInfo(const StreamInfo& info) -> void override; - auto ProcessChunk(const cpp::span& chunk) -> void override; - auto ProcessEndOfStream() -> void override; - auto Process() -> void override; + auto Process(std::vector* inputs, MutableStream* output) + -> void override; AudioDecoder(const AudioDecoder&) = delete; AudioDecoder& operator=(const AudioDecoder&) = delete; private: - memory::Arena arena_; std::unique_ptr current_codec_; std::optional stream_info_; - std::optional chunk_reader_; - bool has_sent_stream_info_; + bool has_set_stream_info_; bool has_samples_to_send_; bool needs_more_input_; + + auto ProcessStreamInfo(const StreamInfo& info) -> bool; }; } // namespace audio diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index 91036348..c9192e4a 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -40,62 +40,8 @@ class IAudioElement { IAudioElement(); virtual ~IAudioElement(); - /* - * Returns the stack size in bytes that this element requires. This should - * be tuned according to the observed stack size of each element, as different - * elements have fairly different stack requirements (particular decoders). - */ - virtual auto StackSizeBytes() const -> std::size_t { return 4096; }; - - /* Returns this element's input buffer. */ - auto InputEventQueue() const -> QueueHandle_t { return input_events_; } - /* Returns this element's output buffer. */ - auto OutputEventQueue() const -> QueueHandle_t { return output_events_; } - auto OutputEventQueue(const QueueHandle_t q) -> void { output_events_ = q; } - - virtual auto HasUnprocessedInput() -> bool = 0; - - virtual auto IsOverBuffered() -> bool { return false; } - - auto HasUnflushedOutput() -> bool { return !buffered_output_.empty(); } - auto FlushBufferedOutput() -> bool; - - /* - * Called when a StreamInfo message is received. Used to configure this - * element in preperation for incoming chunks. - */ - virtual auto ProcessStreamInfo(const StreamInfo& info) -> void = 0; - - /* - * Called when a ChunkHeader message is received. Includes the data associated - * with this chunk of stream data. This method should return the number of - * bytes in this chunk that were actually used; leftover bytes will be - * prepended to the next call. - */ - virtual auto ProcessChunk(const cpp::span& chunk) -> void = 0; - - virtual auto ProcessEndOfStream() -> void = 0; - - virtual auto ProcessLogStatus() -> void {} - - /* - * Called when there has been no data received over the input buffer for some - * time. This could be used to synthesize output, or to save memory by - * releasing unused resources. - */ - virtual auto Process() -> void = 0; - - protected: - auto SendOrBufferEvent(std::unique_ptr event) -> bool; - - // Queue for events coming into this element. Owned by us. - QueueHandle_t input_events_; - // Queue for events going into the next element. Not owned by us, may be null - // if we're not yet in a pipeline. - // FIXME: it would be nicer if this was non-nullable. - QueueHandle_t output_events_; - // Output events that have been generated, but are yet to be sent downstream. - std::deque> buffered_output_; + virtual auto Process(std::vector* inputs, MutableStream* output) + -> void = 0; }; } // namespace audio diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp index a1bea64f..507e6f73 100644 --- a/src/audio/include/audio_playback.hpp +++ b/src/audio/include/audio_playback.hpp @@ -5,7 +5,9 @@ #include #include +#include "audio_task.hpp" #include "esp_err.h" +#include "fatfs_audio_input.hpp" #include "result.hpp" #include "span.hpp" @@ -23,12 +25,10 @@ namespace audio { class AudioPlayback { public: enum Error { ERR_INIT_ELEMENT, ERR_MEM }; - static auto create(drivers::GpioExpander* expander, - std::shared_ptr storage) + static auto create(drivers::GpioExpander* expander) -> cpp::result, Error>; - // TODO(jacqueline): configure on the fly once we have things to configure. - AudioPlayback(); + AudioPlayback(FatfsAudioInput *file_input); ~AudioPlayback(); /* @@ -44,9 +44,10 @@ class AudioPlayback { AudioPlayback& operator=(const AudioPlayback&) = delete; private: - auto ConnectElements(IAudioElement* src, IAudioElement* sink) -> void; + FatfsAudioInput *file_source; - QueueHandle_t input_handle_; + std::vector> all_elements_; + std::unique_ptr pipeline_; }; } // namespace audio diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index df70ebaa..8db99850 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -6,17 +6,38 @@ #include "audio_element.hpp" #include "freertos/portmacro.h" +#include "pipeline.hpp" namespace audio { +namespace task { struct AudioTaskArgs { - std::shared_ptr& element; + Pipeline* pipeline; + QueueHandle_t input; }; -auto StartAudioTask(const std::string& name, - std::optional core_id, - std::shared_ptr element) -> void; +extern "C" void AudioTaskMain(void* args); -void AudioTaskMain(void* args); +enum Command { PLAY, PAUSE, QUIT }; + +class Handle { + public: + explicit Handle(QueueHandle_t input); + ~Handle(); + + auto SetStreamInfo() -> void; + auto Play() -> void; + auto Pause() -> void; + auto Quit() -> void; + + auto OutputBuffer() -> StreamBufferHandle_t; + + private: + QueueHandle_t input; +}; + +auto Start(Pipeline* pipeline) -> Handle*; + +} // namespace task } // namespace audio diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 9f2d676c..b3a6d843 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -3,41 +3,36 @@ #include #include #include +#include #include "arena.hpp" #include "chunk.hpp" #include "freertos/FreeRTOS.h" +#include "ff.h" #include "freertos/message_buffer.h" #include "freertos/queue.h" #include "span.hpp" #include "audio_element.hpp" -#include "storage.hpp" #include "stream_buffer.hpp" namespace audio { class FatfsAudioInput : public IAudioElement { public: - explicit FatfsAudioInput(std::shared_ptr storage); + explicit FatfsAudioInput(); ~FatfsAudioInput(); - auto HasUnprocessedInput() -> bool override; - auto IsOverBuffered() -> bool override; + auto OpenFile(const std::string& path) -> void; - auto ProcessStreamInfo(const StreamInfo& info) -> void override; - auto ProcessChunk(const cpp::span& chunk) -> void override; - auto ProcessEndOfStream() -> void override; - auto Process() -> void override; + auto Process(std::vector* inputs, MutableStream* output) + -> void override; FatfsAudioInput(const FatfsAudioInput&) = delete; FatfsAudioInput& operator=(const FatfsAudioInput&) = delete; private: - memory::Arena arena_; - std::shared_ptr storage_; - FIL current_file_; bool is_file_open_; }; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 2bea091b..57881b35 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "audio_element.hpp" #include "chunk.hpp" @@ -9,6 +10,7 @@ #include "dac.hpp" #include "gpio_expander.hpp" +#include "stream_info.hpp" namespace audio { @@ -22,14 +24,8 @@ class I2SAudioOutput : public IAudioElement { std::unique_ptr dac); ~I2SAudioOutput(); - auto HasUnprocessedInput() -> bool override; - auto IsOverBuffered() -> bool override; - - auto ProcessStreamInfo(const StreamInfo& info) -> void override; - auto ProcessChunk(const cpp::span& chunk) -> void override; - auto ProcessEndOfStream() -> void override; - auto ProcessLogStatus() -> void override; - auto Process() -> void override; + auto Process(std::vector* inputs, MutableStream* output) + -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; @@ -40,8 +36,9 @@ class I2SAudioOutput : public IAudioElement { drivers::GpioExpander* expander_; std::unique_ptr dac_; - std::optional chunk_reader_; - cpp::span latest_chunk_; + std::optional current_config_; + + auto ProcessStreamInfo(const StreamInfo& info) -> bool; }; } // namespace audio diff --git a/src/audio/include/pipeline.hpp b/src/audio/include/pipeline.hpp new file mode 100644 index 00000000..42f70828 --- /dev/null +++ b/src/audio/include/pipeline.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include "freertos/portmacro.h" + +#include "audio_element.hpp" +#include "himem.hpp" +#include "stream_info.hpp" + +namespace audio { + +static const std::size_t kPipelineBufferSize = 32 * 1024; + +class Pipeline { + public: + Pipeline(IAudioElement* output); + ~Pipeline(); + auto AddInput(IAudioElement* input) -> Pipeline*; + + auto OutputElement() const -> IAudioElement*; + + auto NumInputs() const -> std::size_t; + + auto InStreams(std::vector>*, + std::vector*) -> void; + + auto OutStream(MappableRegion*) -> MutableStream; + + auto GetIterationOrder() -> std::vector; + + private: + IAudioElement* root_; + std::vector> subtrees_; + + HimemAlloc output_buffer_; + StreamInfo output_info_; +}; + +} // namespace audio diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index bf67364f..47f65649 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -4,19 +4,68 @@ #include #include #include +#include -#include "cbor.h" #include "result.hpp" -#include "sys/_stdint.h" +#include "span.hpp" +#include "types.hpp" namespace audio { struct StreamInfo { - std::optional path; - std::optional channels; - std::optional bits_per_sample; - std::optional sample_rate; - std::optional chunk_size; + // The number of bytes that are available for consumption within this + // stream's buffer. + std::size_t bytes_in_stream; + + // The total length of this stream, in case its source is finite (e.g. a + // file on disk). May be absent for endless streams (internet streams, + // generated audio, etc.) + std::optional length_bytes; + + struct Encoded { + // The codec that this stream is associated with. + codecs::StreamType type; + + bool operator==(const Encoded&) const = default; + }; + + struct Pcm { + // Number of channels in this stream. + uint8_t channels; + // Number of bits per sample. + uint8_t bits_per_sample; + // The sample rate. + uint16_t sample_rate; + + bool operator==(const Pcm&) const = default; + }; + + std::variant data; + + bool operator==(const StreamInfo&) const = default; +}; + +class MutableStream { + public: + StreamInfo* info; + cpp::span data; + + MutableStream(StreamInfo* i, cpp::span d) + : info(i), data(d) {} +}; + +/* + * A byte buffer + associated metadata, which is not allowed to modify any of + * the underlying data. + */ +class Stream { + public: + explicit Stream(const MutableStream& s) : info(*s.info), data(s.data) {} + + const StreamInfo& info; + // `data` itself left mutable for signalling how much of the stream was + // consumed + cpp::span data; }; } // 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/include/audio_decoder.hpp | 8 ++-- src/audio/include/audio_element.hpp | 8 ++-- src/audio/include/audio_playback.hpp | 10 ++--- src/audio/include/audio_sink.hpp | 22 +++++++++++ src/audio/include/audio_task.hpp | 34 +++++++--------- src/audio/include/fatfs_audio_input.hpp | 3 +- src/audio/include/i2s_audio_output.hpp | 11 +++--- src/audio/include/pipeline.hpp | 7 ++-- src/audio/include/stream_info.hpp | 69 +++++++++++++++++++++++++++------ 9 files changed, 117 insertions(+), 55 deletions(-) create mode 100644 src/audio/include/audio_sink.hpp (limited to 'src/audio/include') diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index be8daf99..6a1b5177 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -24,7 +24,7 @@ class AudioDecoder : public IAudioElement { AudioDecoder(); ~AudioDecoder(); - auto Process(std::vector* inputs, MutableStream* output) + auto Process(const std::vector& inputs, OutputStream* output) -> void override; AudioDecoder(const AudioDecoder&) = delete; @@ -32,11 +32,9 @@ class AudioDecoder : public IAudioElement { private: std::unique_ptr current_codec_; - std::optional stream_info_; - - bool has_set_stream_info_; + std::optional current_input_format_; + std::optional current_output_format_; bool has_samples_to_send_; - bool needs_more_input_; auto ProcessStreamInfo(const StreamInfo& info) -> bool; }; diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index c9192e4a..5884f7b2 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -37,11 +37,11 @@ static const size_t kEventQueueSize = 8; */ class IAudioElement { public: - IAudioElement(); - virtual ~IAudioElement(); + IAudioElement() {} + virtual ~IAudioElement() {} - virtual auto Process(std::vector* inputs, MutableStream* output) - -> void = 0; + virtual auto Process(const std::vector& inputs, + OutputStream* output) -> void = 0; }; } // namespace audio diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp index 507e6f73..88dc29aa 100644 --- a/src/audio/include/audio_playback.hpp +++ b/src/audio/include/audio_playback.hpp @@ -8,6 +8,7 @@ #include "audio_task.hpp" #include "esp_err.h" #include "fatfs_audio_input.hpp" +#include "i2s_audio_output.hpp" #include "result.hpp" #include "span.hpp" @@ -28,7 +29,7 @@ class AudioPlayback { static auto create(drivers::GpioExpander* expander) -> cpp::result, Error>; - AudioPlayback(FatfsAudioInput *file_input); + explicit AudioPlayback(std::unique_ptr output); ~AudioPlayback(); /* @@ -44,10 +45,9 @@ class AudioPlayback { AudioPlayback& operator=(const AudioPlayback&) = delete; private: - FatfsAudioInput *file_source; - - std::vector> all_elements_; - std::unique_ptr pipeline_; + std::unique_ptr file_source_; + std::unique_ptr i2s_output_; + std::vector> elements_; }; } // namespace audio diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp new file mode 100644 index 00000000..ed7eb02b --- /dev/null +++ b/src/audio/include/audio_sink.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "audio_element.hpp" +#include "stream_info.hpp" +namespace audio { + +class IAudioSink { + private: + static const std::size_t kDrainBufferSize = 8 * 1024; + StreamBufferHandle_t buffer_; + + public: + IAudioSink() : buffer_(xStreamBufferCreate(kDrainBufferSize, 1)) {} + virtual ~IAudioSink() { vStreamBufferDelete(buffer_); } + + virtual auto Configure(const StreamInfo::Format& format) -> bool = 0; + virtual auto Send(const cpp::span& data) -> void = 0; + + auto buffer() const -> StreamBufferHandle_t { return buffer_; } +}; + +} // namespace audio diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index 8db99850..a7b7a0fa 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -5,38 +5,32 @@ #include #include "audio_element.hpp" +#include "audio_sink.hpp" +#include "dac.hpp" #include "freertos/portmacro.h" #include "pipeline.hpp" +#include "stream_buffer.hpp" namespace audio { namespace task { + +enum Command { PLAY, PAUSE, QUIT }; + struct AudioTaskArgs { Pipeline* pipeline; - QueueHandle_t input; + IAudioSink* sink; +}; +struct AudioDrainArgs { + IAudioSink* sink; + std::atomic* command; }; extern "C" void AudioTaskMain(void* args); +extern "C" void AudioDrainMain(void* args); -enum Command { PLAY, PAUSE, QUIT }; - -class Handle { - public: - explicit Handle(QueueHandle_t input); - ~Handle(); - - auto SetStreamInfo() -> void; - auto Play() -> void; - auto Pause() -> void; - auto Quit() -> void; - - auto OutputBuffer() -> StreamBufferHandle_t; - - private: - QueueHandle_t input; -}; - -auto Start(Pipeline* pipeline) -> Handle*; +auto StartPipeline(Pipeline* pipeline, IAudioSink* sink) -> void; +auto StartDrain(IAudioSink* sink) -> void; } // namespace task diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index b3a6d843..24f62e3c 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -16,6 +16,7 @@ #include "audio_element.hpp" #include "stream_buffer.hpp" +#include "stream_info.hpp" namespace audio { @@ -26,7 +27,7 @@ class FatfsAudioInput : public IAudioElement { auto OpenFile(const std::string& path) -> void; - auto Process(std::vector* inputs, MutableStream* output) + auto Process(const std::vector& inputs, OutputStream* output) -> void override; FatfsAudioInput(const FatfsAudioInput&) = delete; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 57881b35..77019228 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -5,6 +5,7 @@ #include #include "audio_element.hpp" +#include "audio_sink.hpp" #include "chunk.hpp" #include "result.hpp" @@ -14,18 +15,18 @@ namespace audio { -class I2SAudioOutput : public IAudioElement { +class I2SAudioOutput : public IAudioSink { public: enum Error { DAC_CONFIG, I2S_CONFIG, STREAM_INIT }; static auto create(drivers::GpioExpander* expander) - -> cpp::result, Error>; + -> cpp::result, Error>; I2SAudioOutput(drivers::GpioExpander* expander, std::unique_ptr dac); ~I2SAudioOutput(); - auto Process(std::vector* inputs, MutableStream* output) - -> void override; + auto Configure(const StreamInfo::Format& format) -> bool override; + auto Send(const cpp::span& data) -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; @@ -37,8 +38,6 @@ class I2SAudioOutput : public IAudioElement { std::unique_ptr dac_; std::optional current_config_; - - auto ProcessStreamInfo(const StreamInfo& info) -> bool; }; } // namespace audio diff --git a/src/audio/include/pipeline.hpp b/src/audio/include/pipeline.hpp index 42f70828..2e9247bc 100644 --- a/src/audio/include/pipeline.hpp +++ b/src/audio/include/pipeline.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "freertos/portmacro.h" @@ -16,7 +17,7 @@ static const std::size_t kPipelineBufferSize = 32 * 1024; class Pipeline { public: - Pipeline(IAudioElement* output); + explicit Pipeline(IAudioElement* output); ~Pipeline(); auto AddInput(IAudioElement* input) -> Pipeline*; @@ -25,9 +26,9 @@ class Pipeline { auto NumInputs() const -> std::size_t; auto InStreams(std::vector>*, - std::vector*) -> void; + std::vector*) -> void; - auto OutStream(MappableRegion*) -> MutableStream; + auto OutStream(MappableRegion*) -> RawStream; auto GetIterationOrder() -> std::vector; diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 47f65649..5622517f 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "result.hpp" @@ -35,37 +37,82 @@ struct StreamInfo { // Number of bits per sample. uint8_t bits_per_sample; // The sample rate. - uint16_t sample_rate; + uint32_t sample_rate; bool operator==(const Pcm&) const = default; }; - std::variant data; + typedef std::variant Format; + Format format; bool operator==(const StreamInfo&) const = default; }; -class MutableStream { +class RawStream { public: StreamInfo* info; cpp::span data; + bool is_incomplete; - MutableStream(StreamInfo* i, cpp::span d) - : info(i), data(d) {} + RawStream(StreamInfo* i, cpp::span d) + : info(i), data(d), is_incomplete(false) {} }; /* * A byte buffer + associated metadata, which is not allowed to modify any of * the underlying data. */ -class Stream { +class InputStream { public: - explicit Stream(const MutableStream& s) : info(*s.info), data(s.data) {} + explicit InputStream(RawStream* s) : raw_(s) {} - const StreamInfo& info; - // `data` itself left mutable for signalling how much of the stream was - // consumed - cpp::span data; + void consume(std::size_t bytes) const { + auto new_data = raw_->data.subspan(bytes); + std::move(new_data.begin(), new_data.end(), raw_->data.begin()); + raw_->info->bytes_in_stream = new_data.size_bytes(); + } + + void mark_incomplete() const { raw_->is_incomplete = true; } + + const StreamInfo& info() const { return *raw_->info; } + + cpp::span data() const { + return raw_->data.first(raw_->info->bytes_in_stream); + } + + private: + RawStream* raw_; +}; + +class OutputStream { + public: + explicit OutputStream(RawStream* s) : raw_(s) {} + + void add(std::size_t bytes) const { raw_->info->bytes_in_stream += bytes; } + + bool prepare(const StreamInfo::Format& new_format) { + if (new_format == raw_->info->format) { + raw_->info->format = new_format; + return true; + } + if (raw_->is_incomplete) { + raw_->info->format = new_format; + raw_->info->bytes_in_stream = 0; + return true; + } + return false; + } + + const StreamInfo& info() const { return *raw_->info; } + + cpp::span data() const { + return raw_->data.subspan(raw_->info->bytes_in_stream); + } + + bool is_incomplete() const { return raw_->is_incomplete; } + + private: + RawStream* raw_; }; } // namespace audio -- 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/include/audio_sink.hpp | 1 + src/audio/include/i2s_audio_output.hpp | 1 + src/audio/include/stream_info.hpp | 13 ++++++++----- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index ed7eb02b..ad63ec2e 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -15,6 +15,7 @@ class IAudioSink { virtual auto Configure(const StreamInfo::Format& format) -> bool = 0; virtual auto Send(const cpp::span& data) -> void = 0; + virtual auto Log() -> void {} auto buffer() const -> StreamBufferHandle_t { return buffer_; } }; diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 77019228..31510a91 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -27,6 +27,7 @@ class I2SAudioOutput : public IAudioSink { auto Configure(const StreamInfo::Format& format) -> bool override; auto Send(const cpp::span& data) -> void override; + auto Log() -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 5622517f..5a36384c 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -17,12 +17,12 @@ namespace audio { struct StreamInfo { // The number of bytes that are available for consumption within this // stream's buffer. - std::size_t bytes_in_stream; + std::size_t bytes_in_stream{0}; // The total length of this stream, in case its source is finite (e.g. a // file on disk). May be absent for endless streams (internet streams, // generated audio, etc.) - std::optional length_bytes; + std::optional length_bytes{}; struct Encoded { // The codec that this stream is associated with. @@ -42,8 +42,8 @@ struct StreamInfo { bool operator==(const Pcm&) const = default; }; - typedef std::variant Format; - Format format; + typedef std::variant Format; + Format format{}; bool operator==(const StreamInfo&) const = default; }; @@ -91,8 +91,11 @@ class OutputStream { void add(std::size_t bytes) const { raw_->info->bytes_in_stream += bytes; } bool prepare(const StreamInfo::Format& new_format) { - if (new_format == raw_->info->format) { + if (std::holds_alternative(raw_->info->format)) { raw_->info->format = new_format; + raw_->info->bytes_in_stream = 0; + } + if (new_format == raw_->info->format) { return true; } if (raw_->is_incomplete) { -- cgit v1.2.3 From 25c5896dc5c239e3a175c6c8fc5d7368946adeb6 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 6 Apr 2023 16:19:02 +1000 Subject: add some bounds checking asserts --- src/audio/include/stream_info.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/audio/include') diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 5a36384c..6256f2ee 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -67,6 +67,7 @@ class InputStream { explicit InputStream(RawStream* s) : raw_(s) {} void consume(std::size_t bytes) const { + assert(raw_->info->bytes_in_stream >= bytes); auto new_data = raw_->data.subspan(bytes); std::move(new_data.begin(), new_data.end(), raw_->data.begin()); raw_->info->bytes_in_stream = new_data.size_bytes(); @@ -88,7 +89,10 @@ class OutputStream { public: explicit OutputStream(RawStream* s) : raw_(s) {} - void add(std::size_t bytes) const { raw_->info->bytes_in_stream += bytes; } + void add(std::size_t bytes) const { + assert(raw_->info->bytes_in_stream + bytes <= raw_->data.size_bytes()); + raw_->info->bytes_in_stream += bytes; + } bool prepare(const StreamInfo::Format& new_format) { if (std::holds_alternative(raw_->info->format)) { -- 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/include/audio_sink.hpp | 3 ++- src/audio/include/stream_info.hpp | 52 ++++++++++----------------------------- 2 files changed, 15 insertions(+), 40 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index ad63ec2e..03a4690d 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -6,7 +6,8 @@ namespace audio { class IAudioSink { private: - static const std::size_t kDrainBufferSize = 8 * 1024; + // TODO: tune. at least about 12KiB seems right for mp3 + static const std::size_t kDrainBufferSize = 24 * 1024; StreamBufferHandle_t buffer_; public: diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 6256f2ee..28095935 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -66,20 +66,13 @@ class InputStream { public: explicit InputStream(RawStream* s) : raw_(s) {} - void consume(std::size_t bytes) const { - assert(raw_->info->bytes_in_stream >= bytes); - auto new_data = raw_->data.subspan(bytes); - std::move(new_data.begin(), new_data.end(), raw_->data.begin()); - raw_->info->bytes_in_stream = new_data.size_bytes(); - } + void consume(std::size_t bytes) const; - void mark_incomplete() const { raw_->is_incomplete = true; } + void mark_incomplete() const; - const StreamInfo& info() const { return *raw_->info; } + const StreamInfo& info() const; - cpp::span data() const { - return raw_->data.first(raw_->info->bytes_in_stream); - } + cpp::span data() const; private: RawStream* raw_; @@ -89,34 +82,15 @@ class OutputStream { public: explicit OutputStream(RawStream* s) : raw_(s) {} - void add(std::size_t bytes) const { - assert(raw_->info->bytes_in_stream + bytes <= raw_->data.size_bytes()); - raw_->info->bytes_in_stream += bytes; - } - - bool prepare(const StreamInfo::Format& new_format) { - if (std::holds_alternative(raw_->info->format)) { - raw_->info->format = new_format; - raw_->info->bytes_in_stream = 0; - } - if (new_format == raw_->info->format) { - return true; - } - if (raw_->is_incomplete) { - raw_->info->format = new_format; - raw_->info->bytes_in_stream = 0; - return true; - } - return false; - } - - const StreamInfo& info() const { return *raw_->info; } - - cpp::span data() const { - return raw_->data.subspan(raw_->info->bytes_in_stream); - } - - bool is_incomplete() const { return raw_->is_incomplete; } + void add(std::size_t bytes) const; + + bool prepare(const StreamInfo::Format& new_format); + + const StreamInfo& info() const; + + cpp::span data() const; + + bool is_incomplete() const; private: RawStream* raw_; -- 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/include/audio_sink.hpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index 03a4690d..a11a9c92 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -1,6 +1,9 @@ #pragma once +#include #include "audio_element.hpp" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" #include "stream_info.hpp" namespace audio { @@ -8,17 +11,30 @@ class IAudioSink { private: // TODO: tune. at least about 12KiB seems right for mp3 static const std::size_t kDrainBufferSize = 24 * 1024; - StreamBufferHandle_t buffer_; + uint8_t *buffer_; + StaticStreamBuffer_t *metadata_; + StreamBufferHandle_t *handle_; public: - IAudioSink() : buffer_(xStreamBufferCreate(kDrainBufferSize, 1)) {} - virtual ~IAudioSink() { vStreamBufferDelete(buffer_); } + IAudioSink() : + buffer_(reinterpret_cast(heap_caps_malloc(kDrainBufferSize, MALLOC_CAP_DMA))), + metadata_(reinterpret_cast(heap_caps_malloc(sizeof(StaticStreamBuffer_t), MALLOC_CAP_DMA))), + handle_(reinterpret_cast(heap_caps_malloc(sizeof(StreamBufferHandle_t), MALLOC_CAP_DMA))) { + *handle_ = xStreamBufferCreateStatic(kDrainBufferSize, 1, buffer_, metadata_); + } + + virtual ~IAudioSink() { + vStreamBufferDelete(*handle_); + free(buffer_); + free(handle_); + free(metadata_); + } virtual auto Configure(const StreamInfo::Format& format) -> bool = 0; virtual auto Send(const cpp::span& data) -> void = 0; virtual auto Log() -> void {} - auto buffer() const -> StreamBufferHandle_t { return buffer_; } + auto buffer() -> StreamBufferHandle_t* { return handle_; } }; } // namespace audio -- 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/include/audio_sink.hpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index a11a9c92..eaed04ec 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -11,22 +11,26 @@ class IAudioSink { private: // TODO: tune. at least about 12KiB seems right for mp3 static const std::size_t kDrainBufferSize = 24 * 1024; - uint8_t *buffer_; - StaticStreamBuffer_t *metadata_; - StreamBufferHandle_t *handle_; + uint8_t* buffer_; + StaticStreamBuffer_t* metadata_; + StreamBufferHandle_t handle_; public: - IAudioSink() : - buffer_(reinterpret_cast(heap_caps_malloc(kDrainBufferSize, MALLOC_CAP_DMA))), - metadata_(reinterpret_cast(heap_caps_malloc(sizeof(StaticStreamBuffer_t), MALLOC_CAP_DMA))), - handle_(reinterpret_cast(heap_caps_malloc(sizeof(StreamBufferHandle_t), MALLOC_CAP_DMA))) { - *handle_ = xStreamBufferCreateStatic(kDrainBufferSize, 1, buffer_, metadata_); - } + IAudioSink() + : buffer_(reinterpret_cast( + heap_caps_malloc(kDrainBufferSize, + MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))), + metadata_(reinterpret_cast( + heap_caps_malloc(sizeof(StaticStreamBuffer_t), + MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))), + handle_(xStreamBufferCreateStatic(kDrainBufferSize, + 1, + buffer_, + metadata_)) {} virtual ~IAudioSink() { - vStreamBufferDelete(*handle_); + vStreamBufferDelete(handle_); free(buffer_); - free(handle_); free(metadata_); } @@ -34,7 +38,7 @@ class IAudioSink { virtual auto Send(const cpp::span& data) -> void = 0; virtual auto Log() -> void {} - auto buffer() -> StreamBufferHandle_t* { return handle_; } + auto buffer() -> StreamBufferHandle_t { return handle_; } }; } // namespace audio -- 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/include/audio_sink.hpp | 2 +- src/audio/include/pipeline.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_sink.hpp b/src/audio/include/audio_sink.hpp index eaed04ec..7a535c35 100644 --- a/src/audio/include/audio_sink.hpp +++ b/src/audio/include/audio_sink.hpp @@ -10,7 +10,7 @@ namespace audio { class IAudioSink { private: // TODO: tune. at least about 12KiB seems right for mp3 - static const std::size_t kDrainBufferSize = 24 * 1024; + static const std::size_t kDrainBufferSize = 48 * 1024; uint8_t* buffer_; StaticStreamBuffer_t* metadata_; StreamBufferHandle_t handle_; diff --git a/src/audio/include/pipeline.hpp b/src/audio/include/pipeline.hpp index 2e9247bc..7a658a38 100644 --- a/src/audio/include/pipeline.hpp +++ b/src/audio/include/pipeline.hpp @@ -13,7 +13,7 @@ namespace audio { -static const std::size_t kPipelineBufferSize = 32 * 1024; +static const std::size_t kPipelineBufferSize = 64 * 1024; class Pipeline { public: -- cgit v1.2.3 From 7083459cf3c62c32d0c039a4665e702d70a27bba Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 21 Apr 2023 15:27:57 +1000 Subject: wrap driver instance ownership + di in a class --- src/audio/include/audio_playback.hpp | 7 ++----- src/audio/include/i2s_audio_output.hpp | 8 ++------ 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src/audio/include') diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp index 88dc29aa..cd4be3e7 100644 --- a/src/audio/include/audio_playback.hpp +++ b/src/audio/include/audio_playback.hpp @@ -6,6 +6,7 @@ #include #include "audio_task.hpp" +#include "driver_cache.hpp" #include "esp_err.h" #include "fatfs_audio_input.hpp" #include "i2s_audio_output.hpp" @@ -25,11 +26,7 @@ namespace audio { */ class AudioPlayback { public: - enum Error { ERR_INIT_ELEMENT, ERR_MEM }; - static auto create(drivers::GpioExpander* expander) - -> cpp::result, Error>; - - explicit AudioPlayback(std::unique_ptr output); + explicit AudioPlayback(drivers::DriverCache* drivers); ~AudioPlayback(); /* diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 31510a91..07430777 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -17,12 +17,8 @@ namespace audio { class I2SAudioOutput : public IAudioSink { public: - enum Error { DAC_CONFIG, I2S_CONFIG, STREAM_INIT }; - static auto create(drivers::GpioExpander* expander) - -> cpp::result, Error>; - I2SAudioOutput(drivers::GpioExpander* expander, - std::unique_ptr dac); + std::shared_ptr dac); ~I2SAudioOutput(); auto Configure(const StreamInfo::Format& format) -> bool override; @@ -36,7 +32,7 @@ class I2SAudioOutput : public IAudioSink { auto SetVolume(uint8_t volume) -> void; drivers::GpioExpander* expander_; - std::unique_ptr dac_; + std::shared_ptr dac_; std::optional current_config_; }; -- cgit v1.2.3