Skip to content

Commit 95f5f88

Browse files
committed
Simplify WAV Header Parsing
1 parent 2e88628 commit 95f5f88

File tree

1 file changed

+72
-104
lines changed

1 file changed

+72
-104
lines changed

src/AudioCodecs/CodecWAV.h

Lines changed: 72 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
#include "AudioCodecs/AudioCodecsBase.h"
44
#include "AudioCodecs/AudioFormat.h"
5-
5+
#include "AudioBasic/Str.h"
66

77
#define TAG(a, b, c, d) \
88
((static_cast<uint32_t>(a) << 24) | (static_cast<uint32_t>(b) << 16) | \
99
(static_cast<uint32_t>(c) << 8) | (d))
1010
#define READ_BUFFER_SIZE 512
11+
#define MAX_WAV_HEADER_LEN 50
1112

1213
namespace audio_tools {
1314

@@ -50,95 +51,48 @@ class WAVHeader {
5051

5152
/// Adds data to the 44 byte wav header data buffer and make it available for parsing
5253
int write(uint8_t *data, size_t data_len) {
53-
int write_len = min(data_len, 44 - len);
54-
memmove(buffer, data + len, write_len);
55-
len += write_len;
56-
LOGI("WAVHeader::write: %u -> %d -> %d", (unsigned)data_len, write_len,
57-
(int)len);
58-
return write_len;
54+
return buffer.writeArray(data, data_len);
5955
}
6056

6157
/// Call begin when header data is complete to parse the data
62-
void parse() {
63-
LOGI("WAVHeader::begin: %u", (unsigned)len);
58+
bool parse() {
59+
LOGI("WAVHeader::begin: %u", (unsigned)buffer.size());
6460
this->data_pos = 0l;
6561
memset((void *)&headerInfo, 0, sizeof(WAVAudioInfo));
66-
while (!eof()) {
67-
uint32_t tag, tag2, length;
68-
tag = read_tag();
69-
if (eof()) break;
70-
length = read_int32();
71-
if (!length || length >= 0x7fff0000) {
72-
headerInfo.is_streamed = true;
73-
length = ~0;
74-
}
75-
if (tag != TAG('R', 'I', 'F', 'F') || length < 4) {
76-
seek(length, SEEK_CUR);
77-
continue;
78-
}
79-
headerInfo.file_size = length;
80-
tag2 = read_tag();
81-
length -= 4;
82-
if (tag2 != TAG('W', 'A', 'V', 'E')) {
83-
seek(length, SEEK_CUR);
84-
continue;
85-
}
86-
// RIFF chunk found, iterate through it
87-
while (length >= 8) {
88-
uint32_t subtag, sublength;
89-
subtag = read_tag();
90-
if (eof()) break;
91-
sublength = read_int32();
92-
length -= 8;
93-
if (length < sublength) break;
94-
if (subtag == TAG('f', 'm', 't', ' ')) {
95-
if (sublength < 16) {
96-
// Insufficient data for 'fmt '
97-
break;
98-
}
99-
headerInfo.format = (AudioFormat)read_int16();
100-
headerInfo.channels = read_int16();
101-
headerInfo.sample_rate = read_int32();
102-
headerInfo.byte_rate = read_int32();
103-
headerInfo.block_align = read_int16();
104-
headerInfo.bits_per_sample = read_int16();
105-
if (headerInfo.format == (AudioFormat) 0xfffe) {
106-
if (sublength < 28) {
107-
// Insufficient data for waveformatex
108-
break;
109-
}
110-
skip(8);
111-
headerInfo.format = (AudioFormat)read_int32();
112-
skip(sublength - 28);
113-
} else {
114-
skip(sublength - 16);
115-
}
116-
headerInfo.is_valid = true;
117-
} else if (subtag == TAG('d', 'a', 't', 'a')) {
118-
sound_pos = tell();
119-
headerInfo.data_length = sublength;
120-
if (!headerInfo.data_length || headerInfo.is_streamed) {
121-
headerInfo.is_streamed = true;
122-
logInfo();
123-
return;
124-
}
125-
seek(sublength, SEEK_CUR);
126-
} else {
127-
skip(sublength);
128-
}
129-
length -= sublength;
130-
}
131-
if (length > 0) {
132-
// Bad chunk?
133-
seek(length, SEEK_CUR);
134-
}
62+
63+
if (!setPos("RIFF")) return false;
64+
int riff_len = read_int32();
65+
if (!setPos("WAVE")) return false;
66+
if (!setPos("fmt ")) return false;
67+
int fmt_length = read_int32();
68+
headerInfo.format = (AudioFormat)read_int16();
69+
headerInfo.channels = read_int16();
70+
headerInfo.sample_rate = read_int32();
71+
headerInfo.byte_rate = read_int32();
72+
headerInfo.block_align = read_int16();
73+
headerInfo.bits_per_sample = read_int16();
74+
if (!setPos("data")) return false;
75+
headerInfo.data_length = read_int32();
76+
if (!headerInfo.data_length==0 || headerInfo.data_length >= 0x7fff0000) {
77+
headerInfo.is_streamed = true;
78+
headerInfo.data_length = ~0;
13579
}
80+
13681
logInfo();
137-
len = 0;
82+
buffer.clear();
83+
return true;
13884
}
13985

140-
/// Returns true if the header is complete (with 44 bytes)
141-
bool isDataComplete() { return len == 44; }
86+
/// Returns true if the header is complete (with 44 bytes): contains data + 4 byte len
87+
bool isDataComplete() {
88+
int pos = getDataPos();
89+
return pos > 0 && buffer.available() >= pos;;
90+
}
91+
92+
int getDataPos() {
93+
int pos = Str((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.size()).indexOf("data");
94+
return pos > 0 ? pos + 8 : 0;
95+
}
14296

14397
/// provides the info from the header
14498
WAVAudioInfo &audioInfo() { return headerInfo; }
@@ -148,23 +102,40 @@ class WAVHeader {
148102
headerInfo = info;
149103
}
150104

151-
/// Just write a wav header to the indicated output
105+
/// Just write a wav header to the indicated outputbu
152106
int writeHeader(Print *out) {
153-
SingleBuffer<uint8_t> buffer(50);
154107
writeRiffHeader(buffer);
155108
writeFMT(buffer);
156109
writeDataHeader(buffer);
157-
len = buffer.available();
110+
int len = buffer.available();
158111
out->write(buffer.data(), buffer.available());
159112
return len;
160113
}
161114

115+
void clear() {
116+
data_pos = 0;
117+
memset(&headerInfo,0,sizeof(WAVAudioInfo));
118+
buffer.setClearWithZero(true);
119+
buffer.reset();
120+
}
121+
122+
162123
protected:
163124
struct WAVAudioInfo headerInfo;
164-
uint8_t buffer[44];
165-
size_t len = 0;
125+
SingleBuffer<uint8_t> buffer{MAX_WAV_HEADER_LEN};
166126
size_t data_pos = 0;
167-
size_t sound_pos = 0;
127+
128+
bool setPos(const char*id){
129+
int id_len = strlen(id);
130+
int pos = indexOf(id);
131+
if (pos < 0) return false;
132+
data_pos = pos + id_len;
133+
return true;
134+
}
135+
136+
int indexOf(const char* str){
137+
return Str((char*)buffer.data(),MAX_WAV_HEADER_LEN, buffer.available()).indexOf(str);
138+
}
168139

169140
uint32_t read_tag() {
170141
uint32_t tag = 0;
@@ -199,8 +170,8 @@ class WAVHeader {
199170
}
200171

201172
int getChar() {
202-
if (data_pos < len)
203-
return buffer[data_pos++];
173+
if (data_pos < buffer.size())
174+
return buffer.data()[data_pos++];
204175
else
205176
return -1;
206177
}
@@ -215,10 +186,10 @@ class WAVHeader {
215186

216187
size_t tell() { return data_pos; }
217188

218-
bool eof() { return data_pos >= len - 1; }
189+
bool eof() { return data_pos >= buffer.size() - 1; }
219190

220191
void logInfo() {
221-
LOGI("WAVHeader sound_pos: %lu", (unsigned long)sound_pos);
192+
LOGI("WAVHeader sound_pos: %d", getDataPos());
222193
LOGI("WAVHeader channels: %d ", headerInfo.channels);
223194
LOGI("WAVHeader bits_per_sample: %d", headerInfo.bits_per_sample);
224195
LOGI("WAVHeader sample_rate: %d ", (int) headerInfo.sample_rate);
@@ -301,6 +272,7 @@ class WAVDecoder : public AudioDecoder {
301272

302273
bool begin() override {
303274
TRACED();
275+
header.clear();
304276
setupEncodedAudio();
305277
buffer24.reset();
306278
isFirst = true;
@@ -325,10 +297,12 @@ class WAVDecoder : public AudioDecoder {
325297
size_t result = 0;
326298
if (active) {
327299
if (isFirst) {
328-
result = decodeHeader((uint8_t*) data, len);
329-
// if (result<len){
330-
// result += write_out((uint8_t *)data+result, len-result);
331-
// }
300+
int data_start = decodeHeader((uint8_t*) data, len);
301+
// we do not have the complete header yet: need more data
302+
if (data_start == 0) return len;
303+
// process the outstanding data
304+
result = data_start + write_out((uint8_t *)data+data_start, len-data_start);
305+
332306
} else if (isValid) {
333307
result = write_out((uint8_t *)data, len);
334308
}
@@ -405,7 +379,7 @@ class WAVDecoder : public AudioDecoder {
405379

406380

407381
int decodeHeader(uint8_t *in_ptr, size_t in_size) {
408-
int result = 0;
382+
int result = in_size;
409383
// we expect at least the full header
410384
int written = header.write(in_ptr, in_size);
411385
if (!header.isDataComplete()) {
@@ -414,8 +388,6 @@ class WAVDecoder : public AudioDecoder {
414388
// parse header
415389
header.parse();
416390

417-
size_t len = in_size - written;
418-
uint8_t *sound_ptr = (uint8_t *)in_ptr + written;
419391
isFirst = false;
420392
isValid = header.audioInfo().is_valid;
421393

@@ -441,15 +413,10 @@ class WAVDecoder : public AudioDecoder {
441413
bi.channels = header.audioInfo().channels;
442414
bi.bits_per_sample = header.audioInfo().bits_per_sample;
443415
notifyAudioChange(bi);
444-
// write prm data from first record
445-
if (len > 0) {
446-
LOGI("WAVDecoder writing first sound data");
447-
result = out().write(sound_ptr, len);
448-
}
449416
} else {
450417
LOGE("WAV format not supported: %d", (int)format);
451418
}
452-
return result;
419+
return header.getDataPos();
453420
}
454421

455422
void setupEncodedAudio() {
@@ -549,6 +516,7 @@ class WAVEncoder : public AudioEncoder {
549516

550517
/// starts the processing
551518
bool begin(WAVAudioInfo ai) {
519+
header.clear();
552520
setAudioInfo(ai);
553521
return begin();
554522
}

0 commit comments

Comments
 (0)