Skip to content

Commit

Permalink
Simplify WAV header decoding (#7)
Browse files Browse the repository at this point in the history
* remove commented/unused code

* organize code

* add function to decode header
  • Loading branch information
kahrendt authored Nov 3, 2024
1 parent d3ef600 commit df8d67d
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 44 deletions.
28 changes: 11 additions & 17 deletions include/wav_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@

namespace wav_decoder {

const std::size_t min_buffer_size = 24;

enum WAVDecoderState {

WAV_DECODER_BEFORE_RIFF = 0,
Expand All @@ -47,16 +45,18 @@ enum WAVDecoderState {
enum WAVDecoderResult {
WAV_DECODER_SUCCESS_NEXT = 0,
WAV_DECODER_SUCCESS_IN_DATA = 1,
WAV_DECODER_ERROR_NO_RIFF = 2,
WAV_DECODER_ERROR_NO_WAVE = 3,
WAV_DECODER_WARNING_INCOMPLETE_DATA = 2,
WAV_DECODER_ERROR_NO_RIFF = 3,
WAV_DECODER_ERROR_NO_WAVE = 4,
WAV_DECODER_ERROR_FAILED = 5,
};

class WAVDecoder {
public:
WAVDecoder(uint8_t **buffer) : buffer_(buffer) {};
~WAVDecoder() {};

WAVDecoderState state() { return this->state_; }
std::size_t bytes_processed() { return this->bytes_processed_; }
std::size_t bytes_to_skip() { return this->bytes_to_skip_; }
std::size_t bytes_needed() { return this->bytes_needed_; }
std::string chunk_name() { return this->chunk_name_; }
Expand All @@ -65,27 +65,21 @@ class WAVDecoder {
uint16_t num_channels() { return this->num_channels_; }
uint16_t bits_per_sample() { return this->bits_per_sample_; }

WAVDecoderResult decode_header(uint8_t *buffer, size_t bytes_available);

// Advance decoding:
// 1. Check bytes_to_skip() first, and skip that many bytes.
// 2. Read exactly bytes_needed() into the start of the buffer.
// 3. Run next() and loop to 1 until the result is
// WAV_DECODER_SUCCESS_IN_DATA.
// 4. Use chunk_bytes_left() to read the data samples.
WAVDecoderResult next();
WAVDecoderResult next(uint8_t *buffer);

void reset() {
this->state_ = WAV_DECODER_BEFORE_RIFF;
this->bytes_to_skip_ = 0;
this->chunk_name_ = "";
this->chunk_bytes_left_ = 0;

this->sample_rate_ = 0;
this->num_channels_ = 0;
this->bits_per_sample_ = 0;
}
void reset();

protected:
uint8_t **buffer_;
size_t bytes_processed_;

WAVDecoderState state_ = WAV_DECODER_BEFORE_RIFF;
std::size_t bytes_needed_ = 8; // chunk name + size
std::size_t bytes_to_skip_ = 0;
Expand Down
90 changes: 63 additions & 27 deletions src/decode/wav_decoder.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,59 @@
#include "wav_decoder.h"
#include <cstdint>

namespace wav_decoder {

WAVDecoderResult WAVDecoder::next() {
WAVDecoderResult WAVDecoder::decode_header(uint8_t *buffer, size_t bytes_available) {
size_t bytes_to_skip = this->bytes_to_skip();
size_t bytes_to_read = this->bytes_needed();
this->bytes_processed_ = 0;

while ((bytes_to_skip + bytes_to_read) > 0) {
if ((bytes_to_skip > bytes_available) || (bytes_to_read > bytes_available)) {
return WAV_DECODER_WARNING_INCOMPLETE_DATA;
}

if (bytes_to_skip > 0) {
// Adjust pointer to skip the appropriate bytes
buffer += bytes_to_skip;
this->bytes_processed_ += bytes_to_skip;

bytes_available -= bytes_to_skip;
bytes_to_skip = 0;
} else if (bytes_to_read > 0) {
WAVDecoderResult result = this->next(buffer);
buffer += bytes_to_read;
this->bytes_processed_ += bytes_to_read;

bytes_available -= bytes_to_read;

if (result == WAV_DECODER_SUCCESS_IN_DATA) {
return result;
} else if (result == WAV_DECODER_SUCCESS_NEXT) {
// Continue parsing header
bytes_to_skip = this->bytes_to_skip();
bytes_to_read = this->bytes_needed();
} else {
// Unexpected error parsing the wav header
return result;
}
}
}

return WAV_DECODER_ERROR_FAILED;
}

WAVDecoderResult WAVDecoder::next(uint8_t *buffer) {
this->bytes_to_skip_ = 0;

switch (this->state_) {
case WAV_DECODER_BEFORE_RIFF: {
this->chunk_name_ = std::string((const char *) *this->buffer_, 4);
this->chunk_name_ = std::string((const char *) buffer, 4);
if (this->chunk_name_ != "RIFF") {
return WAV_DECODER_ERROR_NO_RIFF;
}

this->chunk_bytes_left_ = *((uint32_t *) (*this->buffer_ + 4));
this->chunk_bytes_left_ = *((uint32_t *) (buffer + 4));
if ((this->chunk_bytes_left_ % 2) != 0) {
// Pad byte
this->chunk_bytes_left_++;
Expand All @@ -25,7 +66,7 @@ WAVDecoderResult WAVDecoder::next() {
}

case WAV_DECODER_BEFORE_WAVE: {
this->chunk_name_ = std::string((const char *) *this->buffer_, 4);
this->chunk_name_ = std::string((const char *) buffer, 4);
if (this->chunk_name_ != "WAVE") {
return WAV_DECODER_ERROR_NO_WAVE;
}
Expand All @@ -37,8 +78,8 @@ WAVDecoderResult WAVDecoder::next() {
}

case WAV_DECODER_BEFORE_FMT: {
this->chunk_name_ = std::string((const char *) *this->buffer_, 4);
this->chunk_bytes_left_ = *((uint32_t *) (*this->buffer_ + 4));
this->chunk_name_ = std::string((const char *) buffer, 4);
this->chunk_bytes_left_ = *((uint32_t *) (buffer + 4));
if ((this->chunk_bytes_left_ % 2) != 0) {
// Pad byte
this->chunk_bytes_left_++;
Expand All @@ -50,20 +91,12 @@ WAVDecoderResult WAVDecoder::next() {
this->bytes_needed_ = this->chunk_bytes_left_;
} else {
// Skip over chunk
// this->state_ = WAV_DECODER_BEFORE_FMT_SKIP_CHUNK;
this->bytes_to_skip_ = this->chunk_bytes_left_;
this->bytes_needed_ = 8;
}
break;
}

// case WAV_DECODER_BEFORE_FMT_SKIP_CHUNK: {
// // Next chunk header
// this->state_ = WAV_DECODER_BEFORE_FMT;
// this->bytes_needed_ = 8; // chunk name + size
// break;
// }

case WAV_DECODER_IN_FMT: {
/**
* audio format (uint16_t)
Expand All @@ -74,9 +107,9 @@ WAVDecoderResult WAVDecoder::next() {
* bits per sample (uint16_t)
* [rest of format chunk]
*/
this->num_channels_ = *((uint16_t *) (*this->buffer_ + 2));
this->sample_rate_ = *((uint32_t *) (*this->buffer_ + 4));
this->bits_per_sample_ = *((uint16_t *) (*this->buffer_ + 14));
this->num_channels_ = *((uint16_t *) (buffer + 2));
this->sample_rate_ = *((uint32_t *) (buffer + 4));
this->bits_per_sample_ = *((uint16_t *) (buffer + 14));

// Next chunk
this->state_ = WAV_DECODER_BEFORE_DATA;
Expand All @@ -85,8 +118,8 @@ WAVDecoderResult WAVDecoder::next() {
}

case WAV_DECODER_BEFORE_DATA: {
this->chunk_name_ = std::string((const char *) *this->buffer_, 4);
this->chunk_bytes_left_ = *((uint32_t *) (*this->buffer_ + 4));
this->chunk_name_ = std::string((const char *) buffer, 4);
this->chunk_bytes_left_ = *((uint32_t *) (buffer + 4));
if ((this->chunk_bytes_left_ % 2) != 0) {
// Pad byte
this->chunk_bytes_left_++;
Expand All @@ -100,19 +133,11 @@ WAVDecoderResult WAVDecoder::next() {
}

// Skip over chunk
// this->state_ = WAV_DECODER_BEFORE_DATA_SKIP_CHUNK;
this->bytes_to_skip_ = this->chunk_bytes_left_;
this->bytes_needed_ = 8;
break;
}

// case WAV_DECODER_BEFORE_DATA_SKIP_CHUNK: {
// // Next chunk header
// this->state_ = WAV_DECODER_BEFORE_DATA;
// this->bytes_needed_ = 8; // chunk name + size
// break;
// }

case WAV_DECODER_IN_DATA: {
return WAV_DECODER_SUCCESS_IN_DATA;
break;
Expand All @@ -122,4 +147,15 @@ WAVDecoderResult WAVDecoder::next() {
return WAV_DECODER_SUCCESS_NEXT;
}

void WAVDecoder::reset() {
this->state_ = WAV_DECODER_BEFORE_RIFF;
this->bytes_to_skip_ = 0;
this->chunk_name_ = "";
this->chunk_bytes_left_ = 0;

this->sample_rate_ = 0;
this->num_channels_ = 0;
this->bits_per_sample_ = 0;
}

} // namespace wav_decoder

0 comments on commit df8d67d

Please sign in to comment.