arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
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
141 protected:
142 bool is_active = false;
143 bool is_ogg = false;
144 bool is_md5_checing = false;
145 AudioInfo info;
146 FLAC__StreamDecoder *decoder = nullptr;
147 FLAC__StreamDecoderInitStatus init_status;
148 uint64_t time_last_read = 0;
149 uint64_t read_timeout_ms = FLAC_READ_TIMEOUT_MS;
150
151
153 bool isInputFromStream() { return p_input != nullptr; }
154
156 static void error_callback(const FLAC__StreamDecoder *decoder,
157 FLAC__StreamDecoderErrorStatus status,
158 void *client_data) {
159 LOGE(FLAC__StreamDecoderErrorStatusString[status]);
160 }
161
162 size_t readBytes(uint8_t *data, size_t len) override {
163 return p_input->readBytes(data, len);
164 }
165
167 static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte result_buffer[],size_t *bytes, void *client_data) {
168 FLAC__StreamDecoderReadStatus result = FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
169 LOGD("read_callback: %d", (int) *bytes);
170 FLACDecoder *self = (FLACDecoder *)client_data;
171 if (self == nullptr || !self->is_active) {
172 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
173 }
174
175 // get data directly from stream
176 *bytes = self->readBytes(result_buffer, *bytes);
177 LOGD("-> %d", (int) *bytes);
178 if (self->isEof(*bytes)){
179 result = FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
180 self->is_active = false;
181 }
182 return result;
183 }
184
186 bool isEof(int bytes) {
187 bool result = false;
188 if (bytes==0){
189 delay(5);
190 } else {
191 time_last_read=millis();
192 }
193 if (millis() - time_last_read >= read_timeout_ms){
194 result = true;
195 }
196 return result;
197 }
198
200 static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,const FLAC__int32 *const buffer[], void *client_data) {
201 LOGD("write_callback: %u", (unsigned)frame->header.blocksize);
202 FLACDecoder *self = (FLACDecoder *)client_data;
203
204 AudioInfo actual_info = self->audioInfo();
205 if (self->info != actual_info){
206 self->info = actual_info;
207 self->info.logInfo();
208 int bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
209 if (bps!=16){
210 LOGI("Converting from %d bits", bps);
211 }
212 self->info = actual_info;
213 self->notifyAudioChange(self->info);
214 }
215
216 // write audio data
217 int bps = FLAC__stream_decoder_get_bits_per_sample(decoder);
218 int16_t result_frame[actual_info.channels];
219
220 switch(bps){
221 case 8:
222 for (int j = 0; j < frame->header.blocksize; j++) {
223 for (int i = 0; i < actual_info.channels; i++) {
224 //self->output_buffer[j*actual_info.channels + i] = buffer[i][j]<<8;
225 result_frame[i] = buffer[i][j]<<8;
226 }
227 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
228 }
229 break;
230 case 16:
231 for (int j = 0; j < frame->header.blocksize; j++) {
232 for (int i = 0; i < actual_info.channels; i++) {
233 result_frame[i] = buffer[i][j];
234 }
235 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
236 }
237 break;
238 case 24:
239 for (int j = 0; j < frame->header.blocksize; j++) {
240 for (int i = 0; i < actual_info.channels; i++) {
241 result_frame[i] = buffer[i][j] >> 8;
242 }
243 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
244 }
245 break;
246 case 32:
247 for (int j = 0; j < frame->header.blocksize; j++) {
248 for (int i = 0; i < actual_info.channels; i++) {
249 result_frame[i] = buffer[i][j] >> 16;
250 }
251 self->p_print->write((uint8_t *)result_frame, sizeof(result_frame));
252 }
253 break;
254 default:
255 LOGE("Unsupported bps: %d", bps);
256 }
257
258 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
259 }
260};
261
262
270class FLACEncoder : public AudioEncoder {
271 public:
273 FLACEncoder(bool isOgg = false) {
274 setOgg(isOgg);
275 }
276
279
280 void setOgg(bool isOgg) {
281 is_ogg = isOgg;
282 }
283
284 bool isOgg() {return is_ogg;}
285
286 void setBlockSize(int size){
287 flac_block_size = size;
288 }
289
290 int blockSize() {return flac_block_size; }
291
292 void setCompressionLevel(int level){
293 flac_compression_level = level;
294 }
295
296 int compressionLevel() {return flac_compression_level;}
297
299 void setOutput(Print &out_stream) override { p_print = &out_stream; }
300
302 const char *mime() override { return "audio/flac"; }
303
305 virtual void setAudioInfo(AudioInfo from) override {
306 cfg = from;
307 cfg.logInfo();
308 }
309
311 virtual bool begin() override {
312 TRACED();
313 if (p_encoder==nullptr){
314 p_encoder = FLAC__stream_encoder_new();
315 if (p_encoder==nullptr){
316 LOGE("FLAC__stream_encoder_new");
317 return false;
318 }
319 }
320
321 is_open = false;
322
323 FLAC__stream_encoder_set_channels(p_encoder, cfg.channels);
324 FLAC__stream_encoder_set_bits_per_sample(p_encoder, cfg.bits_per_sample);
325 FLAC__stream_encoder_set_sample_rate(p_encoder, cfg.sample_rate);
326 FLAC__stream_encoder_set_blocksize(p_encoder, flac_block_size);
327 FLAC__stream_encoder_set_compression_level(p_encoder, flac_compression_level);
328
329 // setup stream
330 FLAC__StreamEncoderInitStatus status;
331 if (is_ogg){
332 status = FLAC__stream_encoder_init_ogg_stream(p_encoder, nullptr, write_callback, nullptr, nullptr, nullptr, this);
333 } else {
334 status = FLAC__stream_encoder_init_stream(p_encoder, write_callback, nullptr, nullptr, nullptr, this);
335 }
336 if (status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
337 LOGE("ERROR: initializing decoder: %s", FLAC__StreamEncoderInitStatusString[status]);
338 if (status==FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR){
339 LOGE(" -> %s", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(p_encoder)]);
340 }
341 return false;
342 }
343 is_open = true;
344 return true;
345 }
346
348 bool begin(Print &out) {
349 p_print = &out;
350 return begin();
351 }
352
354 void end() override {
355 TRACED();
356 if (p_encoder != nullptr) {
357 FLAC__stream_encoder_delete(p_encoder);
358 p_encoder = nullptr;
359 is_open = false;
360 }
361 }
362
364 virtual size_t write(const uint8_t *data, size_t len) override {
365 if (!is_open || p_print == nullptr) return 0;
366 LOGD("write: %zu", len);
367 size_t result = 0;
368 int samples=0;
369 int frames=0;
370 int32_t *data32=nullptr;
371 switch(cfg.bits_per_sample){
372 case 16:
373 samples = len / sizeof(int16_t);
374 frames = samples / cfg.channels;
375 writeBuffer((int16_t*)data, samples);
376 data32 = buffer.data();
377 break;
378
379 case 24:
380 case 32:
381 samples = len / sizeof(int32_t);
382 frames = samples / cfg.channels;
383 data32 = (int32_t*) data;
384 break;
385
386 default:
387 LOGE("bits_per_sample not supported: %d", (int) cfg.bits_per_sample);
388 break;
389 }
390
391 if (frames>0){
392 if (FLAC__stream_encoder_process_interleaved(p_encoder, data32, frames)){
393 result = len;
394 } else {
395 LOGE("FLAC__stream_encoder_process_interleaved");
396 }
397 }
398
399 return result;
400 }
401
402 operator bool() override { return is_open; }
403
404 bool isOpen() { return is_open; }
405
406 protected:
407 AudioInfo cfg;
408 Vector<FLAC__int32> buffer;
409 Print *p_print = nullptr;
410 FLAC__StreamEncoder *p_encoder=nullptr;
411 bool is_open = false;
412 bool is_ogg = false;
413 int flac_block_size = 512; // small value to minimize allocated memory
414 int flac_compression_level = 8;
415
416 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){
417 FLACEncoder *self = (FLACEncoder *)client_data;
418 if (self->p_print!=nullptr){
419 size_t written = self->p_print->write((uint8_t*)buffer, bytes);
420 if (written!=bytes){
421 LOGE("write_callback %zu -> %zu", bytes, written);
422 return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
423 }
424 }
425 return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
426 }
427
428 void writeBuffer(int16_t * data, size_t samples) {
429 buffer.resize(samples);
430 for (int j=0;j<samples;j++){
431 buffer[j] = data[j];
432 }
433 }
434};
435
436} // namespace audio_tools
437
Encoding of PCM data.
Definition AudioCodecsBase.h:90
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
bool isEof(int bytes)
We return eof when we were subsequently getting 0 bytes for the timeout period.
Definition CodecFLAC.h:186
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:167
bool copy()
Stream Interface: Process a single frame - only relevant when input stream has been defined.
Definition CodecFLAC.h:119
AudioInfo audioInfo()
Provides the last available MP3FrameInfo.
Definition CodecFLAC.h:53
~FLACDecoder()
Destructor - calls end();.
Definition CodecFLAC.h:44
void end()
Releases the reserved memory.
Definition CodecFLAC.h:99
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:200
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:156
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:153
FLACEncoder.
Definition CodecFLAC.h:270
virtual size_t write(const uint8_t *data, size_t len) override
Writes FLAC Packet.
Definition CodecFLAC.h:364
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecFLAC.h:299
~FLACEncoder()
Destructor - calls end();.
Definition CodecFLAC.h:278
virtual bool begin() override
starts the processing using the actual AudioInfo
Definition CodecFLAC.h:311
void end() override
stops the processing
Definition CodecFLAC.h:354
bool begin(Print &out)
starts the processing
Definition CodecFLAC.h:348
FLACEncoder(bool isOgg=false)
Default Constructor.
Definition CodecFLAC.h:273
const char * mime() override
Provides "audio/pcm".
Definition CodecFLAC.h:302
virtual void setAudioInfo(AudioInfo from) override
We update the audio information which will be used in the begin method.
Definition CodecFLAC.h:305
Definition NoArduino.h:62
A Streaming Decoder where we provide both the input and output as streams.
Definition AudioCodecsBase.h:160
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