diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-01-12 14:28:52 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-01-12 14:28:52 +1100 |
| commit | 2056cad0ab7b805f0ed5629b100b50f8ea9e127e (patch) | |
| tree | 1e8385d48e18551240e9ef9683b8696292f8d760 /src/audio/include | |
| parent | 01be69eca1fa89c047fc29f5cb0ea8ba0898dad1 (diff) | |
| download | tangara-fw-2056cad0ab7b805f0ed5629b100b50f8ea9e127e.tar.gz | |
WIP
Diffstat (limited to 'src/audio/include')
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 2 | ||||
| -rw-r--r-- | src/audio/include/audio_element.hpp | 18 | ||||
| -rw-r--r-- | src/audio/include/audio_playback.hpp | 9 | ||||
| -rw-r--r-- | src/audio/include/audio_task.hpp | 4 | ||||
| -rw-r--r-- | src/audio/include/chunk.hpp | 40 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 2 | ||||
| -rw-r--r-- | src/audio/include/i2s_audio_output.hpp | 1 | ||||
| -rw-r--r-- | src/audio/include/stream_buffer.hpp | 22 |
8 files changed, 82 insertions, 16 deletions
diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index eaef2f8c..9c0626db 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -42,6 +42,8 @@ class AudioDecoder : public IAudioElement { private: std::unique_ptr<codecs::ICodec> current_codec_; std::optional<StreamInfo> stream_info_; + + ChunkWriter chunk_writer_; }; } // namespace audio diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index 590889bd..ec6d6e80 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -1,10 +1,11 @@ #pragma once +#include <atomic> #include <cstdint> -#include "chunk.hpp" #include "freertos/FreeRTOS.h" +#include "chunk.hpp" #include "freertos/message_buffer.h" #include "freertos/portmacro.h" #include "result.hpp" @@ -16,6 +17,12 @@ namespace audio { +enum ElementState { + STATE_RUN, + STATE_PAUSE, + STATE_QUIT, +}; + /* * Errors that may be returned by any of the Process* methods of an audio * element. @@ -42,7 +49,8 @@ enum AudioProcessingError { */ class IAudioElement { public: - IAudioElement() : input_buffer_(nullptr), output_buffer_(nullptr) {} + IAudioElement() + : input_buffer_(nullptr), output_buffer_(nullptr), state_(STATE_RUN) {} virtual ~IAudioElement() {} /* @@ -71,6 +79,9 @@ class IAudioElement { auto OutputBuffer(StreamBuffer* b) -> void { output_buffer_ = b; } + auto ElementState() const -> ElementState { return state_; } + auto ElementState(enum ElementState e) -> void { state_ = e; } + /* * Called when a StreamInfo message is received. Used to configure this * element in preperation for incoming chunks. @@ -94,9 +105,12 @@ class IAudioElement { */ virtual auto ProcessIdle() -> cpp::result<void, AudioProcessingError> = 0; + virtual auto PrepareForPause() -> void{}; + protected: StreamBuffer* input_buffer_; StreamBuffer* output_buffer_; + std::atomic<enum ElementState> state_; }; } // namespace audio diff --git a/src/audio/include/audio_playback.hpp b/src/audio/include/audio_playback.hpp index 9a7c5fc0..bffc3f02 100644 --- a/src/audio/include/audio_playback.hpp +++ b/src/audio/include/audio_playback.hpp @@ -6,6 +6,7 @@ #include <vector> #include "audio_element.hpp" +#include "audio_element_handle.hpp" #include "esp_err.h" #include "gpio_expander.hpp" #include "result.hpp" @@ -16,7 +17,8 @@ namespace audio { /* - * TODO. + * Creates and links together audio elements into a pipeline. This is the main + * entrypoint to playing audio on the system. */ class AudioPlayback { public: @@ -29,6 +31,10 @@ class AudioPlayback { AudioPlayback(); ~AudioPlayback(); + /* + * Begins playing the file at the given FatFS path. This will interrupt any + * currently in-progress playback. + */ auto Play(const std::string& filename) -> void; // Not copyable or movable. @@ -41,6 +47,7 @@ class AudioPlayback { StreamBuffer stream_start_; StreamBuffer stream_end_; std::vector<std::unique_ptr<StreamBuffer>> element_buffers_; + std::vector<std::unique_ptr<AudioElementHandle>> element_handles_; }; } // namespace audio diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index ca75fbd2..9a76ea7e 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -3,6 +3,7 @@ #include <memory> #include "audio_element.hpp" +#include "audio_element_handle.hpp" namespace audio { @@ -11,7 +12,8 @@ struct AudioTaskArgs { }; auto StartAudioTask(const std::string& name, - std::shared_ptr<IAudioElement> element) -> void; + std::shared_ptr<IAudioElement> element) + -> std::unique_ptr<AudioElementHandle>; void AudioTaskMain(void* args); diff --git a/src/audio/include/chunk.hpp b/src/audio/include/chunk.hpp index d55e5d9d..5c7e73dd 100644 --- a/src/audio/include/chunk.hpp +++ b/src/audio/include/chunk.hpp @@ -19,6 +19,8 @@ namespace audio { enum ChunkWriteResult { // Returned when the callback does not write any data. + CHUNK_WRITE_OKAY, + // Returned when the callback does not write any data. CHUNK_OUT_OF_DATA, // Returned when there is an error encoding a chunk header using cbor. CHUNK_ENCODING_ERROR, @@ -27,18 +29,32 @@ enum ChunkWriteResult { CHUNK_WRITE_TIMEOUT, }; -/* - * Invokes the given callback to receive data, breaks the received data up into - * chunks with headers, and writes those chunks to the given output stream. - * - * The callback will be invoked with a byte buffer and its size. The callback - * should write as much data as it can to this buffer, and then return the - * number of bytes it wrote. Return a value of 0 to indicate that there is no - * more input to read. - */ -auto WriteChunksToStream(StreamBuffer* stream, - std::function<size_t(cpp::span<std::byte>)> callback, - TickType_t max_wait) -> ChunkWriteResult; +class ChunkWriter { + public: + explicit ChunkWriter(StreamBuffer* buffer); + ~ChunkWriter(); + + auto Reset() -> void; + + auto GetLastMessage() -> cpp::span<std::byte>; + + /* + * Invokes the given callback to receive data, breaks the received data up + * into chunks with headers, and writes those chunks to the given output + * stream. + * + * The callback will be invoked with a byte buffer and its size. The callback + * should write as much data as it can to this buffer, and then return the + * number of bytes it wrote. Return a value of 0 to indicate that there is no + * more input to read. + */ + auto WriteChunkToStream(std::function<size_t(cpp::span<std::byte>)> callback, + TickType_t max_wait) -> ChunkWriteResult; + + private: + StreamBuffer* stream_; + std::size_t leftover_bytes_ = 0; +}; enum ChunkReadResult { CHUNK_READ_OKAY, diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 21c729be..040b2b54 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -46,6 +46,8 @@ class FatfsAudioInput : public IAudioElement { FIL current_file_; bool is_file_open_; + + ChunkWriter chunk_writer_; }; } // namespace audio diff --git a/src/audio/include/i2s_audio_output.hpp b/src/audio/include/i2s_audio_output.hpp index 9e59f8fd..75a3be76 100644 --- a/src/audio/include/i2s_audio_output.hpp +++ b/src/audio/include/i2s_audio_output.hpp @@ -34,6 +34,7 @@ class I2SAudioOutput : public IAudioElement { auto ProcessChunk(const cpp::span<std::byte>& chunk) -> cpp::result<std::size_t, AudioProcessingError> override; auto ProcessIdle() -> cpp::result<void, AudioProcessingError> override; + auto PrepareForPause() -> void override; I2SAudioOutput(const I2SAudioOutput&) = delete; I2SAudioOutput& operator=(const I2SAudioOutput&) = delete; diff --git a/src/audio/include/stream_buffer.hpp b/src/audio/include/stream_buffer.hpp index cfb4bf9d..3853a53f 100644 --- a/src/audio/include/stream_buffer.hpp +++ b/src/audio/include/stream_buffer.hpp @@ -10,13 +10,35 @@ namespace audio { +/* + * A collection of the buffers required for two IAudioElement implementations to + * stream data between each other. + * + * Currently, we use a FreeRTOS MessageBuffer to hold the byte stream, and also + * maintain two chunk-sized buffers for the elements to stage their read and + * write operations (as MessageBuffer copies the given data into its memory + * space). A future optimisation here could be to instead post himem memory + * addresses to the message buffer, and then maintain address spaces into which + * we map these messages, rather than 'real' allocated buffers as we do now. + */ class StreamBuffer { public: explicit StreamBuffer(std::size_t chunk_size, std::size_t buffer_size); ~StreamBuffer(); + /* Returns the handle for the underlying message buffer. */ auto Handle() -> MessageBufferHandle_t* { return &handle_; } + + /* + * Returns a chunk-sized staging buffer that should be used *only* by the + * reader (sink) element. + */ auto ReadBuffer() -> cpp::span<std::byte> { return input_chunk_; } + + /* + * Returns a chunk-sized staging buffer that should be used *only* by the + * writer (source) element. + */ auto WriteBuffer() -> cpp::span<std::byte> { return output_chunk_; } StreamBuffer(const StreamBuffer&) = delete; |
