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