Last active
June 11, 2022 13:39
-
-
Save wernsey/fa8f8b7a145e4cf06df736e3e5659eb1 to your computer and use it in GitHub Desktop.
C code for manipulating WAV files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
gcc -Wall -DWAV_TEST wav.c | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <errno.h> | |
#include "wav.h" | |
#pragma pack(push, 1) | |
typedef struct { | |
char group_id[4]; | |
uint32_t filelength; | |
char riff_type[4]; | |
} wav_header; | |
typedef struct { | |
char chunk_id[4]; | |
uint32_t chunksize; | |
uint16_t audio_fmt; | |
uint16_t num_channels; | |
uint32_t sample_rate; | |
uint32_t byte_rate; | |
uint16_t block_align; | |
uint16_t bits_per_sample; | |
} fmt_chunk; | |
typedef struct { | |
char chunk_id[4]; | |
uint32_t chunksize; | |
} data_chunk; | |
#pragma pack(pop) | |
wav_t *wav_create(uint16_t num_channels, uint32_t sample_rate, uint16_t bits_per_sample, uint32_t initial_nsamples) { | |
wav_t *w = malloc(sizeof *w); | |
w->num_channels = num_channels; | |
w->sample_rate = sample_rate; | |
w->bits_per_sample = bits_per_sample; | |
w->nbytes = 0; | |
if(initial_nsamples) { | |
w->abytes = initial_nsamples * bits_per_sample / 8; | |
} else { | |
w->abytes = sample_rate * bits_per_sample / 8; | |
} | |
w->bytes = malloc(w->abytes); | |
return w; | |
} | |
void wav_free(wav_t *w) { | |
free(w->bytes); | |
free(w); | |
} | |
void wav_add_samples(wav_t *w, void *bytes, uint32_t nsamples) { | |
uint32_t nbytes = nsamples * w->bits_per_sample / 8; | |
if(w->nbytes + nbytes > w->abytes) { | |
while(w->nbytes + nbytes > w->abytes) | |
w->abytes <<= 1; | |
w->bytes = realloc(w->bytes, w->abytes); | |
} | |
memcpy(w->bytes + w->nbytes, bytes, nbytes); | |
w->nbytes += nbytes; | |
} | |
wav_t *wav_load(const char *filename) { | |
FILE *f = fopen(filename, "rb"); | |
if(!f) | |
return NULL; | |
wav_t *w = NULL; | |
wav_header hdr; | |
if(fread(&hdr, sizeof hdr, 1, f) != 1) { | |
perror("header"); | |
goto end; | |
} | |
if(memcmp(hdr.group_id, "RIFF", 4)) { | |
fprintf(stderr, "not RIFF"); | |
goto end; | |
} | |
if(memcmp(hdr.riff_type, "WAVE", 4)) { | |
fprintf(stderr, "not WAVE"); | |
goto end; | |
} | |
fmt_chunk fmt; | |
if(fread(&fmt, sizeof fmt, 1, f) != 1) { | |
perror("format chunk"); | |
goto end; | |
} | |
if(memcmp(fmt.chunk_id, "fmt ", 4)) { | |
fprintf(stderr, "not fmt??"); | |
goto end; | |
} | |
printf("length ............: %u bytes\n", hdr.filelength); | |
printf("chunksize .........: %u bytes\n", fmt.chunksize); | |
printf("audio_fmt .........: %u\n", fmt.audio_fmt); | |
printf("num_channels ......: %u\n", fmt.num_channels); | |
printf("sample_rate .......: %u Hz\n", fmt.sample_rate); | |
printf("byte_rate .........: %u\n", fmt.byte_rate); | |
printf("block_align .......: %u\n", fmt.block_align); | |
printf("bits_per_sample ...: %u\n", fmt.bits_per_sample); | |
if(fmt.audio_fmt != 1 || fmt.chunksize != 16) { | |
fprintf(stderr, "Unsupported format"); | |
} | |
data_chunk data; | |
if(fread(&data, sizeof data, 1, f) != 1) { | |
perror("data chunk"); | |
goto end; | |
} | |
if(memcmp(data.chunk_id, "data", 4)) { | |
fprintf(stderr, "not data??"); | |
goto end; | |
} | |
printf("data size .........: %u\n", data.chunksize); | |
w = wav_create(fmt.num_channels, fmt.sample_rate, fmt.bits_per_sample, data.chunksize); | |
if(fread(w->bytes, 1, data.chunksize, f) != data.chunksize) { | |
perror("fread samples"); | |
wav_free(w); w = NULL; | |
goto end; | |
} | |
w->nbytes = data.chunksize; | |
printf("%u bytes read\n", data.chunksize); | |
end: | |
fclose(f); | |
return w; | |
} | |
int wav_save(wav_t *w, const char *filename) { | |
FILE *f = fopen(filename, "wb"); | |
if(!f || !w) return 0; | |
int rv = 0; | |
wav_header hdr; | |
memcpy(&hdr.group_id, "RIFF", 4); | |
memcpy(&hdr.riff_type, "WAVE", 4); | |
if(fwrite(&hdr, sizeof hdr, 1, f) != 1) | |
goto end; | |
fmt_chunk fmt; | |
memset(&fmt, 0, sizeof fmt); | |
fmt.chunksize = 16; | |
fmt.audio_fmt = 1; | |
fmt.num_channels = w->num_channels; | |
fmt.sample_rate = w->sample_rate; | |
fmt.byte_rate = w->sample_rate * w->num_channels * w->bits_per_sample / 8; | |
fmt.block_align = w->num_channels * w->bits_per_sample / 8; | |
fmt.bits_per_sample = w->bits_per_sample; | |
memcpy(&fmt.chunk_id, "fmt ", 4); | |
if(fwrite(&fmt, sizeof fmt, 1, f) != 1) | |
goto end; | |
data_chunk data; | |
memset(&data, 0, sizeof data); | |
memcpy(&data.chunk_id, "data", 4); | |
data.chunksize = w->nbytes; | |
if(fwrite(&data, sizeof data, 1, f) != 1) | |
goto end; | |
if(fwrite(w->bytes, 1, w->nbytes, f) != w->nbytes) | |
goto end; | |
hdr.filelength = ftell(f) - 8; | |
rewind(f); | |
if(fwrite(&hdr, sizeof hdr, 1, f) != 1) | |
goto end; | |
rv = 1; | |
end: | |
fclose(f); | |
return rv; | |
} | |
#ifdef WAV_TEST | |
int main(int argc, char *argv[]) { | |
if(argc > 1) { | |
wav_t *w = wav_load(argv[1]); | |
if(w) { | |
if(argc > 2) { | |
if(wav_save(w, argv[2])) { | |
printf("save success"); | |
} else { | |
printf("save fail"); | |
} | |
} | |
wav_free(w); | |
} else { | |
fprintf(stderr, "load fail %s\n", argv[1]); | |
} | |
} else { | |
fprintf(stderr, "missing argument\n"); | |
} | |
return 0; | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Utilities for manipulating PCM WAV files. | |
* | |
* | |
* | |
* * https://en.wikipedia.org/wiki/WAV | |
* * http://soundfile.sapp.org/doc/WaveFormat/ | |
* * http://www.topherlee.com/software/pcm-tut-wavformat.html | |
* * https://blogs.msdn.microsoft.com/dawate/2009/06/23/intro-to-audio-programming-part-2-demystifying-the-wav-format/ | |
* * http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html | |
*/ | |
typedef struct { | |
uint16_t num_channels; | |
uint32_t sample_rate; | |
uint16_t bits_per_sample; | |
uint32_t abytes; | |
uint32_t nbytes; | |
char *bytes; | |
} wav_t; | |
/** | |
* | |
*/ | |
wav_t *wav_create(uint16_t num_channels, uint32_t sample_rate, uint16_t bits_per_sample, uint32_t initial_nsamples); | |
/** | |
* | |
*/ | |
void wav_free(wav_t *w); | |
/** | |
* | |
*/ | |
void wav_add_samples(wav_t *w, void *bytes, uint32_t nsamples); | |
/** | |
* | |
*/ | |
wav_t *wav_load(const char *filename); | |
/** | |
* | |
*/ | |
int wav_save(wav_t *w, const char *filename); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment