Use drflac instead of miniflac

This one is fast as hell! Does seeking really good too. Thank u Doctor
Flac.
custom
jacqueline 1 year ago
parent 77145e56f4
commit d41f9f7033
  1. 2
      lib/drflac/CMakeLists.txt
  2. 4
      lib/drflac/dr_flac.c
  3. 12536
      lib/drflac/dr_flac.h
  4. 5
      lib/miniflac/miniflac.c
  5. 6092
      lib/miniflac/miniflac.h
  6. 3
      lua/licenses.lua
  7. 6
      src/audio/audio_converter.cpp
  8. 4
      src/codecs/CMakeLists.txt
  9. 4
      src/codecs/codec.cpp
  10. 119
      src/codecs/dr_flac.cpp
  11. 19
      src/codecs/include/dr_flac.hpp
  12. 213
      src/codecs/miniflac.cpp
  13. 2
      tools/cmake/common.cmake

@ -1,5 +1,5 @@
# Copyright 2023 jacqueline <me@jacqueline.id.au>
#
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(SRCS miniflac.c INCLUDE_DIRS .)
idf_component_register(SRCS dr_flac.c INCLUDE_DIRS .)
target_compile_options("${COMPONENT_LIB}" PRIVATE -Ofast)

@ -0,0 +1,4 @@
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
#define DR_FLAC_NO_SIMD
#include "dr_flac.h"

File diff suppressed because it is too large Load Diff

@ -1,5 +0,0 @@
#define MINIFLAC_IMPLEMENTATION
#define MINIFLAC_API
#define MINIFLAC_PRIVATE static inline
#include "miniflac.h"

File diff suppressed because it is too large Load Diff

@ -149,9 +149,6 @@ return function()
library("MillerShuffle", "Apache 2.0", function()
apache("Copyright 2022 Ronald Ross Miller")
end)
library("miniflac", "BSD", function()
bsd("Copyright (C) 2022 John Regan <john@jrjrtech.com>")
end)
library("ogg", "BSD", function()
xiphbsd("Copyright (c) 2002, Xiph.org Foundation")
end)

@ -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));
}
}

@ -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})

@ -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:

@ -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

@ -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

@ -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

@ -28,7 +28,7 @@ list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/lua-term")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/luavgl")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/lvgl")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/millershuffle")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/miniflac")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/drflac")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/ogg")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/opusfile")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{PROJ_PATH}/lib/result")

Loading…
Cancel
Save