summaryrefslogtreecommitdiff
path: root/src/codecs/dr_flac.cpp
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-02-29 12:08:12 +1100
committerjacqueline <me@jacqueline.id.au>2024-02-29 12:08:12 +1100
commitd41f9f703375171d5766840c9edec32ff47bb25d (patch)
tree25028408ed16d5aad3bdb315b6503536a875c0ac /src/codecs/dr_flac.cpp
parent77145e56f4062cd060ee7fa0af9ad1a2e46df5b1 (diff)
downloadtangara-fw-d41f9f703375171d5766840c9edec32ff47bb25d.tar.gz
Use drflac instead of miniflac
This one is fast as hell! Does seeking really good too. Thank u Doctor Flac.
Diffstat (limited to 'src/codecs/dr_flac.cpp')
-rw-r--r--src/codecs/dr_flac.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/codecs/dr_flac.cpp b/src/codecs/dr_flac.cpp
new file mode 100644
index 00000000..cacf7a6e
--- /dev/null
+++ b/src/codecs/dr_flac.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "dr_flac.hpp"
+
+#include <cstdint>
+#include <cstdlib>
+
+#include "codec.hpp"
+#include "dr_flac.h"
+#include "esp_heap_caps.h"
+#include "esp_log.h"
+#include "result.hpp"
+#include "sample.hpp"
+
+namespace codecs {
+
+[[maybe_unused]] static const char kTag[] = "flac";
+
+static void* onMalloc(size_t sz, void* pUserData) {
+ return heap_caps_malloc(sz, MALLOC_CAP_SPIRAM);
+}
+
+static void* onRealloc(void* p, size_t sz, void* pUserData) {
+ return heap_caps_realloc(p, sz, MALLOC_CAP_SPIRAM);
+}
+
+static void onFree(void* p, void* pUserData) {
+ heap_caps_free(p);
+}
+
+static drflac_allocation_callbacks kAllocCallbacks{
+ .pUserData = nullptr,
+ .onMalloc = onMalloc,
+ .onRealloc = onRealloc,
+ .onFree = onFree,
+};
+
+static size_t readProc(void* pUserData, void* pBufferOut, size_t bytesToRead) {
+ IStream* stream = reinterpret_cast<IStream*>(pUserData);
+ ssize_t res =
+ stream->Read({reinterpret_cast<std::byte*>(pBufferOut), bytesToRead});
+ return res < 0 ? 0 : res;
+}
+
+static drflac_bool32 seekProc(void* pUserData,
+ int offset,
+ drflac_seek_origin origin) {
+ IStream* stream = reinterpret_cast<IStream*>(pUserData);
+ if (!stream->CanSeek()) {
+ return DRFLAC_FALSE;
+ }
+
+ IStream::SeekFrom seek_from;
+ switch (origin) {
+ case drflac_seek_origin_start:
+ seek_from = IStream::SeekFrom::kStartOfStream;
+ break;
+ case drflac_seek_origin_current:
+ seek_from = IStream::SeekFrom::kCurrentPosition;
+ break;
+ default:
+ return DRFLAC_FALSE;
+ }
+ stream->SeekTo(offset, seek_from);
+
+ // FIXME: Detect falling off the end of the file.
+ return DRFLAC_TRUE;
+}
+
+DrFlacDecoder::DrFlacDecoder() : input_(), flac_() {}
+
+DrFlacDecoder::~DrFlacDecoder() {
+ if (flac_) {
+ drflac_free(flac_, &kAllocCallbacks);
+ }
+}
+
+auto DrFlacDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
+ -> cpp::result<OutputFormat, Error> {
+ input_ = input;
+
+ flac_ = drflac_open(readProc, seekProc, input_.get(), &kAllocCallbacks);
+ if (!flac_) {
+ return cpp::fail(Error::kMalformedData);
+ }
+
+ if (offset && !drflac_seek_to_pcm_frame(flac_, offset * flac_->sampleRate)) {
+ return cpp::fail(Error::kMalformedData);
+ }
+
+ OutputFormat format{
+ .num_channels = static_cast<uint8_t>(flac_->channels),
+ .sample_rate_hz = static_cast<uint32_t>(flac_->sampleRate),
+ .total_samples = flac_->totalPCMFrameCount * flac_->channels,
+ };
+ return format;
+}
+
+auto DrFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
+ -> cpp::result<OutputInfo, Error> {
+ size_t frames_to_read = output.size() / flac_->channels / 2;
+
+ auto frames_written = drflac_read_pcm_frames_s16(
+ flac_, output.size() / flac_->channels, output.data());
+
+ return OutputInfo{
+ .samples_written = static_cast<size_t>(frames_written * flac_->channels),
+ .is_stream_finished = frames_written < frames_to_read};
+}
+
+auto DrFlacDecoder::SeekTo(size_t target) -> cpp::result<void, Error> {
+ return {};
+}
+
+} // namespace codecs