summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/audio/audio_converter.cpp6
-rw-r--r--src/codecs/CMakeLists.txt4
-rw-r--r--src/codecs/codec.cpp4
-rw-r--r--src/codecs/dr_flac.cpp119
-rw-r--r--src/codecs/include/dr_flac.hpp (renamed from src/codecs/include/miniflac.hpp)19
-rw-r--r--src/codecs/miniflac.cpp213
6 files changed, 133 insertions, 232 deletions
diff --git a/src/audio/audio_converter.cpp b/src/audio/audio_converter.cpp
index dc2fef95..946a0b63 100644
--- a/src/audio/audio_converter.cpp
+++ b/src/audio/audio_converter.cpp
@@ -77,9 +77,9 @@ auto SampleConverter::ConvertSamples(cpp::span<sample::Sample> input,
reinterpret_cast<std::byte*>(input.data()), input.size_bytes()};
size_t bytes_sent = 0;
while (bytes_sent < input_as_bytes.size()) {
- bytes_sent +=
- xStreamBufferSend(source_, input_as_bytes.subspan(bytes_sent).data(),
- input_as_bytes.size() - bytes_sent, portMAX_DELAY);
+ bytes_sent += xStreamBufferSend(
+ source_, input_as_bytes.subspan(bytes_sent).data(),
+ input_as_bytes.size() - bytes_sent, pdMS_TO_TICKS(100));
}
}
diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt
index 4b296c84..b6481bd1 100644
--- a/src/codecs/CMakeLists.txt
+++ b/src/codecs/CMakeLists.txt
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "miniflac.cpp" "codec.cpp" "mad.cpp" "opus.cpp" "vorbis.cpp"
+ SRCS "dr_flac.cpp" "codec.cpp" "mad.cpp" "opus.cpp" "vorbis.cpp"
"source_buffer.cpp" "sample.cpp" "wav.cpp"
INCLUDE_DIRS "include"
- REQUIRES "result" "span" "libmad" "miniflac" "tremor" "opusfile" "memory" "util"
+ REQUIRES "result" "span" "libmad" "drflac" "tremor" "opusfile" "memory" "util"
"komihash")
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/codecs/codec.cpp b/src/codecs/codec.cpp
index 7bc591aa..a51c40d6 100644
--- a/src/codecs/codec.cpp
+++ b/src/codecs/codec.cpp
@@ -10,7 +10,7 @@
#include <optional>
#include "mad.hpp"
-#include "miniflac.hpp"
+#include "dr_flac.hpp"
#include "opus.hpp"
#include "types.hpp"
#include "vorbis.hpp"
@@ -42,7 +42,7 @@ auto CreateCodecForType(StreamType type) -> std::optional<ICodec*> {
case StreamType::kVorbis:
return new TremorVorbisDecoder();
case StreamType::kFlac:
- return new MiniFlacDecoder();
+ return new DrFlacDecoder();
case StreamType::kOpus:
return new XiphOpusDecoder();
case StreamType::kWav:
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
diff --git a/src/codecs/include/miniflac.hpp b/src/codecs/include/dr_flac.hpp
index d1daca2f..8dcfdaf5 100644
--- a/src/codecs/include/miniflac.hpp
+++ b/src/codecs/include/dr_flac.hpp
@@ -6,7 +6,6 @@
#pragma once
-#include <sys/_stdint.h>
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -14,7 +13,7 @@
#include <string>
#include <utility>
-#include "miniflac.h"
+#include "dr_flac.h"
#include "sample.hpp"
#include "source_buffer.hpp"
#include "span.hpp"
@@ -23,10 +22,10 @@
namespace codecs {
-class MiniFlacDecoder : public ICodec {
+class DrFlacDecoder : public ICodec {
public:
- MiniFlacDecoder();
- ~MiniFlacDecoder();
+ DrFlacDecoder();
+ ~DrFlacDecoder();
auto OpenStream(std::shared_ptr<IStream> input,uint32_t offset)
-> cpp::result<OutputFormat, Error> override;
@@ -36,16 +35,12 @@ class MiniFlacDecoder : public ICodec {
auto SeekTo(std::size_t target_sample) -> cpp::result<void, Error> override;
- MiniFlacDecoder(const MiniFlacDecoder&) = delete;
- MiniFlacDecoder& operator=(const MiniFlacDecoder&) = delete;
+ DrFlacDecoder(const DrFlacDecoder&) = delete;
+ DrFlacDecoder& operator=(const DrFlacDecoder&) = delete;
private:
std::shared_ptr<IStream> input_;
- SourceBuffer buffer_;
-
- std::unique_ptr<miniflac_t> flac_;
- std::array<int32_t*, 2> samples_by_channel_;
- std::optional<size_t> current_sample_;
+ drflac *flac_;
};
} // namespace codecs
diff --git a/src/codecs/miniflac.cpp b/src/codecs/miniflac.cpp
deleted file mode 100644
index 45e063c7..00000000
--- a/src/codecs/miniflac.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright 2023 jacqueline <me@jacqueline.id.au>
- *
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-#include "miniflac.hpp"
-#include <stdint.h>
-
-#include <cstdint>
-#include <cstdlib>
-
-#include "esp_heap_caps.h"
-#include "esp_log.h"
-#include "miniflac.h"
-#include "result.hpp"
-#include "sample.hpp"
-
-namespace codecs {
-
-[[maybe_unused]] static const char kTag[] = "flac";
-
-static constexpr size_t kMaxFrameSize = 4608;
-
-MiniFlacDecoder::MiniFlacDecoder()
- : input_(),
- buffer_(),
- flac_(reinterpret_cast<miniflac_t*>(
- heap_caps_malloc(sizeof(miniflac_t),
- MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))),
- current_sample_() {
- miniflac_init(flac_.get(), MINIFLAC_CONTAINER_UNKNOWN);
- for (int i = 0; i < samples_by_channel_.size(); i++) {
- uint32_t caps;
- if (i == 0) {
- caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL;
- } else {
- // FIXME: We can *almost* fit two channels into internal ram, but we're a
- // few KiB shy of being able to do it safely.
- caps = MALLOC_CAP_SPIRAM;
- }
- samples_by_channel_[i] = reinterpret_cast<int32_t*>(
- heap_caps_malloc(kMaxFrameSize * sizeof(int32_t), caps));
- }
-}
-
-MiniFlacDecoder::~MiniFlacDecoder() {
- for (int i = 0; i < samples_by_channel_.size(); i++) {
- heap_caps_free(samples_by_channel_[i]);
- }
-}
-
-auto MiniFlacDecoder::OpenStream(std::shared_ptr<IStream> input,
- uint32_t offset)
- -> cpp::result<OutputFormat, Error> {
- input_ = input;
-
- MINIFLAC_RESULT res;
- auto read_until_result = [&](auto fn) {
- while (true) {
- buffer_.ConsumeBytes(fn);
- if (res == MINIFLAC_CONTINUE && !buffer_.Refill(input_.get())) {
- continue;
- }
- break;
- }
- };
-
- uint32_t sample_rate = 0;
-
- read_until_result([&](cpp::span<std::byte> buf) -> size_t {
- uint32_t bytes_used = 0;
- res = miniflac_streaminfo_sample_rate(
- flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
- buf.size_bytes(), &bytes_used, &sample_rate);
- return bytes_used;
- });
-
- if (res != MINIFLAC_OK) {
- return cpp::fail(Error::kMalformedData);
- }
-
- uint8_t channels = 0;
-
- read_until_result([&](cpp::span<std::byte> buf) -> size_t {
- uint32_t bytes_used = 0;
- res = miniflac_streaminfo_channels(
- flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
- buf.size_bytes(), &bytes_used, &channels);
- return bytes_used;
- });
-
- if (res != MINIFLAC_OK) {
- return cpp::fail(Error::kMalformedData);
- }
-
- uint64_t total_samples = 0;
-
- read_until_result([&](cpp::span<std::byte> buf) -> size_t {
- uint32_t bytes_used = 0;
- res = miniflac_streaminfo_total_samples(
- flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
- buf.size_bytes(), &bytes_used, &total_samples);
- return bytes_used;
- });
-
- if (res != MINIFLAC_OK) {
- return cpp::fail(Error::kMalformedData);
- }
-
- if (channels == 0 || channels > 2) {
- return cpp::fail(Error::kMalformedData);
- }
-
- if (offset) {
- // TODO: This assumes a constant sample_rate
- uint64_t target_sample = sample_rate * offset;
- ESP_LOGI(kTag, "seeking to %lu seconds (sample=%llu)", offset,
- target_sample);
-
- while (true) {
- read_until_result([&](cpp::span<std::byte> buf) -> size_t {
- uint32_t bytes_used = 0;
- res = miniflac_sync(flac_.get(),
- reinterpret_cast<const uint8_t*>(buf.data()),
- buf.size_bytes(), &bytes_used);
- return bytes_used;
- });
-
- if (res != MINIFLAC_OK) {
- return cpp::fail(Error::kMalformedData);
- }
-
- if (!miniflac_is_frame(flac_.get())) {
- continue;
- }
-
- uint64_t samples_in_frame = miniflac_frame_block_size(flac_.get());
- if (samples_in_frame <= target_sample) {
- target_sample -= samples_in_frame;
- } else {
- break;
- }
- }
- }
-
- OutputFormat format{
- .num_channels = static_cast<uint8_t>(channels),
- .sample_rate_hz = static_cast<uint32_t>(sample_rate),
- .total_samples = total_samples * channels,
- };
- return format;
-}
-
-auto MiniFlacDecoder::DecodeTo(cpp::span<sample::Sample> output)
- -> cpp::result<OutputInfo, Error> {
- bool is_eof = false;
-
- if (!current_sample_) {
- MINIFLAC_RESULT res = MINIFLAC_CONTINUE;
- while (res == MINIFLAC_CONTINUE && !is_eof) {
- is_eof = buffer_.Refill(input_.get());
- buffer_.ConsumeBytes([&](cpp::span<std::byte> buf) -> size_t {
- // FIXME: We should do a miniflac_sync first, in order to check that
- // our sample buffers have enough space for the next frame.
- uint32_t bytes_read = 0;
- res = miniflac_decode(
- flac_.get(), reinterpret_cast<const uint8_t*>(buf.data()),
- buf.size_bytes(), &bytes_read, samples_by_channel_.data());
- return bytes_read;
- });
- }
-
- if (res == MINIFLAC_OK) {
- current_sample_ = 0;
- } else if (is_eof) {
- return OutputInfo{
- .samples_written = 0,
- .is_stream_finished = true,
- };
- } else {
- return cpp::fail(Error::kMalformedData);
- }
- }
-
- size_t samples_written = 0;
- if (current_sample_) {
- while (*current_sample_ < flac_->frame.header.block_size) {
- if (samples_written + flac_->frame.header.channels >= output.size()) {
- // We can't fit the next full PCM frame into the buffer.
- return OutputInfo{.samples_written = samples_written,
- .is_stream_finished = false};
- }
-
- for (int channel = 0; channel < flac_->frame.header.channels; channel++) {
- output[samples_written++] =
- sample::FromSigned(samples_by_channel_[channel][*current_sample_],
- flac_->frame.header.bps);
- }
- (*current_sample_)++;
- }
- }
-
- current_sample_.reset();
- return OutputInfo{.samples_written = samples_written,
- .is_stream_finished = samples_written == 0 && is_eof};
-}
-
-auto MiniFlacDecoder::SeekTo(size_t target) -> cpp::result<void, Error> {
- return {};
-}
-
-} // namespace codecs