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  memset(&headerInfo,0,sizeof(WAVAudioInfo));
121  buffer.setClearWithZero(true);
122  buffer.reset();
123  }
124 
125  void dumpHeader() {
126  char msg[buffer.available()+1] = {0};
127  for (int j = 0; j< buffer.available();j++){
128  char c = (char)buffer.data()[j];
129  if (!isalpha(c)){
130  c = '.';
131  }
132  msg[j] = c;
133  }
134  LOGI("Header: %s", msg);
135  }
136 
137 
138  protected:
139  struct WAVAudioInfo headerInfo;
140  SingleBuffer<uint8_t> buffer{MAX_WAV_HEADER_LEN};
141  size_t data_pos = 0;
142 
143  bool setPos(const char*id){
144  int id_len = strlen(id);
145  int pos = indexOf(id);
146  if (pos < 0) return false;
147  data_pos = pos + id_len;
148  return true;
149  }
150 
151  int indexOf(const char* str){
152  return StrView((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf(str);
153  }
154 
155  uint32_t read_tag() {
156  uint32_t tag = 0;
157  tag = (tag << 8) | getChar();
158  tag = (tag << 8) | getChar();
159  tag = (tag << 8) | getChar();
160  tag = (tag << 8) | getChar();
161  return tag;
162  }
163 
164  uint32_t getChar32() { return getChar(); }
165 
166  uint32_t read_int32() {
167  uint32_t value = 0;
168  value |= getChar32() << 0;
169  value |= getChar32() << 8;
170  value |= getChar32() << 16;
171  value |= getChar32() << 24;
172  return value;
173  }
174 
175  uint16_t read_int16() {
176  uint16_t value = 0;
177  value |= getChar() << 0;
178  value |= getChar() << 8;
179  return value;
180  }
181 
182  void skip(int n) {
183  int i;
184  for (i = 0; i < n; i++) getChar();
185  }
186 
187  int getChar() {
188  if (data_pos < buffer.size())
189  return buffer.data()[data_pos++];
190  else
191  return -1;
192  }
193 
194  void seek(long int offset, int origin) {
195  if (origin == SEEK_SET) {
196  data_pos = offset;
197  } else if (origin == SEEK_CUR) {
198  data_pos += offset;
199  }
200  }
201 
202  size_t tell() { return data_pos; }
203 
204  bool eof() { return data_pos >= buffer.size() - 1; }
205 
206  void logInfo() {
207  LOGI("WAVHeader sound_pos: %d", getDataPos());
208  LOGI("WAVHeader channels: %d ", headerInfo.channels);
209  LOGI("WAVHeader bits_per_sample: %d", headerInfo.bits_per_sample);
210  LOGI("WAVHeader sample_rate: %d ", (int) headerInfo.sample_rate);
211  LOGI("WAVHeader format: %d", (int)headerInfo.format);
212  }
213 
214  void writeRiffHeader(BaseBuffer<uint8_t> &buffer) {
215  buffer.writeArray((uint8_t *)"RIFF", 4);
216  write32(buffer, headerInfo.file_size - 8);
217  buffer.writeArray((uint8_t *)"WAVE", 4);
218  }
219 
220  void writeFMT(BaseBuffer<uint8_t> &buffer) {
221  uint16_t fmt_len = 16;
222  buffer.writeArray((uint8_t *)"fmt ", 4);
223  write32(buffer, fmt_len);
224  write16(buffer, (uint16_t)headerInfo.format); // PCM
225  write16(buffer, headerInfo.channels);
226  write32(buffer, headerInfo.sample_rate);
227  write32(buffer, headerInfo.byte_rate);
228  write16(buffer, headerInfo.block_align); // frame size
229  write16(buffer, headerInfo.bits_per_sample);
230  }
231 
232  void write32(BaseBuffer<uint8_t> &buffer, uint64_t value) {
233  buffer.writeArray((uint8_t *)&value, 4);
234  }
235 
236  void write16(BaseBuffer<uint8_t> &buffer, uint16_t value) {
237  buffer.writeArray((uint8_t *)&value, 2);
238  }
239 
240  void writeDataHeader(BaseBuffer<uint8_t> &buffer) {
241  buffer.writeArray((uint8_t *)"data", 4);
242  write32(buffer, headerInfo.file_size);
243  int offset = headerInfo.offset;
244  if (offset > 0) {
245  uint8_t empty[offset];
246  memset(empty, 0, offset);
247  buffer.writeArray(empty, offset); // resolve issue with wrong aligment
248  }
249  }
250 
251 };
252 
265 class WAVDecoder : public AudioDecoder {
266  public:
270  WAVDecoder() = default;
271 
277  setDecoder(dec, fmt);
278  }
279 
282  TRACED();
283  decoder_format = fmt;
284  p_decoder = &dec;
285  }
286 
288  void setOutput(Print &out_stream) override { this->p_print = &out_stream; }
289 
290  bool begin() override {
291  TRACED();
292  header.clear();
293  setupEncodedAudio();
294  buffer24.reset();
295  isFirst = true;
296  active = true;
297  return true;
298  }
299 
300  void end() override {
301  TRACED();
302  buffer24.reset();
303  active = false;
304  }
305 
306  const char *mime() { return wav_mime; }
307 
308  WAVAudioInfo &audioInfoEx() { return header.audioInfo(); }
309 
310  AudioInfo audioInfo() override { return header.audioInfo(); }
311 
312  virtual size_t write(const uint8_t *data, size_t len) override {
313  TRACED();
314  size_t result = 0;
315  if (active) {
316  if (isFirst) {
317  int data_start = decodeHeader((uint8_t*) data, len);
318  // we do not have the complete header yet: need more data
319  if (data_start == 0) return len;
320  // process the outstanding data
321  result = data_start + write_out((uint8_t *)data+data_start, len-data_start);
322 
323  } else if (isValid) {
324  result = write_out((uint8_t *)data, len);
325  }
326  }
327  return result;
328  }
329 
330  virtual operator bool() override { return active; }
331 
332  protected:
333  WAVHeader header;
334  bool isFirst = true;
335  bool isValid = true;
336  bool active = false;
337  AudioFormat decoder_format = AudioFormat::PCM;
338  AudioDecoderExt *p_decoder = nullptr;
339  EncodedAudioOutput dec_out;
340  SingleBuffer<uint8_t> buffer24;
341 
342  Print& out() {
343  return p_decoder==nullptr ? *p_print : dec_out;
344  }
345 
346  virtual size_t write_out(const uint8_t *in_ptr, size_t in_size) {
347  // check if we need to convert int24 data from 3 bytes to 4 bytes
348  size_t result = 0;
349  if (header.audioInfo().format == AudioFormat::PCM
350  && header.audioInfo().bits_per_sample == 24 && sizeof(int24_t)==4){
351  write_out_24(in_ptr, in_size);
352  result = in_size;
353  } else {
354  result = out().write(in_ptr, in_size);
355  }
356  return result;
357  }
358 
359  // convert int24 to int32
360  size_t write_out_24(const uint8_t *in_ptr, size_t in_size) {
361  // make sure we can store a frame of 24bit (3bytes)
362  AudioInfo& info = header.audioInfo();
363  // in_size might be not a multiple of 3, so we use a buffer for a single frame
364  buffer24.resize(info.channels*3);
365  int result = 0;
366  int32_t frame[info.channels];
367  uint8_t val24[3]={0};
368 
369  // add all bytes to buffer
370  for (int j=0;j<in_size;j++){
371  buffer24.write(in_ptr[j]);
372  // if buffer is full convert and output
373  if (buffer24.availableForWrite()==0){
374  for (int ch=0;ch<info.channels;ch++){
375  buffer24.readArray((uint8_t*)&val24[0], 3);
376  frame[ch] = interpret24bitAsInt32(val24);
377  //LOGW("%d", frame[ch]);
378  }
379  assert(buffer24.available()==0);
380  buffer24.reset();
381  size_t written = out().write((uint8_t*)frame,sizeof(frame));
382  assert(written==sizeof(frame));
383  result += written;
384  }
385  }
386  return result;
387  }
388 
389  int32_t interpret24bitAsInt32(uint8_t* byteArray) {
390  return (
391  (static_cast<int32_t>(byteArray[2]) << 24)
392  | (static_cast<int32_t>(byteArray[1]) << 16)
393  | (static_cast<int32_t>(byteArray[0]) << 8)
394  );
395  }
396 
398  int decodeHeader(uint8_t *in_ptr, size_t in_size) {
399  int result = in_size;
400  // we expect at least the full header
401  int written = header.write(in_ptr, in_size);
402  if (!header.isDataComplete()) {
403  LOGW("WAV header misses 'data' section in len: %d", (int) header.available());
404  header.dumpHeader();
405  return 0;
406  }
407  // parse header
408  if (!header.parse()){
409  LOGE("WAV header parsing failed");
410  return 0;
411  }
412 
413  isFirst = false;
414  isValid = header.audioInfo().is_valid;
415 
416  LOGI("WAV sample_rate: %d", (int) header.audioInfo().sample_rate);
417  LOGI("WAV data_length: %u", (unsigned)header.audioInfo().data_length);
418  LOGI("WAV is_streamed: %d", header.audioInfo().is_streamed);
419  LOGI("WAV is_valid: %s",
420  header.audioInfo().is_valid ? "true" : "false");
421 
422  // check format
423  AudioFormat format = header.audioInfo().format;
424  isValid = format == decoder_format;
425  if (isValid) {
426  // update blocksize
427  if(p_decoder!=nullptr){
428  int block_size = header.audioInfo().block_align;
429  p_decoder->setBlockSize(block_size);
430  }
431 
432  // update sampling rate if the target supports it
433  AudioInfo bi;
434  bi.sample_rate = header.audioInfo().sample_rate;
435  bi.channels = header.audioInfo().channels;
436  bi.bits_per_sample = header.audioInfo().bits_per_sample;
437  notifyAudioChange(bi);
438  } else {
439  LOGE("WAV format not supported: %d", (int)format);
440  }
441  return header.getDataPos();
442  }
443 
444  void setupEncodedAudio() {
445  if (p_decoder!=nullptr){
446  assert(p_print!=nullptr);
447  dec_out.setOutput(p_print);
448  dec_out.setDecoder(p_decoder);
449  dec_out.begin(info);
450  }
451  }
452 };
453 
463 class WAVEncoder : public AudioEncoder {
464  public:
468  WAVEncoder() = default;
469 
474  setEncoder(enc, fmt);
475  };
476 
477  void setEncoder(AudioEncoderExt &enc, AudioFormat fmt) {
478  TRACED();
479  audioInfo.format = fmt;
480  p_encoder = &enc;
481  }
482 
484  void setOutput(Print &out) override {
485  TRACED();
486  p_print = &out;
487  }
488 
490  const char *mime() override { return wav_mime; }
491 
492  // Provides the default configuration
493  WAVAudioInfo defaultConfig() {
494  WAVAudioInfo info;
495  info.format = AudioFormat::PCM;
496  info.sample_rate = DEFAULT_SAMPLE_RATE;
497  info.bits_per_sample = DEFAULT_BITS_PER_SAMPLE;
498  info.channels = DEFAULT_CHANNELS;
499  info.is_streamed = true;
500  info.is_valid = true;
501  info.data_length = 0x7fff0000;
502  info.file_size = info.data_length + 36;
503  return info;
504  }
505 
507  virtual void setAudioInfo(AudioInfo from) override {
508  audioInfo.sample_rate = from.sample_rate;
509  audioInfo.channels = from.channels;
510  audioInfo.bits_per_sample = from.bits_per_sample;
511  // recalculate byte rate, block align...
512  setAudioInfo(audioInfo);
513  }
514 
516  virtual void setAudioInfo(WAVAudioInfo ai) {
518  if (p_encoder) p_encoder->setAudioInfo(ai);
519  audioInfo = ai;
520  LOGI("sample_rate: %d", (int)audioInfo.sample_rate);
521  LOGI("channels: %d", audioInfo.channels);
522  // bytes per second
523  audioInfo.byte_rate = audioInfo.sample_rate * audioInfo.channels * audioInfo.bits_per_sample / 8;
524  if (audioInfo.format == AudioFormat::PCM){
525  audioInfo.block_align = audioInfo.bits_per_sample / 8 * audioInfo.channels;
526  }
527  if (audioInfo.is_streamed || audioInfo.data_length == 0 ||
528  audioInfo.data_length >= 0x7fff0000) {
529  LOGI("is_streamed! because length is %u",
530  (unsigned)audioInfo.data_length);
531  audioInfo.is_streamed = true;
532  audioInfo.data_length = ~0;
533  } else {
534  size_limit = audioInfo.data_length;
535  LOGI("size_limit is %d", (int)size_limit);
536  }
537  }
538 
540  bool begin(WAVAudioInfo ai) {
541  header.clear();
542  setAudioInfo(ai);
543  return begin();
544  }
545 
547  virtual bool begin() override {
548  TRACED();
549  setupEncodedAudio();
550  header_written = false;
551  is_open = true;
552  return true;
553  }
554 
556  void end() override { is_open = false; }
557 
559  virtual size_t write(const uint8_t *data, size_t len) override {
560  if (!is_open) {
561  LOGE("The WAVEncoder is not open - please call begin()");
562  return 0;
563  }
564 
565  if (p_print == nullptr) {
566  LOGE("No output stream was provided");
567  return 0;
568  }
569 
570  if (!header_written) {
571  LOGI("Writing Header");
572  header.setAudioInfo(audioInfo);
573  int len = header.writeHeader(p_print);
574  audioInfo.file_size -= len;
575  header_written = true;
576  }
577 
578  int32_t result = 0;
579  Print *p_out = p_encoder==nullptr ? p_print : &enc_out;;
580  if (audioInfo.is_streamed) {
581  result = p_out->write((uint8_t *)data, len);
582  } else if (size_limit > 0) {
583  size_t write_size = min((size_t)len, (size_t)size_limit);
584  result = p_out->write((uint8_t *)data, write_size);
585  size_limit -= result;
586 
587  if (size_limit <= 0) {
588  LOGI("The defined size was written - so we close the WAVEncoder now");
589  is_open = false;
590  }
591  }
592  return result;
593  }
594 
595  operator bool() override { return is_open; }
596 
597  bool isOpen() { return is_open; }
598 
600  void setDataOffset(uint16_t offset) { audioInfo.offset = offset; }
601 
602  protected:
603  WAVHeader header;
604  Print *p_print = nullptr; // final output CopyEncoder copy; // used for PCM
605  AudioEncoderExt *p_encoder = nullptr;
606  EncodedAudioOutput enc_out;
607  WAVAudioInfo audioInfo = defaultConfig();
608  int64_t size_limit = 0;
609  bool header_written = false;
610  volatile bool is_open = false;
611 
612  void setupEncodedAudio() {
613  if (p_encoder!=nullptr){
614  assert(p_print!=nullptr);
615  enc_out.setOutput(p_print);
616  enc_out.setEncoder(p_encoder);
617  enc_out.setAudioInfo(audioInfo);
618  enc_out.begin();
619  // block size only available after begin(): update block size
620  audioInfo.block_align = p_encoder->blockSize();
621  }
622  }
623 };
624 
625 } // 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:265
void setOutput(Print &out_stream) override
Defines the output Stream.
Definition: CodecWAV.h:288
void setDecoder(AudioDecoderExt &dec, AudioFormat fmt)
Defines an optional decoder if the format is not PCM.
Definition: CodecWAV.h:281
int decodeHeader(uint8_t *in_ptr, size_t in_size)
Decodes the header data: Returns the start pos of the data.
Definition: CodecWAV.h:398
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:276
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: CodecWAV.h:310
A simple WAV file encoder. If no AudioEncoderExt is specified the WAV file contains PCM data,...
Definition: CodecWAV.h:463
virtual size_t write(const uint8_t *data, size_t len) override
Writes PCM data to be encoded as WAV.
Definition: CodecWAV.h:559
void setOutput(Print &out) override
Defines the otuput stream.
Definition: CodecWAV.h:484
virtual bool begin() override
starts the processing using the actual WAVAudioInfo
Definition: CodecWAV.h:547
bool begin(WAVAudioInfo ai)
starts the processing
Definition: CodecWAV.h:540
const char * mime() override
Provides "audio/wav".
Definition: CodecWAV.h:490
void end() override
stops the processing
Definition: CodecWAV.h:556
virtual void setAudioInfo(WAVAudioInfo ai)
Defines the WAVAudioInfo.
Definition: CodecWAV.h:516
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:600
WAVEncoder(AudioEncoderExt &enc, AudioFormat fmt)
Construct a new WAVEncoder object for ADPCM data.
Definition: CodecWAV.h:473
virtual void setAudioInfo(AudioInfo from) override
Update actual WAVAudioInfo.
Definition: CodecWAV.h:507
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:823
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