arduino-audio-tools
Loading...
Searching...
No Matches
CodecWAV.h
Go to the documentation of this file.
1#pragma once
2
7
8#define READ_BUFFER_SIZE 512
9#define MAX_WAV_HEADER_LEN 200
14#define MAX_WAV_HEADER_LEN_LIMIT 2048
20#define WAV_ENCODER_COMPRESSED_CHUNK_SIZE 1024
21
22namespace audio_tools {
23
31 WAVAudioInfo() = default;
32 WAVAudioInfo(const AudioInfo &from) {
34 channels = from.channels;
36 }
37
39 int byte_rate = 0;
40 int block_align = 0;
41 bool is_streamed = true;
42 bool is_valid = false;
43 uint32_t data_length = 0;
44 uint32_t file_size = 0;
45 int offset = 0;
47 bool ext_adpcm_header = false;
48};
49
50static const char *wav_mime = "audio/wav";
51
78class WAVHeader {
79 public:
80 WAVHeader() = default;
81
86 int write(uint8_t *data, size_t data_len) {
87 size_t needed = (size_t)buffer.available() + data_len;
88 if (needed > buffer.size() && buffer.size() < MAX_WAV_HEADER_LEN_LIMIT) {
89 size_t new_size = needed < (size_t)MAX_WAV_HEADER_LEN_LIMIT
90 ? needed
92 buffer.resize(new_size);
93 }
94 return buffer.writeArray(data, data_len);
95 }
96
98 bool parse() {
99 LOGI("WAVHeader::begin: %u", (unsigned)buffer.available());
100 this->data_pos = 0l;
101 memset((void *)&headerInfo, 0, sizeof(WAVAudioInfo));
102
103 if (!setPos("RIFF")) return false;
104 // RIFF stores chunk_size (= file_size - 8): normalize to full file size
106 if (!setPos("WAVE")) return false;
107 if (!setPos("fmt ")) return false;
108 int fmt_length = read_int32();
115 if (!setPos("data")) return false;
117 if (headerInfo.data_length == 0 || headerInfo.data_length >= 0x7fff0000) {
118 headerInfo.is_streamed = true;
120 }
121
122 logInfo();
123 buffer.clear();
124 return true;
125 }
126
129 int pos = getDataPos();
130 return pos > 0 && buffer.available() >= pos;
131 }
132
136 bool isOverflow() {
138 }
139
141 size_t available() { return buffer.available(); }
142
145 int pos =
147 .indexOf("data");
148 return pos > 0 ? pos + 8 : 0;
149 }
150
153
155 void setAudioInfo(WAVAudioInfo info) { headerInfo = info; }
156
158 bool writeHeader(Print *out) {
159 return writeHeader(out, headerInfo);
160 }
161
163 bool writeHeader(Print *out, const WAVAudioInfo &info) {
164 // reset first: buffer otherwise keeps accumulating bytes from earlier calls
165 buffer.reset();
166 writeRiffHeader(buffer, info);
167 writeFMT(buffer, info);
168 if (isADPCM(info.format) && info.ext_adpcm_header) {
169 writeFactChunk(buffer, info);
170 }
171 writeDataHeader(buffer, info);
172 int len = buffer.available();
173 int written = out->write(buffer.data(), len);
174 if (written != len) {
175 LOGE("Failed to write WAV header to output: written %d of %d bytes", written, len);
176 }
177 return written == len;
178 }
179
184 static int extraHeaderBytes(const WAVAudioInfo &info) {
185 if (!info.ext_adpcm_header) return 0;
186 switch (info.format) {
187 case AudioFormat::ADPCM: // MS ADPCM: 34 byte fmt extension + 12 byte fact chunk
188 return 34 + 12;
189 case AudioFormat::DVI_ADPCM: // IMA/DVI ADPCM: 4 byte fmt extension + 12 byte fact chunk
190 return 4 + 12;
191 default:
192 return 0;
193 }
194 }
195
197 void clear() {
198 data_pos = 0;
199 WAVAudioInfo empty;
200 empty.sample_rate = 0;
201 empty.channels = 0;
202 empty.bits_per_sample = 0;
203 headerInfo = empty;
205 buffer.reset();
206 }
207
209 void dumpHeader() {
210 char msg[buffer.available() + 1];
211 memset(msg, 0, buffer.available() + 1);
212 for (int j = 0; j < buffer.available(); j++) {
213 char c = (char)buffer.data()[j];
214 if (!isalpha(c)) {
215 c = '.';
216 }
217 msg[j] = c;
218 }
219 LOGI("Header: %s", msg);
220 }
221
222 protected:
225 size_t data_pos = 0;
226
227 bool setPos(const char *id) {
228 int id_len = strlen(id);
229 int pos = indexOf(id);
230 if (pos < 0) return false;
231 data_pos = pos + id_len;
232 return true;
233 }
234
235 int indexOf(const char *str) {
236 return StrView((char *)buffer.data(), MAX_WAV_HEADER_LEN,
238 .indexOf(str);
239 }
240
241 uint32_t read_tag() {
242 uint32_t tag = 0;
243 tag = (tag << 8) | getChar();
244 tag = (tag << 8) | getChar();
245 tag = (tag << 8) | getChar();
246 tag = (tag << 8) | getChar();
247 return tag;
248 }
249
250 uint32_t getChar32() { return getChar(); }
251
252 uint32_t read_int32() {
253 uint32_t value = 0;
254 value |= getChar32() << 0;
255 value |= getChar32() << 8;
256 value |= getChar32() << 16;
257 value |= getChar32() << 24;
258 return value;
259 }
260
261 uint16_t read_int16() {
262 uint16_t value = 0;
263 value |= getChar() << 0;
264 value |= getChar() << 8;
265 return value;
266 }
267
268 void skip(int n) {
269 int i;
270 for (i = 0; i < n; i++) getChar();
271 }
272
273 int getChar() {
274 if (data_pos < buffer.size())
275 return buffer.data()[data_pos++];
276 else
277 return -1;
278 }
279
280 void seek(long int offset, int origin) {
281 if (origin == SEEK_SET) {
282 data_pos = offset;
283 } else if (origin == SEEK_CUR) {
284 data_pos += offset;
285 }
286 }
287
288 size_t tell() { return data_pos; }
289
290 bool eof() { return data_pos >= buffer.size() - 1; }
291
292 void logInfo() {
293 LOGI("WAVHeader sound_pos: %d", getDataPos());
294 LOGI("WAVHeader channels: %d ", headerInfo.channels);
295 LOGI("WAVHeader bits_per_sample: %d", headerInfo.bits_per_sample);
296 LOGI("WAVHeader sample_rate: %d ", (int)headerInfo.sample_rate);
297 LOGI("WAVHeader format: %d", (int)headerInfo.format);
298 }
299
301 const WAVAudioInfo &info) {
302 buffer.writeArray((uint8_t *)"RIFF", 4);
303 // chunk_size = file_size - 8 (RIFF header size)
304 uint32_t chunk_size = info.file_size > 8 ? info.file_size - 8 : 0;
305 LOGI("writeRiffHeader: file_size=%u riff_size=%u", info.file_size, chunk_size);
306 write32(buffer, chunk_size);
307 buffer.writeArray((uint8_t *)"WAVE", 4);
308 }
309
311 bool is_ms_adpcm = info.ext_adpcm_header && info.format == AudioFormat::ADPCM;
312 bool is_ima_adpcm = info.ext_adpcm_header && info.format == AudioFormat::DVI_ADPCM;
313 uint16_t fmt_len = 16;
314 if (is_ima_adpcm) fmt_len = 20;
315 else if (is_ms_adpcm) fmt_len = 50;
316
317 uint16_t spb = samplesPerBlock(info);
318 uint32_t byte_rate = info.byte_rate;
319 // use the real average byte rate for ADPCM formats whenever the block
320 // layout is known, regardless of whether the extended 'fmt '/'fact'
321 // chunk is written: byte_rate is informational (e.g. used by some
322 // players for scrubbing/duration estimates) and the linear-PCM-based
323 // default in info.byte_rate is misleading for compressed formats
324 if (isADPCM(info.format) && spb > 0 && info.block_align > 0) {
325 // average bytes/sec = (sample_rate * block_align) / samples_per_block
326 byte_rate = ((uint64_t)info.sample_rate * info.block_align) / spb;
327 }
328
329 buffer.writeArray((uint8_t *)"fmt ", 4);
330 write32(buffer, fmt_len);
331 write16(buffer, (uint16_t)info.format);
332 write16(buffer, info.channels);
334 write32(buffer, byte_rate);
335 write16(buffer, info.block_align); // frame size
337
338 if (is_ima_adpcm) {
339 write16(buffer, 2); // cbSize: size of extra format bytes
340 write16(buffer, spb); // wSamplesPerBlock
341 } else if (is_ms_adpcm) {
342 // standard MS ADPCM coefficient table (7 predictor pairs)
343 static const int16_t ms_adpcm_coef[7][2] = {
344 {256, 0}, {512, -256}, {0, 0}, {192, 64},
345 {240, 0}, {460, -208}, {392, -232}};
346 write16(buffer, 32); // cbSize: size of extra format bytes
347 write16(buffer, spb); // wSamplesPerBlock
348 write16(buffer, 7); // wNumCoef
349 for (auto &c : ms_adpcm_coef) {
350 write16(buffer, (uint16_t)c[0]);
351 write16(buffer, (uint16_t)c[1]);
352 }
353 }
354 }
355
359 buffer.writeArray((uint8_t *)"fact", 4);
360 write32(buffer, 4); // chunk size
361 uint32_t sample_length = 0;
362 uint16_t spb = samplesPerBlock(info);
363 if (!info.is_streamed && spb > 0 && info.block_align > 0) {
364 sample_length = (info.data_length / info.block_align) * spb;
365 }
366 write32(buffer, sample_length);
367 }
368
370 buffer.writeArray((uint8_t *)"data", 4);
371 uint32_t data_length = info.data_length;
372 uint32_t header_bytes = 36 + extraHeaderBytes(info);
373 if (headerInfo.is_streamed && data_length == 0) {
374 data_length = ~0; // use max value for streamed data if not set
375 }
376 if (!headerInfo.is_streamed && info.file_size >= header_bytes && (data_length == 0 || data_length == ~0)) {
377 data_length = info.file_size - header_bytes; // data length = file size - header size
378 }
379 LOGI("writeDataHeader: data_length=%u", data_length);
380 write32(buffer, data_length);
381 int offset = info.offset;
382 if (offset > 0) {
383 uint8_t empty[offset];
384 memset(empty, 0, offset);
385 buffer.writeArray(empty, offset); // resolve issue with wrong aligment
386 }
387 }
388
389 static bool isADPCM(AudioFormat format) {
390 return format == AudioFormat::ADPCM || format == AudioFormat::DVI_ADPCM;
391 }
392
395 static uint16_t samplesPerBlock(const WAVAudioInfo &info) {
396 if (info.channels <= 0 || info.block_align <= 0) return 0;
397 switch (info.format) {
398 case AudioFormat::ADPCM: // MS ADPCM
399 return ((info.block_align / info.channels) - 7) * 2 + 2;
400 case AudioFormat::DVI_ADPCM: // IMA/DVI ADPCM
401 return ((info.block_align / info.channels) - 4) * 2 + 1;
402 default:
403 return 0;
404 }
405 }
406
407 void write32(BaseBuffer<uint8_t> &buffer, uint64_t value) {
408 buffer.writeArray((uint8_t *)&value, 4);
409 }
410
411 void write16(BaseBuffer<uint8_t> &buffer, uint16_t value) {
412 buffer.writeArray((uint8_t *)&value, 2);
413 }
414
415};
416
451class WAVDecoder : public AudioDecoder {
452
453 public:
457 WAVDecoder() = default;
458
466
479 TRACED();
480 decoders.clear();
481 addDecoder(dec, fmt);
482 }
483
493 TRACED();
494 if (fmt == AudioFormat::UNKNOWN) fmt = dec.wavFormat();
495 if (fmt == AudioFormat::UNKNOWN) {
496 LOGE(
497 "addDecoder: could not determine the WAV format tag for this "
498 "decoder - please provide it explicitly");
499 return;
500 }
501 for (int i = 0; i < decoders.size(); i++) {
502 if (decoders[i].format == fmt) {
503 decoders[i].decoder = &dec;
504 return;
505 }
506 }
507 decoders.push_back({fmt, &dec});
508 }
509
511 void setOutput(Print &out_stream) override { this->p_print = &out_stream; }
512
514 bool begin() override {
515 TRACED();
516 header.clear();
517 // close the decoder used for a previous stream (if any) before
518 // selecting a new one for this stream: the match is only known once
519 // the header has been parsed, so setupEncodedAudio() is triggered from
520 // decodeHeader() instead of here
523 buffer24.reset();
525 isFirst = true;
526 active = true;
527 return true;
528 }
529
531 void end() override {
532 TRACED();
535 buffer24.reset();
537 active = false;
538 }
539
541 const char *mime() { return wav_mime; }
542
545
547 AudioInfo audioInfo() override {
549 if (convert8to16 && info.format == AudioFormat::PCM &&
550 info.bits_per_sample == 8) {
552 }
553 // 32 bits gives better result
554 if (convert24 && info.format == AudioFormat::PCM &&
555 info.bits_per_sample == 24) {
557 }
558 // ALAW/MULAW are expanded from 8-bit logarithmic to 16-bit linear PCM
559 if (convertALawMuLaw && (info.format == AudioFormat::ALAW ||
560 info.format == AudioFormat::MULAW)) {
562 }
563 // IEEE_FLOAT can optionally be converted to 16-bit linear PCM
565 info.bits_per_sample == 32) {
567 }
568 // any other non-PCM/non-IEEE_FLOAT format (e.g. ADPCM) is decoded by
569 // p_decoder to 16-bit PCM; IEEE_FLOAT is passed through as-is
570 if (info.format != AudioFormat::PCM &&
571 info.format != AudioFormat::IEEE_FLOAT &&
572 info.format != AudioFormat::ALAW &&
573 info.format != AudioFormat::MULAW) {
575 }
576 return info;
577 }
578
580 virtual size_t write(const uint8_t *data, size_t len) override {
581 TRACED();
582 size_t result = 0;
583 if (active) {
584 if (isFirst) {
585 int data_start = decodeHeader((uint8_t *)data, len);
586 // we do not have the complete header yet: need more data
587 if (data_start == 0) return len;
588 // process the outstanding data - only if the format is supported;
589 // otherwise report 0 bytes written, consistent with subsequent
590 // write() calls once the format has been found to be invalid
591 if (isValid) {
592 result = data_start +
593 write_out((uint8_t *)data + data_start, len - data_start);
594 }
595
596 } else if (isValid) {
597 result = write_out((uint8_t *)data, len);
598 }
599 }
600 return result;
601 }
602
604 virtual operator bool() override { return active; }
605
607 void setConvert8Bit(bool enable) {
608 convert8to16 = enable;
609 }
610
612 void setConvert24Bit(bool enable) {
613 convert24 = enable;
614 }
615
619 void setConvertALawMuLaw(bool enable) {
620 convertALawMuLaw = enable;
621 }
622
627 void setConvertFloatToInt16(bool enable) {
628 convertFloatToInt16 = enable;
629 }
630
633
634 protected:
643
645 bool isFirst = true;
646 bool isValid = true;
647 bool active = false;
657 bool convert8to16 = true; // Optional conversion flag
658 bool convert24 = true; // Optional conversion flag
659 bool convertALawMuLaw = true; // Optional conversion flag
660 bool convertFloatToInt16 = true; // Optional conversion flag
661 const size_t batch_size = 256;
662
663 Print &out() { return p_decoder == nullptr ? *p_print : dec_out; }
664
665 virtual size_t write_out(const uint8_t *in_ptr, size_t in_size) {
666 // check if we need to convert int24 data from 3 bytes to 4 bytes
667 size_t result = 0;
669 if (convert24 && format == AudioFormat::PCM &&
670 header.audioInfo().bits_per_sample == 24 && sizeof(int24_t) == 4) {
671 write_out_24(in_ptr, in_size);
672 result = in_size;
673 } else if (convert8to16 && format == AudioFormat::PCM &&
675 result = write_out_8to16(in_ptr, in_size);
676 } else if (convertALawMuLaw && format == AudioFormat::ALAW) {
677 result = write_out_alaw(in_ptr, in_size);
678 } else if (convertALawMuLaw && format == AudioFormat::MULAW) {
679 result = write_out_mulaw(in_ptr, in_size);
680 } else if (convertFloatToInt16 && format == AudioFormat::IEEE_FLOAT &&
682 result = write_out_float_to_int16(in_ptr, in_size);
683 } else {
684 result = out().write(in_ptr, in_size);
685 }
686 return result;
687 }
688
690 size_t write_out_8to16(const uint8_t *in_ptr, size_t in_size) {
691 size_t samples_remaining = in_size;
692 size_t offset = 0;
693 int16_t out_buf[batch_size];
694 while (samples_remaining > 0) {
695 size_t current_batch =
696 samples_remaining > batch_size ? batch_size : samples_remaining;
697 for (size_t i = 0; i < current_batch; ++i) {
698 out_buf[i] = ((int16_t)in_ptr[offset + i] - 128) << 8;
699 }
700 writeDataT<int16_t>(&out(), out_buf, current_batch);
701 offset += current_batch;
702 samples_remaining -= current_batch;
703 }
704 return in_size;
705 }
706
708 size_t write_out_24(const uint8_t *in_ptr, size_t in_size) {
709 // store 1 sample
712
713 for (size_t i = 0; i < in_size; i++) {
714 // Add byte to buffer
715 byte_buffer.write(in_ptr[i]);
716
717 // Process complete sample when buffer is full
718 if (byte_buffer.isFull()) {
719 int24_3bytes_t sample24{byte_buffer.data()};
720 int32_t converted_sample = sample24.scale32();
721 buffer24.write(converted_sample);
722 if (buffer24.isFull()) {
723 writeDataT<int32_t>(&out(), buffer24.data(), buffer24.available());
724 buffer24.reset();
725 }
727 }
728 }
729
730 return in_size;
731 }
732
735 size_t write_out_float_to_int16(const uint8_t *in_ptr, size_t in_size) {
738
739 for (size_t i = 0; i < in_size; i++) {
740 byte_buffer.write(in_ptr[i]);
741
742 if (byte_buffer.isFull()) {
743 float sample_f;
744 memcpy(&sample_f, byte_buffer.data(), sizeof(sample_f));
745 if (sample_f > 1.0f) sample_f = 1.0f;
746 if (sample_f < -1.0f) sample_f = -1.0f;
747 buffer_float16.write((int16_t)(sample_f * 32767.0f));
748 if (buffer_float16.isFull()) {
749 writeDataT<int16_t>(&out(), buffer_float16.data(),
752 }
754 }
755 }
756
757 return in_size;
758 }
759
761 size_t write_out_alaw(const uint8_t *in_ptr, size_t in_size) {
762 size_t remaining = in_size;
763 size_t offset = 0;
764 int16_t out_buf[batch_size];
765 while (remaining > 0) {
766 size_t current_batch =
767 remaining > batch_size ? batch_size : remaining;
768 for (size_t i = 0; i < current_batch; ++i) {
769 out_buf[i] = alaw2linear(in_ptr[offset + i]);
770 }
771 writeDataT<int16_t>(&out(), out_buf, current_batch);
772 offset += current_batch;
773 remaining -= current_batch;
774 }
775 return in_size;
776 }
777
779 size_t write_out_mulaw(const uint8_t *in_ptr, size_t in_size) {
780 size_t remaining = in_size;
781 size_t offset = 0;
782 int16_t out_buf[batch_size];
783 while (remaining > 0) {
784 size_t current_batch =
785 remaining > batch_size ? batch_size : remaining;
786 for (size_t i = 0; i < current_batch; ++i) {
787 out_buf[i] = ulaw2linear(in_ptr[offset + i]);
788 }
789 writeDataT<int16_t>(&out(), out_buf, current_batch);
790 offset += current_batch;
791 remaining -= current_batch;
792 }
793 return in_size;
794 }
795
797 static int16_t alaw2linear(uint8_t a_val) {
798 a_val ^= 0x55;
799 int t = (a_val & 0x0f) << 4;
800 int seg = ((unsigned)a_val & 0x70) >> 4;
801 switch (seg) {
802 case 0:
803 t += 8;
804 break;
805 case 1:
806 t += 0x108;
807 break;
808 default:
809 t += 0x108;
810 t <<= (seg - 1);
811 break;
812 }
813 return (int16_t)((a_val & 0x80) ? t : -t);
814 }
815
817 static int16_t ulaw2linear(uint8_t u_val) {
818 u_val = ~u_val;
819 int t = ((u_val & 0x0f) << 3) + 0x84;
820 t <<= ((unsigned)u_val & 0x70) >> 4;
821 return (int16_t)((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
822 }
823
825 int decodeHeader(uint8_t *in_ptr, size_t in_size) {
826 // we expect at least the full header
827 header.write(in_ptr, in_size);
828 if (!header.isDataComplete()) {
829 if (header.isOverflow()) {
830 // the header (up to and including the 'data' chunk tag) exceeds
831 // MAX_WAV_HEADER_LEN_LIMIT without ever completing - give up
832 // instead of waiting forever for data that will never arrive
833 LOGE(
834 "WAV header exceeds the maximum supported size of %d bytes "
835 "without a 'data' chunk - aborting",
837 isFirst = false;
838 isValid = false;
839 return 0;
840 }
841 LOGW("WAV header misses 'data' section in len: %d",
842 (int)header.available());
844 return 0;
845 }
846 // parse header
847 if (!header.parse()) {
848 LOGE("WAV header parsing failed");
849 return 0;
850 }
851
852 isFirst = false;
853
854 LOGI("WAV sample_rate: %d", (int)header.audioInfo().sample_rate);
855 LOGI("WAV data_length: %u", (unsigned)header.audioInfo().data_length);
856 LOGI("WAV is_streamed: %d", header.audioInfo().is_streamed);
857
858 // select the decoder matching the format found in the header (if any
859 // was registered via setDecoder()/addDecoder()); otherwise fall back to
860 // the natively supported formats (PCM, IEEE_FLOAT, ALAW, MULAW)
862 p_decoder = findDecoder(format);
863 isValid = p_decoder != nullptr ? true : isNativelySupported(format);
864 if (isValid) {
865 if (p_decoder != nullptr) {
866 // update blocksize before begin() is called in setupEncodedAudio()
867 int block_size = header.audioInfo().block_align;
868 p_decoder->setBlockSize(block_size);
870 }
871
872 // update sampling rate if the target supports it
873 AudioInfo bi = audioInfo();
875 } else {
876 LOGE("WAV format not supported: 0x%04X", (unsigned)format);
878 }
879 return header.getDataPos();
880 }
881
886 LOGE(
887 "Natively supported: PCM (0x%04X), IEEE_FLOAT (0x%04X), ALAW "
888 "(0x%04X), MULAW (0x%04X)",
889 (unsigned)AudioFormat::PCM, (unsigned)AudioFormat::IEEE_FLOAT,
890 (unsigned)AudioFormat::ALAW, (unsigned)AudioFormat::MULAW);
891 if (decoders.empty()) {
892 LOGE(
893 "No additional decoders registered - use "
894 "setDecoder()/addDecoder() to support e.g. ADPCM formats");
895 } else {
896 for (int i = 0; i < decoders.size(); i++) {
897 LOGE("Registered decoder for format: 0x%04X",
898 (unsigned)decoders[i].format);
899 }
900 }
901 }
902
905 for (int i = 0; i < decoders.size(); i++) {
906 if (decoders[i].format == format) return decoders[i].decoder;
907 }
908 return nullptr;
909 }
910
912 if (p_decoder != nullptr) {
913 assert(p_print != nullptr);
917 }
918 }
919
924 if (p_decoder != nullptr) {
925 p_decoder->end();
926 p_decoder = nullptr;
927 }
928 }
929
931 static bool isNativelySupported(AudioFormat format) {
932 return format == AudioFormat::PCM || format == AudioFormat::IEEE_FLOAT ||
933 format == AudioFormat::ALAW || format == AudioFormat::MULAW;
934 }
935};
936
947class CountingPrint : public Print {
948 public:
949 void setOutput(Print *out) { p_out = out; }
950 size_t write(uint8_t c) override { return write(&c, 1); }
951 size_t write(const uint8_t *data, size_t len) override {
952 size_t written = p_out != nullptr ? p_out->write(data, len) : 0;
953 count += written;
954 return written;
955 }
956 size_t count = 0;
957
958 protected:
959 Print *p_out = nullptr;
960};
961
972class WAVEncoder : public AudioEncoder {
973 public:
977 WAVEncoder() = default;
978
983
986 TRACED();
987 wav_info.format = fmt;
988 p_encoder = &enc;
989 }
990
992 void setOutput(Print &out) override {
993 TRACED();
994 p_print = &out;
995 }
996
998 const char *mime() override { return wav_mime; }
999
1003 info.format = AudioFormat::PCM;
1007 info.is_streamed = true;
1008 info.is_valid = true;
1009 info.data_length = 0x7fff0000;
1010 info.file_size = info.data_length + 36;
1011 return info;
1012 }
1013
1015 virtual void setAudioInfo(AudioInfo from) override {
1017 wav_info.channels = from.channels;
1019 // recalculate byte rate, block align...
1021 }
1022
1028 virtual void setAudioInfo(WAVAudioInfo ai) {
1031 wav_info = ai;
1032 LOGI("sample_rate: %d", (int)wav_info.sample_rate);
1033 LOGI("channels: %d", wav_info.channels);
1034 LOGI("bits_per_sample: %d", wav_info.bits_per_sample);
1035 // bytes per second
1038 // uncompressed formats with a fixed size per sample: block_align is
1039 // simply the frame size. Compressed formats (e.g. ADPCM) are handled
1040 // via p_encoder instead.
1047 }
1049 }
1050
1053 setAudioInfo(ai);
1054 return begin();
1055 }
1056
1058 virtual bool begin() override {
1059 TRACED();
1060 header.clear();
1061 if (!setupEncodedAudio()) {
1062 is_open = false;
1063 return false;
1064 }
1065
1066 // normalize streaming mode and payload limits at start time
1068 wav_info.data_length >= 0x7fff0000) {
1069 LOGI("is_streamed! because length is %u",
1070 (unsigned)wav_info.data_length);
1071 wav_info.is_streamed = true;
1072 wav_info.data_length = ~0;
1073 size_limit = 0;
1074 } else {
1075 wav_info.is_streamed = false;
1077 LOGI("size_limit is %d", (int)size_limit);
1078 }
1079
1080 header_written = false;
1081 is_open = true;
1082 return true;
1083 }
1084
1086 void end() override { is_open = false; }
1087
1089 virtual size_t write(const uint8_t *data, size_t len) override {
1090 if (!is_open) {
1091 LOGE("The WAVEncoder is not open - please call begin()");
1092 return 0;
1093 }
1094
1095 if (p_print == nullptr) {
1096 LOGE("No output stream was provided");
1097 return 0;
1098 }
1099
1100 if (!header_written) {
1101 LOGI("Writing Header");
1103 LOGE("Failed to write WAV header");
1104 is_open = false;
1105 return 0;
1106 }
1107 header_written = true;
1108 }
1109
1110 size_t result = 0;
1111 Print *p_out = p_encoder == nullptr ? p_print : &enc_out;
1112
1113 if (wav_info.is_streamed) {
1114 result = p_out->write((uint8_t *)data, len);
1115 } else if (size_limit > 0) {
1116 if (p_encoder == nullptr) {
1117 // uncompressed: input bytes map 1:1 to output bytes, so we can
1118 // truncate exactly at the declared data_length boundary
1119 size_t write_size = min((size_t)len, (size_t)size_limit);
1120 result = p_out->write((uint8_t *)data, write_size);
1121 size_limit -= result;
1122 } else {
1123 // compressed: the number of encoded bytes an external encoder
1124 // produces for a given amount of PCM input is not known in advance
1125 // (and may only be emitted once a full internal block is ready), so
1126 // AudioEncoder::write()'s return value (raw PCM input bytes
1127 // accepted) cannot be used to track progress towards data_length.
1128 // Track the *actual* encoded bytes written via counting_print, and
1129 // feed the encoder in frame-aligned chunks so we can stop as soon
1130 // as data_length is reached - result then correctly reflects only
1131 // the bytes of `data` that were actually consumed, so the caller
1132 // knows what still needs to be resent (e.g. to a new file).
1133 int frame_size = wav_info.bits_per_sample / 8 * wav_info.channels;
1134 if (frame_size <= 0) frame_size = 1;
1135 size_t chunk = (WAV_ENCODER_COMPRESSED_CHUNK_SIZE / frame_size) * frame_size;
1136 if (chunk == 0) chunk = frame_size;
1137
1138 while (result < len && size_limit > 0) {
1139 size_t n = min(chunk, len - result);
1141 size_t accepted = p_out->write((uint8_t *)data + result, n);
1143 result += accepted;
1144 if (accepted < n) break; // encoder didn't accept the full chunk
1145 }
1146 }
1147
1148 if (size_limit <= 0) {
1149 LOGI("The defined size was written - so we close the WAVEncoder now");
1150 is_open = false;
1151 }
1152 }
1153 return result;
1154 }
1155
1157 operator bool() override { return is_open; }
1158
1160 bool isOpen() { return is_open; }
1161
1165 void setDataOffset(uint16_t offset) {
1166 has_pending_offset = true;
1167 pending_offset = offset;
1168 wav_info.offset = offset;
1169 }
1170
1175 void setExtADPCMHeader(bool enable) {
1177 pending_ext_adpcm_header = enable;
1178 wav_info.ext_adpcm_header = enable;
1179 }
1180
1185 void setDataLength(uint32_t data_length) {
1187 pending_data_length = data_length;
1190 }
1191
1194
1197
1198 protected:
1200 Print *p_print = nullptr; // final output CopyEncoder copy; // used for PCM
1206 int64_t size_limit = 0;
1207 bool header_written = false;
1208 volatile bool is_open = false;
1209
1210 // fields set via setDataLength()/setDataOffset()/setExtADPCMHeader() that
1211 // must survive a subsequent wholesale setAudioInfo(WAVAudioInfo)/
1212 // begin(WAVAudioInfo) call, regardless of call order - see
1213 // applyPendingOverrides()
1217 uint16_t pending_offset = 0;
1220
1227 // must run before pending_data_length below: its file_size calculation
1228 // depends on wav_info.ext_adpcm_header
1231 }
1232 if (has_pending_offset) {
1234 }
1238 (pending_data_length == 0 || pending_data_length >= 0x7fff0000);
1239 if (!wav_info.is_streamed) {
1240 // full file size = RIFF chunk (36) + format specific extra header
1241 // bytes (e.g. ADPCM 'fmt ' extension + 'fact' chunk) + data chunk
1242 // payload
1245 }
1246 }
1247 }
1248
1253 if (p_encoder != nullptr) {
1254 if (p_print == nullptr) {
1255 LOGE(
1256 "setupEncodedAudio: no output stream was provided - call "
1257 "setOutput() before begin()");
1258 return false;
1259 }
1265 enc_out.begin();
1266 // block size only available after begin(): update block size
1268 }
1269 return true;
1270 }
1271};
1272
1273} // namespace audio_tools
WAV Audio Formats used by Microsoft e.g. in AVI video files.
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define DEFAULT_BITS_PER_SAMPLE
Definition AudioToolsConfig.h:99
#define DEFAULT_CHANNELS
Definition AudioToolsConfig.h:95
#define DEFAULT_SAMPLE_RATE
Definition AudioToolsConfig.h:91
#define MAX_WAV_HEADER_LEN
Definition CodecWAV.h:9
#define MAX_WAV_HEADER_LEN_LIMIT
Definition CodecWAV.h:14
#define WAV_ENCODER_COMPRESSED_CHUNK_SIZE
Definition CodecWAV.h:20
#define assert(T)
Definition avr.h:10
Definition Arduino.h:56
virtual size_t write(const uint8_t *data, size_t len)
Definition Arduino.h:120
Extended AudioDecoder interface to support block size configuration.
Definition AudioCodecsBase.h:124
virtual void setBlockSize(int blockSize)=0
virtual AudioFormat wavFormat()
Definition AudioCodecsBase.h:133
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:19
AudioInfo info
Definition AudioCodecsBase.h:80
void end() override
Definition AudioCodecsBase.h:63
Print * p_print
Definition AudioCodecsBase.h:79
Extended AudioEncoder interface to support block size configuration.
Definition AudioCodecsBase.h:137
Encoding of PCM data.
Definition AudioCodecsBase.h:101
AudioInfo info
Definition AudioCodecsBase.h:120
void setAudioInfo(AudioInfo from) override
Defines the sample rate, number of channels and bits per sample.
Definition AudioCodecsBase.h:110
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:175
Shared functionality of all buffers.
Definition Buffers.h:23
void clear()
same as reset
Definition Buffers.h:96
Minimal Print wrapper that counts the bytes actually written to the wrapped output....
Definition CodecWAV.h:947
size_t write(uint8_t c) override
Definition CodecWAV.h:950
void setOutput(Print *out)
Definition CodecWAV.h:949
size_t write(const uint8_t *data, size_t len) override
Definition CodecWAV.h:951
size_t count
Definition CodecWAV.h:956
Print * p_out
Definition CodecWAV.h:959
A more natural Print class to process encoded data (aac, wav, mp3...). Just define the output and the...
Definition AudioEncoded.h:21
void setEncoder(AudioEncoder *encoder)
Definition AudioEncoded.h:131
bool begin() override
Starts the processing - sets the status to active.
Definition AudioEncoded.h:161
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioEncoded.h:87
void setDecoder(AudioDecoder *decoder)
Definition AudioEncoded.h:144
void setOutput(Print *outputStream)
Defines the output.
Definition AudioEncoded.h:107
A simple Buffer implementation which just uses a (dynamically sized) array.
Definition Buffers.h:189
size_t size() override
Definition Buffers.h:320
bool write(T sample) override
write add an entry to the buffer
Definition Buffers.h:223
void setClearWithZero(bool flag)
Sets the buffer to 0 on clear.
Definition Buffers.h:331
int available() override
provides the number of entries that are available to read
Definition Buffers.h:250
bool isFull() override
checks if the buffer is full
Definition Buffers.h:257
int writeArray(const T data[], int len) override
Fills the buffer data.
Definition Buffers.h:218
T * data()
Provides address of actual data.
Definition Buffers.h:301
bool resize(size_t size)
Resizes the buffer if supported: returns false if not supported.
Definition Buffers.h:322
void reset() override
clears the buffer
Definition Buffers.h:303
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:302
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
A simple WAVDecoder: We parse the header data on the first record to determine the format....
Definition CodecWAV.h:451
virtual size_t write(const uint8_t *data, size_t len) override
Write incoming WAV data (header + PCM) into output.
Definition CodecWAV.h:580
bool active
Definition CodecWAV.h:647
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition CodecWAV.h:511
bool isFirst
Definition CodecWAV.h:645
bool convertFloatToInt16
Definition CodecWAV.h:660
size_t write_out_24(const uint8_t *in_ptr, size_t in_size)
convert 3 byte int24 to 4 byte int32
Definition CodecWAV.h:708
SingleBuffer< int16_t > buffer_float16
Definition CodecWAV.h:656
void setConvertFloatToInt16(bool enable)
Definition CodecWAV.h:627
static bool isNativelySupported(AudioFormat format)
Formats that are handled internally without an external decoder.
Definition CodecWAV.h:931
Print & out()
Definition CodecWAV.h:663
void setDecoder(AudioDecoderExt &dec, AudioFormat fmt=AudioFormat::UNKNOWN)
Definition CodecWAV.h:478
Vector< DecoderEntry > decoders
decoders registered via setDecoder()/addDecoder(), keyed by format tag
Definition CodecWAV.h:652
int decodeHeader(uint8_t *in_ptr, size_t in_size)
Decodes the header data: Returns the start pos of the data.
Definition CodecWAV.h:825
size_t write_out_float_to_int16(const uint8_t *in_ptr, size_t in_size)
Definition CodecWAV.h:735
AudioDecoderExt * p_decoder
Definition CodecWAV.h:650
void setConvert8Bit(bool enable)
Convert 8 bit to 16 bit PCM data (default: enabled)
Definition CodecWAV.h:607
void setupEncodedAudio()
Definition CodecWAV.h:911
void end() override
Finish decoding and release temporary buffers.
Definition CodecWAV.h:531
void endCurrentDecoder()
Definition CodecWAV.h:923
WAVHeader & getHeader()
Access to the internal header parser and info.
Definition CodecWAV.h:632
const char * mime()
Provides MIME type "audio/wav".
Definition CodecWAV.h:541
size_t write_out_alaw(const uint8_t *in_ptr, size_t in_size)
Expand G.711 A-law 8-bit samples to 16-bit linear PCM and write out.
Definition CodecWAV.h:761
bool isValid
Definition CodecWAV.h:646
bool convertALawMuLaw
Definition CodecWAV.h:659
void logSupportedFormats()
Definition CodecWAV.h:885
AudioDecoderExt * findDecoder(AudioFormat format)
Finds the decoder registered for the given format tag, if any.
Definition CodecWAV.h:904
WAVDecoder()=default
Construct a new WAVDecoder object for PCM data.
WAVDecoder(AudioDecoderExt &dec, AudioFormat fmt=AudioFormat::UNKNOWN)
Construct a new WAVDecoder object for ADPCM data. If fmt is not provided, it is derived from dec....
Definition CodecWAV.h:463
static int16_t alaw2linear(uint8_t a_val)
Converts a G.711 A-law encoded byte to a 16-bit linear PCM sample.
Definition CodecWAV.h:797
const size_t batch_size
Definition CodecWAV.h:661
SingleBuffer< uint8_t > byte_buffer
Definition CodecWAV.h:654
WAVAudioInfo & audioInfoEx()
Extended WAV specific info (original header values)
Definition CodecWAV.h:544
size_t write_out_mulaw(const uint8_t *in_ptr, size_t in_size)
Expand G.711 mu-law 8-bit samples to 16-bit linear PCM and write out.
Definition CodecWAV.h:779
WAVHeader header
Definition CodecWAV.h:644
void addDecoder(AudioDecoderExt &dec, AudioFormat fmt=AudioFormat::UNKNOWN)
Definition CodecWAV.h:492
static int16_t ulaw2linear(uint8_t u_val)
Converts a G.711 mu-law encoded byte to a 16-bit linear PCM sample.
Definition CodecWAV.h:817
void setConvert24Bit(bool enable)
Convert 24 bit (3 byte) to 32 bit (4 byte) PCM data (default: enabled)
Definition CodecWAV.h:612
bool begin() override
Prepare decoder for a new WAV stream.
Definition CodecWAV.h:514
size_t write_out_8to16(const uint8_t *in_ptr, size_t in_size)
Convert 8-bit PCM to 16-bit PCM and write out.
Definition CodecWAV.h:690
EncodedAudioOutput dec_out
Definition CodecWAV.h:653
bool convert24
Definition CodecWAV.h:658
virtual size_t write_out(const uint8_t *in_ptr, size_t in_size)
Definition CodecWAV.h:665
AudioInfo audioInfo() override
Exposed AudioInfo (may reflect conversion flags)
Definition CodecWAV.h:547
bool convert8to16
Definition CodecWAV.h:657
SingleBuffer< int32_t > buffer24
Definition CodecWAV.h:655
void setConvertALawMuLaw(bool enable)
Definition CodecWAV.h:619
A simple WAV file encoder. If no AudioEncoderExt is specified the WAV file contains PCM data,...
Definition CodecWAV.h:972
virtual size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as WAV.
Definition CodecWAV.h:1089
void setOutput(Print &out) override
Defines the otuput stream.
Definition CodecWAV.h:992
CountingPrint counting_print
tracks the actual encoded bytes p_encoder emits to p_print (see write())
Definition CodecWAV.h:1204
volatile bool is_open
Definition CodecWAV.h:1208
uint32_t pending_data_length
Definition CodecWAV.h:1215
bool isOpen()
Check if encoder is open.
Definition CodecWAV.h:1160
bool has_pending_offset
Definition CodecWAV.h:1216
virtual bool begin() override
starts the processing using the actual WAVAudioInfo
Definition CodecWAV.h:1058
bool header_written
Definition CodecWAV.h:1207
AudioEncoderExt * p_encoder
Definition CodecWAV.h:1201
bool begin(WAVAudioInfo ai)
starts the processing
Definition CodecWAV.h:1052
WAVAudioInfo wav_info
Definition CodecWAV.h:1205
void end() override
stops the processing
Definition CodecWAV.h:1086
virtual void setAudioInfo(WAVAudioInfo ai)
Definition CodecWAV.h:1028
int64_t size_limit
Definition CodecWAV.h:1206
WAVEncoder()=default
Construct a new WAVEncoder object for PCM data.
void setDataOffset(uint16_t offset)
Definition CodecWAV.h:1165
WAVHeader & getHeader()
Access to the internal header parser and info.
Definition CodecWAV.h:1196
EncodedAudioOutput enc_out
Definition CodecWAV.h:1202
void setExtADPCMHeader(bool enable)
Definition CodecWAV.h:1175
bool setupEncodedAudio()
Definition CodecWAV.h:1252
bool has_pending_data_length
Definition CodecWAV.h:1214
WAVAudioInfo & audioInfoEx()
Extended WAV specific info.
Definition CodecWAV.h:1193
bool pending_ext_adpcm_header
Definition CodecWAV.h:1219
WAVEncoder(AudioEncoderExt &enc, AudioFormat fmt)
Construct a new WAVEncoder object for ADPCM data.
Definition CodecWAV.h:982
WAVAudioInfo defaultConfig()
Provides the default configuration.
Definition CodecWAV.h:1001
WAVHeader header
Definition CodecWAV.h:1199
const char * mime() override
Provides "audio/wav".
Definition CodecWAV.h:998
virtual void setAudioInfo(AudioInfo from) override
Update actual WAVAudioInfo.
Definition CodecWAV.h:1015
Print * p_print
Definition CodecWAV.h:1200
void applyPendingOverrides()
Definition CodecWAV.h:1226
void setEncoder(AudioEncoderExt &enc, AudioFormat fmt)
Associates an external encoder for non-PCM formats.
Definition CodecWAV.h:985
uint16_t pending_offset
Definition CodecWAV.h:1217
bool has_pending_ext_adpcm_header
Definition CodecWAV.h:1218
void setDataLength(uint32_t data_length)
Definition CodecWAV.h:1185
Parser for Wav header data for details see https://de.wikipedia.org/wiki/RIFF_WAVE.
Definition CodecWAV.h:78
void writeDataHeader(BaseBuffer< uint8_t > &buffer, const WAVAudioInfo &info)
Definition CodecWAV.h:369
void logInfo()
Definition CodecWAV.h:292
void write16(BaseBuffer< uint8_t > &buffer, uint16_t value)
Definition CodecWAV.h:411
bool parse()
Call when header data write is complete to parse the data.
Definition CodecWAV.h:98
bool isDataComplete()
Returns true if the header is complete (containd data tag)
Definition CodecWAV.h:128
void skip(int n)
Definition CodecWAV.h:268
void seek(long int offset, int origin)
Definition CodecWAV.h:280
void setAudioInfo(WAVAudioInfo info)
Sets the info in the header.
Definition CodecWAV.h:155
bool eof()
Definition CodecWAV.h:290
void writeFMT(BaseBuffer< uint8_t > &buffer, const WAVAudioInfo &info)
Definition CodecWAV.h:310
bool writeHeader(Print *out, const WAVAudioInfo &info)
Just write a wav header with explicit info to the indicated output.
Definition CodecWAV.h:163
size_t available()
number of bytes available in the header buffer
Definition CodecWAV.h:141
bool writeHeader(Print *out)
Just write a wav header to the indicated outputbu.
Definition CodecWAV.h:158
int getChar()
Definition CodecWAV.h:273
int getDataPos()
Determines the data start position using the data tag.
Definition CodecWAV.h:144
SingleBuffer< uint8_t > buffer
Definition CodecWAV.h:224
WAVAudioInfo & audioInfo()
provides the info from the header
Definition CodecWAV.h:152
static uint16_t samplesPerBlock(const WAVAudioInfo &info)
Definition CodecWAV.h:395
static bool isADPCM(AudioFormat format)
Definition CodecWAV.h:389
int write(uint8_t *data, size_t data_len)
Definition CodecWAV.h:86
void clear()
Reset internal stored header information and buffer.
Definition CodecWAV.h:197
uint32_t read_tag()
Definition CodecWAV.h:241
void writeRiffHeader(BaseBuffer< uint8_t > &buffer, const WAVAudioInfo &info)
Definition CodecWAV.h:300
WAVAudioInfo headerInfo
Definition CodecWAV.h:223
uint16_t read_int16()
Definition CodecWAV.h:261
bool isOverflow()
Definition CodecWAV.h:136
bool setPos(const char *id)
Definition CodecWAV.h:227
uint32_t read_int32()
Definition CodecWAV.h:252
int indexOf(const char *str)
Definition CodecWAV.h:235
void writeFactChunk(BaseBuffer< uint8_t > &buffer, const WAVAudioInfo &info)
Definition CodecWAV.h:358
size_t data_pos
Definition CodecWAV.h:225
static int extraHeaderBytes(const WAVAudioInfo &info)
Definition CodecWAV.h:184
void write32(BaseBuffer< uint8_t > &buffer, uint64_t value)
Definition CodecWAV.h:407
void dumpHeader()
Debug helper: dumps header bytes as printable characters.
Definition CodecWAV.h:209
size_t tell()
Definition CodecWAV.h:288
uint32_t getChar32()
Definition CodecWAV.h:250
24bit integer which is used for I2S sound processing. The values are really using 3 bytes....
Definition int24_3bytes_t.h:21
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition int24_4bytes_t.h:22
AudioFormat
Audio format codes used by Microsoft e.g. in avi or wav files.
Definition AudioFormat.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static const char * wav_mime
Definition CodecWAV.h:50
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
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
Sound information which is available in the WAV header.
Definition CodecWAV.h:30
AudioFormat format
Definition CodecWAV.h:38
bool is_streamed
Definition CodecWAV.h:41
bool is_valid
Definition CodecWAV.h:42
int block_align
Definition CodecWAV.h:40
WAVAudioInfo(const AudioInfo &from)
Definition CodecWAV.h:32
uint32_t file_size
Definition CodecWAV.h:44
int offset
Definition CodecWAV.h:45
int byte_rate
Definition CodecWAV.h:39
uint32_t data_length
Definition CodecWAV.h:43
bool ext_adpcm_header
write the extended 'fmt ' chunk + 'fact' chunk for ADPCM formats
Definition CodecWAV.h:47
Associates a WAV format tag with the decoder responsible for it.
Definition CodecWAV.h:636
AudioFormat format
Definition CodecWAV.h:637
DecoderEntry()=default
AudioDecoderExt * decoder
Definition CodecWAV.h:638
DecoderEntry(AudioFormat fmt, AudioDecoderExt *dec)
Definition CodecWAV.h:640