arduino-audio-tools
ContainerBinary.h
Go to the documentation of this file.
1 
16 #pragma once
17 #include <string.h>
18 
19 #include "AudioCodecs/AudioEncoded.h"
20 
21 namespace audio_tools {
22 
23 enum class ContainerType : uint8_t {
24  Header = 1,
25  Audio = 2,
26  Meta = 3,
27  Undefined = 0
28 };
29 
30 struct CommonHeader {
31  CommonHeader() = default;
32  CommonHeader(ContainerType type, uint16_t len) {
33  this->type = type;
34  this->len = len;
35  }
36  char header[2] = {'\r','\n'};
37  ContainerType type;
38  uint16_t len;
39  uint8_t checksum = 0;
40 };
41 
43  SimpleContainerConfig() = default;
44  CommonHeader common{ContainerType::Header, sizeof(AudioInfo)};
45  AudioInfo info;
46 };
47 
49  CommonHeader common{ContainerType::Audio, 0};
50 };
51 
53  CommonHeader common{ContainerType::Meta, 0};
54 };
55 
56 // struct ProcessedResult {
57 // ContainerType type = ContainerType::Undefined;
58 // // total length incl header
59 // int total_len = 0;
60 // // processed bytes incl header of last step
61 // int processed = 0;
62 // // still (total) open
63 // int open = 0;
64 // };
65 
67 static uint8_t checkSum(const uint8_t *data, size_t len) {
68  uint8_t result = 0;
69  for (int j = 0; j < len; j++) {
70  result ^= data[j];
71  }
72  return result;
73 }
75 enum BinaryContainerEncoderError { InvalidHeader, InvalidChecksum, DataMissing};
76 
88 public:
89  BinaryContainerEncoder() = default;
90  BinaryContainerEncoder(AudioEncoder &encoder) { p_codec = &encoder; }
91  BinaryContainerEncoder(AudioEncoder *encoder) { p_codec = encoder; }
92 
93  void setEncoder(AudioEncoder *encoder) { p_codec = encoder; }
94 
95  void setOutput(Print &outStream) {
96  LOGD("BinaryContainerEncoder::setOutput");
97  p_out = &outStream;
98  }
99 
100  bool begin() override {
101  TRACED();
102  // target.begin();
103  bool rc = p_codec->begin();
104  p_codec->setAudioInfo(cfg.info);
105  is_beginning = true;
106  return rc;
107  }
108 
109  void setAudioInfo(AudioInfo info) override {
110  TRACED();
111  if (info != audioInfo()) {
112  cfg.info = info;
113  }
114  }
115 
116  AudioInfo audioInfo() override { return cfg.info; }
117 
119  size_t writeMeta(const uint8_t *data, size_t len) {
120  LOGD("BinaryContainerEncoder::writeMeta: %d", (int)len);
121  meta.common.len = len + sizeof(SimpleContainerMetaDataHeader);
122  uint8_t tmp_array[meta.common.len];
123  memcpy(tmp_array, &meta, sizeof(meta));
124  memcpy(tmp_array + sizeof(meta), data, len);
125  output(tmp_array, meta.common.len);
126  return len;
127  }
128 
130  size_t write(const void *data, size_t len) {
131  LOGD("BinaryContainerEncoder::write: %d", (int)len);
132  if (is_beginning) {
133  writeHeader();
134  is_beginning = false;
135  }
136  writeAudio((uint8_t *)data, len);
137  return len;
138  }
139 
140  void end() { p_codec->end(); }
141 
142  operator bool() { return true; };
143 
144  virtual const char *mime() { return "audio/binary"; };
145 
146 protected:
147  uint64_t packet_count = 0;
148  bool is_beginning = true;
149  int repeat_header;
153  AudioEncoder *p_codec = nullptr;
154  Print *p_out = nullptr;
155 
156  void writeAudio(const uint8_t *data, size_t len) {
157  LOGD("writeAudio: %d", (int)len);
158  // encode data
159  SingleBuffer<uint8_t> tmp_buffer{(int)len};
160  QueueStream<uint8_t> tmp{tmp_buffer};
161  tmp.begin();
162  p_codec->setOutput(tmp);
163  p_codec->write(data, len);
164 
165  // output of audio data header
166  dh.common.len = tmp.available() + sizeof(CommonHeader);
167  dh.common.checksum = checkSum(tmp_buffer.data(), tmp_buffer.available());
168  output((uint8_t *)&dh, sizeof(dh));
169 
170  // output of data
171  output(tmp_buffer.data(), tmp_buffer.available());
172  }
173 
174  void writeHeader() {
175  LOGD("writeHeader");
176  output((uint8_t *)&cfg, sizeof(cfg));
177  }
178 
179  size_t output(const uint8_t *data, size_t len) {
180  if (p_out != nullptr) {
181  int written = p_out->write((uint8_t *)data, len);
182  LOGD("output: %d -> %d", (int)len, written);
183  } else
184  LOGW("output not defined");
185  return len;
186  }
187 };
188 
197 public:
198  BinaryContainerDecoder() = default;
199  BinaryContainerDecoder(AudioDecoder &decoder) { p_codec = &decoder; }
200  BinaryContainerDecoder(AudioDecoder *decoder) { p_codec = decoder; }
201 
202  void setDecoder(AudioDecoder *decoder){
203  p_codec = decoder;
204  }
205 
206  // Defines the output: this method is called 2 times: first to define
207  // output defined in the EnocdedAudioStream and then to define the
208  // real output in the output chain.
209  void setOutput(Print &outStream) {
210  LOGD("BinaryContainerDecoder::setOutput")
211  p_out = &outStream;
212  }
213 
214  void setMetaCallback(void (*callback)(uint8_t*, int, void*)) {
215  meta_callback = callback;
216  }
217 
218  bool begin() {
219  TRACED();
220  is_first = true;
221  return true;
222  }
223 
224  void end() { TRACED(); }
225 
226  size_t write(const void *data, size_t len) {
227  LOGD("write: %d", (int)len);
228  uint8_t *data8 = (uint8_t *)data;
229  if (buffer.size() < len) {
230  buffer.resize(
231  std::max(static_cast<int>(DEFAULT_BUFFER_SIZE + header_size),
232  static_cast<int>(len * 4 + header_size)));
233  }
234 
235  size_t result = buffer.writeArray(data8, len);
236  while (parseBuffer())
237  ;
238  return ignore_write_errors ? len : result;
239  }
240 
241  operator bool() { return true; };
242 
243  void addErrorHandler(void (*error_handler)(BinaryContainerEncoderError error, BinaryContainerDecoder* source, void* ref)){
244  this->error_handler = error_handler;
245  }
246 
248  void setIgnoreWriteErrors(bool flag){
249  ignore_write_errors = flag;
250  }
251 
253  void setReference(void* ref){
254  reference = ref;
255  }
256 
257 protected:
258  bool is_first = true;
259  CommonHeader header;
260  const size_t header_size = sizeof(header);
261  AudioDecoder *p_codec = nullptr;
262  SingleBuffer<uint8_t> buffer{0};
263  Print *p_out = nullptr;
264  void (*meta_callback)(uint8_t* data, int len, void* ref) = nullptr;
265  void (*error_handler)(BinaryContainerEncoderError error, BinaryContainerDecoder* source, void* ref) = nullptr;
266  bool ignore_write_errors = true;
267  void * reference = nullptr;
268 
269 
270  bool parseBuffer() {
271  LOGD("parseBuffer");
272  bool result = false;
273 
274  Str str{(const char *)buffer.data()};
275  int start = str.indexOf("\r\n");
276  LOGD("start: %d", start);
277  if (start < 0) {
278  return false;
279  }
280  // get next record
281  if (buffer.available() - start > sizeof(header)) {
282  // determine header
283  memmove((uint8_t *)&header, buffer.data() + start, sizeof(header));
284 
285  // check header
286  if (!isValidHeader()) {
287  LOGW("invalid header: %d", header.type);
288  if (error_handler) error_handler(InvalidHeader, this, reference);
289  nextRecord();
290  return false;
291  };
292 
293  if (buffer.available() - start >= header.len) {
294  // move to start of frame
295  buffer.clearArray(start);
296  // process frame
297  result = processData();
298  } else {
299  LOGD("not enough data - available %d / req: %d", buffer.available(),
300  header.len);
301  if (error_handler) error_handler(DataMissing, this, reference);
302 
303  }
304  } else {
305  LOGD("not enough data for header: %d", buffer.available());
306  if (error_handler) error_handler(DataMissing, this, reference);
307  }
308  return result;
309  }
310 
311  // processes the completed data from the buffer: e.g. writes it
312  bool processData() {
313  LOGD("processData");
314  bool rc = false;
315  switch (header.type) {
316  case ContainerType::Header: {
317  LOGD("Header");
318  SimpleContainerConfig config;
319  buffer.readArray((uint8_t *)&config, sizeof(config));
320  info = config.info;
321  notifyAudioChange(info);
322  info.logInfo();
323  p_codec->setAudioInfo(info);
324  p_codec->begin();
325  rc = true;
326  } break;
327 
328  case ContainerType::Audio: {
329  LOGD("Audio");
330  buffer.clearArray(sizeof(header));
331  int data_len = header.len - header_size;
332  uint8_t crc = checkSum(buffer.data(), data_len);
333  if (header.checksum == crc) {
334  // decode
335  SingleBuffer<uint8_t> tmp_buffer{data_len * 5};
336  QueueStream<uint8_t> tmp{tmp_buffer};
337  tmp.begin();
338  p_codec->setOutput(tmp);
339  p_codec->write(buffer.data(), data_len);
340 
341  // output decoded data
342  output(tmp_buffer.data(), tmp_buffer.available());
343  buffer.clearArray(data_len);
344  } else {
345  LOGW("invalid checksum");
346  if (error_handler) error_handler(InvalidChecksum, this, reference);
347  // move to next record
348  nextRecord();
349  return false;
350  }
351  rc = true;
352  } break;
353 
354  case ContainerType::Meta: {
355  LOGD("Meta");
356  buffer.clearArray(sizeof(header));
357  int data_len = header.len - header_size;
358  if (meta_callback != nullptr) {
359  meta_callback(buffer.data(), data_len, reference);
360  }
361  buffer.clearArray(data_len);
362  rc = true;
363  } break;
364  }
365  return rc;
366  }
367 
368  bool isValidHeader() {
369  switch (header.type) {
370  case ContainerType::Header:
371  return header.checksum == 0;
372  case ContainerType::Audio:
373  return true;
374  case ContainerType::Meta:
375  return header.checksum == 0;
376  }
377  return false;
378  }
379 
380  void nextRecord() {
381  TRACED();
382  while (buffer.available() && buffer.peek() != '\n')
383  buffer.read();
384  }
385 
386  // writes the data to the decoder which forwards it to the output; if there
387  // is no coded we write to the output instead
388  size_t output(uint8_t *data, size_t len) {
389  LOGD("output: %d", (int)len);
390  if (p_out != nullptr)
391  p_out->write((uint8_t *)data, len);
392  else
393  LOGW("output not defined");
394 
395  return len;
396  }
397 };
398 
399 } // namespace audio_tools
minimial implementtion of Meta which just ignores the data
Definition: AudioFaustDSP.h:39
Docoding of encoded audio into PCM data.
Definition: AudioEncoded.h:18
virtual void setOutput(AudioStream &out_stream)
Defines where the decoded result is written to.
Definition: AudioEncoded.h:36
virtual void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition: AudioEncoded.h:28
Encoding of PCM data.
Definition: AudioEncoded.h:88
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition: AudioEncoded.h:97
virtual int readArray(T data[], int len)
reads multiple values
Definition: Buffers.h:41
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:65
Decodes the provided data from the DAT and CFG segments.
Definition: ContainerBinary.h:196
void setIgnoreWriteErrors(bool flag)
If set to true we do not expect a retry to write the missing data but continue just with the next....
Definition: ContainerBinary.h:248
void setOutput(Print &outStream)
Defines where the decoded result is written to.
Definition: ContainerBinary.h:209
void setReference(void *ref)
Provide additional information for callback.
Definition: ContainerBinary.h:253
Wraps the encoded data into Config, Data, and Meta segments so that we can recover the audio configur...
Definition: ContainerBinary.h:87
size_t write(const void *data, size_t len)
Add data segment. On first write we also add a AudioInfo header.
Definition: ContainerBinary.h:130
size_t writeMeta(const uint8_t *data, size_t len)
Adds meta data segment.
Definition: ContainerBinary.h:119
void setAudioInfo(AudioInfo info) override
Defines the sample rate, number of channels and bits per sample.
Definition: ContainerBinary.h:109
virtual const char * mime()
Provides the mime type of the encoded result.
Definition: ContainerBinary.h:144
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: ContainerBinary.h:116
Parent class for all container formats.
Definition: AudioEncoded.h:78
Definition: NoArduino.h:58
T * data()
Provides address of actual data.
Definition: Buffers.h:246
T peek() override
peeks the actual entry from the buffer
Definition: Buffers.h:205
int available() override
provides the number of entries that are available to read
Definition: Buffers.h:213
T read() override
reads a single value
Definition: Buffers.h:197
int clearArray(int len) override
consumes len bytes and moves current data to the beginning
Definition: Buffers.h:223
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
static uint8_t checkSum(const uint8_t *data, size_t len)
Calculates the checksum.
Definition: ContainerBinary.h:67
BinaryContainerEncoderError
Error types.
Definition: ContainerBinary.h:75
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
Definition: ContainerBinary.h:30
Definition: ContainerBinary.h:42
Definition: ContainerBinary.h:48
Definition: ContainerBinary.h:52