diff options
| author | jacqueline <me@jacqueline.id.au> | 2022-12-07 15:36:47 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2022-12-07 15:36:47 +1100 |
| commit | 01be69eca1fa89c047fc29f5cb0ea8ba0898dad1 (patch) | |
| tree | d40f749b3ebf6327f13d51d585f7c315a6d864c3 /src/audio/include | |
| parent | f35bb64c2b8dbb72fd15f1880e4d01d263660910 (diff) | |
| download | tangara-fw-01be69eca1fa89c047fc29f5cb0ea8ba0898dad1.tar.gz | |
better handling of chunk buffer
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 13 | ||||
| -rw-r--r-- | src/audio/include/audio_element.hpp | 18 | ||||
| -rw-r--r-- | src/audio/include/audio_playback.hpp | 85 | ||||
| -rw-r--r-- | src/audio/include/audio_task.hpp | 3 | ||||
| -rw-r--r-- | src/audio/include/chunk.hpp | 13 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 11 | ||||
| -rw-r--r-- | src/audio/include/i2s_audio_output.hpp | 8 | ||||
| -rw-r--r-- | src/audio/include/stream_buffer.hpp | 37 | ||||
| -rw-r--r-- | src/audio/include/stream_info.hpp | 5 |
9 files changed, 96 insertions, 97 deletions
diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index a6b15d9e..eaef2f8c 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -21,11 +21,15 @@ class AudioDecoder : public IAudioElement { AudioDecoder(); ~AudioDecoder(); - auto SetInputBuffer(MessageBufferHandle_t*) -> void; - auto SetOutputBuffer(MessageBufferHandle_t*) -> void; - auto StackSizeBytes() const -> std::size_t override { return 8196; }; + auto InputMinChunkSize() const -> std::size_t override { + // 128 kbps MPEG-1 @ 44.1 kHz is approx. 418 bytes according to the + // internet. + // TODO(jacqueline): tune as more codecs are added. + return 1024; + } + auto ProcessStreamInfo(const StreamInfo& info) -> cpp::result<void, AudioProcessingError> override; auto ProcessChunk(const cpp::span<std::byte>& chunk) @@ -38,9 +42,6 @@ class AudioDecoder : public IAudioElement { private: std::unique_ptr<codecs::ICodec> current_codec_; std::optional<StreamInfo> stream_info_; - - std::byte* raw_chunk_buffer_; - cpp::span<std::byte> chunk_buffer_; }; } // namespace audio diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index 1973fccf..590889bd 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -2,6 +2,7 @@ #include <cstdint> +#include "chunk.hpp" #include "freertos/FreeRTOS.h" #include "freertos/message_buffer.h" @@ -9,6 +10,7 @@ #include "result.hpp" #include "span.hpp" +#include "stream_buffer.hpp" #include "stream_info.hpp" #include "types.hpp" @@ -41,7 +43,7 @@ enum AudioProcessingError { class IAudioElement { public: IAudioElement() : input_buffer_(nullptr), output_buffer_(nullptr) {} - virtual ~IAudioElement(); + virtual ~IAudioElement() {} /* * Returns the stack size in bytes that this element requires. This should @@ -57,11 +59,17 @@ class IAudioElement { */ virtual auto IdleTimeout() const -> TickType_t { return portMAX_DELAY; } + virtual auto InputMinChunkSize() const -> std::size_t { return 0; } + /* Returns this element's input buffer. */ - auto InputBuffer() const -> MessageBufferHandle_t* { return input_buffer_; } + auto InputBuffer() const -> StreamBuffer* { return input_buffer_; } /* Returns this element's output buffer. */ - auto OutputBuffer() const -> MessageBufferHandle_t* { return output_buffer_; } + auto OutputBuffer() const -> StreamBuffer* { return output_buffer_; } + + auto InputBuffer(StreamBuffer* b) -> void { input_buffer_ = b; } + + auto OutputBuffer(StreamBuffer* b) -> void { output_buffer_ = b; } /* * Called when a StreamInfo message is received. Used to configure this @@ -87,8 +95,8 @@ class IAudioElement { virtual auto ProcessIdle() -> cpp::result<void, AudioProcessingError> = 0; protected: - MessageBufferHandle_t* input_buffer_; - MessageBufferHandle_t* output_buffer_; + StreamBuffer* input_buffer_; + StreamBuffer* output_buffer_; }; } // namespace audio diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp index 41ab46d2..9a7c5fc0 100644 --- a/src/audio/include/audio_playback.hpp +++ b/src/audio/include/audio_playback.hpp @@ -3,95 +3,44 @@ #include <cstdint> #include <memory> #include <string> +#include <vector> -#include "audio_common.h" -#include "audio_element.h" -#include "audio_event_iface.h" -#include "audio_pipeline.h" +#include "audio_element.hpp" #include "esp_err.h" -#include "fatfs_stream.h" -#include "i2s_stream.h" -#include "mp3_decoder.h" +#include "gpio_expander.hpp" #include "result.hpp" - -#include "audio_output.hpp" -#include "dac.hpp" +#include "span.hpp" #include "storage.hpp" +#include "stream_buffer.hpp" -namespace drivers { +namespace audio { /* - * Sends an I2S audio stream to the DAC. Includes basic controls for pausing - * and resuming the stream, as well as support for gapless playback of the next - * queued song, but does not implement any kind of sophisticated queing or - * playback control; these should be handled at a higher level. + * TODO. */ class AudioPlayback { public: - enum Error { FATFS_INIT, I2S_INIT, PIPELINE_INIT }; - static auto create(std::unique_ptr<IAudioOutput> output) + enum Error { ERR_INIT_ELEMENT, ERR_MEM }; + static auto create(drivers::GpioExpander* expander, + std::shared_ptr<drivers::SdStorage> storage) -> cpp::result<std::unique_ptr<AudioPlayback>, Error>; - AudioPlayback(std::unique_ptr<IAudioOutput>& output, - audio_pipeline_handle_t pipeline, - audio_element_handle_t source_element, - audio_event_iface_handle_t event_interface); + // TODO(jacqueline): configure on the fly once we have things to configure. + AudioPlayback(); ~AudioPlayback(); - /* - * Replaces any currently playing file with the one given, and begins - * playback. - * - * Any value set in `set_next_file` is cleared by this method. - */ auto Play(const std::string& filename) -> void; - /* Toogle between resumed and paused. */ - auto Toggle() -> void; - auto Resume() -> void; - auto Pause() -> void; - - enum PlaybackState { PLAYING, PAUSED, STOPPED }; - auto GetPlaybackState() const -> PlaybackState; - - /* - * Handles any pending events from the underlying audio pipeline. This must - * be called regularly in order to handle configuring the I2S stream for - * different audio types (e.g. sample rate, bit depth), and for gapless - * playback. - */ - auto ProcessEvents(uint16_t max_time_ms) -> void; - - /* - * Sets the file that should be played immediately after the current file - * finishes. This is used for gapless playback - */ - auto SetNextFile(const std::string& filename) -> void; - - auto SetVolume(uint8_t volume) -> void; - auto GetVolume() const -> uint8_t; // Not copyable or movable. AudioPlayback(const AudioPlayback&) = delete; AudioPlayback& operator=(const AudioPlayback&) = delete; private: - PlaybackState playback_state_; - - enum Decoder { NONE, MP3, AMR, OPUS, OGG, FLAC, WAV, AAC }; - auto GetDecoderForFilename(std::string filename) const -> Decoder; - auto CreateDecoder(Decoder decoder) const -> audio_element_handle_t; - auto ReconfigurePipeline(Decoder decoder) -> void; - - std::unique_ptr<IAudioOutput> output_; - - std::string next_filename_ = ""; - - audio_pipeline_handle_t pipeline_; - audio_element_handle_t source_element_; - audio_event_iface_handle_t event_interface_; + auto ConnectElements(IAudioElement* src, IAudioElement* sink) -> void; - audio_element_handle_t decoder_ = nullptr; - Decoder decoder_type_ = NONE; + StreamBuffer stream_start_; + StreamBuffer stream_end_; + std::vector<std::unique_ptr<StreamBuffer>> element_buffers_; }; -} // namespace drivers +} // namespace audio diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index 05888170..ca75fbd2 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -10,7 +10,8 @@ struct AudioTaskArgs { std::shared_ptr<IAudioElement>& element; }; -auto StartAudioTask(std::shared_ptr<IAudioElement>& element) -> void; +auto StartAudioTask(const std::string& name, + std::shared_ptr<IAudioElement> element) -> void; void AudioTaskMain(void* args); diff --git a/src/audio/include/chunk.hpp b/src/audio/include/chunk.hpp index aadcbbdb..d55e5d9d 100644 --- a/src/audio/include/chunk.hpp +++ b/src/audio/include/chunk.hpp @@ -13,11 +13,10 @@ #include "freertos/queue.h" #include "result.hpp" #include "span.hpp" +#include "stream_buffer.hpp" namespace audio { -extern const std::size_t kMaxChunkSize; - enum ChunkWriteResult { // Returned when the callback does not write any data. CHUNK_OUT_OF_DATA, @@ -37,8 +36,7 @@ enum ChunkWriteResult { * number of bytes it wrote. Return a value of 0 to indicate that there is no * more input to read. */ -auto WriteChunksToStream(MessageBufferHandle_t* stream, - cpp::span<std::byte> working_buffer, +auto WriteChunksToStream(StreamBuffer* stream, std::function<size_t(cpp::span<std::byte>)> callback, TickType_t max_wait) -> ChunkWriteResult; @@ -59,7 +57,7 @@ enum ChunkReadResult { class ChunkReader { public: - ChunkReader(MessageBufferHandle_t* stream); + explicit ChunkReader(StreamBuffer* buffer); ~ChunkReader(); auto Reset() -> void; @@ -83,10 +81,7 @@ class ChunkReader { TickType_t max_wait) -> ChunkReadResult; private: - MessageBufferHandle_t* stream_; - std::byte* raw_working_buffer_; - cpp::span<std::byte> working_buffer_; - + StreamBuffer* stream_; std::size_t leftover_bytes_ = 0; std::size_t last_message_size_ = 0; }; diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 63555ddc..21c729be 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -4,6 +4,7 @@ #include <memory> #include <string> +#include "chunk.hpp" #include "freertos/FreeRTOS.h" #include "freertos/message_buffer.h" @@ -12,6 +13,7 @@ #include "audio_element.hpp" #include "storage.hpp" +#include "stream_buffer.hpp" namespace audio { @@ -28,6 +30,9 @@ class FatfsAudioInput : public IAudioElement { auto SendChunk(cpp::span<std::byte> dest) -> size_t; + FatfsAudioInput(const FatfsAudioInput&) = delete; + FatfsAudioInput& operator=(const FatfsAudioInput&) = delete; + private: auto GetRingBufferDistance() const -> size_t; @@ -39,14 +44,8 @@ class FatfsAudioInput : public IAudioElement { cpp::span<std::byte>::iterator pending_read_pos_; cpp::span<std::byte>::iterator file_buffer_write_pos_; - std::byte* raw_chunk_buffer_; - cpp::span<std::byte> chunk_buffer_; - FIL current_file_; bool is_file_open_; - - uint8_t* output_buffer_memory_; - StaticMessageBuffer_t output_buffer_metadata_; }; } // namespace audio diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 4b4a458d..9e59f8fd 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -16,13 +16,17 @@ class I2SAudioOutput : public IAudioElement { public: enum Error { DAC_CONFIG, I2S_CONFIG, STREAM_INIT }; static auto create(drivers::GpioExpander* expander) - -> cpp::result<std::unique_ptr<I2SAudioOutput>, Error>; + -> cpp::result<std::shared_ptr<I2SAudioOutput>, Error>; I2SAudioOutput(drivers::GpioExpander* expander, std::unique_ptr<drivers::AudioDac> dac); ~I2SAudioOutput(); - auto SetInputBuffer(MessageBufferHandle_t* in) -> void { input_buffer_ = in; } + auto InputMinChunkSize() const -> std::size_t override { + // TODO(jacqueline): work out a good value here. Maybe similar to the total + // DMA buffer size? + return 128; + } auto IdleTimeout() const -> TickType_t override; auto ProcessStreamInfo(const StreamInfo& info) diff --git a/src/audio/include/stream_buffer.hpp b/src/audio/include/stream_buffer.hpp new file mode 100644 index 00000000..cfb4bf9d --- /dev/null +++ b/src/audio/include/stream_buffer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <cstddef> +#include <cstdint> + +#include "freertos/FreeRTOS.h" + +#include "freertos/message_buffer.h" +#include "span.hpp" + +namespace audio { + +class StreamBuffer { + public: + explicit StreamBuffer(std::size_t chunk_size, std::size_t buffer_size); + ~StreamBuffer(); + + auto Handle() -> MessageBufferHandle_t* { return &handle_; } + auto ReadBuffer() -> cpp::span<std::byte> { return input_chunk_; } + auto WriteBuffer() -> cpp::span<std::byte> { return output_chunk_; } + + StreamBuffer(const StreamBuffer&) = delete; + StreamBuffer& operator=(const StreamBuffer&) = delete; + + private: + std::byte* raw_memory_; + StaticMessageBuffer_t metadata_; + MessageBufferHandle_t handle_; + + std::byte* raw_input_chunk_; + cpp::span<std::byte> input_chunk_; + + std::byte* raw_output_chunk_; + cpp::span<std::byte> output_chunk_; +}; + +} // namespace audio diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index 47bcaa45..45f10fc6 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -3,6 +3,7 @@ #include <cstdint> #include <optional> #include <string> +#include <string_view> #include "cbor.h" #include "result.hpp" @@ -19,10 +20,14 @@ class StreamInfo { ~StreamInfo() = default; auto Path() const -> const std::optional<std::string>& { return path_; } + auto Path(const std::string_view& d) -> void { path_ = d; } + auto Channels() const -> const std::optional<uint8_t>& { return channels_; } + auto BitsPerSample() const -> const std::optional<uint8_t>& { return bits_per_sample_; } + auto SampleRate() const -> const std::optional<uint16_t>& { return sample_rate_; } |
