Skip to content

Commit a2407de

Browse files
committed
Foxen FLAC Decoder
1 parent 09f1538 commit a2407de

File tree

2 files changed

+268
-0
lines changed

2 files changed

+268
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @file streams-url_flac_foxen-i2s.ino
3+
* @author Phil Schatzmann
4+
* @brief Demo using the Foxen FLAC decoder
5+
* @version 0.1
6+
* @date 2025-01-09
7+
*
8+
* @copyright Copyright (c) 2022
9+
*
10+
* Warning: The WIFI speed is quite limited and the FLAC files are quite big. So there
11+
* is a big chance that the WIFI is just not fast enough. For my test I was downsampling
12+
* the file to 8000 samples per second!
13+
*/
14+
#include "AudioTools.h"
15+
#include "AudioTools/AudioCodecs/CodecFLACFoxen.h"
16+
#include "AudioTools/AudioLibs/AudioBoardStream.h"
17+
18+
const char* ssid = "ssid";
19+
const char* pwd = "password";
20+
URLStream url(ssid, pwd);
21+
AudioBoardStream i2s(AudioKitEs8388V1); // or replace with e.g. I2SStream i2s;
22+
23+
FLACFoxenDecoder flac(5*1024, 2);
24+
EncodedAudioStream dec(&i2s, &flac); // Decoding to i2s
25+
StreamCopy copier(dec, url, 1024); // copy url to decoder
26+
27+
void setup() {
28+
Serial.begin(115200);
29+
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
30+
while(!Serial);
31+
Serial.println("starting...");
32+
33+
// Open I2S: if the buffer is too slow audio is breaking up
34+
auto cfg =i2s.defaultConfig(TX_MODE);
35+
cfg.buffer_size= 1024;
36+
cfg.buffer_count = 20;
37+
if (!i2s.begin(cfg)){
38+
Serial.println("i2s error");
39+
stop();
40+
}
41+
42+
// Open url
43+
url.begin("https://pschatzmann.github.io/Resources/audio/audio.flac", "audio/flac");
44+
45+
// Start decoder
46+
if (!dec.begin()){
47+
Serial.println("Decoder failed");
48+
}
49+
}
50+
51+
void loop() {
52+
copier.copy();
53+
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#pragma once
2+
3+
#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
4+
#include "AudioTools/CoreAudio/Buffers.h"
5+
#include "foxen-flac.h"
6+
7+
namespace audio_tools {
8+
9+
#define FOXEN_IN_BUFFER_SIZE 1024 * 2
10+
#define FOXEN_OUT_BUFFER_SIZE 1024 * 4
11+
12+
/**
13+
* @brief Foxen FLAC Decoder.
14+
* @ingroup codecs
15+
* @ingroup decoder
16+
* @author Phil Schatzmann
17+
* @copyright GPLv3
18+
*/
19+
class FLACFoxenDecoder : public AudioDecoder {
20+
public:
21+
FLACFoxenDecoder() = default;
22+
23+
/// Default Constructor
24+
FLACFoxenDecoder(int maxBlockSize, int maxChannels,
25+
bool convertTo16Bits = true) {
26+
is_convert_to_16 = convertTo16Bits;
27+
max_block_size = maxBlockSize;
28+
max_channels = maxChannels;
29+
};
30+
31+
/// Destructor - calls end();
32+
~FLACFoxenDecoder() { end(); }
33+
34+
bool begin() {
35+
TRACEI();
36+
is_active = false;
37+
if (flac == nullptr) {
38+
size_t foxen_size = fx_flac_size(max_block_size, max_channels);
39+
if (foxen_size > 0) {
40+
foxen_data.resize(foxen_size);
41+
}
42+
if (foxen_data.data() != nullptr) {
43+
flac = fx_flac_init(foxen_data.data(), max_block_size, max_channels);
44+
}
45+
}
46+
if (flac != nullptr) {
47+
is_active = true;
48+
} else {
49+
LOGE("not enough memory");
50+
if (is_stop_on_error) stop();
51+
}
52+
if (buffer.size() == 0) {
53+
buffer.resize(in_buffer_size);
54+
out.resize(FOXEN_OUT_BUFFER_SIZE);
55+
}
56+
57+
return is_active;
58+
}
59+
60+
void end() {
61+
TRACEI();
62+
flush();
63+
if (flac != nullptr) {
64+
foxen_data.resize(0);
65+
flac = nullptr;
66+
}
67+
buffer.resize(0);
68+
out.resize(0);
69+
is_active = false;
70+
}
71+
72+
size_t write(const uint8_t *data, size_t len) override {
73+
LOGD("write: %d", len);
74+
// no processing if not active
75+
if (!is_active) return 0;
76+
77+
size_t result = buffer.writeArray(data, len);
78+
LOGD("buffer availabe: %d", buffer.available());
79+
80+
while (buffer.available() > 0) {
81+
if (!decode()) break;
82+
}
83+
84+
// if the buffer is full we could not decode anything
85+
if (buffer.available() == buffer.size()) {
86+
LOGE("Decoder did not consume any data");
87+
if (is_stop_on_error) stop();
88+
}
89+
90+
LOGD("write: %d -> %d", len, result);
91+
return result;
92+
}
93+
94+
void flush() { decode(); }
95+
96+
operator bool() override { return is_active; }
97+
98+
/// Defines the input buffer size (default is 2k)
99+
void setInBufferSize(int size){
100+
in_buffer_size = size;
101+
}
102+
103+
/// Defines the number of 32 bit samples for providing the result (default is 4k)
104+
void setOutBufferSize(int size){
105+
out_buffer_size = size;
106+
}
107+
108+
/// Defines the maximum FLAC blocksize: drives the buffer allocation
109+
void setMaxBlockSize(int size){
110+
max_block_size = size;
111+
}
112+
113+
/// Defines the maximum number of channels: drives the buffer allocation
114+
void setMaxChannels(int ch){
115+
max_channels = ch;
116+
}
117+
118+
/// Select between 16 and 32 bit output: the default is 16 bits
119+
void set32Bit(bool flag) {
120+
is_convert_to_16 = !flag;
121+
}
122+
123+
protected:
124+
fx_flac_t *flac = nullptr;
125+
SingleBuffer<uint8_t> buffer{0};
126+
Vector<int32_t> out;
127+
Vector<uint8_t> foxen_data{0};
128+
bool is_active = false;
129+
bool is_convert_to_16 = true;
130+
bool is_stop_on_error = true;
131+
int bits_eff = 0;
132+
int max_block_size = 5 * 1024;
133+
int max_channels = 2;
134+
int in_buffer_size = FOXEN_IN_BUFFER_SIZE;
135+
int out_buffer_size = FOXEN_OUT_BUFFER_SIZE;
136+
137+
bool decode() {
138+
TRACED();
139+
uint32_t out_len = out.size();
140+
uint32_t buf_len = buffer.available();
141+
uint32_t buf_len_result = buf_len;
142+
int rc = fx_flac_process(flac, buffer.data(), &buf_len_result, out.data(),
143+
&out_len);
144+
// assert(out_len <= FOXEN_OUT_BUFFER_SIZE);
145+
146+
switch (rc) {
147+
case FLAC_END_OF_METADATA: {
148+
processMetadata();
149+
} break;
150+
151+
case FLAC_ERR: {
152+
LOGE("FLAC decoder in error state!");
153+
if (is_stop_on_error) stop();
154+
} break;
155+
156+
default: {
157+
if (out_len > 0) {
158+
LOGD("Providing data: %d samples", out_len);
159+
if (is_convert_to_16) {
160+
write16BitData(out_len);
161+
} else {
162+
write32BitData(out_len);
163+
}
164+
}
165+
} break;
166+
}
167+
LOGD("processed: %d bytes of %d -> %d samples", buf_len_result, buf_len,
168+
out_len);
169+
// removed processed bytes from buffer
170+
buffer.clearArray(buf_len_result);
171+
return buf_len_result > 0 || out_len > 0;
172+
}
173+
174+
void write32BitData(int out_len) {
175+
TRACED();
176+
// write the result to the output destination
177+
writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int32_t));
178+
}
179+
180+
void write16BitData(int out_len) {
181+
TRACED();
182+
// in place convert to 16 bits
183+
int16_t *out16 = (int16_t *)out.data();
184+
for (int j = 0; j < out_len; j++) {
185+
out16[j] = out.data()[j] >> 16; // 65538;
186+
}
187+
// write the result to the output destination
188+
LOGI("writeBlocking: %d", out_len * sizeof(int16_t));
189+
writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int16_t));
190+
}
191+
192+
void processMetadata() {
193+
bits_eff = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_SIZE);
194+
int info_blocksize = fx_flac_get_streaminfo(flac, FLAC_KEY_MAX_BLOCK_SIZE);
195+
196+
LOGI("bits: %d", bits_eff);
197+
LOGI("blocksize: %d", info_blocksize);
198+
// assert(bits_eff == 32);
199+
info.sample_rate = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_RATE);
200+
info.channels = fx_flac_get_streaminfo(flac, FLAC_KEY_N_CHANNELS);
201+
info.bits_per_sample = is_convert_to_16 ? 16 : bits_eff;
202+
info.logInfo();
203+
if (info.channels > max_channels) {
204+
LOGE("max channels too low: %d -> %d", max_channels, info.channels);
205+
if (is_stop_on_error) stop();
206+
}
207+
if (info_blocksize > max_block_size) {
208+
LOGE("max channels too low: %d -> %d", max_block_size, info_blocksize);
209+
if (is_stop_on_error) stop();
210+
}
211+
notifyAudioChange(info);
212+
}
213+
};
214+
215+
} // namespace audio_tools

0 commit comments

Comments
 (0)