#include "fatfs_audio_input.hpp" #include #include #include #include "audio_element.hpp" #include "esp_heap_caps.h" #include "freertos/portmacro.h" #include "audio_element.hpp" #include "chunk.hpp" #include "stream_message.hpp" static const char* kTag = "SRC"; namespace audio { static const TickType_t kServiceInterval = pdMS_TO_TICKS(50); static const std::size_t kFileBufferSize = 1024 * 128; static const std::size_t kMinFileReadSize = 1024 * 4; static const std::size_t kOutputBufferSize = 1024 * 4; FatfsAudioInput::FatfsAudioInput(std::shared_ptr storage) : IAudioElement(), storage_(storage), raw_file_buffer_(static_cast( heap_caps_malloc(kFileBufferSize, MALLOC_CAP_SPIRAM))), file_buffer_(raw_file_buffer_, kFileBufferSize), file_buffer_read_pos_(file_buffer_.begin()), file_buffer_write_pos_(file_buffer_.begin()), raw_chunk_buffer_(static_cast( heap_caps_malloc(kMaxChunkSize, MALLOC_CAP_SPIRAM))), chunk_buffer_(raw_chunk_buffer_, kMaxChunkSize), current_file_(), is_file_open_(false), output_buffer_memory_(static_cast( heap_caps_malloc(kOutputBufferSize, MALLOC_CAP_SPIRAM))) { output_buffer_ = new MessageBufferHandle_t; *output_buffer_ = xMessageBufferCreateStatic( kOutputBufferSize, output_buffer_memory_, &output_buffer_metadata_); } FatfsAudioInput::~FatfsAudioInput() { free(raw_file_buffer_); free(raw_chunk_buffer_); vMessageBufferDelete(output_buffer_); free(output_buffer_memory_); free(output_buffer_); } auto FatfsAudioInput::ProcessStreamInfo(StreamInfo& info) -> cpp::result { if (is_file_open_) { f_close(¤t_file_); is_file_open_ = false; } if (!info.Path()) { return cpp::fail(UNSUPPORTED_STREAM); } std::string path = info.Path().value(); FRESULT res = f_open(¤t_file_, path.c_str(), FA_READ); if (res != FR_OK) { return cpp::fail(IO_ERROR); } is_file_open_ = true; auto write_size = WriteMessage(TYPE_STREAM_INFO, std::bind(&StreamInfo::Encode, info, std::placeholders::_1), chunk_buffer_); if (write_size.has_error()) { return cpp::fail(IO_ERROR); } else { xMessageBufferSend(output_buffer_, chunk_buffer_.data(), write_size.value(), portMAX_DELAY); } return {}; } auto FatfsAudioInput::ProcessChunk(cpp::span& chunk) -> cpp::result { return cpp::fail(UNSUPPORTED_STREAM); } auto FatfsAudioInput::GetRingBufferDistance() -> size_t { if (file_buffer_read_pos_ == file_buffer_write_pos_) { return 0; } if (file_buffer_read_pos_ < file_buffer_write_pos_) { return file_buffer_write_pos_ - file_buffer_read_pos_; } return // Read position to end of buffer. (file_buffer_.end() - file_buffer_read_pos_) // Start of buffer to write position. + (file_buffer_write_pos_ - file_buffer_.begin()); } auto FatfsAudioInput::ProcessIdle() -> cpp::result { // First, see if we're able to fill up the input buffer with any more of the // file's contents. if (is_file_open_) { size_t ringbuf_distance = GetRingBufferDistance(); if (file_buffer_.size() - ringbuf_distance > kMinFileReadSize) { size_t read_size; if (file_buffer_write_pos_ < file_buffer_read_pos_) { // Don't worry about the start of buffer -> read pos size; we can get to // it next iteration. read_size = file_buffer_read_pos_ - file_buffer_write_pos_; } else { read_size = file_buffer_.begin() - file_buffer_write_pos_; } UINT bytes_read = 0; FRESULT result = f_read(¤t_file_, std::addressof(file_buffer_write_pos_), read_size, &bytes_read); if (result != FR_OK) { ESP_LOGE(kTag, "file I/O error %d", result); return cpp::fail(IO_ERROR); } if (f_eof(¤t_file_)) { f_close(¤t_file_); is_file_open_ = false; // TODO: open the next file? } file_buffer_write_pos_ += bytes_read; if (file_buffer_write_pos_ == file_buffer_.end()) { file_buffer_write_pos_ = file_buffer_.begin(); } } } // Now stream data into the output buffer until it's full. pending_read_pos_ = file_buffer_read_pos_; ChunkWriteResult result = WriteChunksToStream( output_buffer_, chunk_buffer_, [&](cpp::span d) { return SendChunk(d); }, kServiceInterval); switch (result) { case CHUNK_WRITE_TIMEOUT: case CHUNK_OUT_OF_DATA: // Both of these are fine; SendChunk keeps track of where it's up to // internally, so we will pick back up where we left off. return {}; default: return cpp::fail(IO_ERROR); } } auto FatfsAudioInput::SendChunk(cpp::span dest) -> size_t { file_buffer_read_pos_ = pending_read_pos_; if (file_buffer_read_pos_ == file_buffer_write_pos_) { return 0; } std::size_t chunk_size; if (file_buffer_read_pos_ > file_buffer_write_pos_) { chunk_size = file_buffer_.end() - file_buffer_read_pos_; } else { chunk_size = file_buffer_write_pos_ - file_buffer_read_pos_; } chunk_size = std::min(chunk_size, dest.size()); cpp::span source(file_buffer_read_pos_, chunk_size); std::copy(source.begin(), source.end(), dest.begin()); pending_read_pos_ = file_buffer_read_pos_ + chunk_size; if (pending_read_pos_ == file_buffer_.end()) { pending_read_pos_ = file_buffer_.begin(); } return chunk_size; } } // namespace audio