arduino-audio-tools
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 
25 namespace audio_tools {
26 
36 class FLACDecoder : public StreamingDecoder {
37  public:
39  FLACDecoder(bool isOgg=false) {
40  is_ogg = isOgg;
41  }
42 
44  ~FLACDecoder() { end(); }
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 
270 class FLACEncoder : public AudioEncoder {
271  public:
273  FLACEncoder(bool isOgg = false) {
274  setOgg(isOgg);
275  }
276 
278  ~FLACEncoder() { end(); }
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:84
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
const char * mime() override
Provides "audio/pcm".
Definition: CodecFLAC.h:302
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
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:58
A Streaming Decoder where we provide both the input and output as streams.
Definition: AudioCodecsBase.h:154
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
uint32_t millis()
Returns the milliseconds since the start.
Definition: Time.h:12
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
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