Graph now runs (again??).
[aftubes.git] / wavefile.c
blob458a7e66ec53dea92a45ed0d65801391b9e3ed5f
1 #include "wavefile.h"
2 #include "stdint.h"
3 #include "assert.h"
4 #include "string.h"
5 #include "attr.h"
6 #include "errors.h"
8 struct wavehdr
10 // http://mathmatrix.narod.ru/Wavefmt.html
11 char magic[4]; // "RIFF"
12 uint32_t size; // size of file
13 char wave_magic[4]; // "WAVE"
14 char fmt_magic[4]; // "fmt "
15 uint32_t wave_section_chunk_size;
16 uint16_t wave_fmt_type; // 1 = linear quantization
17 uint16_t n_channels;
18 uint32_t srate;
19 uint32_t bytes_per_second;
20 uint16_t block_align;
21 uint16_t bits_per_sample;
24 struct chunkhdr
26 char name[4]; // name of chunk
27 uint32_t size; // size (bytes) of chunk (excluding header)
30 pure int waveformat_get_sample_size(const struct waveformat *restrict wf)
32 return (wf->bits / 8) * wf->channels;
35 static err_t read_header(struct wavefile *wav)
37 struct wavehdr whdr;
38 struct chunkhdr chdr;
40 // read wave header
41 if (fread(&whdr, sizeof whdr, 1, wav->f) < 1){
42 return make_error(EFREAD, wav, "%s: could not read wave file header", wav->filename);
45 // check wave magic strings
46 if (strncmp(whdr.magic, "RIFF", 4)
47 || strncmp(whdr.wave_magic, "WAVE", 4)
48 || strncmp(whdr.fmt_magic, "fmt ", 4)){
49 // invalid file format
50 return make_error(EFORMAT, wav, "%s: invalid wave file format", wav->filename);
53 // make sure it's in PCM format
54 if (whdr.wave_fmt_type != 1){
55 return make_error(EFORMAT, wav, "%s: unsupported wave file format", wav->filename);
58 // store format information
59 wav->format.srate = whdr.srate;
60 wav->format.bits = whdr.bits_per_sample;
61 wav->format.channels = whdr.n_channels;
63 // read chunk headers
64 for (;;){
65 if (fread(&chdr, sizeof chdr, 1, wav->f) < 1){
66 // read failure
67 return 1;
69 if (!strncmp(chdr.name, "data", 4)){
70 // found the data chunk
71 break;
72 } else {
73 // this is not the data.
74 // Next chunk!
75 fseek(wav->f, chdr.size, SEEK_CUR);
79 wav->length = chdr.size / ((wav->format.bits / 8) * wav->format.channels);
80 wav->pcm_start = ftell(wav->f);
81 return 0;
84 err_t wavefile_open(struct wavefile *wav, const char *filename)
86 // open the wave file
87 wav->filename = filename; // TODO: dup?
88 wav->f = fopen(filename, "r");
89 if (!wav->f){
90 return make_error(EFOPEN, wav, "%s: cannot open file", filename);
93 // read wave file header
94 return read_header(wav);
97 err_t wavefile_read_at(struct wavefile *wav, off_t sample_start, void *buf, size_t n_samples)
99 if (sample_start + n_samples > wav->length){
100 // past the end of file
101 return make_error(EWAVE_EOF, wav, "%s: tried reading beyond the end of the file (at sample number %llu)", (unsigned long long) sample_start);
104 fseek(wav->f, wav->pcm_start + sample_start * waveformat_get_sample_size(&wav->format), SEEK_SET);
105 fread(buf, 1, n_samples * waveformat_get_sample_size(&wav->format), wav->f);
106 return EOK;