arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
CodecWAV.h
1#pragma once
2
3#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
5#include "AudioTools/AudioCodecs/AudioEncoded.h"
6#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
7
8#define READ_BUFFER_SIZE 512
9#define MAX_WAV_HEADER_LEN 200
10
11namespace audio_tools {
12
20 WAVAudioInfo() = default;
21 WAVAudioInfo(const AudioInfo &from) {
23 channels = from.channels;
25 }
26
27 AudioFormat format = AudioFormat::PCM;
28 int byte_rate = 0;
29 int block_align = 0;
30 bool is_streamed = true;
31 bool is_valid = false;
32 uint32_t data_length = 0;
33 uint32_t file_size = 0;
34 int offset = 0;
35};
36
37static const char *wav_mime = "audio/wav";
38
46class WAVHeader {
47 public:
48 WAVHeader() = default;
49
51 int write(uint8_t *data, size_t data_len) {
52 return buffer.writeArray(data, data_len);
53 }
54
56 bool parse() {
57 LOGI("WAVHeader::begin: %u", (unsigned)buffer.available());
58 this->data_pos = 0l;
59 memset((void *)&headerInfo, 0, sizeof(WAVAudioInfo));
60
61 if (!setPos("RIFF")) return false;
62 headerInfo.file_size = read_int32();
63 if (!setPos("WAVE")) return false;
64 if (!setPos("fmt ")) return false;
65 int fmt_length = read_int32();
66 headerInfo.format = (AudioFormat)read_int16();
67 headerInfo.channels = read_int16();
68 headerInfo.sample_rate = read_int32();
69 headerInfo.byte_rate = read_int32();
70 headerInfo.block_align = read_int16();
71 headerInfo.bits_per_sample = read_int16();
72 if (!setPos("data")) return false;
73 headerInfo.data_length = read_int32();
74 if (headerInfo.data_length==0 || headerInfo.data_length >= 0x7fff0000) {
75 headerInfo.is_streamed = true;
76 headerInfo.data_length = ~0;
77 }
78
79 logInfo();
80 buffer.clear();
81 return true;
82 }
83
86 int pos = getDataPos();
87 return pos > 0 && buffer.available() >= pos;
88 }
89
91 size_t available(){
92 return buffer.available();
93 }
94
96 int getDataPos() {
97 int pos = StrView((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf("data");
98 return pos > 0 ? pos + 8 : 0;
99 }
100
102 WAVAudioInfo &audioInfo() { return headerInfo; }
103
106 headerInfo = info;
107 }
108
110 int writeHeader(Print *out) {
111 writeRiffHeader(buffer);
112 writeFMT(buffer);
113 writeDataHeader(buffer);
114 int len = buffer.available();
115 out->write(buffer.data(), buffer.available());
116 return len;
117 }
118
119 void clear() {
120 data_pos = 0;
121 WAVAudioInfo empty;
122 empty.sample_rate = 0;
123 empty.channels = 0;
124 empty.bits_per_sample = 0;
125 headerInfo = empty;
126 buffer.setClearWithZero(true);
127 buffer.reset();
128 }
129
130 void dumpHeader() {
131 char msg[buffer.available()+1];
132 memset(msg, 0, buffer.available()+1);
133 for (int j = 0; j< buffer.available();j++){
134 char c = (char)buffer.data()[j];
135 if (!isalpha(c)){
136 c = '.';
137 }
138 msg[j] = c;
139 }
140 LOGI("Header: %s", msg);
141 }
142
143
144 protected:
145 struct WAVAudioInfo headerInfo;
146 SingleBuffer<uint8_t> buffer{MAX_WAV_HEADER_LEN};
147 size_t data_pos = 0;
148
149 bool setPos(const char*id){
150 int id_len = strlen(id);
151 int pos = indexOf(id);
152 if (pos < 0) return false;
153 data_pos = pos + id_len;
154 return true;
155 }
156
157 int indexOf(const char* str){
158 return StrView((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf(str);
159 }
160
161 uint32_t read_tag() {
162 uint32_t tag = 0;
163 tag = (tag << 8) | getChar();
164 tag = (tag << 8) | getChar();
165 tag = (tag << 8) | getChar();
166 tag = (tag << 8) | getChar();
167 return tag;
168 }
169
170 uint32_t getChar32() { return getChar(); }
171
172 uint32_t read_int32() {
173 uint32_t value = 0;
174 value |= getChar32() << 0;
175 value |= getChar32() << 8;
176 value |= getChar32() << 16;
177 value |= getChar32() << 24;
178 return value;
179 }
180
181 uint16_t read_int16() {
182 uint16_t value = 0;
183 value |= getChar() << 0;
184 value |= getChar() << 8;
185 return value;
186 }
187
188 void skip(int n) {
189 int i;
190 for (i = 0; i < n; i++) getChar();
191 }
192
193 int getChar() {
194 if (data_pos < buffer.size())
195 return buffer.data()[data_pos++];
196 else
197 return -1;
198 }
199
200 void seek(long int offset, int origin) {
201 if (origin == SEEK_SET) {
202 data_pos = offset;
203 } else if (origin == SEEK_CUR) {
204 data_pos += offset;
205 }
206 }
207
208 size_t tell() { return data_pos; }
209
210 bool eof() { return data_pos >= buffer.size() - 1; }
211
212 void logInfo() {
213 LOGI("WAVHeader sound_pos: %d", getDataPos());
214 LOGI("WAVHeader channels: %d ", headerInfo.channels);
215 LOGI("WAVHeader bits_per_sample: %d", headerInfo.bits_per_sample);
216 LOGI("WAVHeader sample_rate: %d ", (int) headerInfo.sample_rate);
217 LOGI("WAVHeader format: %d", (int)headerInfo.format);
218 }
219
220 void writeRiffHeader(BaseBuffer<uint8_t> &buffer) {
221 buffer.writeArray((uint8_t *)"RIFF", 4);
222 write32(buffer, headerInfo.file_size - 8);
223 buffer.writeArray((uint8_t *)"WAVE", 4);
224 }
225
226 void writeFMT(BaseBuffer<uint8_t> &buffer) {
227 uint16_t fmt_len = 16;
228 buffer.writeArray((uint8_t *)"fmt ", 4);
229 write32(buffer, fmt_len);
230 write16(buffer, (uint16_t)headerInfo.format); // PCM
231 write16(buffer, headerInfo.channels);
232 write32(buffer, headerInfo.sample_rate);
233 write32(buffer, headerInfo.byte_rate);
234 write16(buffer, headerInfo.block_align); // frame size
235 write16(buffer, headerInfo.bits_per_sample);
236 }
237
238 void write32(BaseBuffer<uint8_t> &buffer, uint64_t value) {
239 buffer.writeArray((uint8_t *)&value, 4);
240 }
241
242 void write16(BaseBuffer<uint8_t> &buffer, uint16_t value) {
243 buffer.writeArray((uint8_t *)&value, 2);
244 }
245
246 void writeDataHeader(BaseBuffer<uint8_t> &buffer) {
247 buffer.writeArray((uint8_t *)"data", 4);
248 write32(buffer, headerInfo.file_size);
249 int offset = headerInfo.offset;
250 if (offset > 0) {
251 uint8_t empty[offset];
252 memset(empty, 0, offset);
253 buffer.writeArray(empty, offset); // resolve issue with wrong aligment
254 }
255 }
256
257};
258
271class WAVDecoder : public AudioDecoder {
272 public:
276 WAVDecoder() = default;
277
283 setDecoder(dec, fmt);
284 }
285
288 TRACED();
289 decoder_format = fmt;
290 p_decoder = &dec;
291 }
292
294 void setOutput(Print &out_stream) override { this->p_print = &out_stream; }
295
296 bool begin() override {
297 TRACED();
298 header.clear();
299 setupEncodedAudio();
300 buffer24.reset();
301 isFirst = true;
302 active = true;
303 return true;
304 }
305
306 void end() override {
307 TRACED();
308 buffer24.reset();
309 active = false;
310 }
311
312 const char *mime() { return wav_mime; }
313
314 WAVAudioInfo &audioInfoEx() { return header.audioInfo(); }
315
316 AudioInfo audioInfo() override { return header.audioInfo(); }
317
318 virtual size_t write(const uint8_t *data, size_t len) override {
319 TRACED();
320 size_t result = 0;
321 if (active) {
322 if (isFirst) {
323 int data_start = decodeHeader((uint8_t*) data, len);
324 // we do not have the complete header yet: need more data
325 if (data_start == 0) return len;
326 // process the outstanding data
327 result = data_start + write_out((uint8_t *)data+data_start, len-data_start);
328
329 } else if (isValid) {
330 result = write_out((uint8_t *)data, len);
331 }
332 }
333 return result;
334 }
335
336 virtual operator bool() override { return active; }
337
338 protected:
339 WAVHeader header;
340 bool isFirst = true;
341 bool isValid = true;
342 bool active = false;
343 AudioFormat decoder_format = AudioFormat::PCM;
344 AudioDecoderExt *p_decoder = nullptr;
345 EncodedAudioOutput dec_out;
346 SingleBuffer<uint8_t> buffer24;
347
348 Print& out() {
349 return p_decoder==nullptr ? *p_print : dec_out;
350 }
351
352 virtual size_t write_out(const uint8_t *in_ptr, size_t in_size) {
353 // check if we need to convert int24 data from 3 bytes to 4 bytes
354 size_t result = 0;
355 if (header.audioInfo().format == AudioFormat::PCM
356 && header.audioInfo().bits_per_sample == 24 && sizeof(int24_t)==4){
357 write_out_24(in_ptr, in_size);
358 result = in_size;
359 } else {
360 result = out().write(in_ptr, in_size);
361 }
362 return result;
363 }
364
365 // convert int24 to int32
366 size_t write_out_24(const uint8_t *in_ptr, size_t in_size) {
367 // make sure we can store a frame of 24bit (3bytes)
368 AudioInfo& info = header.audioInfo();
369 // in_size might be not a multiple of 3, so we use a buffer for a single frame
370 buffer24.resize(info.channels*3);
371 int result = 0;
372 int32_t frame[info.channels];
373 uint8_t val24[3]={0};
374
375 // add all bytes to buffer
376 for (int j=0;j<in_size;j++){
377 buffer24.write(in_ptr[j]);
378 // if buffer is full convert and output
379 if (buffer24.availableForWrite()==0){
380 for (int ch=0;ch<info.channels;ch++){
381 buffer24.readArray((uint8_t*)&val24[0], 3);
382 frame[ch] = interpret24bitAsInt32(val24);
383 //LOGW("%d", frame[ch]);
384 }
385 assert(buffer24.available()==0);
386 buffer24.reset();
387 size_t written = out().write((uint8_t*)frame,sizeof(frame));
388 assert(written==sizeof(frame));
389 result += written;
390 }
391 }
392 return result;
393 }
394
395 int32_t interpret24bitAsInt32(uint8_t* byteArray) {
396 return (
397 (static_cast<int32_t>(byteArray[2]) << 24)
398 | (static_cast<int32_t>(byteArray[1]) << 16)
399 | (static_cast<int32_t>(byteArray[0]) << 8)
400 );
401 }
402
404 int decodeHeader(uint8_t *in_ptr, size_t in_size) {
405 int result = in_size;
406 // we expect at least the full header
407 int written = header.write(in_ptr, in_size);
408 if (!header.isDataComplete()) {
409 LOGW("WAV header misses 'data' section in len: %d", (int) header.available());
410 header.dumpHeader();
411 return 0;
412 }
413 // parse header
414 if (!header.parse()){
415 LOGE("WAV header parsing failed");
416 return 0;
417 }
418
419 isFirst = false;
420 isValid = header.audioInfo().is_valid;
421
422 LOGI("WAV sample_rate: %d", (int) header.audioInfo().sample_rate);
423 LOGI("WAV data_length: %u", (unsigned)header.audioInfo().data_length);
424 LOGI("WAV is_streamed: %d", header.audioInfo().is_streamed);
425 LOGI("WAV is_valid: %s",
426 header.audioInfo().is_valid ? "true" : "false");
427
428 // check format
429 AudioFormat format = header.audioInfo().format;
430 isValid = format == decoder_format;
431 if (isValid) {
432 // update blocksize
433 if(p_decoder!=nullptr){
434 int block_size = header.audioInfo().block_align;
435 p_decoder->setBlockSize(block_size);
436 }
437
438 // update sampling rate if the target supports it
439 AudioInfo bi;
440 bi.sample_rate = header.audioInfo().sample_rate;
441 bi.channels = header.audioInfo().channels;
442 bi.bits_per_sample = header.audioInfo().bits_per_sample;
443 notifyAudioChange(bi);
444 } else {
445 LOGE("WAV format not supported: %d", (int)format);
446 }
447 return header.getDataPos();
448 }
449
450 void setupEncodedAudio() {
451 if (p_decoder!=nullptr){
452 assert(p_print!=nullptr);
453 dec_out.setOutput(p_print);
454 dec_out.setDecoder(p_decoder);
455 dec_out.begin(info);
456 }
457 }
458};
459
469class WAVEncoder : public AudioEncoder {
470 public:
474 WAVEncoder() = default;
475
480 setEncoder(enc, fmt);
481 };
482
483 void setEncoder(AudioEncoderExt &enc, AudioFormat fmt) {
484 TRACED();
485 audioInfo.format = fmt;
486 p_encoder = &enc;
487 }
488
490 void setOutput(Print &out) override {
491 TRACED();
492 p_print = &out;
493 }
494
496 const char *mime() override { return wav_mime; }
497
498 // Provides the default configuration
499 WAVAudioInfo defaultConfig() {
500 WAVAudioInfo info;
501 info.format = AudioFormat::PCM;
502 info.sample_rate = DEFAULT_SAMPLE_RATE;
503 info.bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
504 info.channels = DEFAULT_CHANNELS;
505 info.is_streamed = true;
506 info.is_valid = true;
507 info.data_length = 0x7fff0000;
508 info.file_size = info.data_length + 36;
509 return info;
510 }
511
513 virtual void setAudioInfo(AudioInfo from) override {
514 audioInfo.sample_rate = from.sample_rate;
515 audioInfo.channels = from.channels;
516 audioInfo.bits_per_sample = from.bits_per_sample;
517 // recalculate byte rate, block align...
518 setAudioInfo(audioInfo);
519 }
520
522 virtual void setAudioInfo(WAVAudioInfo ai) {
524 if (p_encoder) p_encoder->setAudioInfo(ai);
525 audioInfo = ai;
526 LOGI("sample_rate: %d", (int)audioInfo.sample_rate);
527 LOGI("channels: %d", audioInfo.channels);
528 // bytes per second
529 audioInfo.byte_rate = audioInfo.sample_rate * audioInfo.channels * audioInfo.bits_per_sample / 8;
530 if (audioInfo.format == AudioFormat::PCM){
531 audioInfo.block_align = audioInfo.bits_per_sample / 8 * audioInfo.channels;
532 }
533 if (audioInfo.is_streamed || audioInfo.data_length == 0 ||
534 audioInfo.data_length >= 0x7fff0000) {
535 LOGI("is_streamed! because length is %u",
536 (unsigned)audioInfo.data_length);
537 audioInfo.is_streamed = true;
538 audioInfo.data_length = ~0;
539 } else {
540 size_limit = audioInfo.data_length;
541 LOGI("size_limit is %d", (int)size_limit);
542 }
543 }
544
547 header.clear();
548 setAudioInfo(ai);
549 return begin();
550 }
551
553 virtual bool begin() override {
554 TRACED();
555 setupEncodedAudio();
556 header_written = false;
557 is_open = true;
558 return true;
559 }
560
562 void end() override { is_open = false; }
563
565 virtual size_t write(const uint8_t *data, size_t len) override {
566 if (!is_open) {
567 LOGE("The WAVEncoder is not open - please call begin()");
568 return 0;
569 }
570
571 if (p_print == nullptr) {
572 LOGE("No output stream was provided");
573 return 0;
574 }
575
576 if (!header_written) {
577 LOGI("Writing Header");
578 header.setAudioInfo(audioInfo);
579 int len = header.writeHeader(p_print);
580 audioInfo.file_size -= len;
581 header_written = true;
582 }
583
584 int32_t result = 0;
585 Print *p_out = p_encoder==nullptr ? p_print : &enc_out;;
586 if (audioInfo.is_streamed) {
587 result = p_out->write((uint8_t *)data, len);
588 } else if (size_limit > 0) {
589 size_t write_size = min((size_t)len, (size_t)size_limit);
590 result = p_out->write((uint8_t *)data, write_size);
591 size_limit -= result;
592
593 if (size_limit <= 0) {
594 LOGI("The defined size was written - so we close the WAVEncoder now");
595 is_open = false;
596 }
597 }
598 return result;
599 }
600
601 operator bool() override { return is_open; }
602
603 bool isOpen() { return is_open; }
604
606 void setDataOffset(uint16_t offset) { audioInfo.offset = offset; }
607
608 protected:
609 WAVHeader header;
610 Print *p_print = nullptr; // final output CopyEncoder copy; // used for PCM
611 AudioEncoderExt *p_encoder = nullptr;
612 EncodedAudioOutput enc_out;
613 WAVAudioInfo audioInfo = defaultConfig();
614 int64_t size_limit = 0;
615 bool header_written = false;
616 volatile bool is_open = false;
617
618 void setupEncodedAudio() {
619 if (p_encoder!=nullptr){
620 assert(p_print!=nullptr);
621 enc_out.setOutput(p_print);
622 enc_out.setEncoder(p_encoder);
623 enc_out.setAudioInfo(audioInfo);
624 enc_out.begin();
625 // block size only available after begin(): update block size
626 audioInfo.block_align = p_encoder->blockSize();
627 }
628 }
629};
630
631} // namespace audio_tools
WAV Audio Formats used by Microsoft e.g. in AVI video files.
Definition AudioCodecsBase.h:106
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
Definition AudioCodecsBase.h:111
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 writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
void clear()
same as reset
Definition Buffers.h:95
A more natural Print class to process encoded data (aac, wav, mp3...). Just define the output and the...
Definition AudioEncoded.h:21
void setOutput(Print &outputStream) override
Defines/Changes the output target.
Definition AudioEncoded.h:97
bool begin() override
Starts the processing - sets the status to active.
Definition AudioEncoded.h:137
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioEncoded.h:87
Definition NoArduino.h:62
void setClearWithZero(bool flag)
Sets the buffer to 0 on clear.
Definition Buffers.h:281
int available() override
provides the number of entries that are available to read
Definition Buffers.h:218
T * data()
Provides address of actual data.
Definition Buffers.h:252
void reset() override
clears the buffer
Definition Buffers.h:254
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual int indexOf(const char c, int start=0)
Definition StrView.h:260
A simple WAVDecoder: We parse the header data on the first record to determine the format....
Definition CodecWAV.h:271
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecWAV.h:294
void setDecoder(AudioDecoderExt &dec, AudioFormat fmt)
Defines an optional decoder if the format is not PCM.
Definition CodecWAV.h:287
int decodeHeader(uint8_t *in_ptr, size_t in_size)
Decodes the header data: Returns the start pos of the data.
Definition CodecWAV.h:404
WAVDecoder()=default
Construct a new WAVDecoder object for PCM data.
WAVDecoder(AudioDecoderExt &dec, AudioFormat fmt)
Construct a new WAVDecoder object for ADPCM data.
Definition CodecWAV.h:282
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition CodecWAV.h:316
A simple WAV file encoder. If no AudioEncoderExt is specified the WAV file contains PCM data,...
Definition CodecWAV.h:469
virtual size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as WAV.
Definition CodecWAV.h:565
void setOutput(Print &out) override
Defines the otuput stream.
Definition CodecWAV.h:490
virtual bool begin() override
starts the processing using the actual WAVAudioInfo
Definition CodecWAV.h:553
bool begin(WAVAudioInfo ai)
starts the processing
Definition CodecWAV.h:546
void end() override
stops the processing
Definition CodecWAV.h:562
virtual void setAudioInfo(WAVAudioInfo ai)
Defines the WAVAudioInfo.
Definition CodecWAV.h:522
WAVEncoder()=default
Construct a new WAVEncoder object for PCM data.
void setDataOffset(uint16_t offset)
Adds n empty bytes at the beginning of the data.
Definition CodecWAV.h:606
WAVEncoder(AudioEncoderExt &enc, AudioFormat fmt)
Construct a new WAVEncoder object for ADPCM data.
Definition CodecWAV.h:479
const char * mime() override
Provides "audio/wav".
Definition CodecWAV.h:496
virtual void setAudioInfo(AudioInfo from) override
Update actual WAVAudioInfo.
Definition CodecWAV.h:513
Parser for Wav header data for details see https://de.wikipedia.org/wiki/RIFF_WAVE.
Definition CodecWAV.h:46
bool parse()
Call begin when header data is complete to parse the data.
Definition CodecWAV.h:56
bool isDataComplete()
Returns true if the header is complete (containd data tag)
Definition CodecWAV.h:85
void setAudioInfo(WAVAudioInfo info)
Sets the info in the header.
Definition CodecWAV.h:105
size_t available()
number of bytes available in the header buffer
Definition CodecWAV.h:91
int getDataPos()
Determines the data start position using the data tag.
Definition CodecWAV.h:96
WAVAudioInfo & audioInfo()
provides the info from the header
Definition CodecWAV.h:102
int write(uint8_t *data, size_t data_len)
Adds data to the 44 byte wav header data buffer and make it available for parsing.
Definition CodecWAV.h:51
int writeHeader(Print *out)
Just write a wav header to the indicated outputbu.
Definition CodecWAV.h:110
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:19
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
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
Sound information which is available in the WAV header.
Definition CodecWAV.h:19