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