Progress on own pipeline. Still very WIP

custom
jacqueline 2 years ago
parent ddcbcad6d4
commit acd6d78900
  1. 103
      src/audio/audio_decoder.cpp
  2. 29
      src/audio/audio_task.cpp
  3. 110
      src/audio/fatfs_audio_input.cpp
  4. 34
      src/audio/include/audio_decoder.hpp
  5. 13
      src/audio/include/audio_element.hpp
  6. 16
      src/audio/include/audio_output.hpp
  7. 11
      src/audio/include/fatfs_audio_input.hpp
  8. 22
      src/codecs/codec.hpp
  9. 2
      src/drivers/CMakeLists.txt
  10. 3
      src/drivers/include/storage.hpp
  11. 4
      src/drivers/storage.cpp

@ -0,0 +1,103 @@
#include "audio_decoder.hpp"
#include <cstddef>
#include "esp_heap_caps.h"
#include "include/audio_element.hpp"
#include "include/fatfs_audio_input.hpp"
namespace audio {
static const TickType_t kMaxWaitTicks = portMAX_DELAY;
// 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::InputCommandQueue() -> QueueHandle_t {
return input_queue_;
}
auto AudioDecoder::SetInputCommandQueue(QueueHandle_t queue) -> void {
input_queue_ = queue;
}
auto AudioDecoder::SetOutputCommandQueue(QueueHandle_t queue) -> void {
output_queue_ = queue;
}
auto AudioDecoder::InputBuffer() -> StreamBufferHandle_t {
return input_buffer_;
}
auto AudioDecoder::SetInputBuffer(StreamBufferHandle_t buffer) -> void {
input_buffer_ = buffer;
}
auto AudioDecoder::SetOutputBuffer(StreamBufferHandle_t buffer) -> void {
output_buffer_ = buffer;
}
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 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;
}
auto result = current_codec_->Process(data, length, working_buffer_, kWorkingBufferSize);
if (result.has_value()) {
xStreamBufferSend(&output_buffer_, working_buffer_, result.value(), kMaxWaitTicks);
} else {
// TODO: handle i guess
return ERROR;
}
return OK;
}
auto AudioDecoder::ProcessIdle() -> ProcessResult {
// Not used.
return OK;
}
auto AudioDecoder::Pause() -> void {
// TODO.
}
auto AudioDecoder::IsPaused() -> bool {
// TODO.
}
auto AudioDecoder::Resume() -> void {
// TODO.
}
} // namespace audio

@ -10,10 +10,12 @@
#include "freertos/stream_buffer.h" #include "freertos/stream_buffer.h"
#include "audio_element.hpp" #include "audio_element.hpp"
#include "include/audio_element.hpp"
namespace audio { namespace audio {
static const TickType_t kCommandWaitTicks = 1; static const TickType_t kCommandWaitTicks = 1;
static const TickType_t kIdleTaskDelay = 1;
void audio_task(void* args) { void audio_task(void* args) {
AudioTaskArgs* real_args = reinterpret_cast<AudioTaskArgs*>(args); AudioTaskArgs* real_args = reinterpret_cast<AudioTaskArgs*>(args);
@ -30,8 +32,16 @@ void audio_task(void* args) {
while (1) { while (1) {
IAudioElement::Command command; IAudioElement::Command command;
ProcessResult result;
if (!xQueueReceive(commands, &command, kCommandWaitTicks)) { if (!xQueueReceive(commands, &command, kCommandWaitTicks)) {
element->ProcessIdle(); result = element->ProcessIdle();
if (result == IAudioElement::ERROR) {
break;
}
if (result == IAudioElement::OUTPUT_FULL) {
vTaskDelay(kIdleTaskDelay);
}
continue; continue;
}; };
@ -39,7 +49,6 @@ void audio_task(void* args) {
if (command.sequence_number > current_sequence_number) { if (command.sequence_number > current_sequence_number) {
current_sequence_number = command.sequence_number; current_sequence_number = command.sequence_number;
} }
continue; continue;
} }
@ -49,7 +58,13 @@ void audio_task(void* args) {
xStreamBufferReceive(stream, &frame_buffer, command.read_size, 0); xStreamBufferReceive(stream, &frame_buffer, command.read_size, 0);
if (command.sequence_number == current_sequence_number) { if (command.sequence_number == current_sequence_number) {
element->ProcessData(frame_buffer, command.read_size); result = element->ProcessData(frame_buffer, command.read_size);
if (result == IAudioElement::ERROR) {
break;
}
if (result == IAudioElement::OUTPUT_FULL) {
// TODO: Do we care about this? could just park indefinitely.
}
} }
continue; continue;
@ -58,7 +73,13 @@ void audio_task(void* args) {
if (command.type == IAudioElement::ELEMENT) { if (command.type == IAudioElement::ELEMENT) {
assert(command.data != NULL); assert(command.data != NULL);
if (command.sequence_number == current_sequence_number) { if (command.sequence_number == current_sequence_number) {
element->ProcessElementCommand(command.data); result = element->ProcessElementCommand(command.data);
if (result == IAudioElement::ERROR) {
break;
}
if (result == IAudioElement::OUTPUT_FULL) {
// TODO: what does this mean lol
}
} else { } else {
element->SkipElementCommand(command.data); element->SkipElementCommand(command.data);
} }

@ -1,28 +1,42 @@
#include "fatfs_audio_input.hpp" #include "fatfs_audio_input.hpp<D-c>ccc
#include <cstdint>
#include <memory> #include <memory>
#include "esp-adf/components/input_key_service/include/input_key_service.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "audio_element.hpp" #include "audio_element.hpp"
#include "freertos/portmacro.h"
#include "include/audio_element.hpp"
namespace audio { namespace audio {
static const size_t kQueueItems = 0; static const TickType_t kMaxWaitTicks = portMAX_DELAY;
static constexpr size_t kQueueItemSize = sizeof(IAudioElement::Command);
static constexpr size_t kQueueSize = kQueueItems * kQueueItemSize;
static const size_t kOutputBufferSize = 1024; // Large output buffer size, so that we can keep a get as much of the input file
// into memory as soon as possible.
static constexpr std::size_t kOutputBufferSize = 1024 * 128;
static constexpr std::size_t kQueueItemSize = sizeof(IAudioElement::Command);
// Use a large enough command queue size that we can fit reads for the full
// buffer into the queue.
static constexpr std::size_t kOutputQueueItemNumber = kOutputBufferSize / kMaxFrameSize;
static constexpr std::size_t kOutputQueueSize = kOutputQueueItemNumber * kQueueItemSize;
// This should be a relatively responsive element, so no need for a particularly
// large queue.
static constexpr std::size_t kInputQueueItemNumber = 4;
static constexpr std::size_t kInputQueueSize = kInputQueueItemNumber * kQueueItemSize;
FatfsAudioInput::FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage) FatfsAudioInput::FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage)
: IAudioElement(), storage_(storage) { : IAudioElement(), storage_(storage) {
input_queue_memory_ = heap_caps_malloc(kQueueSize, MALLOC_CAP_SPIRAM); working_buffer_ = heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_SPIRAM);
input_queue_memory_ = heap_caps_malloc(kInputQueueSize, MALLOC_CAP_SPIRAM);
input_queue_ = xQueueCreateStatic( input_queue_ = xQueueCreateStatic(
kQueueItems, kQueueItemSize, input_queue_memory_, &input_queue_metadata_); kInputQueueItemNumber, kQueueItemSize, input_queue_memory_, &input_queue_metadata_);
output_queue_memory_ = heap_caps_malloc(kQueueSize, MALLOC_CAP_SPIRAM); output_queue_memory_ = heap_caps_malloc(kOutputQueueSize, MALLOC_CAP_SPIRAM);
output_queue_ = output_queue_ =
xQueueCreateStatic(kQueueItems, kQueueItemSize, output_queue_memory_, xQueueCreateStatic(kOutputQueueItems, kQueueItemSize, output_queue_memory_,
&output_queue_metadata_); &output_queue_metadata_);
output_buffer_memory_ = output_buffer_memory_ =
@ -33,6 +47,7 @@ FatfsAudioInput::FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage)
} }
FatfsAudioInput::~FatfsAudioInput() { FatfsAudioInput::~FatfsAudioInput() {
free(working_buffer_);
vStreamBufferDelete(output_buffer_); vStreamBufferDelete(output_buffer_);
free(output_buffer_memory_); free(output_buffer_memory_);
vQueueDelete(output_queue_); vQueueDelete(output_queue_);
@ -57,23 +72,84 @@ auto FatfsAudioInput::OutputBuffer() -> StreamBufferHandle_t {
return output_buffer_; return output_buffer_;
} }
auto FatfsAudioInput::ProcessElementCommand(void* command) -> void { auto FatfsAudioInput::ProcessElementCommand(void* command) -> ProcessResult {
InputCommand *real = std::reinterpret_pointer_cast<input_key_service_add_key*>(command); InputCommand *real = std::reinterpret_cast<InputCommand*>(command);
if (uxQueueSpacesAvailable(output_queue_) < 2) {
return OUTPUT_FULL;
}
if (is_file_open_) {
f_close(&current_file_);
}
FRESULT res = f_open(&current_file_, real->filename.c_str(), FA_READ);
if (res != FR_OK) {
delete real;
return ERROR;
}
if (real->seek_to && f_lseek(&current_file_, real->seek_to) {
return ERROR;
}
is_file_open_ = true;
current_sequence_++;
Command sequence_update;
sequence_update.type = SEQUENCE_NUMBER;
sequence_update.sequence_number = current_sequence_;
if (real->interrupt) {
xQueueSendToFront(output_queue_, &sequence_update, kMaxWaitTicks);
} else {
xQueueSendToBack(output_queue_, &sequence_update, kMaxWaitTicks);
}
OutputCommand *data = new OutputCommand;
data->extension = "txt";
Command file_info;
file_info.type = ELEMENT;
file_info.sequence_number = current_sequence_;
file_info.data = &data;
xQueueSendToBack(output_queue_, &file_info, kMaxWaitTicks);
// TODO. delete real;
return OK;
} }
auto FatfsAudioInput::SkipElementCommand(void* command) -> void { auto FatfsAudioInput::SkipElementCommand(void* command) -> void {
InputCommand *real = std::reinterpret_pointer_cast<input_key_service_add_key*>(command); InputCommand *real = std::reinterpret_cast<input_key_service_add_key*>(command);
delete real; delete real;
} }
auto FatfsAudioInput::ProcessData(uint8_t* data, uint16_t length) -> void { auto FatfsAudioInput::ProcessData(uint8_t* data, uint16_t length) -> void {
// Not implemented. // Not used, since we have no input stream.
} }
auto FatfsAudioInput::ProcessIdle() -> void { auto FatfsAudioInput::ProcessIdle() -> ProcessResult {
// TODO. if (!is_file_open_) {
return OK;
}
if (xStreamBufferSpacesAvailable(output_buffer) < kMaxFrameSize) {
return OUTPUT_FULL;
}
UINT bytes_read = 0;
FRESULT result = f_read(&current_file_, working_buffer_, kMaxFrameSize, &bytes_read);
if (!FR_OK) {
return ERROR;
}
xStreamBufferSend(&output_buffer_, working_buffer_, bytes_read, kMaxWaitTicks);
if (f_eof(&current_file_)) {
f_close(&current_file_);
is_file_open_ = false;
}
return OK;
} }
} // namespace audio } // namespace audio

@ -1,34 +1,36 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "audio_element.hpp"
#include "ff.h" #include "ff.h"
#include "codec.hpp"
namespace audio { namespace audio {
enum SampleRate {}; class AudioDecoder : public IAudioElement {
enum BitDepth {};
struct PcmStreamHeader {
SampleRate sample_rate;
BitDepth bit_depth;
bool configure_now;
};
class AudioDecoder {
public: public:
AudioDecoder(); AudioDecoder();
~AudioDecoder(); ~AudioDecoder();
auto SetSource(RingbufHandle_t& source) -> void; auto Pause() -> void;
auto IsPaused() -> bool;
enum Status {}; auto Resume() -> void;
auto ProcessChunk() -> Status;
auto GetOutputStream() const -> RingbufHandle_t; auto SetInputCommandQueue(QueueHandle_t) -> void;
auto SetOutputCommandQueue(QueueHandle_t) -> void;
auto SetInputBuffer(StreamBufferHandle_t) -> void;
auto SetOutputBuffer(StreamBufferHandle_t) -> void;
private: private:
RingbufHandle_t input_; std::unique_ptr<codecs::ICodec> current_codec_;
RingbufHandle_t output_;
uint8_t *working_buffer_;
QueueHandle_t input_queue_;
QueueHandle_t output_queue_;
StreamBufferHandle_t input_buffer_;
StreamBufferHandle_t output_buffer_;
}; };
} // namespace audio } // namespace audio

@ -33,6 +33,7 @@ class IAudioElement {
struct Command { struct Command {
CommandType type; CommandType type;
uint8_t sequence_number; uint8_t sequence_number;
// TODO: tag data's type
union { union {
void* data; void* data;
std::size_t frame_size; std::size_t frame_size;
@ -52,22 +53,28 @@ class IAudioElement {
*/ */
virtual auto InputBuffer() -> StreamBufferHandle_t = 0; virtual auto InputBuffer() -> StreamBufferHandle_t = 0;
enum ProcessResult {
OK,
OUTPUT_FULL,
ERROR,
};
/* /*
* Called when an element-specific command has been received. * Called when an element-specific command has been received.
*/ */
virtual auto ProcessElementCommand(void* command) -> void = 0; virtual auto ProcessElementCommand(void* command) -> ProcessResult = 0;
virtual auto SkipElementCommand(void* command) -> void = 0; virtual auto SkipElementCommand(void* command) -> void = 0;
/* /*
* Called with the result of a read bytes command. * Called with the result of a read bytes command.
*/ */
virtual auto ProcessData(uint8_t* data, uint16_t length) -> void = 0; virtual auto ProcessData(uint8_t* data, uint16_t length) -> ProcessResult = 0;
/* /*
* Called periodically when there are no pending commands. * Called periodically when there are no pending commands.
*/ */
virtual auto ProcessIdle() -> void = 0; virtual auto ProcessIdle() -> ProcessResult = 0;
}; };
} // namespace audio } // namespace audio

@ -5,25 +5,25 @@
#include "audio_common.h" #include "audio_common.h"
#include "audio_element.h" #include "audio_element.h"
#include "audio_element.hpp"
namespace drivers { namespace audio {
class IAudioOutput { class AudioOutput : IAudioElement {
public: public:
IAudioOutput(audio_element_handle_t element) : element_(element) {} AudioOutput();
virtual ~IAudioOutput() { audio_element_deinit(element_); } ~AudioOutput();
auto GetAudioElement() -> audio_element_handle_t { return element_; } auto GetAudioElement() -> audio_element_handle_t { return element_; }
virtual auto SetVolume(uint8_t volume) -> void = 0; auto SetInputBuffer(StreamBufferHandle_t buffer) -> void;
virtual auto GetVolume() const -> uint8_t { return volume_; }
virtual auto Configure(audio_element_info_t& info) -> void = 0; virtual auto Configure(audio_element_info_t& info) -> void = 0;
virtual auto SetSoftMute(bool enabled) -> void = 0; virtual auto SetSoftMute(bool enabled) -> void = 0;
protected: private:
audio_element_handle_t element_; audio_element_handle_t element_;
uint8_t volume_; uint8_t volume_;
}; };
} // namespace drivers } // namespace audio

@ -2,6 +2,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <string>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/queue.h" #include "freertos/queue.h"
@ -16,10 +17,12 @@ class FatfsAudioInput : public IAudioElement {
public: public:
struct InputCommand { struct InputCommand {
std::string filename; std::string filename;
size_t seek_to;
bool interrupt;
}; };
struct OutputCommand { struct OutputCommand {
// TODO: does this actually need any special output? std::string extension;
}; };
FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage); FatfsAudioInput(std::shared_ptr<drivers::SdStorage> storage);
@ -31,7 +34,11 @@ class FatfsAudioInput : public IAudioElement {
private: private:
std::shared_ptr<drivers::SdStorage> storage_; std::shared_ptr<drivers::SdStorage> storage_;
uint8_t current_sequence = 0; uint8_t *working_buffer_;
uint8_t current_sequence_ = 0;
FIL current_file_;
bool is_file_open_ = false;
uint8_t* input_queue_memory_; uint8_t* input_queue_memory_;
StaticQueue_t input_queue_metadata_; StaticQueue_t input_queue_metadata_;

@ -2,16 +2,28 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "result.hpp"
namespace codecs { namespace codecs {
class IAudioDecoder { enum CreateCodecError {};
auto CreateCodecForExtension(std::string extension) -> cpp::result<std::unique_ptr<ICodec>, CreateCodecError>;
class ICodec {
public: public:
virtual ~IAudioDecoder() {} virtual ~ICodec() {}
virtual auto CanHandleExtension(std::string extension) -> bool = 0;
enum Error {};
virtual auto ProcessData( virtual auto Process(
uint8_t *input, uint8_t *input,
size_t input_len, std::size_t input_len,
uint8_t *output) -> size_t = 0; uint8_t *output,
std::size_t output_length) -> cpp::result<size_t, Error> = 0;
}; };
} // namespace codecs } // namespace codecs

@ -2,5 +2,5 @@ idf_component_register(
SRCS "dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp" SRCS "dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp"
"audio_playback.cpp" "i2s_audio_output.cpp" "display.cpp" "display_init.cpp" "spi.cpp" "audio_playback.cpp" "i2s_audio_output.cpp" "display.cpp" "display_init.cpp" "spi.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "esp_adc_cal" "fatfs" "audio_pipeline" "audio_stream" "result" "lvgl") REQUIRES "esp_adc_cal" "fatfs" "result" "lvgl")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})

@ -6,6 +6,7 @@
#include "driver/sdspi_host.h" #include "driver/sdspi_host.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_vfs_fat.h" #include "esp_vfs_fat.h"
#include "ff.h"
#include "result.hpp" #include "result.hpp"
#include "gpio_expander.hpp" #include "gpio_expander.hpp"
@ -38,6 +39,8 @@ class SdStorage {
auto HandleTransaction(sdspi_dev_handle_t handle, sdmmc_command_t* cmdinfo) auto HandleTransaction(sdspi_dev_handle_t handle, sdmmc_command_t* cmdinfo)
-> esp_err_t; -> esp_err_t;
auto GetFs() -> FATFS*;
// Not copyable or movable. // Not copyable or movable.
// TODO: maybe this could be movable? // TODO: maybe this could be movable?
SdStorage(const SdStorage&) = delete; SdStorage(const SdStorage&) = delete;

@ -146,4 +146,8 @@ auto SdStorage::HandleTransaction(sdspi_dev_handle_t handle,
return do_transaction_(handle, cmdinfo); return do_transaction_(handle, cmdinfo);
} }
auto SdStorage::GetFs() -> FATFS* {
return fs_;
}
} // namespace drivers } // namespace drivers

Loading…
Cancel
Save