Skip to content

Instantly share code, notes, and snippets.

@SteelPh0enix
Last active June 15, 2024 20:46
Show Gist options
  • Save SteelPh0enix/e44d4a030dd8816309af84809ed75604 to your computer and use it in GitHub Desktop.
Save SteelPh0enix/e44d4a030dd8816309af84809ed75604 to your computer and use it in GitHub Desktop.
Simple parser for WAV files, written in C
#include "wave.h"
#include <stdio.h>
#include <string.h>
// Convert 32-bit unsigned little-endian value to big-endian from byte array
static inline uint32_t little2big_u32(uint8_t const* data) {
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
// Convert 16-bit unsigned little-endian value to big-endian from byte array
static inline uint16_t little2big_u16(uint8_t const* data) {
return data[0] | (data[1] << 8);
}
// Copy n bytes from source to destination and terminate the destination with
// null character. Destination must be at least (amount + 1) bytes big to
// account for null character.
static inline void bytes_to_string(uint8_t const* source,
char* destination,
size_t amount) {
memcpy(destination, source, amount);
destination[amount] = '\0';
}
// Parse the header of WAV file and return WAVFile structure with header and
// pointer to data
WAVFile WAV_ParseFileData(uint8_t const* data) {
WAVFile file;
uint8_t const* data_ptr = data;
bytes_to_string(data_ptr, file.header.file_id, 4);
data_ptr += 4;
file.header.file_size = little2big_u32(data_ptr);
data_ptr += 4;
bytes_to_string(data_ptr, file.header.format, 4);
data_ptr += 4;
bytes_to_string(data_ptr, file.header.subchunk_id, 4);
data_ptr += 4;
file.header.subchunk_size = little2big_u32(data_ptr);
data_ptr += 4;
file.header.audio_format = little2big_u16(data_ptr);
data_ptr += 2;
file.header.number_of_channels = little2big_u16(data_ptr);
data_ptr += 2;
file.header.sample_rate = little2big_u32(data_ptr);
data_ptr += 4;
file.header.byte_rate = little2big_u32(data_ptr);
data_ptr += 4;
file.header.block_align = little2big_u16(data_ptr);
data_ptr += 2;
file.header.bits_per_sample = little2big_u16(data_ptr);
data_ptr += 2;
bytes_to_string(data_ptr, file.header.data_id, 4);
data_ptr += 4;
file.header.data_size = little2big_u32(data_ptr);
data_ptr += 4;
file.data = data_ptr;
file.data_length = file.header.data_size;
return file;
}
#ifndef WAVE_H
#define WAVE_H
#include <stddef.h>
#include <stdint.h>
// Normalized WAV file header structure
typedef struct WAVHeader_t {
// Should contain the letters "RIFF"
char file_id[5];
// The size of entire file in bytes, minus 8 bytes for chunk_id and
// chunk_size.
uint32_t file_size;
// Should contain the letters "WAVE"
char format[5];
// Should contain the letters "fmt "
char subchunk_id[5];
// 16 for PCM. This is the size of the rest of the sunchunk which follows this
// number.
uint32_t subchunk_size;
// PCM = 1, values other than 1 indicate some form of compression
uint16_t audio_format;
// mono = 1, stereo = 2, etc.
uint16_t number_of_channels;
// self-explanatory
uint32_t sample_rate;
// sample_rate * number of channels * bits per sample / 8
uint32_t byte_rate;
// number of channels * bits per sample / 8. Number of bytes for one sample
// including all channels.
uint16_t block_align;
// self-explanatory. BITS, not BYTES.
uint16_t bits_per_sample;
// Should contain the letters "data"
char data_id[5];
// number of samples * number of channels * bits per sample / 8
// Actual number of bytes in the sound data.
uint32_t data_size;
} WAVHeader;
// WAV file with header and pointer to data
typedef struct WAVFile_t {
// WAV header (copy re-constructed from the file with proper byte aligment)
WAVHeader header;
// Pointer to audio data
uint8_t const* data;
// Length of data, copy of header.data_size field
uint32_t data_length;
} WAVFile;
// Parse the header of WAV file and return WAVFile structure with header and
// pointer to data
WAVFile WAV_ParseFileData(uint8_t const* data);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment