arduino-audio-tools
Loading...
Searching...
No Matches
CodecFLAC.h
Go to the documentation of this file.
1
9#pragma once
10
11#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
12#include "AudioTools/CoreAudio/Buffers.h"
13#include "AudioTools/CoreAudio/AudioBasic/Net.h"
14#include "flac.h"
15
16#ifndef FLAC_READ_TIMEOUT_MS
17#define FLAC_READ_TIMEOUT_MS 10000
18#endif
19
20#ifndef FLAC_BUFFER_SIZE
21#define FLAC_BUFFER_SIZE (8 * 1024)
22#endif
23
24
25namespace audio_tools {
26
37 public:
39 FLACDecoder(bool isOgg=false) {
40 is_ogg = isOgg;
41 }
42
45
46 void setTimeout(uint64_t readTimeout=FLAC_READ_TIMEOUT_MS) {
47 read_timeout_ms = readTimeout;
48 }
49 void setOgg(bool isOgg) {
50 is_ogg = isOgg;
51 }
52
54 AudioInfo info;
55 info.sample_rate = FLAC__stream_decoder_get_sample_rate(decoder);
56 info.channels = FLAC__stream_decoder_get_channels(decoder);
57 info.bits_per_sample = 16; // only 16 is supported
58 return info;
59 }
60
61 bool begin() {
62 TRACEI();
63 is_active = false;
64 if (decoder == nullptr) {
65 if ((decoder = FLAC__stream_decoder_new()) == NULL) {
66 LOGE("ERROR: allocating decoder");
67 is_active = false;
68 return false;
69 }
70 LOGI("FLAC__stream_decoder_new");
71 }
72
73 // if it is already active we close it
74 auto state = FLAC__stream_decoder_get_state(decoder);
75 if (state != FLAC__STREAM_DECODER_UNINITIALIZED){
76 FLAC__stream_decoder_finish(decoder);
77 }
78
79 // deactivate md5 checking
80 FLAC__stream_decoder_set_md5_checking(decoder, is_md5_checing);
81
82 // init decoder
83 if (is_ogg){
84 init_status = FLAC__stream_decoder_init_ogg_stream( decoder, read_callback, nullptr, nullptr, nullptr, nullptr, write_callback, nullptr, error_callback, this);
85 } else {
86 init_status = FLAC__stream_decoder_init_stream( decoder, read_callback, nullptr, nullptr, nullptr, nullptr, write_callback, nullptr, error_callback, this);
87 }
88
89 if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
90 LOGE("ERROR: initializing decoder: %s", FLAC__StreamDecoderInitStatusString[init_status]);
91 is_active = false;
92 return false;
93 }
94 LOGI("FLAC is open");
95 is_active = true;
96 return true;
97 }
98
99 void end() {
100 TRACEI();
101 if (decoder != nullptr){
102 flush();
103 FLAC__stream_decoder_delete(decoder);
104 decoder = nullptr;
105 }
106 is_active = false;
107 }
108
110 void flush() {
111 while(FLAC__stream_decoder_process_single(decoder));
112 }
113
114
115 operator bool() { return is_active; }
116
117
119 bool copy() {
120 LOGD("copy");
121 if (!is_active) {
122 LOGW("FLAC not active");
123 return false;
124 }
125 if (p_input == nullptr) {
126 LOGE("setInput was not called");
127 return false;
128 }
129 if (!FLAC__stream_decoder_process_single(decoder)) {
130 LOGE("FLAC__stream_decoder_process_single");
131 return false;
132 }
133 return true;
134 }
135
137 void setMD5(bool flag){
138 is_md5_checing = flag;
139 }
140
142 bool isOgg() const { return is_ogg; }
143
145 const char *mime() override { return is_ogg ? "audio/ogg; codecs=flac" : "audio/flac"; }
146
147
148 protected:
149 bool is_active = false;
150 bool is_ogg = false;
151 bool is_md5_checing = false;
152 AudioInfo info;
153 FLAC__StreamDecoder *decoder = nullptr;
154 FLAC__StreamDecoderInitStatus init_status;
155 uint64_t time_last_read = 0;
156 uint64_t read_timeout_ms = FLAC_READ_TIMEOUT_MS;
157
158
160 bool isInputFromStream() { return p_input != nullptr; }
161
163 static void error_callback(const FLAC__StreamDecoder *decoder,
164 FLAC__StreamDecoderErrorStatus status,
165 void *client_data) {
166 LOGE(FLAC__StreamDecoderErrorStatusString[status]);
167 }
168
169 size_t readBytes(uint8_t *data, size_t len) override {
170 return p_input->readBytes(data, len);
171 }
172
174 static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte result_buffer[],size_t *bytes, void *client_data) {
175 FLAC__StreamDecoderReadStatus result = FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
176 LOGD("read_callback: %d", (int) *bytes);
177 FLACDecoder *self = (FLACDecoder *)client_data;
178 if (self == nullptr || !self->is_active) {
179 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
180 }
181
182 // get data directly from stream
183 *bytes = self->readBytes(result_buffer, *bytes);
184 LOGD("-> %d", (int) *bytes);
185 if (self->isEof(*bytes)){
186 result = FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
187 self->is_active = false;
188 }
189 return result;
190 }
191
193 bool isEof(int bytes) {
194 bool result = false;
195 if (bytes==0){
196 delay(5);
197 } else {
198 time_last_read=millis();
199 }
200 if (millis() - time_last_read >= read_timeout_ms){
201 result = true;
202 }
203 return result;
204 }
205
207 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,const FLAC__int32 *const buffer[], void *client_data) {
208 LOGD("write_callback: %u", (unsigned)frame->header.blocksize);
209 FLACDecoder *self = (FLACDecoder *)client_data;
210
211 AudioInfo actual_info = self->audioInfo();
212 if (self->info != actual_info){
213 self->info = actual_info;
214 self->info.logInfo();
215 int bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
216 if (bps!=16){
217 LOGI("Converting from %d bits", bps);
218 }
219 self->info = actual_info;
220 self->notifyAudioChange(self->info);
221 }
222
223 // write audio data
224 int bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
225 int16_t result_frame[actual_info.channels];
226
227 switch(bps){
228 case 8:
229 for (int j = 0; j < frame->header.blocksize; j++) {
230 for (int i = 0; i < actual_info.channels; i++) {
231 //self->output_buffer[j*actual_info.channels + i] = buffer[i][j]<<8;
232 result_frame[i] = buffer[i][j]<<8;
233 }
234 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
235 }
236 break;
237 case 16:
238 for (int j = 0; j < frame->header.blocksize; j++) {
239 for (int i = 0; i < actual_info.channels; i++) {
240 result_frame[i] = buffer[i][j];
241 }
242 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
243 }
244 break;
245 case 24:
246 for (int j = 0; j < frame->header.blocksize; j++) {
247 for (int i = 0; i < actual_info.channels; i++) {
248 result_frame[i] = buffer[i][j] >> 8;
249 }
250 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
251 }
252 break;
253 case 32:
254 for (int j = 0; j < frame->header.blocksize; j++) {
255 for (int i = 0; i < actual_info.channels; i++) {
256 result_frame[i] = buffer[i][j] >> 16;
257 }
258 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
259 }
260 break;
261 default:
262 LOGE("Unsupported bps: %d", bps);
263 }
264
265 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
266 }
267};
268
269
277class FLACEncoder : public AudioEncoder {
278 public:
280 FLACEncoder(bool isOgg = false) {
281 setOgg(isOgg);
282 }
283
286
287 void setOgg(bool isOgg) {
288 is_ogg = isOgg;
289 }
290
291 bool isOgg() {return is_ogg;}
292
293 void setBlockSize(int size){
294 flac_block_size = size;
295 }
296
297 int blockSize() {return flac_block_size; }
298
299 void setCompressionLevel(int level){
300 flac_compression_level = level;
301 }
302
303 int compressionLevel() {return flac_compression_level;}
304
306 void setOutput(Print &out_stream) override { p_print = &out_stream; }
307
309 const char *mime() override { return "audio/flac"; }
310
312 virtual void setAudioInfo(AudioInfo from) override {
313 cfg = from;
314 cfg.logInfo();
315 }
316
318 virtual bool begin() override {
319 TRACED();
320 if (p_encoder==nullptr){
321 p_encoder = FLAC__stream_encoder_new();
322 if (p_encoder==nullptr){
323 LOGE("FLAC__stream_encoder_new");
324 return false;
325 }
326 }
327
328 is_open = false;
329
330 FLAC__stream_encoder_set_channels(p_encoder, cfg.channels);
331 FLAC__stream_encoder_set_bits_per_sample(p_encoder, cfg.bits_per_sample);
332 FLAC__stream_encoder_set_sample_rate(p_encoder, cfg.sample_rate);
333 FLAC__stream_encoder_set_blocksize(p_encoder, flac_block_size);
334 FLAC__stream_encoder_set_compression_level(p_encoder, flac_compression_level);
335
336 // setup stream
337 FLAC__StreamEncoderInitStatus status;
338 if (is_ogg){
339 status = FLAC__stream_encoder_init_ogg_stream(p_encoder, nullptr, write_callback, nullptr, nullptr, nullptr, this);
340 } else {
341 status = FLAC__stream_encoder_init_stream(p_encoder, write_callback, nullptr, nullptr, nullptr, this);
342 }
343 if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
344 LOGE("ERROR: initializing decoder: %s", FLAC__StreamEncoderInitStatusString[status]);
345 if (status==FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR){
346 LOGE(" -> %s", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(p_encoder)]);
347 }
348 return false;
349 }
350 is_open = true;
351 return true;
352 }
353
355 bool begin(Print &out) {
356 p_print = &out;
357 return begin();
358 }
359
361 void end() override {
362 TRACED();
363 if (p_encoder != nullptr) {
364 FLAC__stream_encoder_delete(p_encoder);
365 p_encoder = nullptr;
366 is_open = false;
367 }
368 }
369
371 virtual size_t write(const uint8_t *data, size_t len) override {
372 if (!is_open || p_print == nullptr) return 0;
373 LOGD("write: %zu", len);
374 size_t result = 0;
375 int samples=0;
376 int frames=0;
377 int32_t *data32=nullptr;
378 switch(cfg.bits_per_sample){
379 case 16:
380 samples = len / sizeof(int16_t);
381 frames = samples / cfg.channels;
382 writeBuffer((int16_t*)data, samples);
383 data32 = buffer.data();
384 break;
385
386 case 24:
387 case 32:
388 samples = len / sizeof(int32_t);
389 frames = samples / cfg.channels;
390 data32 = (int32_t*) data;
391 break;
392
393 default:
394 LOGE("bits_per_sample not supported: %d", (int) cfg.bits_per_sample);
395 break;
396 }
397
398 if (frames>0){
399 if (FLAC__stream_encoder_process_interleaved(p_encoder, data32, frames)){
400 result = len;
401 } else {
402 LOGE("FLAC__stream_encoder_process_interleaved");
403 }
404 }
405
406 return result;
407 }
408
409 operator bool() override { return is_open; }
410
411 bool isOpen() { return is_open; }
412
413 protected:
414 AudioInfo cfg;
415 Vector<FLAC__int32> buffer;
416 Print *p_print = nullptr;
417 FLAC__StreamEncoder *p_encoder=nullptr;
418 bool is_open = false;
419 bool is_ogg = false;
420 int flac_block_size = 512; // small value to minimize allocated memory
421 int flac_compression_level = 8;
422
423 static FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data){
424 FLACEncoder *self = (FLACEncoder *)client_data;
425 if (self->p_print!=nullptr){
426 size_t written = self->p_print->write((uint8_t*)buffer, bytes);
427 if (written!=bytes){
428 LOGE("write_callback %zu -> %zu", bytes, written);
429 return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
430 }
431 }
432 return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
433 }
434
435 void writeBuffer(int16_t * data, size_t samples) {
436 buffer.resize(samples);
437 for (int j=0;j<samples;j++){
438 buffer[j] = data[j];
439 }
440 }
441};
442
443} // namespace audio_tools
444
Encoding of PCM data.
Definition AudioCodecsBase.h:96
Decoder for FLAC. Depends on https://github.com/pschatzmann/arduino-libflac. We support an efficient ...
Definition CodecFLAC.h:36
void setMD5(bool flag)
Activate/deactivate md5 checking: call this before calling begin()
Definition CodecFLAC.h:137
size_t readBytes(uint8_t *data, size_t len) override
Reads bytes from the input stream.
Definition CodecFLAC.h:169
bool isEof(int bytes)
We return eof when we were subsequently getting 0 bytes for the timeout period.
Definition CodecFLAC.h:193
bool isOgg() const
returns true of the stream is ogg
Definition CodecFLAC.h:142
bool begin()
Starts the processing.
Definition CodecFLAC.h:61
static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte result_buffer[], size_t *bytes, void *client_data)
Callback which reads from stream.
Definition CodecFLAC.h:174
bool copy()
Stream Interface: Process a single frame - only relevant when input stream has been defined.
Definition CodecFLAC.h:119
AudioInfo audioInfo()
Provides the audio information for the current stream.
Definition CodecFLAC.h:53
~FLACDecoder()
Destructor - calls end();.
Definition CodecFLAC.h:44
void end()
Releases the reserved memory.
Definition CodecFLAC.h:99
const char * mime() override
Provides "audio/flac" or "audio/ogg".
Definition CodecFLAC.h:145
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
Output decoded result to final output stream.
Definition CodecFLAC.h:207
void flush()
Process all data in the buffer.
Definition CodecFLAC.h:110
static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
Error callback.
Definition CodecFLAC.h:163
FLACDecoder(bool isOgg=false)
Default Constructor.
Definition CodecFLAC.h:39
bool isInputFromStream()
Check if input is directly from stream - instead of writes.
Definition CodecFLAC.h:160
FLACEncoder.
Definition CodecFLAC.h:277
virtual size_t write(const uint8_t *data, size_t len) override
Writes FLAC Packet.
Definition CodecFLAC.h:371
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecFLAC.h:306
~FLACEncoder()
Destructor - calls end();.
Definition CodecFLAC.h:285
virtual bool begin() override
starts the processing using the actual AudioInfo
Definition CodecFLAC.h:318
void end() override
stops the processing
Definition CodecFLAC.h:361
bool begin(Print &out)
starts the processing
Definition CodecFLAC.h:355
FLACEncoder(bool isOgg=false)
Default Constructor.
Definition CodecFLAC.h:280
const char * mime() override
Provides "audio/pcm".
Definition CodecFLAC.h:309
virtual void setAudioInfo(AudioInfo from) override
We update the audio information which will be used in the begin method.
Definition CodecFLAC.h:312
Definition NoArduino.h:62
A Streaming Decoder where we provide both the input and output as streams.
Definition StreamingDecoder.h:29
Stream * p_input
Input stream for encoded audio data.
Definition StreamingDecoder.h:169
Print * p_print
Output stream for decoded PCM data.
Definition StreamingDecoder.h:168
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59