1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/*
* 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,
};
if (input->Size() && format.total_samples) {
double sample_size = *(input->Size()) * 8.0 / *(format.total_samples);
format.bitrate_kbps = static_cast<uint32_t>(
flac_->sampleRate * flac_->channels * sample_size / 1024);
}
return format;
}
auto DrFlacDecoder::DecodeTo(std::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};
}
} // namespace codecs
|