summaryrefslogtreecommitdiff
path: root/src/audio/include
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-01-12 14:28:52 +1100
committerjacqueline <me@jacqueline.id.au>2023-01-12 14:28:52 +1100
commit2056cad0ab7b805f0ed5629b100b50f8ea9e127e (patch)
tree1e8385d48e18551240e9ef9683b8696292f8d760 /src/audio/include
parent01be69eca1fa89c047fc29f5cb0ea8ba0898dad1 (diff)
downloadtangara-fw-2056cad0ab7b805f0ed5629b100b50f8ea9e127e.tar.gz
WIP
Diffstat (limited to 'src/audio/include')
-rw-r--r--src/audio/include/audio_decoder.hpp2
-rw-r--r--src/audio/include/audio_element.hpp18
-rw-r--r--src/audio/include/audio_playback.hpp9
-rw-r--r--src/audio/include/audio_task.hpp4
-rw-r--r--src/audio/include/chunk.hpp40
-rw-r--r--src/audio/include/fatfs_audio_input.hpp2
-rw-r--r--src/audio/include/i2s_audio_output.hpp1
-rw-r--r--src/audio/include/stream_buffer.hpp22
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;