From 62f6179abe24339c2e5b7350528afbcad4c52067 Mon Sep 17 00:00:00 2001 From: ailurux Date: Thu, 15 Feb 2024 16:12:07 +1100 Subject: Added offset for track seeking, wav impl. only rn --- src/codecs/include/codec.hpp | 2 +- src/codecs/include/mad.hpp | 2 +- src/codecs/include/miniflac.hpp | 2 +- src/codecs/include/opus.hpp | 2 +- src/codecs/include/vorbis.hpp | 2 +- src/codecs/include/wav.hpp | 2 +- src/codecs/mad.cpp | 2 +- src/codecs/miniflac.cpp | 2 +- src/codecs/opus.cpp | 2 +- src/codecs/vorbis.cpp | 2 +- src/codecs/wav.cpp | 6 ++++-- 11 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src/codecs') diff --git a/src/codecs/include/codec.hpp b/src/codecs/include/codec.hpp index 8aa391b6..fb1ec771 100644 --- a/src/codecs/include/codec.hpp +++ b/src/codecs/include/codec.hpp @@ -117,7 +117,7 @@ class ICodec { * Decodes metadata or headers from the given input stream, and returns the * format for the samples that will be decoded from it. */ - virtual auto OpenStream(std::shared_ptr input) + virtual auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result = 0; struct OutputInfo { diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp index 813aa86d..35e3284d 100644 --- a/src/codecs/include/mad.hpp +++ b/src/codecs/include/mad.hpp @@ -26,7 +26,7 @@ class MadMp3Decoder : public ICodec { MadMp3Decoder(); ~MadMp3Decoder(); - auto OpenStream(std::shared_ptr input) + auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result override; auto DecodeTo(cpp::span destination) diff --git a/src/codecs/include/miniflac.hpp b/src/codecs/include/miniflac.hpp index d57b08a3..d1daca2f 100644 --- a/src/codecs/include/miniflac.hpp +++ b/src/codecs/include/miniflac.hpp @@ -28,7 +28,7 @@ class MiniFlacDecoder : public ICodec { MiniFlacDecoder(); ~MiniFlacDecoder(); - auto OpenStream(std::shared_ptr input) + auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result override; auto DecodeTo(cpp::span destination) diff --git a/src/codecs/include/opus.hpp b/src/codecs/include/opus.hpp index 45b1b07a..1431fa54 100644 --- a/src/codecs/include/opus.hpp +++ b/src/codecs/include/opus.hpp @@ -26,7 +26,7 @@ class XiphOpusDecoder : public ICodec { XiphOpusDecoder(); ~XiphOpusDecoder(); - auto OpenStream(std::shared_ptr input) + auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result override; auto DecodeTo(cpp::span destination) diff --git a/src/codecs/include/vorbis.hpp b/src/codecs/include/vorbis.hpp index 2f93c37e..b32ef8d5 100644 --- a/src/codecs/include/vorbis.hpp +++ b/src/codecs/include/vorbis.hpp @@ -28,7 +28,7 @@ class TremorVorbisDecoder : public ICodec { TremorVorbisDecoder(); ~TremorVorbisDecoder(); - auto OpenStream(std::shared_ptr input) + auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result override; auto DecodeTo(cpp::span destination) diff --git a/src/codecs/include/wav.hpp b/src/codecs/include/wav.hpp index 896976dd..e884a9bb 100644 --- a/src/codecs/include/wav.hpp +++ b/src/codecs/include/wav.hpp @@ -31,7 +31,7 @@ class WavDecoder : public ICodec { WavDecoder(); ~WavDecoder(); - auto OpenStream(std::shared_ptr input) + auto OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result override; auto DecodeTo(cpp::span destination) diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index f36636a1..0617295f 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -58,7 +58,7 @@ auto MadMp3Decoder::GetBytesUsed() -> std::size_t { } } -auto MadMp3Decoder::OpenStream(std::shared_ptr input) +auto MadMp3Decoder::OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result { input_ = input; diff --git a/src/codecs/miniflac.cpp b/src/codecs/miniflac.cpp index ace73466..d15410fe 100644 --- a/src/codecs/miniflac.cpp +++ b/src/codecs/miniflac.cpp @@ -42,7 +42,7 @@ MiniFlacDecoder::~MiniFlacDecoder() { } } -auto MiniFlacDecoder::OpenStream(std::shared_ptr input) +auto MiniFlacDecoder::OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result { input_ = input; diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp index e4917a33..2f700510 100644 --- a/src/codecs/opus.cpp +++ b/src/codecs/opus.cpp @@ -78,7 +78,7 @@ XiphOpusDecoder::~XiphOpusDecoder() { } } -auto XiphOpusDecoder::OpenStream(std::shared_ptr input) +auto XiphOpusDecoder::OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result { input_ = input; diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp index 3b3798cb..66237c28 100644 --- a/src/codecs/vorbis.cpp +++ b/src/codecs/vorbis.cpp @@ -84,7 +84,7 @@ TremorVorbisDecoder::~TremorVorbisDecoder() { ov_clear(&vorbis_); } -auto TremorVorbisDecoder::OpenStream(std::shared_ptr input) +auto TremorVorbisDecoder::OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result { int res = ov_open_callbacks(input.get(), &vorbis_, NULL, 0, kCallbacks); if (res < 0) { diff --git a/src/codecs/wav.cpp b/src/codecs/wav.cpp index a67f3ff4..652f4f88 100644 --- a/src/codecs/wav.cpp +++ b/src/codecs/wav.cpp @@ -84,7 +84,7 @@ WavDecoder::WavDecoder() : input_(), buffer_() {} WavDecoder::~WavDecoder() {} -auto WavDecoder::OpenStream(std::shared_ptr input) +auto WavDecoder::OpenStream(std::shared_ptr input,uint32_t offset) -> cpp::result { input_ = input; @@ -199,8 +199,10 @@ auto WavDecoder::OpenStream(std::shared_ptr input) return cpp::fail(Error::kUnsupportedFormat); } + auto data_offset = offset * samples_per_second * bits_per_sample; + // Seek track to start of data - input->SeekTo(data_chunk_index + 8, IStream::SeekFrom::kStartOfStream); + input->SeekTo(data_chunk_index + 8 + data_offset, IStream::SeekFrom::kStartOfStream); output_format_ = {.num_channels = (uint8_t)num_channels_, .sample_rate_hz = samples_per_second, -- cgit v1.2.3 From a49d754da6c293445be16ac643d10849c01ea96b Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 16 Feb 2024 10:57:47 +1100 Subject: Seeking working with hardcoded event, wav only --- src/codecs/wav.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/codecs') diff --git a/src/codecs/wav.cpp b/src/codecs/wav.cpp index 652f4f88..22cbd49c 100644 --- a/src/codecs/wav.cpp +++ b/src/codecs/wav.cpp @@ -199,7 +199,7 @@ auto WavDecoder::OpenStream(std::shared_ptr input,uint32_t offset) return cpp::fail(Error::kUnsupportedFormat); } - auto data_offset = offset * samples_per_second * bits_per_sample; + int64_t data_offset = offset * samples_per_second * bytes_per_sample_; // Seek track to start of data input->SeekTo(data_chunk_index + 8 + data_offset, IStream::SeekFrom::kStartOfStream); @@ -218,6 +218,7 @@ auto WavDecoder::DecodeTo(cpp::span output) buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { size_t bytes_read = buf.size_bytes(); + ESP_LOGI(kTag, "Bytes read: %d", bytes_read); size_t frames_read = bytes_read / bytes_per_sample_ / output_format_.num_channels; @@ -243,6 +244,11 @@ auto WavDecoder::DecodeTo(cpp::span output) return samples_written * bytes_per_sample_; }); + ESP_LOGI(kTag, "Samples written %d", samples_written); + if (is_eof) { + ESP_LOGI(kTag, "EOF"); + } + return OutputInfo{.samples_written = samples_written, .is_stream_finished = samples_written == 0 && is_eof}; } -- cgit v1.2.3 From 665679b8854d34c13d8eb92167aa8a4691619d8b Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 16 Feb 2024 12:55:11 +1100 Subject: WIP: seeking in lua example --- src/codecs/wav.cpp | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/codecs') diff --git a/src/codecs/wav.cpp b/src/codecs/wav.cpp index 22cbd49c..5dd6f031 100644 --- a/src/codecs/wav.cpp +++ b/src/codecs/wav.cpp @@ -218,7 +218,6 @@ auto WavDecoder::DecodeTo(cpp::span output) buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { size_t bytes_read = buf.size_bytes(); - ESP_LOGI(kTag, "Bytes read: %d", bytes_read); size_t frames_read = bytes_read / bytes_per_sample_ / output_format_.num_channels; @@ -244,10 +243,6 @@ auto WavDecoder::DecodeTo(cpp::span output) return samples_written * bytes_per_sample_; }); - ESP_LOGI(kTag, "Samples written %d", samples_written); - if (is_eof) { - ESP_LOGI(kTag, "EOF"); - } return OutputInfo{.samples_written = samples_written, .is_stream_finished = samples_written == 0 && is_eof}; -- cgit v1.2.3 From aece1c6b58587aaeda0d1ce082df13e409f930f1 Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 16 Feb 2024 16:48:53 +1100 Subject: Opus seeking impl --- src/codecs/opus.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/codecs') diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp index 2f700510..ec587bc7 100644 --- a/src/codecs/opus.cpp +++ b/src/codecs/opus.cpp @@ -128,6 +128,10 @@ auto XiphOpusDecoder::OpenStream(std::shared_ptr input,uint32_t offset) length = l * 2; } + if (offset) { + SeekTo(offset * 48000); + } + return OutputFormat{ .num_channels = 2, .sample_rate_hz = 48000, -- cgit v1.2.3 From 0baad11b188e7bac6b968017716186d9da0e492f Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 16 Feb 2024 17:01:22 +1100 Subject: WIP: Vorbis seeking --- src/codecs/vorbis.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/codecs') diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp index 66237c28..65783dac 100644 --- a/src/codecs/vorbis.cpp +++ b/src/codecs/vorbis.cpp @@ -124,6 +124,10 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr input,uint32_t off length = l * info->channels; } + if (offset) { + ov_time_seek(&vorbis_, offset*1000); + } + return OutputFormat{ .num_channels = static_cast(info->channels), .sample_rate_hz = static_cast(info->rate), -- cgit v1.2.3 From 912060de1b31721103efc0ee321dfb1f80f8ccc4 Mon Sep 17 00:00:00 2001 From: ailurux Date: Fri, 16 Feb 2024 17:08:45 +1100 Subject: Vorbis seeking impl --- src/codecs/vorbis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/codecs') diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp index e51c6bca..ada92fb6 100644 --- a/src/codecs/vorbis.cpp +++ b/src/codecs/vorbis.cpp @@ -118,7 +118,7 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr input,uint32_t off } if (offset) { - ov_time_seek(&vorbis_, offset*1000); + ov_time_seek(vorbis_.get(), offset*1000); } return OutputFormat{ -- cgit v1.2.3 From f54347794f45261e0c0fde1104a70d1063c77305 Mon Sep 17 00:00:00 2001 From: ailurux Date: Thu, 22 Feb 2024 14:37:14 +1100 Subject: WIP: Flac not working-- coming back to this later --- src/codecs/CMakeLists.txt | 2 +- src/codecs/include/source_buffer.hpp | 1 + src/codecs/miniflac copy.cpp | 207 +++++++++++++++++++++++++++ src/codecs/miniflac.cpp | 130 ++++++++++++++++- src/codecs/miniflac.cpp.bak2 | 266 +++++++++++++++++++++++++++++++++++ src/codecs/source_buffer.cpp | 5 + 6 files changed, 609 insertions(+), 2 deletions(-) create mode 100644 src/codecs/miniflac copy.cpp create mode 100644 src/codecs/miniflac.cpp.bak2 (limited to 'src/codecs') diff --git a/src/codecs/CMakeLists.txt b/src/codecs/CMakeLists.txt index 1ef4bb2d..4b296c84 100644 --- a/src/codecs/CMakeLists.txt +++ b/src/codecs/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "codec.cpp" "mad.cpp" "miniflac.cpp" "opus.cpp" "vorbis.cpp" + SRCS "miniflac.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" diff --git a/src/codecs/include/source_buffer.hpp b/src/codecs/include/source_buffer.hpp index d0d7635a..7834834d 100644 --- a/src/codecs/include/source_buffer.hpp +++ b/src/codecs/include/source_buffer.hpp @@ -24,6 +24,7 @@ class SourceBuffer { auto Refill(IStream* src) -> bool; auto AddBytes(std::function)> writer) -> void; auto ConsumeBytes(std::function)> reader) -> void; + auto Empty() -> void; SourceBuffer(const SourceBuffer&) = delete; SourceBuffer& operator=(const SourceBuffer&) = delete; diff --git a/src/codecs/miniflac copy.cpp b/src/codecs/miniflac copy.cpp new file mode 100644 index 00000000..866cb49b --- /dev/null +++ b/src/codecs/miniflac copy.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "miniflac.hpp" + +#include +#include + +#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( + 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( + 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 input,uint32_t offset) + -> cpp::result { + input_ = input; + + MINIFLAC_RESULT res; + auto read_until_result = [&](auto fn) { + while (true) { + bool eof = buffer_.Refill(input_.get()); + buffer_.ConsumeBytes(fn); + if (res == MINIFLAC_CONTINUE && !eof) { + continue; + } + break; + } + }; + + uint32_t sample_rate = 0; + + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_sample_rate( + flac_.get(), reinterpret_cast(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 buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_channels( + flac_.get(), reinterpret_cast(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 buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_total_samples( + flac_.get(), reinterpret_cast(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) { + uint64_t samples_count = 0; + uint32_t offset_count = 0; + while (offset_count < offset) { + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_sync( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + + uint32_t frame_samplerate = flac_.get()->frame.header.sample_rate; + uint16_t frame_blocksize = flac_.get()->frame.header.block_size; + if (!frame_samplerate || !frame_blocksize) { + continue; + } + + samples_count += frame_blocksize; + offset_count = samples_count / sample_rate; + } + } + + OutputFormat format{ + .num_channels = static_cast(channels), + .sample_rate_hz = static_cast(sample_rate), + .total_samples = total_samples * channels, + }; + + return format; +} + +auto MiniFlacDecoder::DecodeTo(cpp::span output) + -> cpp::result { + 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 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(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 { + return {}; +} + +} // namespace codecs diff --git a/src/codecs/miniflac.cpp b/src/codecs/miniflac.cpp index d0b40f96..8ec12df5 100644 --- a/src/codecs/miniflac.cpp +++ b/src/codecs/miniflac.cpp @@ -111,6 +111,131 @@ auto MiniFlacDecoder::OpenStream(std::shared_ptr input,uint32_t offset) return cpp::fail(Error::kMalformedData); } + // Seeking + offset = 50; + if (offset) { + // TODO: This assumes a constant sample_rate + uint64_t target_sample = sample_rate * offset; + uint32_t num_seekpoints; + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_seektable_seekpoints( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &num_seekpoints); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + // TODO: Not having a seektable is not malformed + // but currently seeking will not work without it. + return cpp::fail(Error::kMalformedData); + } + // Loop over the seek table + ESP_LOGI(kTag, "Found seektable with %lu points", num_seekpoints); + uint64_t sample_number; + uint64_t sample_offset_bytes; + uint16_t num_samples_in_target; + for (uint32_t i = 0; i < num_seekpoints; i++) { + // Get sample number + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_seektable_sample_number( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &sample_number); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_seektable_sample_offset( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &sample_offset_bytes); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_seektable_samples( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &num_samples_in_target); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + + ESP_LOGI(kTag, "Seektable entry %lu", i); + + // Check if we want to seek to this seektable position? + if (sample_number + num_samples_in_target >= target_sample) { + ESP_LOGI(kTag, "Break on Seektable entry %lu", i); + break; + } + } + + // Seek forward to target_sample + if (sample_number > 0) { + ESP_LOGI(kTag, "total samples: %llu", total_samples); + ESP_LOGI(kTag, "TARGET SAMPLE: %llu", target_sample); + ESP_LOGI(kTag, "SAMPLE NUMBER: %llu", sample_number); + } + uint64_t byte_offset = sample_offset_bytes; + ESP_LOGI(kTag, "Byte offset is forward %llu bytes", byte_offset); + ESP_LOGI(kTag, "Decoder state pre: %d", flac_->state); + while(flac_.get()->state == MINIFLAC_METADATA) { + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_sync( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + ESP_LOGI(kTag, "Decoder error 1 %d", res); + } + } + ESP_LOGI(kTag, "Decoder state post: %d", flac_->state); + + + ESP_LOGI(kTag, "Going to skip forward %llu bytes", byte_offset); + if (input_.get()->CanSeek()) { + ESP_LOGI(kTag, "Skipping forward %llu bytes", byte_offset); + buffer_.Empty(); + input_.get()->SeekTo(byte_offset, IStream::SeekFrom::kCurrentPosition); + ESP_LOGI(kTag, "Skipped %llu bytes", byte_offset); + } + + + ESP_LOGI(kTag, "Pre-refill"); + buffer_.Refill(input_.get()); + ESP_LOGI(kTag, "Post-refill"); + + + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_sync( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + ESP_LOGI(kTag, "Decoder error 1 %d", res); + } + ESP_LOGI(kTag, "JOB'S DONE"); + } + + ESP_LOGI(kTag, "Decoder state: %d", flac_->state); + ESP_LOGI(kTag, "Frame header state: %d", flac_->frame.header.state); + + // TODO: Sample number is not guaranteed, could be block index. + ESP_LOGI(kTag, "Ended up... at sample %llu", flac_->frame.header.sample_number); + ESP_LOGI(kTag, "and block index: %lu", flac_->frame.header.frame_number); + ESP_LOGI(kTag, "total samples: %llu", total_samples); + + OutputFormat format{ .num_channels = static_cast(channels), .sample_rate_hz = static_cast(sample_rate), @@ -128,6 +253,7 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) MINIFLAC_RESULT res = MINIFLAC_CONTINUE; while (res == MINIFLAC_CONTINUE && !is_eof) { is_eof = buffer_.Refill(input_.get()); + ESP_LOGI(kTag, "EOF? %s", is_eof ? "true" : "false"); buffer_.ConsumeBytes([&](cpp::span 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. @@ -147,7 +273,8 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) .is_stream_finished = true, }; } else { - return cpp::fail(Error::kMalformedData); + ESP_LOGI(kTag, "Failed: decoder result: %d", res); + // return cpp::fail(Error::kMalformedData); } } @@ -170,6 +297,7 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) } current_sample_.reset(); + ESP_LOGI(kTag, "Samples written %lu", (uint32_t)samples_written); return OutputInfo{.samples_written = samples_written, .is_stream_finished = samples_written == 0 && is_eof}; } diff --git a/src/codecs/miniflac.cpp.bak2 b/src/codecs/miniflac.cpp.bak2 new file mode 100644 index 00000000..843c3003 --- /dev/null +++ b/src/codecs/miniflac.cpp.bak2 @@ -0,0 +1,266 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "miniflac.hpp" + +#include +#include + +#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( + 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( + 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 input,uint32_t offset) + -> cpp::result { + input_ = input; + + MINIFLAC_RESULT res; + bool is_eof; + auto read_until_result = [&](auto fn) { + while (true) { + is_eof = buffer_.Refill(input_.get()); + buffer_.ConsumeBytes(fn); + if (res == MINIFLAC_CONTINUE && !eof) { + continue; + } + break; + } + }; + + uint16_t min_block_size = 0; // In samples + + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_min_block_size( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &min_block_size); + return bytes_used; + }); + + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + + uint16_t max_block_size = 0; // In samples + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_min_block_size( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used, &max_block_size); + return bytes_used; + }); + + if (res != MINIFLAC_OK) { + return cpp::fail(Error::kMalformedData); + } + + ESP_LOGI(kTag, "Blocksize min: %u max %u", min_block_size, max_block_size); + + uint32_t sample_rate = 0; + + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_sample_rate( + flac_.get(), reinterpret_cast(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 buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_channels( + flac_.get(), reinterpret_cast(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 buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_streaminfo_total_samples( + flac_.get(), reinterpret_cast(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); + } + + // Seeking + offset = 0; + if (offset) { + // Super dumb approach, but lets try it first + // Go to the first frame + while(flac_.get()->state == MINIFLAC_METADATA) { + read_until_result([&](cpp::span buf) -> size_t { + uint32_t bytes_used = 0; + res = miniflac_sync( + flac_.get(), reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used); + return bytes_used; + }); + if (res != MINIFLAC_OK) { + ESP_LOGI(kTag, "IT HAPPENED"); + } + } + ESP_LOGI(kTag, "Flac state: %d", flac_->state); + + // Naive approach + uint64_t byte_offset = offset; // TODO + + ESP_LOGI(kTag, "Going to skip forward %llu bytes", byte_offset); + if (input_.get()->CanSeek()) { + ESP_LOGI(kTag, "Skipping forward %llu bytes", byte_offset); + buffer_.Empty(); + input_.get()->SeekTo(byte_offset, IStream::SeekFrom::kCurrentPosition); + } + // buffer_.Refill(input_.get()); + + // // Sync again + // read_until_result([&](cpp::span buf) -> size_t { + // uint32_t bytes_used = 0; + // res = miniflac_sync( + // flac_.get(), reinterpret_cast(buf.data()), + // buf.size_bytes(), &bytes_used); + // return bytes_used; + // }); + // if (res != MINIFLAC_OK) { + // ESP_LOGI(kTag, "IT HAPPENED HERE! %d", res); + // } + + // ESP_LOGI(kTag, "Decoder state: %d", flac_->state); + // ESP_LOGI(kTag, "Frame header state: %d", flac_->frame.header.state); + + // // TODO: Sample number is not guaranteed, could be block index. + // ESP_LOGI(kTag, "Ended up... at sample %llu", flac_->frame.header.sample_number); + // ESP_LOGI(kTag, "and block index: %lu", flac_->frame.header.frame_number); + // ESP_LOGI(kTag, "total samples: %llu", total_samples); + } + + + OutputFormat format{ + .num_channels = static_cast(channels), + .sample_rate_hz = static_cast(sample_rate), + .total_samples = total_samples * channels, + }; + + return format; +} + +auto MiniFlacDecoder::DecodeTo(cpp::span output) + -> cpp::result { + bool is_eof = false; + + if (!current_sample_) { + MINIFLAC_RESULT res = MINIFLAC_CONTINUE; + while (res == MINIFLAC_CONTINUE && !is_eof) { + is_eof = buffer_.Refill(input_.get()); + ESP_LOGI(kTag, "EOF? %s", is_eof ? "true" : "false"); + buffer_.ConsumeBytes([&](cpp::span 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(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 { + ESP_LOGI(kTag, "Failed: decoder result: %d", res); + 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(); + ESP_LOGI(kTag, "Samples written %lu", (uint32_t)samples_written); + return OutputInfo{.samples_written = samples_written, + .is_stream_finished = samples_written == 0 && is_eof}; +} + +auto MiniFlacDecoder::SeekTo(size_t target) -> cpp::result { + return {}; +} + +} // namespace codecs diff --git a/src/codecs/source_buffer.cpp b/src/codecs/source_buffer.cpp index 3b455038..0a986bc3 100644 --- a/src/codecs/source_buffer.cpp +++ b/src/codecs/source_buffer.cpp @@ -74,4 +74,9 @@ auto SourceBuffer::ConsumeBytes( } } +auto SourceBuffer::Empty() -> void { + offset_of_bytes_ = 0; + bytes_in_buffer_ = 0; +} + } // namespace codecs -- cgit v1.2.3 From 77145e56f4062cd060ee7fa0af9ad1a2e46df5b1 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 28 Feb 2024 21:21:23 +1100 Subject: basic working flac and mp3 seeking flac impl is fairly slow as it doesn't use the seek tables; for some reason miniflac seems to get *really* upset if you seek the stream. --- src/codecs/mad.cpp | 46 ++++++++++++++-- src/codecs/miniflac.cpp | 140 ++++++++---------------------------------------- 2 files changed, 65 insertions(+), 121 deletions(-) (limited to 'src/codecs') diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index 0617295f..b11821f0 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -44,6 +44,7 @@ MadMp3Decoder::MadMp3Decoder() mad_frame_init(frame_.get()); mad_synth_init(synth_.get()); } + MadMp3Decoder::~MadMp3Decoder() { mad_stream_finish(stream_.get()); mad_frame_finish(frame_.get()); @@ -58,7 +59,7 @@ auto MadMp3Decoder::GetBytesUsed() -> std::size_t { } } -auto MadMp3Decoder::OpenStream(std::shared_ptr input,uint32_t offset) +auto MadMp3Decoder::OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result { input_ = input; @@ -113,6 +114,45 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr input,uint32_t offset) auto cbr_length = input->Size().value() / (header.bitrate / 8); output.total_samples = cbr_length * output.sample_rate_hz * channels; } + + mad_timer_t timer; + mad_timer_reset(&timer); + bool need_refill = false; + bool seek_err = false; + + while (mad_timer_count(timer, MAD_UNITS_SECONDS) < offset) { + if (seek_err) { + return cpp::fail(ICodec::Error::kMalformedData); + } + + if (need_refill && buffer_.Refill(input_.get())) { + return cpp::fail(ICodec::Error::kMalformedData); + } + need_refill = false; + + buffer_.ConsumeBytes([&](cpp::span buf) -> size_t { + mad_stream_buffer(stream_.get(), + reinterpret_cast(buf.data()), + buf.size()); + + while (mad_header_decode(&header, stream_.get()) < 0) { + if (MAD_RECOVERABLE(stream_->error)) { + continue; + } + if (stream_->error == MAD_ERROR_BUFLEN) { + need_refill = true; + return GetBytesUsed(); + } + // The error is unrecoverable. Give up. + seek_err = true; + return 0; + } + + mad_timer_add(&timer, header.duration); + return GetBytesUsed(); + }); + } + return output; } @@ -222,8 +262,8 @@ auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void { } /* - * Implementation taken from SDL_mixer and modified. Original is zlib-licensed, - * copyright (C) 1997-2022 Sam Lantinga + * Implementation taken from SDL_mixer and modified. Original is + * zlib-licensed, copyright (C) 1997-2022 Sam Lantinga */ auto MadMp3Decoder::GetVbrLength(const mad_header& header) -> std::optional { diff --git a/src/codecs/miniflac.cpp b/src/codecs/miniflac.cpp index 8ec12df5..45e063c7 100644 --- a/src/codecs/miniflac.cpp +++ b/src/codecs/miniflac.cpp @@ -5,6 +5,7 @@ */ #include "miniflac.hpp" +#include #include #include @@ -49,16 +50,16 @@ MiniFlacDecoder::~MiniFlacDecoder() { } } -auto MiniFlacDecoder::OpenStream(std::shared_ptr input,uint32_t offset) +auto MiniFlacDecoder::OpenStream(std::shared_ptr input, + uint32_t offset) -> cpp::result { input_ = input; MINIFLAC_RESULT res; auto read_until_result = [&](auto fn) { while (true) { - bool eof = buffer_.Refill(input_.get()); buffer_.ConsumeBytes(fn); - if (res == MINIFLAC_CONTINUE && !eof) { + if (res == MINIFLAC_CONTINUE && !buffer_.Refill(input_.get())) { continue; } break; @@ -111,137 +112,43 @@ auto MiniFlacDecoder::OpenStream(std::shared_ptr input,uint32_t offset) return cpp::fail(Error::kMalformedData); } - // Seeking - offset = 50; if (offset) { - // TODO: This assumes a constant sample_rate - uint64_t target_sample = sample_rate * offset; - uint32_t num_seekpoints; - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_seektable_seekpoints( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &num_seekpoints); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - // TODO: Not having a seektable is not malformed - // but currently seeking will not work without it. - return cpp::fail(Error::kMalformedData); - } - // Loop over the seek table - ESP_LOGI(kTag, "Found seektable with %lu points", num_seekpoints); - uint64_t sample_number; - uint64_t sample_offset_bytes; - uint16_t num_samples_in_target; - for (uint32_t i = 0; i < num_seekpoints; i++) { - // Get sample number - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_seektable_sample_number( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &sample_number); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - return cpp::fail(Error::kMalformedData); - } - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_seektable_sample_offset( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &sample_offset_bytes); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - return cpp::fail(Error::kMalformedData); - } + // 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 buf) -> size_t { uint32_t bytes_used = 0; - res = miniflac_seektable_samples( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &num_samples_in_target); + res = miniflac_sync(flac_.get(), + reinterpret_cast(buf.data()), + buf.size_bytes(), &bytes_used); return bytes_used; }); + if (res != MINIFLAC_OK) { return cpp::fail(Error::kMalformedData); } - ESP_LOGI(kTag, "Seektable entry %lu", i); + if (!miniflac_is_frame(flac_.get())) { + continue; + } - // Check if we want to seek to this seektable position? - if (sample_number + num_samples_in_target >= target_sample) { - ESP_LOGI(kTag, "Break on Seektable entry %lu", i); + uint64_t samples_in_frame = miniflac_frame_block_size(flac_.get()); + if (samples_in_frame <= target_sample) { + target_sample -= samples_in_frame; + } else { break; } } - - // Seek forward to target_sample - if (sample_number > 0) { - ESP_LOGI(kTag, "total samples: %llu", total_samples); - ESP_LOGI(kTag, "TARGET SAMPLE: %llu", target_sample); - ESP_LOGI(kTag, "SAMPLE NUMBER: %llu", sample_number); - } - uint64_t byte_offset = sample_offset_bytes; - ESP_LOGI(kTag, "Byte offset is forward %llu bytes", byte_offset); - ESP_LOGI(kTag, "Decoder state pre: %d", flac_->state); - while(flac_.get()->state == MINIFLAC_METADATA) { - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_sync( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - ESP_LOGI(kTag, "Decoder error 1 %d", res); - } - } - ESP_LOGI(kTag, "Decoder state post: %d", flac_->state); - - - ESP_LOGI(kTag, "Going to skip forward %llu bytes", byte_offset); - if (input_.get()->CanSeek()) { - ESP_LOGI(kTag, "Skipping forward %llu bytes", byte_offset); - buffer_.Empty(); - input_.get()->SeekTo(byte_offset, IStream::SeekFrom::kCurrentPosition); - ESP_LOGI(kTag, "Skipped %llu bytes", byte_offset); - } - - - ESP_LOGI(kTag, "Pre-refill"); - buffer_.Refill(input_.get()); - ESP_LOGI(kTag, "Post-refill"); - - - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_sync( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - ESP_LOGI(kTag, "Decoder error 1 %d", res); - } - ESP_LOGI(kTag, "JOB'S DONE"); } - ESP_LOGI(kTag, "Decoder state: %d", flac_->state); - ESP_LOGI(kTag, "Frame header state: %d", flac_->frame.header.state); - - // TODO: Sample number is not guaranteed, could be block index. - ESP_LOGI(kTag, "Ended up... at sample %llu", flac_->frame.header.sample_number); - ESP_LOGI(kTag, "and block index: %lu", flac_->frame.header.frame_number); - ESP_LOGI(kTag, "total samples: %llu", total_samples); - - OutputFormat format{ .num_channels = static_cast(channels), .sample_rate_hz = static_cast(sample_rate), .total_samples = total_samples * channels, }; - return format; } @@ -253,7 +160,6 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) MINIFLAC_RESULT res = MINIFLAC_CONTINUE; while (res == MINIFLAC_CONTINUE && !is_eof) { is_eof = buffer_.Refill(input_.get()); - ESP_LOGI(kTag, "EOF? %s", is_eof ? "true" : "false"); buffer_.ConsumeBytes([&](cpp::span 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. @@ -273,8 +179,7 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) .is_stream_finished = true, }; } else { - ESP_LOGI(kTag, "Failed: decoder result: %d", res); - // return cpp::fail(Error::kMalformedData); + return cpp::fail(Error::kMalformedData); } } @@ -297,7 +202,6 @@ auto MiniFlacDecoder::DecodeTo(cpp::span output) } current_sample_.reset(); - ESP_LOGI(kTag, "Samples written %lu", (uint32_t)samples_written); return OutputInfo{.samples_written = samples_written, .is_stream_finished = samples_written == 0 && is_eof}; } -- cgit v1.2.3 From d41f9f703375171d5766840c9edec32ff47bb25d Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 29 Feb 2024 12:08:12 +1100 Subject: Use drflac instead of miniflac This one is fast as hell! Does seeking really good too. Thank u Doctor Flac. --- src/codecs/CMakeLists.txt | 4 +- src/codecs/codec.cpp | 4 +- src/codecs/dr_flac.cpp | 119 ++++++++++++++++++++++ src/codecs/include/dr_flac.hpp | 46 +++++++++ src/codecs/include/miniflac.hpp | 51 ---------- src/codecs/miniflac.cpp | 213 ---------------------------------------- 6 files changed, 169 insertions(+), 268 deletions(-) create mode 100644 src/codecs/dr_flac.cpp create mode 100644 src/codecs/include/dr_flac.hpp delete mode 100644 src/codecs/include/miniflac.hpp delete mode 100644 src/codecs/miniflac.cpp (limited to 'src/codecs') 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 #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 { 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 + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "dr_flac.hpp" + +#include +#include + +#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(pUserData); + ssize_t res = + stream->Read({reinterpret_cast(pBufferOut), bytesToRead}); + return res < 0 ? 0 : res; +} + +static drflac_bool32 seekProc(void* pUserData, + int offset, + drflac_seek_origin origin) { + IStream* stream = reinterpret_cast(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 input, uint32_t offset) + -> cpp::result { + 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(flac_->channels), + .sample_rate_hz = static_cast(flac_->sampleRate), + .total_samples = flac_->totalPCMFrameCount * flac_->channels, + }; + return format; +} + +auto DrFlacDecoder::DecodeTo(cpp::span output) + -> cpp::result { + 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(frames_written * flac_->channels), + .is_stream_finished = frames_written < frames_to_read}; +} + +auto DrFlacDecoder::SeekTo(size_t target) -> cpp::result { + return {}; +} + +} // namespace codecs diff --git a/src/codecs/include/dr_flac.hpp b/src/codecs/include/dr_flac.hpp new file mode 100644 index 00000000..8dcfdaf5 --- /dev/null +++ b/src/codecs/include/dr_flac.hpp @@ -0,0 +1,46 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "dr_flac.h" +#include "sample.hpp" +#include "source_buffer.hpp" +#include "span.hpp" + +#include "codec.hpp" + +namespace codecs { + +class DrFlacDecoder : public ICodec { + public: + DrFlacDecoder(); + ~DrFlacDecoder(); + + auto OpenStream(std::shared_ptr input,uint32_t offset) + -> cpp::result override; + + auto DecodeTo(cpp::span destination) + -> cpp::result override; + + auto SeekTo(std::size_t target_sample) -> cpp::result override; + + DrFlacDecoder(const DrFlacDecoder&) = delete; + DrFlacDecoder& operator=(const DrFlacDecoder&) = delete; + + private: + std::shared_ptr input_; + drflac *flac_; +}; + +} // namespace codecs diff --git a/src/codecs/include/miniflac.hpp b/src/codecs/include/miniflac.hpp deleted file mode 100644 index d1daca2f..00000000 --- a/src/codecs/include/miniflac.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "miniflac.h" -#include "sample.hpp" -#include "source_buffer.hpp" -#include "span.hpp" - -#include "codec.hpp" - -namespace codecs { - -class MiniFlacDecoder : public ICodec { - public: - MiniFlacDecoder(); - ~MiniFlacDecoder(); - - auto OpenStream(std::shared_ptr input,uint32_t offset) - -> cpp::result override; - - auto DecodeTo(cpp::span destination) - -> cpp::result override; - - auto SeekTo(std::size_t target_sample) -> cpp::result override; - - MiniFlacDecoder(const MiniFlacDecoder&) = delete; - MiniFlacDecoder& operator=(const MiniFlacDecoder&) = delete; - - private: - std::shared_ptr input_; - SourceBuffer buffer_; - - std::unique_ptr flac_; - std::array samples_by_channel_; - std::optional current_sample_; -}; - -} // 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 - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "miniflac.hpp" -#include - -#include -#include - -#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( - 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( - 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 input, - uint32_t offset) - -> cpp::result { - 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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_sample_rate( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_channels( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_total_samples( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_sync(flac_.get(), - reinterpret_cast(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(channels), - .sample_rate_hz = static_cast(sample_rate), - .total_samples = total_samples * channels, - }; - return format; -} - -auto MiniFlacDecoder::DecodeTo(cpp::span output) - -> cpp::result { - 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 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(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 { - return {}; -} - -} // namespace codecs -- cgit v1.2.3 From e7e6c70fb31d33ae1e79f9841f5b6fe227f6ebf3 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Thu, 29 Feb 2024 12:18:17 +1100 Subject: Remove unused 'SeekTo' method on codecs --- src/codecs/dr_flac.cpp | 4 - src/codecs/include/codec.hpp | 2 - src/codecs/include/dr_flac.hpp | 2 - src/codecs/include/mad.hpp | 2 - src/codecs/include/opus.hpp | 2 - src/codecs/include/vorbis.hpp | 2 - src/codecs/include/wav.hpp | 2 - src/codecs/mad.cpp | 5 - src/codecs/miniflac copy.cpp | 207 -------------------------------- src/codecs/miniflac.cpp.bak2 | 266 ----------------------------------------- src/codecs/opus.cpp | 14 +-- src/codecs/vorbis.cpp | 14 +-- src/codecs/wav.cpp | 4 - 13 files changed, 8 insertions(+), 518 deletions(-) delete mode 100644 src/codecs/miniflac copy.cpp delete mode 100644 src/codecs/miniflac.cpp.bak2 (limited to 'src/codecs') diff --git a/src/codecs/dr_flac.cpp b/src/codecs/dr_flac.cpp index cacf7a6e..2f9acf8c 100644 --- a/src/codecs/dr_flac.cpp +++ b/src/codecs/dr_flac.cpp @@ -112,8 +112,4 @@ auto DrFlacDecoder::DecodeTo(cpp::span output) .is_stream_finished = frames_written < frames_to_read}; } -auto DrFlacDecoder::SeekTo(size_t target) -> cpp::result { - return {}; -} - } // namespace codecs diff --git a/src/codecs/include/codec.hpp b/src/codecs/include/codec.hpp index fb1ec771..e48e3c58 100644 --- a/src/codecs/include/codec.hpp +++ b/src/codecs/include/codec.hpp @@ -130,8 +130,6 @@ class ICodec { */ virtual auto DecodeTo(cpp::span destination) -> cpp::result = 0; - - virtual auto SeekTo(size_t target_sample) -> cpp::result = 0; }; auto CreateCodecForType(StreamType type) -> std::optional; diff --git a/src/codecs/include/dr_flac.hpp b/src/codecs/include/dr_flac.hpp index 8dcfdaf5..547876f4 100644 --- a/src/codecs/include/dr_flac.hpp +++ b/src/codecs/include/dr_flac.hpp @@ -33,8 +33,6 @@ class DrFlacDecoder : public ICodec { auto DecodeTo(cpp::span destination) -> cpp::result override; - auto SeekTo(std::size_t target_sample) -> cpp::result override; - DrFlacDecoder(const DrFlacDecoder&) = delete; DrFlacDecoder& operator=(const DrFlacDecoder&) = delete; diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp index 35e3284d..ead0b2a2 100644 --- a/src/codecs/include/mad.hpp +++ b/src/codecs/include/mad.hpp @@ -32,8 +32,6 @@ class MadMp3Decoder : public ICodec { auto DecodeTo(cpp::span destination) -> cpp::result override; - auto SeekTo(std::size_t target_sample) -> cpp::result override; - MadMp3Decoder(const MadMp3Decoder&) = delete; MadMp3Decoder& operator=(const MadMp3Decoder&) = delete; diff --git a/src/codecs/include/opus.hpp b/src/codecs/include/opus.hpp index 1431fa54..de2f7131 100644 --- a/src/codecs/include/opus.hpp +++ b/src/codecs/include/opus.hpp @@ -32,8 +32,6 @@ class XiphOpusDecoder : public ICodec { auto DecodeTo(cpp::span destination) -> cpp::result override; - auto SeekTo(std::size_t target_sample) -> cpp::result override; - XiphOpusDecoder(const XiphOpusDecoder&) = delete; XiphOpusDecoder& operator=(const XiphOpusDecoder&) = delete; diff --git a/src/codecs/include/vorbis.hpp b/src/codecs/include/vorbis.hpp index 94868c1a..3cf0f9ce 100644 --- a/src/codecs/include/vorbis.hpp +++ b/src/codecs/include/vorbis.hpp @@ -32,8 +32,6 @@ class TremorVorbisDecoder : public ICodec { auto DecodeTo(cpp::span destination) -> cpp::result override; - auto SeekTo(std::size_t target_sample) -> cpp::result override; - TremorVorbisDecoder(const TremorVorbisDecoder&) = delete; TremorVorbisDecoder& operator=(const TremorVorbisDecoder&) = delete; diff --git a/src/codecs/include/wav.hpp b/src/codecs/include/wav.hpp index e884a9bb..40138968 100644 --- a/src/codecs/include/wav.hpp +++ b/src/codecs/include/wav.hpp @@ -37,8 +37,6 @@ class WavDecoder : public ICodec { auto DecodeTo(cpp::span destination) -> cpp::result override; - auto SeekTo(std::size_t target_sample) -> cpp::result override; - WavDecoder(const WavDecoder&) = delete; WavDecoder& operator=(const WavDecoder&) = delete; diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index b11821f0..e44e9922 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -230,11 +230,6 @@ auto MadMp3Decoder::DecodeTo(cpp::span output) .is_stream_finished = is_eos_}; } -auto MadMp3Decoder::SeekTo(std::size_t target_sample) - -> cpp::result { - return {}; -} - auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void { // First check that the file actually does start with ID3 tags. std::array magic_buf{}; diff --git a/src/codecs/miniflac copy.cpp b/src/codecs/miniflac copy.cpp deleted file mode 100644 index 866cb49b..00000000 --- a/src/codecs/miniflac copy.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "miniflac.hpp" - -#include -#include - -#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( - 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( - 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 input,uint32_t offset) - -> cpp::result { - input_ = input; - - MINIFLAC_RESULT res; - auto read_until_result = [&](auto fn) { - while (true) { - bool eof = buffer_.Refill(input_.get()); - buffer_.ConsumeBytes(fn); - if (res == MINIFLAC_CONTINUE && !eof) { - continue; - } - break; - } - }; - - uint32_t sample_rate = 0; - - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_sample_rate( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_channels( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_total_samples( - flac_.get(), reinterpret_cast(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) { - uint64_t samples_count = 0; - uint32_t offset_count = 0; - while (offset_count < offset) { - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_sync( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - return cpp::fail(Error::kMalformedData); - } - - uint32_t frame_samplerate = flac_.get()->frame.header.sample_rate; - uint16_t frame_blocksize = flac_.get()->frame.header.block_size; - if (!frame_samplerate || !frame_blocksize) { - continue; - } - - samples_count += frame_blocksize; - offset_count = samples_count / sample_rate; - } - } - - OutputFormat format{ - .num_channels = static_cast(channels), - .sample_rate_hz = static_cast(sample_rate), - .total_samples = total_samples * channels, - }; - - return format; -} - -auto MiniFlacDecoder::DecodeTo(cpp::span output) - -> cpp::result { - 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 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(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 { - return {}; -} - -} // namespace codecs diff --git a/src/codecs/miniflac.cpp.bak2 b/src/codecs/miniflac.cpp.bak2 deleted file mode 100644 index 843c3003..00000000 --- a/src/codecs/miniflac.cpp.bak2 +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2023 jacqueline - * - * SPDX-License-Identifier: GPL-3.0-only - */ - -#include "miniflac.hpp" - -#include -#include - -#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( - 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( - 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 input,uint32_t offset) - -> cpp::result { - input_ = input; - - MINIFLAC_RESULT res; - bool is_eof; - auto read_until_result = [&](auto fn) { - while (true) { - is_eof = buffer_.Refill(input_.get()); - buffer_.ConsumeBytes(fn); - if (res == MINIFLAC_CONTINUE && !eof) { - continue; - } - break; - } - }; - - uint16_t min_block_size = 0; // In samples - - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_min_block_size( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &min_block_size); - return bytes_used; - }); - - if (res != MINIFLAC_OK) { - return cpp::fail(Error::kMalformedData); - } - - uint16_t max_block_size = 0; // In samples - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_min_block_size( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used, &max_block_size); - return bytes_used; - }); - - if (res != MINIFLAC_OK) { - return cpp::fail(Error::kMalformedData); - } - - ESP_LOGI(kTag, "Blocksize min: %u max %u", min_block_size, max_block_size); - - uint32_t sample_rate = 0; - - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_sample_rate( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_channels( - flac_.get(), reinterpret_cast(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 buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_streaminfo_total_samples( - flac_.get(), reinterpret_cast(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); - } - - // Seeking - offset = 0; - if (offset) { - // Super dumb approach, but lets try it first - // Go to the first frame - while(flac_.get()->state == MINIFLAC_METADATA) { - read_until_result([&](cpp::span buf) -> size_t { - uint32_t bytes_used = 0; - res = miniflac_sync( - flac_.get(), reinterpret_cast(buf.data()), - buf.size_bytes(), &bytes_used); - return bytes_used; - }); - if (res != MINIFLAC_OK) { - ESP_LOGI(kTag, "IT HAPPENED"); - } - } - ESP_LOGI(kTag, "Flac state: %d", flac_->state); - - // Naive approach - uint64_t byte_offset = offset; // TODO - - ESP_LOGI(kTag, "Going to skip forward %llu bytes", byte_offset); - if (input_.get()->CanSeek()) { - ESP_LOGI(kTag, "Skipping forward %llu bytes", byte_offset); - buffer_.Empty(); - input_.get()->SeekTo(byte_offset, IStream::SeekFrom::kCurrentPosition); - } - // buffer_.Refill(input_.get()); - - // // Sync again - // read_until_result([&](cpp::span buf) -> size_t { - // uint32_t bytes_used = 0; - // res = miniflac_sync( - // flac_.get(), reinterpret_cast(buf.data()), - // buf.size_bytes(), &bytes_used); - // return bytes_used; - // }); - // if (res != MINIFLAC_OK) { - // ESP_LOGI(kTag, "IT HAPPENED HERE! %d", res); - // } - - // ESP_LOGI(kTag, "Decoder state: %d", flac_->state); - // ESP_LOGI(kTag, "Frame header state: %d", flac_->frame.header.state); - - // // TODO: Sample number is not guaranteed, could be block index. - // ESP_LOGI(kTag, "Ended up... at sample %llu", flac_->frame.header.sample_number); - // ESP_LOGI(kTag, "and block index: %lu", flac_->frame.header.frame_number); - // ESP_LOGI(kTag, "total samples: %llu", total_samples); - } - - - OutputFormat format{ - .num_channels = static_cast(channels), - .sample_rate_hz = static_cast(sample_rate), - .total_samples = total_samples * channels, - }; - - return format; -} - -auto MiniFlacDecoder::DecodeTo(cpp::span output) - -> cpp::result { - bool is_eof = false; - - if (!current_sample_) { - MINIFLAC_RESULT res = MINIFLAC_CONTINUE; - while (res == MINIFLAC_CONTINUE && !is_eof) { - is_eof = buffer_.Refill(input_.get()); - ESP_LOGI(kTag, "EOF? %s", is_eof ? "true" : "false"); - buffer_.ConsumeBytes([&](cpp::span 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(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 { - ESP_LOGI(kTag, "Failed: decoder result: %d", res); - 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(); - ESP_LOGI(kTag, "Samples written %lu", (uint32_t)samples_written); - return OutputInfo{.samples_written = samples_written, - .is_stream_finished = samples_written == 0 && is_eof}; -} - -auto MiniFlacDecoder::SeekTo(size_t target) -> cpp::result { - return {}; -} - -} // namespace codecs diff --git a/src/codecs/opus.cpp b/src/codecs/opus.cpp index ec587bc7..a5220c4b 100644 --- a/src/codecs/opus.cpp +++ b/src/codecs/opus.cpp @@ -78,7 +78,8 @@ XiphOpusDecoder::~XiphOpusDecoder() { } } -auto XiphOpusDecoder::OpenStream(std::shared_ptr input,uint32_t offset) +auto XiphOpusDecoder::OpenStream(std::shared_ptr input, + uint32_t offset) -> cpp::result { input_ = input; @@ -128,8 +129,8 @@ auto XiphOpusDecoder::OpenStream(std::shared_ptr input,uint32_t offset) length = l * 2; } - if (offset) { - SeekTo(offset * 48000); + if (offset && op_pcm_seek(opus_, offset * 48000) != 0) { + return cpp::fail(Error::kInternalError); } return OutputFormat{ @@ -155,11 +156,4 @@ auto XiphOpusDecoder::DecodeTo(cpp::span output) }; } -auto XiphOpusDecoder::SeekTo(size_t target) -> cpp::result { - if (op_pcm_seek(opus_, target) != 0) { - return cpp::fail(Error::kInternalError); - } - return {}; -} - } // namespace codecs diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp index ada92fb6..9131451b 100644 --- a/src/codecs/vorbis.cpp +++ b/src/codecs/vorbis.cpp @@ -77,7 +77,8 @@ TremorVorbisDecoder::~TremorVorbisDecoder() { ov_clear(vorbis_.get()); } -auto TremorVorbisDecoder::OpenStream(std::shared_ptr input,uint32_t offset) +auto TremorVorbisDecoder::OpenStream(std::shared_ptr input, + uint32_t offset) -> cpp::result { int res = ov_open_callbacks(input.get(), vorbis_.get(), NULL, 0, kCallbacks); if (res < 0) { @@ -117,8 +118,8 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr input,uint32_t off length = l * info->channels; } - if (offset) { - ov_time_seek(vorbis_.get(), offset*1000); + if (offset && ov_time_seek(vorbis_.get(), offset * 1000) != 0) { + return cpp::fail(Error::kInternalError); } return OutputFormat{ @@ -149,11 +150,4 @@ auto TremorVorbisDecoder::DecodeTo(cpp::span output) }; } -auto TremorVorbisDecoder::SeekTo(size_t target) -> cpp::result { - if (ov_pcm_seek(vorbis_.get(), target) != 0) { - return cpp::fail(Error::kInternalError); - } - return {}; -} - } // namespace codecs diff --git a/src/codecs/wav.cpp b/src/codecs/wav.cpp index 5dd6f031..143a7a4b 100644 --- a/src/codecs/wav.cpp +++ b/src/codecs/wav.cpp @@ -248,10 +248,6 @@ auto WavDecoder::DecodeTo(cpp::span output) .is_stream_finished = samples_written == 0 && is_eof}; } -auto WavDecoder::SeekTo(size_t target) -> cpp::result { - return {}; -} - auto codecs::WavDecoder::GetFormat() const -> uint16_t { if (wave_format_ == kWaveFormatExtensible) { return subformat_; -- cgit v1.2.3