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