parent
67caeb6e3c
commit
d8fc77101d
@ -0,0 +1,70 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "fatfs_source.hpp" |
||||
#include <sys/_stdint.h> |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <memory> |
||||
|
||||
#include "esp_log.h" |
||||
#include "ff.h" |
||||
|
||||
#include "audio_source.hpp" |
||||
#include "codec.hpp" |
||||
#include "types.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
static constexpr char kTag[] = "fatfs_src"; |
||||
|
||||
FatfsSource::FatfsSource(codecs::StreamType t, std::unique_ptr<FIL> file) |
||||
: IStream(t), file_(std::move(file)) {} |
||||
|
||||
FatfsSource::~FatfsSource() { |
||||
f_close(file_.get()); |
||||
} |
||||
|
||||
auto FatfsSource::Read(cpp::span<std::byte> dest) -> ssize_t { |
||||
if (f_eof(file_.get())) { |
||||
ESP_LOGI(kTag, "read from empty file"); |
||||
return 0; |
||||
} |
||||
UINT bytes_read = 0; |
||||
FRESULT res = f_read(file_.get(), dest.data(), dest.size(), &bytes_read); |
||||
if (res != FR_OK) { |
||||
ESP_LOGE(kTag, "error reading from file"); |
||||
return -1; |
||||
} |
||||
ESP_LOGI(kTag, "read %u bytes into %p (%u)", bytes_read, dest.data(), |
||||
dest.size_bytes()); |
||||
return bytes_read; |
||||
} |
||||
|
||||
auto FatfsSource::CanSeek() -> bool { |
||||
return true; |
||||
} |
||||
|
||||
auto FatfsSource::SeekTo(int64_t destination, SeekFrom from) -> void { |
||||
ESP_LOGI(kTag, "seeking to %llu", destination); |
||||
switch (from) { |
||||
case SeekFrom::kStartOfStream: |
||||
f_lseek(file_.get(), destination); |
||||
break; |
||||
case SeekFrom::kEndOfStream: |
||||
f_lseek(file_.get(), f_size(file_.get()) + destination); |
||||
break; |
||||
case SeekFrom::kCurrentPosition: |
||||
f_lseek(file_.get(), f_tell(file_.get()) + destination); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
auto FatfsSource::CurrentPosition() -> int64_t { |
||||
return f_tell(file_.get()); |
||||
} |
||||
} // namespace audio
|
@ -0,0 +1,44 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <memory> |
||||
|
||||
#include "codec.hpp" |
||||
#include "ff.h" |
||||
|
||||
#include "audio_source.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
/*
|
||||
* Handles coordination with a persistent background task to asynchronously |
||||
* read files from disk into a StreamBuffer. |
||||
*/ |
||||
class FatfsSource : public codecs::IStream { |
||||
public: |
||||
FatfsSource(codecs::StreamType, std::unique_ptr<FIL> file); |
||||
~FatfsSource(); |
||||
|
||||
auto Read(cpp::span<std::byte> dest) -> ssize_t override; |
||||
|
||||
auto CanSeek() -> bool override; |
||||
|
||||
auto SeekTo(int64_t destination, SeekFrom from) -> void override; |
||||
|
||||
auto CurrentPosition() -> int64_t override; |
||||
|
||||
FatfsSource(const FatfsSource&) = delete; |
||||
FatfsSource& operator=(const FatfsSource&) = delete; |
||||
|
||||
private: |
||||
std::unique_ptr<FIL> file_; |
||||
}; |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,37 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <functional> |
||||
|
||||
#include "span.hpp" |
||||
|
||||
#include "codec.hpp" |
||||
|
||||
namespace codecs { |
||||
|
||||
class SourceBuffer { |
||||
public: |
||||
SourceBuffer(); |
||||
~SourceBuffer(); |
||||
|
||||
auto Refill(IStream* src) -> bool; |
||||
auto AddBytes(std::function<size_t(cpp::span<std::byte>)> writer) -> void; |
||||
auto ConsumeBytes(std::function<size_t(cpp::span<std::byte>)> reader) -> void; |
||||
|
||||
SourceBuffer(const SourceBuffer&) = delete; |
||||
SourceBuffer& operator=(const SourceBuffer&) = delete; |
||||
|
||||
private: |
||||
const cpp::span<std::byte> buffer_; |
||||
size_t bytes_in_buffer_; |
||||
size_t offset_of_bytes_; |
||||
}; |
||||
|
||||
} // namespace codecs
|
@ -0,0 +1,75 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "source_buffer.hpp" |
||||
#include <sys/_stdint.h> |
||||
|
||||
#include <algorithm> |
||||
#include <cstring> |
||||
|
||||
#include "esp_heap_caps.h" |
||||
#include "esp_log.h" |
||||
|
||||
#include "codec.hpp" |
||||
|
||||
namespace codecs { |
||||
|
||||
static constexpr char kTag[] = "dec_buf"; |
||||
static constexpr size_t kBufferSize = 1024 * 8; |
||||
|
||||
SourceBuffer::SourceBuffer() |
||||
: buffer_(reinterpret_cast<std::byte*>( |
||||
heap_caps_malloc(kBufferSize, MALLOC_CAP_SPIRAM)), |
||||
kBufferSize), |
||||
bytes_in_buffer_(0), |
||||
offset_of_bytes_(0) { |
||||
assert(buffer_.data() != nullptr); |
||||
} |
||||
|
||||
SourceBuffer::~SourceBuffer() { |
||||
free(buffer_.data()); |
||||
} |
||||
|
||||
auto SourceBuffer::Refill(IStream* src) -> bool { |
||||
if (bytes_in_buffer_ == buffer_.size_bytes()) { |
||||
return false; |
||||
} |
||||
bool eof = false; |
||||
AddBytes([&](cpp::span<std::byte> buf) -> size_t { |
||||
size_t bytes_read = src->Read(buf); |
||||
eof = bytes_read == 0; |
||||
return bytes_read; |
||||
}); |
||||
return eof; |
||||
} |
||||
|
||||
auto SourceBuffer::AddBytes(std::function<size_t(cpp::span<std::byte>)> writer) |
||||
-> void { |
||||
if (offset_of_bytes_ > 0) { |
||||
std::memmove(buffer_.data(), buffer_.data() + offset_of_bytes_, |
||||
bytes_in_buffer_); |
||||
offset_of_bytes_ = 0; |
||||
} |
||||
size_t added_bytes = std::invoke(writer, buffer_.subspan(bytes_in_buffer_)); |
||||
assert(bytes_in_buffer_ + added_bytes <= buffer_.size_bytes()); |
||||
bytes_in_buffer_ += added_bytes; |
||||
} |
||||
|
||||
auto SourceBuffer::ConsumeBytes( |
||||
std::function<size_t(cpp::span<std::byte>)> reader) -> void { |
||||
size_t bytes_consumed = std::invoke( |
||||
reader, buffer_.subspan(offset_of_bytes_).first(bytes_in_buffer_)); |
||||
assert(bytes_consumed <= bytes_in_buffer_); |
||||
|
||||
bytes_in_buffer_ -= bytes_consumed; |
||||
if (bytes_in_buffer_ == 0) { |
||||
offset_of_bytes_ = 0; |
||||
} else { |
||||
offset_of_bytes_ += bytes_consumed; |
||||
} |
||||
} |
||||
|
||||
} // namespace codecs
|
Loading…
Reference in new issue