diff options
Diffstat (limited to 'src/audio')
| -rw-r--r-- | src/audio/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/audio/audio_decoder.cpp | 70 | ||||
| -rw-r--r-- | src/audio/audio_task.cpp | 121 | ||||
| -rw-r--r-- | src/audio/chunk.cpp | 36 | ||||
| -rw-r--r-- | src/audio/fatfs_audio_input.cpp | 62 | ||||
| -rw-r--r-- | src/audio/include/audio_decoder.hpp | 9 | ||||
| -rw-r--r-- | src/audio/include/audio_element.hpp | 27 | ||||
| -rw-r--r-- | src/audio/include/chunk.hpp | 37 | ||||
| -rw-r--r-- | src/audio/include/fatfs_audio_input.hpp | 11 | ||||
| -rw-r--r-- | src/audio/include/stream_info.hpp | 14 | ||||
| -rw-r--r-- | src/audio/stream_info.cpp | 53 |
11 files changed, 262 insertions, 180 deletions
diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index e3f7dd33..32ca1a56 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -2,6 +2,6 @@ idf_component_register( SRCS "audio_decoder.cpp" "audio_task.cpp" "chunk.cpp" "fatfs_audio_input.cpp" "stream_info.cpp" INCLUDE_DIRS "include" - REQUIRES "codecs" "drivers" "cbor" "result" "tasks") + REQUIRES "codecs" "drivers" "cbor_wrapper" "result" "tasks") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp index 02217187..a19fd5bf 100644 --- a/src/audio/audio_decoder.cpp +++ b/src/audio/audio_decoder.cpp @@ -1,29 +1,41 @@ #include "audio_decoder.hpp" + #include <string.h> + #include <cstddef> #include <cstdint> -#include "chunk.hpp" + +#include "freertos/FreeRTOS.h" + #include "esp_heap_caps.h" +#include "freertos/message_buffer.h" #include "freertos/portmacro.h" -#include "include/audio_element.hpp" -#include "include/fatfs_audio_input.hpp" + +#include "audio_element.hpp" +#include "chunk.hpp" +#include "fatfs_audio_input.hpp" + +static const char* kTag = "DEC"; namespace audio { AudioDecoder::AudioDecoder() : IAudioElement(), - chunk_buffer_(heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM)), - stream_info_({}) {} + stream_info_({}), + chunk_buffer_(static_cast<uint8_t*>( + heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM))) + +{} AudioDecoder::~AudioDecoder() { free(chunk_buffer_); } -auto AudioDecoder::SetInputBuffer(StreamBufferHandle_t* buffer) -> void { +auto AudioDecoder::SetInputBuffer(MessageBufferHandle_t* buffer) -> void { input_buffer_ = buffer; } -auto AudioDecoder::SetOutputBuffer(StreamBufferHandle_t* buffer) -> void { +auto AudioDecoder::SetOutputBuffer(MessageBufferHandle_t* buffer) -> void { output_buffer_ = buffer; } @@ -34,12 +46,12 @@ auto AudioDecoder::ProcessStreamInfo(StreamInfo&& info) // Reuse the existing codec if we can. This will help with gapless playback, // since we can potentially just continue to decode as we were before, // without any setup overhead. - if (current_codec_->CanHandleFile(info.path)) { + if (current_codec_->CanHandleFile(info.Path().value_or(""))) { current_codec_->ResetForNewStream(); return {}; } - auto result = codecs::CreateCodecForFile(info.path); + auto result = codecs::CreateCodecForFile(info.Path().value()); if (result.has_value()) { current_codec_ = std::move(result.value()); } else { @@ -57,26 +69,46 @@ auto AudioDecoder::ProcessChunk(uint8_t* data, std::size_t length) } current_codec_->SetInput(data, length); - cpp::result<size_t, codecs::ICodec::ProcessingError> result; + + bool has_samples_to_send = false; + bool needs_more_input = false; + std::optional<codecs::ICodec::ProcessingError> error = std::nullopt; WriteChunksToStream( - output_buffer_, working_buffer_, kWorkingBufferSize, - [&](uint8_t* buf, size_t len) { - result = current_codec_->Process(data, length, buf, len); - if (result.has_error()) { - // End our output stream immediately if the codec barfed. - return 0; + output_buffer_, chunk_buffer_, kMaxChunkSize, + [&](uint8_t* buf, size_t len) -> std::size_t { + std::size_t bytes_written = 0; + // Continue filling up the output buffer so long as we have samples + // leftover, or are able to synthesize more samples from the input. + while (has_samples_to_send || !needs_more_input) { + if (!has_samples_to_send) { + auto result = current_codec_->ProcessNextFrame(); + has_samples_to_send = true; + if (result.has_error()) { + error = result.error(); + // End our output stream immediately if the codec barfed. + return 0; + } else { + needs_more_input = result.value(); + } + } else { + auto result = current_codec_->WriteOutputSamples( + buf + bytes_written, len - bytes_written); + bytes_written += result.first; + has_samples_to_send = !result.second; + } } - return result.value(); + return bytes_written; }, // This element doesn't support any kind of out of band commands, so we // can just suspend the whole task if the output buffer fills up. portMAX_DELAY); - if (result.has_error()) { + if (error) { + ESP_LOGE(kTag, "Codec encountered error %d", error.value()); return cpp::fail(IO_ERROR); } - return current_codec_->GetOutputProcessed(); + return current_codec_->GetInputPosition(); } auto AudioDecoder::ProcessIdle() -> cpp::result<void, StreamError> { diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index a125548a..f3362897 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -13,7 +13,7 @@ #include "freertos/stream_buffer.h" #include "audio_element.hpp" -#include "include/audio_element.hpp" +#include "chunk.hpp" #include "stream_message.hpp" #include "tasks.hpp" @@ -25,72 +25,83 @@ static const size_t kChunkBufferSize = kMaxChunkSize * 1.5; auto StartAudioTask(const std::string& name, std::shared_ptr<IAudioElement>& element) -> void { - AudioTaskArgs* args = new AudioTaskArgs(element); + AudioTaskArgs* args = new AudioTaskArgs{.element = element}; xTaskCreate(&AudioTaskMain, name.c_str(), element->StackSizeBytes(), args, kTaskPriorityAudio, NULL); } void AudioTaskMain(void* args) { - AudioTaskArgs* real_args = reinterpret_cast<AudioTaskArgs*>(args); - std::shared_ptr<IAudioElement> element = std::move(real_args->element); - delete real_args; - - ChunkReader chunk_reader = ChunkReader(element->InputBuffer()); - - while (1) { - cpp::result<size_t, IAudioElement::StreamError> process_res; - - // If this element has an input stream, then our top priority is processing - // any chunks from it. Try doing this first, then fall back to the other - // cases. - bool has_received_message = false; - if (stream != nullptr) { - EncodeReadResult chunk_res = chunk_reader.ReadChunkFromStream( - [&](uint8_t* data, std::size_t length) -> std::optional<size_t> { - process_res = element->ProcessChunk(data, length); - if (process_res.has_value()) { - return process_res.value(); - } else { - return {}; - } - }, - element->IdleTimeout()); - - if (chunk_res == CHUNK_PROCESSING_ERROR || - chunk_res == CHUNK_DECODING_ERROR) { - break; // TODO. - } else if (chunk_res == CHUNK_STREAM_ENDED) { - has_received_message = true; + { + AudioTaskArgs* real_args = reinterpret_cast<AudioTaskArgs*>(args); + std::shared_ptr<IAudioElement> element = std::move(real_args->element); + delete real_args; + + ChunkReader chunk_reader = ChunkReader(element->InputBuffer()); + + while (1) { + cpp::result<size_t, StreamError> process_res; + + // If this element has an input stream, then our top priority is + // processing any chunks from it. Try doing this first, then fall back to + // the other cases. + bool has_received_message = false; + if (element->InputBuffer() != nullptr) { + ChunkReadResult chunk_res = chunk_reader.ReadChunkFromStream( + [&](uint8_t* data, std::size_t length) -> std::optional<size_t> { + process_res = element->ProcessChunk(data, length); + if (process_res.has_value()) { + return process_res.value(); + } else { + return {}; + } + }, + element->IdleTimeout()); + + if (chunk_res == CHUNK_PROCESSING_ERROR || + chunk_res == CHUNK_DECODING_ERROR) { + break; // TODO. + } else if (chunk_res == CHUNK_STREAM_ENDED) { + has_received_message = true; + } } - } - if (has_received_message) { - auto& [buffer, length] = chunk_reader.GetLastMessage(); - auto decoder_res = cbor::ArrayDecoder::Create(buffer, length); - if (decoder_res.has_error()) { - // TODO. - break; - } - auto decoder = decoder_res.value(); - MessageType message_type = decoder->NextValue(); - if (message_type == TYPE_STREAM_INFO) { - element->ProcessStreamInfo(StreamInfo(decoder->Iterator());); + if (has_received_message) { + auto [buffer, length] = chunk_reader.GetLastMessage(); + auto decoder_res = cbor::ArrayDecoder::Create(buffer, length); + if (decoder_res.has_error()) { + // TODO. + break; + } + auto decoder = std::move(decoder_res.value()); + // TODO: this can be more elegant i think + cpp::result<uint64_t, CborError> message_type = + decoder->NextValue<uint64_t>(); + if (message_type.has_error()) { + break; // TODO. + } else if (message_type.value() == TYPE_STREAM_INFO) { + auto info_decoder = cbor::MapDecoder::Create(decoder->Iterator()); + if (info_decoder.has_value()) { + auto process_error = element->ProcessStreamInfo( + StreamInfo(info_decoder.value().get())); + if (process_error.has_error()) { + break; // TODO. + } + } else { + break; // TODO. + } + } } - } - // TODO: Do any out of band reading, such a a pause command, here. + // TODO: Do any out of band reading, such a a pause command, here. - // Chunk reading must have timed out, or we don't have an input stream. - // Signal the element to do any of its idle tasks. - process_res = element->ProcessIdle(); - if (process_res.has_error()) { - break; // TODO. + // Chunk reading must have timed out, or we don't have an input stream. + // Signal the element to do any of its idle tasks. + auto process_error = element->ProcessIdle(); + if (process_error.has_error()) { + break; // TODO. + } } } - - element.clear(); - free(chunk_buffer_); - vTaskDelete(NULL); } diff --git a/src/audio/chunk.cpp b/src/audio/chunk.cpp index a157b946..718b2649 100644 --- a/src/audio/chunk.cpp +++ b/src/audio/chunk.cpp @@ -1,17 +1,20 @@ #include "chunk.hpp" #include <string.h> + #include <cstddef> #include <cstdint> + +#include "cbor.h" + #include "cbor_decoder.hpp" #include "cbor_encoder.hpp" -#include "esp-idf/components/cbor/tinycbor/src/cbor.h" #include "stream_message.hpp" namespace audio { // TODO: tune. -static const std::size_t kMaxChunkSize = 512; +const std::size_t kMaxChunkSize = 512; // TODO: tune static const std::size_t kWorkingBufferSize = kMaxChunkSize * 1.5; @@ -28,7 +31,7 @@ auto WriteChunksToStream(MessageBufferHandle_t* stream, uint8_t* working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*, size_t)> callback, - TickType_t max_wait) -> EncodeWriteResult { + TickType_t max_wait) -> ChunkWriteResult { size_t header_size = kInitialHeaderSize; while (1) { // First, ask the callback for some data to write. @@ -43,9 +46,9 @@ auto WriteChunksToStream(MessageBufferHandle_t* stream, // Put together a header. cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, working_buffer_length); - encoder.WriteUnsigned(TYPE_CHUNK_HEADER); - encoder.WriteUnsigned(header_size); - encoder.WriteUnsigned(chunk_size); + encoder.WriteValue(TYPE_CHUNK_HEADER); + encoder.WriteValue(header_size); + encoder.WriteValue(chunk_size); size_t new_header_size = header_size; cpp::result<size_t, CborError> encoder_res = encoder.Finish(); @@ -76,7 +79,8 @@ auto WriteChunksToStream(MessageBufferHandle_t* stream, } ChunkReader::ChunkReader(MessageBufferHandle_t* stream) : stream_(stream) { - working_buffer_ = heap_caps_malloc(kWorkingBufferSize, MALLOC_CAP_SPIRAM); + working_buffer_ = static_cast<uint8_t*>( + heap_caps_malloc(kWorkingBufferSize, MALLOC_CAP_SPIRAM)); }; ChunkReader::~ChunkReader() { @@ -94,7 +98,7 @@ auto ChunkReader::GetLastMessage() -> std::pair<uint8_t*, size_t> { auto ChunkReader::ReadChunkFromStream( std::function<std::optional<size_t>(uint8_t*, size_t)> callback, - TickType_t max_wait) -> EncodeReadResult { + TickType_t max_wait) -> ChunkReadResult { // First, wait for a message to arrive over the buffer. last_message_size_ = xMessageBufferReceive(*stream_, working_buffer_ + leftover_bytes_, @@ -104,14 +108,15 @@ auto ChunkReader::ReadChunkFromStream( return CHUNK_READ_TIMEOUT; } - auto decoder = cbor::MapDecoder::Create(working_buffer_ + leftover_bytes_, - last_message_size_); + auto decoder = cbor::ArrayDecoder::Create(working_buffer_ + leftover_bytes_, + last_message_size_); if (decoder.has_error()) { // Weird; this implies someone is shoving invalid data into the buffer. return CHUNK_DECODING_ERROR; } - MessageType type = decoder.value().ParseUnsigned().value_or(TYPE_UNKNOWN); + MessageType type = static_cast<MessageType>( + decoder.value()->NextValue<uint64_t>().value_or(TYPE_UNKNOWN)); if (type != TYPE_CHUNK_HEADER) { // This message wasn't for us, so let the caller handle it. Reset(); @@ -119,9 +124,9 @@ auto ChunkReader::ReadChunkFromStream( } // Work the size and position of the chunk. - header_length = decoder.ParseUnsigned().value_or(0); - chunk_length = decoder.ParseUnsigned().value_or(0); - if (decoder.Failed()) { + size_t header_length = decoder.value()->NextValue<uint64_t>().value_or(0); + size_t chunk_length = decoder.value()->NextValue<uint64_t>().value_or(0); + if (decoder.value()->Failed()) { return CHUNK_DECODING_ERROR; } @@ -146,7 +151,10 @@ auto ChunkReader::ReadChunkFromStream( if (leftover_bytes_ > 0) { memmove(working_buffer_, combined_buffer + amount_processed.value(), leftover_bytes_); + return CHUNK_LEFTOVER_DATA; } + + return CHUNK_READ_OKAY; } } // namespace audio diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp index 351fd017..dee090d0 100644 --- a/src/audio/fatfs_audio_input.cpp +++ b/src/audio/fatfs_audio_input.cpp @@ -1,14 +1,15 @@ -#include "fatfs_audio_input.hpp<D-c>ccc +#include "fatfs_audio_input.hpp" + #include <cstdint> #include <memory> -#include "chunk.hpp" -#include "fatfs_audio_input.hpp" +#include <string> +#include "audio_element.hpp" #include "esp_heap_caps.h" +#include "freertos/portmacro.h" #include "audio_element.hpp" -#include "freertos/portmacro.h" -#include "include/audio_element.hpp" +#include "chunk.hpp" namespace audio { @@ -20,14 +21,17 @@ static const std::size_t kOutputBufferSize = 1024 * 4; FatfsAudioInput::FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage) : IAudioElement(), storage_(storage) { - file_buffer_ = heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_SPIRAM); + file_buffer_ = static_cast<uint8_t*>( + heap_caps_malloc(kFileBufferSize, MALLOC_CAP_SPIRAM)); file_buffer_read_pos_ = file_buffer_; file_buffer_write_pos_ = file_buffer_; - chunk_buffer_ = heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM); + chunk_buffer_ = + static_cast<uint8_t*>(heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM)); - output_buffer_memory_ = - heap_caps_malloc(kOutputBufferSize, MALLOC_CAP_SPIRAM); - output_buffer_ = xMessageBufferCreateStatic( + output_buffer_memory_ = static_cast<uint8_t*>( + heap_caps_malloc(kOutputBufferSize, MALLOC_CAP_SPIRAM)); + output_buffer_ = new MessageBufferHandle_t; + *output_buffer_ = xMessageBufferCreateStatic( kOutputBufferSize, output_buffer_memory_, &output_buffer_metadata_); } @@ -36,24 +40,18 @@ FatfsAudioInput::~FatfsAudioInput() { free(chunk_buffer_); vMessageBufferDelete(output_buffer_); free(output_buffer_memory_); + free(output_buffer_); } -auto FatfsAudioInput::InputBuffer() -> MessageBufferHandle_t { - return input_buffer_; -} - -auto FatfsAudioInput::OutputBuffer() -> MessageBufferHandle_t { - return output_buffer_; -} - -auto FatfsAudioInput::ProcessStreamInfo(StreamInfo& info) +auto FatfsAudioInput::ProcessStreamInfo(StreamInfo&& info) -> cpp::result<void, StreamError> { if (is_file_open_) { f_close(¤t_file_); is_file_open_ = false; } - FRESULT res = f_open(¤t_file_, info.path.c_str(), FA_READ); + std::string path = info.Path().value_or(""); + FRESULT res = f_open(¤t_file_, path.c_str(), FA_READ); if (res != FR_OK) { return cpp::fail(IO_ERROR); } @@ -66,12 +64,12 @@ auto FatfsAudioInput::ProcessStreamInfo(StreamInfo& info) } auto FatfsAudioInput::ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result<void, StreamError> { + -> cpp::result<size_t, StreamError> { // TODO. - return {}; + return 0; } -static auto GetRingBufferDistance() -> size_t { +auto FatfsAudioInput::GetRingBufferDistance() -> size_t { if (file_buffer_read_pos_ == file_buffer_write_pos_) { return 0; } @@ -82,10 +80,10 @@ static auto GetRingBufferDistance() -> size_t { // Read position to end of buffer. (file_buffer_ + kFileBufferSize - file_buffer_read_pos_) // Start of buffer to write position. - + (file_buffer_write_pos_ - file_buffer_) + + (file_buffer_write_pos_ - file_buffer_); } -virtual auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, StreamError> { +auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, StreamError> { // First, see if we're able to fill up the input buffer with any more of the // file's contents. if (is_file_open_) { @@ -103,8 +101,8 @@ virtual auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, StreamError> { UINT bytes_read = 0; FRESULT result = f_read(¤t_file_, file_buffer_write_pos_, read_size, &bytes_read); - if (!FR_OK) { - return ERROR; // TODO; + if (result != FR_OK) { + return cpp::fail(IO_ERROR); // TODO; } if (f_eof(¤t_file_)) { @@ -123,16 +121,16 @@ virtual auto FatfsAudioInput::ProcessIdle() -> cpp::result<void, StreamError> { // Now stream data into the output buffer until it's full. pending_read_pos_ = nullptr; - EncodeWriteResult result = - WriteChunksToStream(&output_buffer_, chunk_buffer_, kMaxChunkSize, - &SendChunk, kServiceInterval); + ChunkWriteResult result = WriteChunksToStream( + output_buffer_, chunk_buffer_, kMaxChunkSize, + [&](uint8_t* b, size_t s) { return SendChunk(b, s); }, kServiceInterval); switch (result) { case CHUNK_WRITE_TIMEOUT: case CHUNK_OUT_OF_DATA: - return; // TODO. + return {}; // TODO. default: - return; // TODO. + return {}; // TODO. } } diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index 98ebdc71..a4508c3e 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -18,8 +18,13 @@ class AudioDecoder : public IAudioElement { AudioDecoder(); ~AudioDecoder(); - auto SetInputBuffer(StreamBufferHandle_t*) -> void; - auto SetOutputBuffer(StreamBufferHandle_t*) -> void; + auto SetInputBuffer(MessageBufferHandle_t*) -> void; + auto SetOutputBuffer(MessageBufferHandle_t*) -> void; + + auto ProcessStreamInfo(StreamInfo&& info) -> cpp::result<void, StreamError>; + auto ProcessChunk(uint8_t* data, std::size_t length) + -> cpp::result<size_t, StreamError>; + auto ProcessIdle() -> cpp::result<void, StreamError>; AudioDecoder(const AudioDecoder&) = delete; AudioDecoder& operator=(const AudioDecoder&) = delete; diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index 2a2f0727..13a48590 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -1,13 +1,28 @@ #pragma once #include <stdint.h> + #include <cstdint> + +#include "freertos/FreeRTOS.h" + +#include "freertos/message_buffer.h" #include "freertos/portmacro.h" #include "result.hpp" + +#include "stream_info.hpp" #include "types.hpp" namespace audio { +/* Errors that may be returned by any of the Process* methods. */ +enum StreamError { + // Indicates that this element is unable to handle the upcoming chunks. + UNSUPPORTED_STREAM, + // Indicates an error with reading or writing stream data. + IO_ERROR, +}; + /* * One indepedentent part of an audio pipeline. Each element has an input and * output message stream, and is responsible for taking data from the input @@ -46,14 +61,6 @@ class IAudioElement { /* Returns this element's output buffer. */ auto OutputBuffer() -> MessageBufferHandle_t* { return output_buffer_; } - /* Errors that may be returned by any of the Process* methods. */ - enum StreamError { - // Indicates that this element is unable to handle the upcoming chunks. - UNSUPPORTED_STREAM, - // Indicates an error with reading or writing stream data. - IO_ERROR, - }; - /* * Called when a StreamInfo message is received. Used to configure this * element in preperation for incoming chunks. @@ -78,8 +85,8 @@ class IAudioElement { virtual auto ProcessIdle() -> cpp::result<void, StreamError> = 0; protected: - StreamBufferHandle_t* input_buffer_; - StreamBufferHandle_t* output_buffer_; + MessageBufferHandle_t* input_buffer_; + MessageBufferHandle_t* output_buffer_; }; } // namespace audio diff --git a/src/audio/include/chunk.hpp b/src/audio/include/chunk.hpp index a3f943ea..365c83d0 100644 --- a/src/audio/include/chunk.hpp +++ b/src/audio/include/chunk.hpp @@ -4,8 +4,13 @@ #include <cstdint> #include <optional> #include <string> -#include "esp-idf/components/cbor/tinycbor/src/cbor.h" + +#include "freertos/FreeRTOS.h" + +#include "cbor.h" +#include "freertos/message_buffer.h" #include "freertos/portmacro.h" +#include "freertos/queue.h" #include "result.hpp" namespace audio { @@ -35,7 +40,22 @@ auto WriteChunksToStream(MessageBufferHandle_t* stream, uint8_t* working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*, size_t)> callback, - TickType_t max_wait) -> EncodeWriteResult; + TickType_t max_wait) -> ChunkWriteResult; + +enum ChunkReadResult { + CHUNK_READ_OKAY, + // Returned when the chunk was read successfully, but the consumer did not + // use all of the data. + CHUNK_LEFTOVER_DATA, + // Returned an error in parsing the cbor-encoded header. + CHUNK_DECODING_ERROR, + // Returned when max_wait expired before any data was read. + CHUNK_READ_TIMEOUT, + // Returned when a non-chunk message is received. + CHUNK_STREAM_ENDED, + // Returned when the processing callback does not return a value. + CHUNK_PROCESSING_ERROR, +}; class ChunkReader { public: @@ -46,17 +66,6 @@ class ChunkReader { auto GetLastMessage() -> std::pair<uint8_t*, size_t>; - enum ChunkReadResult { - // Returned an error in parsing the cbor-encoded header. - CHUNK_DECODING_ERROR, - // Returned when max_wait expired before any data was read. - CHUNK_READ_TIMEOUT, - // Returned when a non-chunk message is received. - CHUNK_STREAM_ENDED, - // Returned when the processing callback does not return a value. - CHUNK_PROCESSING_ERROR, - }; - /* * Reads chunks of data from the given input stream, and invokes the given * callback to process each of them in turn. @@ -71,7 +80,7 @@ class ChunkReader { */ auto ReadChunkFromStream( std::function<std::optional<size_t>(uint8_t*, size_t)> callback, - TickType_t max_wait) -> EncodeReadResult; + TickType_t max_wait) -> ChunkReadResult; private: MessageBufferHandle_t* stream_; diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 0fc2729f..6fe178ab 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -5,8 +5,9 @@ #include <string> #include "freertos/FreeRTOS.h" + +#include "freertos/message_buffer.h" #include "freertos/queue.h" -#include "freertos/stream_buffer.h" #include "audio_element.hpp" #include "storage.hpp" @@ -18,11 +19,16 @@ class FatfsAudioInput : public IAudioElement { FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage); ~FatfsAudioInput(); - auto OutputBuffer() -> MessageBufferHandle_t; + auto ProcessStreamInfo(StreamInfo&& info) -> cpp::result<void, StreamError>; + auto ProcessChunk(uint8_t* data, std::size_t length) + -> cpp::result<size_t, StreamError>; + auto ProcessIdle() -> cpp::result<void, StreamError>; auto SendChunk(uint8_t* buffer, size_t size) -> size_t; private: + auto GetRingBufferDistance() -> size_t; + std::shared_ptr<drivers::SdStorage> storage_; uint8_t* file_buffer_; @@ -39,7 +45,6 @@ class FatfsAudioInput : public IAudioElement { uint8_t* output_buffer_memory_; StaticMessageBuffer_t output_buffer_metadata_; - MessageBufferHandle_t output_buffer_; }; } // namespace audio diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index bf5d4c60..ca28dd4e 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -3,9 +3,13 @@ #include <cstdint> #include <optional> #include <string> -#include "esp-idf/components/cbor/tinycbor/src/cbor.h" + +#include "cbor.h" #include "result.hpp" +#include "cbor_decoder.hpp" +#include "cbor_encoder.hpp" + namespace audio { class StreamInfo { @@ -18,7 +22,7 @@ class StreamInfo { static auto Create(const uint8_t* buffer, size_t length) -> cpp::result<StreamInfo, ParseError>; - StreamInfo(CborValue& map); + StreamInfo(cbor::MapDecoder*); StreamInfo() = default; StreamInfo(const StreamInfo&) = default; @@ -34,11 +38,7 @@ class StreamInfo { return sample_rate_; } - enum EncodeError { - OUT_OF_MEMORY, - }; - - auto WriteToMap(CborEncoder encoder) -> cpp::result<size_t, EncodeError>; + auto WriteToMap(cbor::Encoder& encoder) -> cpp::result<size_t, CborError>; private: std::optional<std::string> path_; diff --git a/src/audio/stream_info.cpp b/src/audio/stream_info.cpp index 6011571d..a5f7bebf 100644 --- a/src/audio/stream_info.cpp +++ b/src/audio/stream_info.cpp @@ -1,26 +1,31 @@ #include "stream_info.hpp" + #include <cstdint> +#include <string> + +#include "cbor.h" + #include "cbor_decoder.hpp" -#include "esp-idf/components/cbor/tinycbor/src/cbor.h" +#include "cbor_encoder.hpp" #include "stream_message.hpp" namespace audio { -static const char* kKeyPath = "p"; -static const char* kKeyChannels = "c"; -static const char* kKeyBitsPerSample = "b"; -static const char* kKeySampleRate = "r"; +static const std::string kKeyPath = "p"; +static const std::string kKeyChannels = "c"; +static const std::string kKeyBitsPerSample = "b"; +static const std::string kKeySampleRate = "r"; -static auto StreamInfo::Create(const uint8_t* buffer, size_t length) +auto StreamInfo::Create(const uint8_t* buffer, size_t length) -> cpp::result<StreamInfo, ParseError> { CborParser parser; CborValue value; - cbor_parser_init(buffer, len, 0, &parser, &value); + cbor_parser_init(buffer, length, 0, &parser, &value); - uint8_t type = 0; - if (!cbor_value_is_integer(&value) || - !cbor_value_get_integer(&value, &type) || type != STREAM_INFO) { + int type = 0; + if (!cbor_value_is_integer(&value) || !cbor_value_get_int(&value, &type) || + type != TYPE_STREAM_INFO) { return cpp::fail(WRONG_TYPE); } @@ -30,26 +35,28 @@ static auto StreamInfo::Create(const uint8_t* buffer, size_t length) return cpp::fail(MISSING_MAP); } - return StreamInfo(value); + auto map_decoder = cbor::MapDecoder::Create(value); + if (map_decoder.has_value()) { + return StreamInfo(map_decoder.value().get()); + } + return cpp::fail(CBOR_ERROR); } -StreamInfo::StreamInfo(CborValue& map) { +StreamInfo::StreamInfo(cbor::MapDecoder* decoder) { // TODO: this method is n^2, which seems less than ideal. But you don't do it // that frequently, so maybe it's okay? Needs investigation. - cbor::MapDecoder decoder(map); - channels_ = decoder.FindValue(kKeyChannels); - bits_per_sample_ = decoder.FindValue(kKeyBitsPerSample); - sample_rate_ = decoder.FindValue(kKeySampleRate); - path_ = decoder.FindValue(kKeyPath); + channels_ = decoder->FindValue<uint64_t>(kKeyChannels); + bits_per_sample_ = decoder->FindValue<uint64_t>(kKeyBitsPerSample); + sample_rate_ = decoder->FindValue<uint64_t>(kKeySampleRate); + path_ = decoder->FindValue<std::string>(kKeyPath); } auto StreamInfo::WriteToMap(cbor::Encoder& map_encoder) - -> cpp::result<size_t, EncodeError> { - CborEncoder map; - map_encoder.WriteKeyValue(kKeyChannels, channels_); - map_encoder.WriteKeyValue(kKeyBitsPerSample, bits_per_sample_); - map_encoder.WriteKeyValue(kKeySampleRate, sample_rate_); - map_encoder.WriteKeyValue(kKeyPath, path_); + -> cpp::result<size_t, CborError> { + map_encoder.WriteKeyValue<uint64_t>(kKeyChannels, channels_); + map_encoder.WriteKeyValue<uint64_t>(kKeyBitsPerSample, bits_per_sample_); + map_encoder.WriteKeyValue<uint64_t>(kKeySampleRate, sample_rate_); + map_encoder.WriteKeyValue<std::string>(kKeyPath, path_); return map_encoder.Finish(); } |
