diff options
Diffstat (limited to 'src/audio/audio_decoder.cpp')
| -rw-r--r-- | src/audio/audio_decoder.cpp | 154 |
1 files changed, 67 insertions, 87 deletions
diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp index 7ed67339..02217187 100644 --- a/src/audio/audio_decoder.cpp +++ b/src/audio/audio_decoder.cpp @@ -1,107 +1,87 @@ #include "audio_decoder.hpp" +#include <string.h> #include <cstddef> #include <cstdint> -#include <string.h> +#include "chunk.hpp" #include "esp_heap_caps.h" +#include "freertos/portmacro.h" #include "include/audio_element.hpp" #include "include/fatfs_audio_input.hpp" namespace audio { - // TODO: could this be larger? depends on the codecs i guess - static const std::size_t kWorkingBufferSize = kMaxFrameSize; - - AudioDecoder::AudioDecoder() { - working_buffer_ = heap_caps_malloc(kWorkingBufferSize, MALLOC_CAP_SPIRAM); - } - - AudioDecoder::~AudioDecoder() { - free(working_buffer_); - } - - auto AudioDecoder::InputBuffer() -> StreamBufferHandle_t { - return input_buffer_; - } - - auto AudioDecoder::OutputBuffer() -> StreamBufferHandle_t { - return output_buffer_; - } - - auto AudioDecoder::SetInputBuffer(StreamBufferHandle_t buffer) -> void { - input_buffer_ = buffer; - } - - auto AudioDecoder::SetOutputBuffer(StreamBufferHandle_t buffer) -> void { - output_buffer_ = buffer; +AudioDecoder::AudioDecoder() + : IAudioElement(), + chunk_buffer_(heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM)), + stream_info_({}) {} + +AudioDecoder::~AudioDecoder() { + free(chunk_buffer_); +} + +auto AudioDecoder::SetInputBuffer(StreamBufferHandle_t* buffer) -> void { + input_buffer_ = buffer; +} + +auto AudioDecoder::SetOutputBuffer(StreamBufferHandle_t* buffer) -> void { + output_buffer_ = buffer; +} + +auto AudioDecoder::ProcessStreamInfo(StreamInfo&& info) + -> cpp::result<void, StreamError> { + stream_info_ = 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)) { + current_codec_->ResetForNewStream(); + return {}; } - auto AudioDecoder::ProcessElementCommand(void* command) -> ProcessResult { - FatfsAudioInput::OutputCommand *real = std::reinterpret_cast<FatfsAudioInput::OutputCommand*>(command); - - if (current_codec_->CanHandleExtension(real->extension)) { - // TODO: Do we need to reset the codec? - delete real; - return OK; - } - - auto result = codecs::CreateCodecForExtension(real->extension); - // TODO: handle error case - if (result.has_value()) { - current_codec_ = result.value(); - } - - delete real; - return OK; + auto result = codecs::CreateCodecForFile(info.path); + if (result.has_value()) { + current_codec_ = std::move(result.value()); + } else { + return cpp::fail(UNSUPPORTED_STREAM); } - auto AudioDecoder::SkipElementCommand(void* command) -> void { - FatfsAudioInput::OutputCommand *real = std::reinterpret_cast<FatfsAudioInput::OutputCommand*>(command); - delete real; - } - - auto AudioDecoder::ProcessData(uint8_t* data, uint16_t length) -> ProcessResult { - if (current_codec_ == nullptr) { - // TODO: signal this - return OK; - } - - while (true) { - auto result = current_codec_->Process(data, length, working_buffer_, kWorkingBufferSize); - - if (result.has_error()) { - // TODO: handle i guess - return ERROR; - } - ICodec::Result process_res = result.value(); + return {}; +} - if (process_res.flush_output) { - xStreamBufferSend(&output_buffer_, working_buffer_, process_res.output_written, kMaxWaitTicks); - } - - if (process_res.need_more_input) { - // TODO: wtf do we do about the leftover bytes? - return OK; - } - } - - return OK; - } - - auto AudioDecoder::ProcessIdle() -> ProcessResult { - // Not used. - return OK; +auto AudioDecoder::ProcessChunk(uint8_t* data, std::size_t length) + -> cpp::result<size_t, StreamError> { + if (current_codec_ == nullptr) { + // Should never happen, but fail explicitly anyway. + return cpp::fail(UNSUPPORTED_STREAM); } - auto AudioDecoder::Pause() -> void { - // TODO. - } - auto AudioDecoder::IsPaused() -> bool { - // TODO. + current_codec_->SetInput(data, length); + cpp::result<size_t, codecs::ICodec::ProcessingError> result; + 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; + } + return result.value(); + }, + // 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()) { + return cpp::fail(IO_ERROR); } - auto AudioDecoder::Resume() -> void { - // TODO. - } + return current_codec_->GetOutputProcessed(); +} +auto AudioDecoder::ProcessIdle() -> cpp::result<void, StreamError> { + // Not used; we delay forever when waiting on IO. + return {}; +} -} // namespace audio +} // namespace audio |
