arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
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
22namespace audio_tools {
23
24enum class ContainerType : uint8_t {
25 Header = 1,
26 Audio = 2,
27 Meta = 3,
28 Undefined = 0
29};
30
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
68static 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}
76enum BinaryContainerEncoderError { InvalidHeader, InvalidChecksum, DataMissing};
77
89public:
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
147protected:
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
198public:
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
258protected:
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 uint8_t peekBufferValue(){
382 uint8_t byte_value=0;
383 buffer.peek(byte_value);
384 return byte_value;
385 }
386
387 void nextRecord() {
388 TRACED();
389 uint8_t byte_value;
390 while (buffer.available() && peekBufferValue() != '\n')
391 buffer.read(byte_value);
392 }
393
394 // writes the data to the decoder which forwards it to the output; if there
395 // is no coded we write to the output instead
396 size_t output(uint8_t *data, size_t len) {
397 LOGD("output: %d", (int)len);
398 if (p_out != nullptr)
399 p_out->write((uint8_t *)data, len);
400 else
401 LOGW("output not defined");
402
403 return len;
404 }
405};
406
407} // namespace audio_tools
minimial implementtion of Meta which just ignores the data
Definition AudioFaustDSP.h:40
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
virtual void setOutput(AudioStream &out_stream)
Defines where the decoded result is written to.
Definition AudioCodecsBase.h:36
virtual void setAudioInfo(AudioInfo from) override
for most decoders this is not needed
Definition AudioCodecsBase.h:28
Encoding of PCM data.
Definition AudioCodecsBase.h:90
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:99
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:37
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
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
virtual const char * mime()
Provides the mime type of the encoded result.
Definition ContainerBinary.h:145
void setAudioInfo(AudioInfo info) override
Defines the sample rate, number of channels and bits per sample.
Definition ContainerBinary.h:110
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:80
Definition NoArduino.h:62
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:175
bool peek(T &result) override
peeks the actual entry from the buffer
Definition Buffers.h:218
bool read(T &result) override
reads a single value
Definition Buffers.h:209
int available() override
provides the number of entries that are available to read
Definition Buffers.h:227
T * data()
Provides address of actual data.
Definition Buffers.h:261
int clearArray(int len) override
consumes len bytes and moves current data to the beginning
Definition Buffers.h:237
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
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:53
Definition ContainerBinary.h:31
Definition ContainerBinary.h:43
Definition ContainerBinary.h:49
Definition ContainerBinary.h:53