1*b290403dSRicardo Garcia /* Sonic library
2*b290403dSRicardo Garcia Copyright 2010
3*b290403dSRicardo Garcia Bill Cox
4*b290403dSRicardo Garcia This file is part of the Sonic Library.
5*b290403dSRicardo Garcia
6*b290403dSRicardo Garcia This file is licensed under the Apache 2.0 license.
7*b290403dSRicardo Garcia */
8*b290403dSRicardo Garcia
9*b290403dSRicardo Garcia /*
10*b290403dSRicardo Garcia This file supports read/write wave files.
11*b290403dSRicardo Garcia */
12*b290403dSRicardo Garcia #include "wave.h"
13*b290403dSRicardo Garcia #include <stdio.h>
14*b290403dSRicardo Garcia #include <stdlib.h>
15*b290403dSRicardo Garcia #include <string.h>
16*b290403dSRicardo Garcia
17*b290403dSRicardo Garcia #define WAVE_BUF_LEN 4096
18*b290403dSRicardo Garcia
19*b290403dSRicardo Garcia struct waveFileStruct {
20*b290403dSRicardo Garcia int numChannels;
21*b290403dSRicardo Garcia int sampleRate;
22*b290403dSRicardo Garcia FILE* soundFile;
23*b290403dSRicardo Garcia int bytesWritten; /* The number of bytes written so far, including header */
24*b290403dSRicardo Garcia int failed;
25*b290403dSRicardo Garcia int isInput;
26*b290403dSRicardo Garcia };
27*b290403dSRicardo Garcia
28*b290403dSRicardo Garcia /* Write a string to a file. */
writeBytes(waveFile file,void * bytes,int length)29*b290403dSRicardo Garcia static void writeBytes(waveFile file, void* bytes, int length) {
30*b290403dSRicardo Garcia size_t bytesWritten;
31*b290403dSRicardo Garcia
32*b290403dSRicardo Garcia if (file->failed) {
33*b290403dSRicardo Garcia return;
34*b290403dSRicardo Garcia }
35*b290403dSRicardo Garcia bytesWritten = fwrite(bytes, sizeof(char), length, file->soundFile);
36*b290403dSRicardo Garcia if (bytesWritten != length) {
37*b290403dSRicardo Garcia fprintf(stderr, "Unable to write to output file");
38*b290403dSRicardo Garcia file->failed = 1;
39*b290403dSRicardo Garcia }
40*b290403dSRicardo Garcia file->bytesWritten += bytesWritten;
41*b290403dSRicardo Garcia }
42*b290403dSRicardo Garcia
43*b290403dSRicardo Garcia /* Write a string to a file. */
writeString(waveFile file,char * string)44*b290403dSRicardo Garcia static void writeString(waveFile file, char* string) {
45*b290403dSRicardo Garcia writeBytes(file, string, strlen(string));
46*b290403dSRicardo Garcia }
47*b290403dSRicardo Garcia
48*b290403dSRicardo Garcia /* Write an integer to a file in little endian order. */
writeInt(waveFile file,int value)49*b290403dSRicardo Garcia static void writeInt(waveFile file, int value) {
50*b290403dSRicardo Garcia char bytes[4];
51*b290403dSRicardo Garcia int i;
52*b290403dSRicardo Garcia
53*b290403dSRicardo Garcia for (i = 0; i < 4; i++) {
54*b290403dSRicardo Garcia bytes[i] = value;
55*b290403dSRicardo Garcia value >>= 8;
56*b290403dSRicardo Garcia }
57*b290403dSRicardo Garcia writeBytes(file, bytes, 4);
58*b290403dSRicardo Garcia }
59*b290403dSRicardo Garcia
60*b290403dSRicardo Garcia /* Write a short integer to a file in little endian order. */
writeShort(waveFile file,short value)61*b290403dSRicardo Garcia static void writeShort(waveFile file, short value) {
62*b290403dSRicardo Garcia char bytes[2];
63*b290403dSRicardo Garcia int i;
64*b290403dSRicardo Garcia
65*b290403dSRicardo Garcia for (i = 0; i < 2; i++) {
66*b290403dSRicardo Garcia bytes[i] = value;
67*b290403dSRicardo Garcia value >>= 8;
68*b290403dSRicardo Garcia }
69*b290403dSRicardo Garcia writeBytes(file, bytes, 2);
70*b290403dSRicardo Garcia }
71*b290403dSRicardo Garcia
72*b290403dSRicardo Garcia /* Read bytes from the input file. Return the number of bytes actually read. */
readBytes(waveFile file,void * bytes,int length)73*b290403dSRicardo Garcia static int readBytes(waveFile file, void* bytes, int length) {
74*b290403dSRicardo Garcia if (file->failed) {
75*b290403dSRicardo Garcia return 0;
76*b290403dSRicardo Garcia }
77*b290403dSRicardo Garcia return fread(bytes, sizeof(char), length, file->soundFile);
78*b290403dSRicardo Garcia }
79*b290403dSRicardo Garcia
80*b290403dSRicardo Garcia /* Read an exact number of bytes from the input file. */
readExactBytes(waveFile file,void * bytes,int length)81*b290403dSRicardo Garcia static void readExactBytes(waveFile file, void* bytes, int length) {
82*b290403dSRicardo Garcia int numRead;
83*b290403dSRicardo Garcia
84*b290403dSRicardo Garcia if (file->failed) {
85*b290403dSRicardo Garcia return;
86*b290403dSRicardo Garcia }
87*b290403dSRicardo Garcia numRead = fread(bytes, sizeof(char), length, file->soundFile);
88*b290403dSRicardo Garcia if (numRead != length) {
89*b290403dSRicardo Garcia fprintf(stderr, "Failed to read requested bytes from input file\n");
90*b290403dSRicardo Garcia file->failed = 1;
91*b290403dSRicardo Garcia }
92*b290403dSRicardo Garcia }
93*b290403dSRicardo Garcia
94*b290403dSRicardo Garcia /* Read an integer from the input file */
readInt(waveFile file)95*b290403dSRicardo Garcia static int readInt(waveFile file) {
96*b290403dSRicardo Garcia unsigned char bytes[4];
97*b290403dSRicardo Garcia int value = 0, i;
98*b290403dSRicardo Garcia
99*b290403dSRicardo Garcia readExactBytes(file, bytes, 4);
100*b290403dSRicardo Garcia for (i = 3; i >= 0; i--) {
101*b290403dSRicardo Garcia value <<= 8;
102*b290403dSRicardo Garcia value |= bytes[i];
103*b290403dSRicardo Garcia }
104*b290403dSRicardo Garcia return value;
105*b290403dSRicardo Garcia }
106*b290403dSRicardo Garcia
107*b290403dSRicardo Garcia /* Read a short from the input file */
readShort(waveFile file)108*b290403dSRicardo Garcia static int readShort(waveFile file) {
109*b290403dSRicardo Garcia unsigned char bytes[2];
110*b290403dSRicardo Garcia int value = 0, i;
111*b290403dSRicardo Garcia
112*b290403dSRicardo Garcia readExactBytes(file, bytes, 2);
113*b290403dSRicardo Garcia for (i = 1; i >= 0; i--) {
114*b290403dSRicardo Garcia value <<= 8;
115*b290403dSRicardo Garcia value |= bytes[i];
116*b290403dSRicardo Garcia }
117*b290403dSRicardo Garcia return value;
118*b290403dSRicardo Garcia }
119*b290403dSRicardo Garcia
120*b290403dSRicardo Garcia /* Read a string from the input and compare it to an expected string. */
expectString(waveFile file,char * expectedString)121*b290403dSRicardo Garcia static void expectString(waveFile file, char* expectedString) {
122*b290403dSRicardo Garcia char buf[11]; /* Be sure that we never call with a longer string */
123*b290403dSRicardo Garcia int length = strlen(expectedString);
124*b290403dSRicardo Garcia
125*b290403dSRicardo Garcia if (length > 10) {
126*b290403dSRicardo Garcia fprintf(stderr, "Internal error: expected string too long\n");
127*b290403dSRicardo Garcia file->failed = 1;
128*b290403dSRicardo Garcia } else {
129*b290403dSRicardo Garcia readExactBytes(file, buf, length);
130*b290403dSRicardo Garcia buf[length] = '\0';
131*b290403dSRicardo Garcia if (strcmp(expectedString, buf)) {
132*b290403dSRicardo Garcia fprintf(stderr, "Unsupported wave file format: Expected '%s', got '%s;\n",
133*b290403dSRicardo Garcia expectedString, buf);
134*b290403dSRicardo Garcia file->failed = 1;
135*b290403dSRicardo Garcia }
136*b290403dSRicardo Garcia }
137*b290403dSRicardo Garcia }
138*b290403dSRicardo Garcia
139*b290403dSRicardo Garcia /* Write the header of the wave file. */
writeHeader(waveFile file,int sampleRate,int numChannels)140*b290403dSRicardo Garcia static void writeHeader(waveFile file, int sampleRate, int numChannels) {
141*b290403dSRicardo Garcia /* write the wav file per the wav file format */
142*b290403dSRicardo Garcia writeString(file, "RIFF"); /* 00 - RIFF */
143*b290403dSRicardo Garcia /* We have to fseek and overwrite this later when we close the file because */
144*b290403dSRicardo Garcia /* we don't know how big it is until then. */
145*b290403dSRicardo Garcia writeInt(file,
146*b290403dSRicardo Garcia 36 /* + dataLength */); /* 04 - how big is the rest of this file? */
147*b290403dSRicardo Garcia writeString(file, "WAVE"); /* 08 - WAVE */
148*b290403dSRicardo Garcia writeString(file, "fmt "); /* 12 - fmt */
149*b290403dSRicardo Garcia writeInt(file, 16); /* 16 - size of this chunk */
150*b290403dSRicardo Garcia writeShort(
151*b290403dSRicardo Garcia file,
152*b290403dSRicardo Garcia 1); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
153*b290403dSRicardo Garcia writeShort(file,
154*b290403dSRicardo Garcia numChannels); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
155*b290403dSRicardo Garcia writeInt(file, sampleRate); /* 24 - samples per second (numbers per second) */
156*b290403dSRicardo Garcia writeInt(file, sampleRate * 2); /* 28 - bytes per second */
157*b290403dSRicardo Garcia writeShort(file, 2); /* 32 - # of bytes in one sample, for all channels */
158*b290403dSRicardo Garcia writeShort(
159*b290403dSRicardo Garcia file, 16); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
160*b290403dSRicardo Garcia writeString(file, "data"); /* 36 - data */
161*b290403dSRicardo Garcia writeInt(file, 0); /* 40 - how big is this data chunk */
162*b290403dSRicardo Garcia }
163*b290403dSRicardo Garcia
164*b290403dSRicardo Garcia /* Read the header of the wave file. */
readHeader(waveFile file)165*b290403dSRicardo Garcia static int readHeader(waveFile file) {
166*b290403dSRicardo Garcia int data;
167*b290403dSRicardo Garcia
168*b290403dSRicardo Garcia expectString(file, "RIFF");
169*b290403dSRicardo Garcia data = readInt(file); /* 04 - how big is the rest of this file? */
170*b290403dSRicardo Garcia expectString(file, "WAVE"); /* 08 - WAVE */
171*b290403dSRicardo Garcia expectString(file, "fmt "); /* 12 - fmt */
172*b290403dSRicardo Garcia int chunkSize = readInt(file); /* 16 or 18 - size of this chunk */
173*b290403dSRicardo Garcia if (chunkSize != 16 && chunkSize != 18) {
174*b290403dSRicardo Garcia fprintf(stderr, "Only basic wave files are supported\n");
175*b290403dSRicardo Garcia return 0;
176*b290403dSRicardo Garcia }
177*b290403dSRicardo Garcia data = readShort(file); /* 20 - what is the audio format? 1 for PCM = Pulse
178*b290403dSRicardo Garcia Code Modulation */
179*b290403dSRicardo Garcia if (data != 1) {
180*b290403dSRicardo Garcia fprintf(stderr, "Only PCM wave files are supported (not %d)\n", data);
181*b290403dSRicardo Garcia return 0;
182*b290403dSRicardo Garcia }
183*b290403dSRicardo Garcia file->numChannels =
184*b290403dSRicardo Garcia readShort(file); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
185*b290403dSRicardo Garcia file->sampleRate =
186*b290403dSRicardo Garcia readInt(file); /* 24 - samples per second (numbers per second) */
187*b290403dSRicardo Garcia readInt(file); /* 28 - bytes per second */
188*b290403dSRicardo Garcia readShort(file); /* 32 - # of bytes in one sample, for all channels */
189*b290403dSRicardo Garcia data = readShort(
190*b290403dSRicardo Garcia file); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
191*b290403dSRicardo Garcia if (data != 16) {
192*b290403dSRicardo Garcia fprintf(stderr, "Only 16 bit PCM wave files are supported\n");
193*b290403dSRicardo Garcia return 0;
194*b290403dSRicardo Garcia }
195*b290403dSRicardo Garcia if (chunkSize == 18) { /* ffmpeg writes 18, and so has 2 extra bytes here */
196*b290403dSRicardo Garcia data = readShort(file);
197*b290403dSRicardo Garcia }
198*b290403dSRicardo Garcia
199*b290403dSRicardo Garcia /* Read and discard chunks until we find the "data" chunk or fail */
200*b290403dSRicardo Garcia char chunk[5];
201*b290403dSRicardo Garcia chunk[4] = 0;
202*b290403dSRicardo Garcia
203*b290403dSRicardo Garcia while (1) {
204*b290403dSRicardo Garcia readExactBytes(file, chunk, 4); /* chunk id */
205*b290403dSRicardo Garcia int size = readInt(file); /* how big is this data chunk */
206*b290403dSRicardo Garcia if (strcmp(chunk, "data") == 0) {
207*b290403dSRicardo Garcia return 1;
208*b290403dSRicardo Garcia }
209*b290403dSRicardo Garcia if (fseek(file->soundFile, size, SEEK_CUR) != 0) {
210*b290403dSRicardo Garcia fprintf(stderr, "Failed to seek on input file.\n");
211*b290403dSRicardo Garcia return 0;
212*b290403dSRicardo Garcia }
213*b290403dSRicardo Garcia }
214*b290403dSRicardo Garcia }
215*b290403dSRicardo Garcia
216*b290403dSRicardo Garcia /* Close the input or output file and free the waveFile. */
closeFile(waveFile file)217*b290403dSRicardo Garcia static void closeFile(waveFile file) {
218*b290403dSRicardo Garcia FILE* soundFile = file->soundFile;
219*b290403dSRicardo Garcia
220*b290403dSRicardo Garcia if (soundFile != NULL) {
221*b290403dSRicardo Garcia fclose(soundFile);
222*b290403dSRicardo Garcia file->soundFile = NULL;
223*b290403dSRicardo Garcia }
224*b290403dSRicardo Garcia free(file);
225*b290403dSRicardo Garcia }
226*b290403dSRicardo Garcia
227*b290403dSRicardo Garcia /* Open a 16-bit little-endian wav file for reading. It may be mono or stereo.
228*b290403dSRicardo Garcia */
openInputWaveFile(const char * fileName,int * sampleRate,int * numChannels)229*b290403dSRicardo Garcia waveFile openInputWaveFile(const char* fileName, int* sampleRate, int* numChannels) {
230*b290403dSRicardo Garcia waveFile file;
231*b290403dSRicardo Garcia FILE* soundFile = fopen(fileName, "rb");
232*b290403dSRicardo Garcia
233*b290403dSRicardo Garcia if (soundFile == NULL) {
234*b290403dSRicardo Garcia fprintf(stderr, "Unable to open wave file %s for reading\n", fileName);
235*b290403dSRicardo Garcia return NULL;
236*b290403dSRicardo Garcia }
237*b290403dSRicardo Garcia file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
238*b290403dSRicardo Garcia file->soundFile = soundFile;
239*b290403dSRicardo Garcia file->isInput = 1;
240*b290403dSRicardo Garcia if (!readHeader(file)) {
241*b290403dSRicardo Garcia closeFile(file);
242*b290403dSRicardo Garcia return NULL;
243*b290403dSRicardo Garcia }
244*b290403dSRicardo Garcia *sampleRate = file->sampleRate;
245*b290403dSRicardo Garcia *numChannels = file->numChannels;
246*b290403dSRicardo Garcia return file;
247*b290403dSRicardo Garcia }
248*b290403dSRicardo Garcia
249*b290403dSRicardo Garcia /* Open a 16-bit little-endian wav file for writing. It may be mono or stereo.
250*b290403dSRicardo Garcia */
openOutputWaveFile(const char * fileName,int sampleRate,int numChannels)251*b290403dSRicardo Garcia waveFile openOutputWaveFile(const char* fileName, int sampleRate, int numChannels) {
252*b290403dSRicardo Garcia waveFile file;
253*b290403dSRicardo Garcia FILE* soundFile = fopen(fileName, "wb");
254*b290403dSRicardo Garcia
255*b290403dSRicardo Garcia if (soundFile == NULL) {
256*b290403dSRicardo Garcia fprintf(stderr, "Unable to open wave file %s for writing\n", fileName);
257*b290403dSRicardo Garcia return NULL;
258*b290403dSRicardo Garcia }
259*b290403dSRicardo Garcia file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
260*b290403dSRicardo Garcia file->soundFile = soundFile;
261*b290403dSRicardo Garcia file->sampleRate = sampleRate;
262*b290403dSRicardo Garcia file->numChannels = numChannels;
263*b290403dSRicardo Garcia writeHeader(file, sampleRate, numChannels);
264*b290403dSRicardo Garcia if (file->failed) {
265*b290403dSRicardo Garcia closeFile(file);
266*b290403dSRicardo Garcia return NULL;
267*b290403dSRicardo Garcia }
268*b290403dSRicardo Garcia return file;
269*b290403dSRicardo Garcia }
270*b290403dSRicardo Garcia
271*b290403dSRicardo Garcia /* Close the sound file. */
closeWaveFile(waveFile file)272*b290403dSRicardo Garcia int closeWaveFile(waveFile file) {
273*b290403dSRicardo Garcia FILE* soundFile = file->soundFile;
274*b290403dSRicardo Garcia int passed = 1;
275*b290403dSRicardo Garcia
276*b290403dSRicardo Garcia if (!file->isInput) {
277*b290403dSRicardo Garcia if (fseek(soundFile, 4, SEEK_SET) != 0) {
278*b290403dSRicardo Garcia fprintf(stderr, "Failed to seek on input file.\n");
279*b290403dSRicardo Garcia passed = 0;
280*b290403dSRicardo Garcia } else {
281*b290403dSRicardo Garcia /* Now update the file to have the correct size. */
282*b290403dSRicardo Garcia writeInt(file, file->bytesWritten - 8);
283*b290403dSRicardo Garcia if (file->failed) {
284*b290403dSRicardo Garcia fprintf(stderr, "Failed to write wave file size.\n");
285*b290403dSRicardo Garcia passed = 0;
286*b290403dSRicardo Garcia }
287*b290403dSRicardo Garcia if (fseek(soundFile, 40, SEEK_SET) != 0) {
288*b290403dSRicardo Garcia fprintf(stderr, "Failed to seek on input file.\n");
289*b290403dSRicardo Garcia passed = 0;
290*b290403dSRicardo Garcia } else {
291*b290403dSRicardo Garcia /* Now update the file to have the correct size. */
292*b290403dSRicardo Garcia writeInt(file, file->bytesWritten - 48);
293*b290403dSRicardo Garcia if (file->failed) {
294*b290403dSRicardo Garcia fprintf(stderr, "Failed to write wave file size.\n");
295*b290403dSRicardo Garcia passed = 0;
296*b290403dSRicardo Garcia }
297*b290403dSRicardo Garcia }
298*b290403dSRicardo Garcia }
299*b290403dSRicardo Garcia }
300*b290403dSRicardo Garcia closeFile(file);
301*b290403dSRicardo Garcia return passed;
302*b290403dSRicardo Garcia }
303*b290403dSRicardo Garcia
304*b290403dSRicardo Garcia /* Read from the wave file. Return the number of samples read.
305*b290403dSRicardo Garcia numSamples and maxSamples are the number of **multi-channel** samples */
readFromWaveFile(waveFile file,short * buffer,int maxSamples)306*b290403dSRicardo Garcia int readFromWaveFile(waveFile file, short* buffer, int maxSamples) {
307*b290403dSRicardo Garcia int i, bytesRead, samplesRead;
308*b290403dSRicardo Garcia int bytePos = 0;
309*b290403dSRicardo Garcia unsigned char bytes[WAVE_BUF_LEN];
310*b290403dSRicardo Garcia short sample;
311*b290403dSRicardo Garcia
312*b290403dSRicardo Garcia if (maxSamples * file->numChannels * 2 > WAVE_BUF_LEN) {
313*b290403dSRicardo Garcia maxSamples = WAVE_BUF_LEN / (file->numChannels * 2);
314*b290403dSRicardo Garcia }
315*b290403dSRicardo Garcia bytesRead = readBytes(file, bytes, maxSamples * file->numChannels * 2);
316*b290403dSRicardo Garcia samplesRead = bytesRead / (file->numChannels * 2);
317*b290403dSRicardo Garcia for (i = 0; i < samplesRead * file->numChannels; i++) {
318*b290403dSRicardo Garcia sample = bytes[bytePos++];
319*b290403dSRicardo Garcia sample |= (unsigned int)bytes[bytePos++] << 8;
320*b290403dSRicardo Garcia *buffer++ = sample;
321*b290403dSRicardo Garcia }
322*b290403dSRicardo Garcia return samplesRead;
323*b290403dSRicardo Garcia }
324*b290403dSRicardo Garcia
325*b290403dSRicardo Garcia /* Write to the wave file. */
writeToWaveFile(waveFile file,short * buffer,int numSamples)326*b290403dSRicardo Garcia int writeToWaveFile(waveFile file, short* buffer, int numSamples) {
327*b290403dSRicardo Garcia int i;
328*b290403dSRicardo Garcia int bytePos = 0;
329*b290403dSRicardo Garcia unsigned char bytes[WAVE_BUF_LEN];
330*b290403dSRicardo Garcia short sample;
331*b290403dSRicardo Garcia int total = numSamples * file->numChannels;
332*b290403dSRicardo Garcia
333*b290403dSRicardo Garcia for (i = 0; i < total; i++) {
334*b290403dSRicardo Garcia if (bytePos == WAVE_BUF_LEN) {
335*b290403dSRicardo Garcia writeBytes(file, bytes, bytePos);
336*b290403dSRicardo Garcia bytePos = 0;
337*b290403dSRicardo Garcia }
338*b290403dSRicardo Garcia sample = buffer[i];
339*b290403dSRicardo Garcia bytes[bytePos++] = sample;
340*b290403dSRicardo Garcia bytes[bytePos++] = sample >> 8;
341*b290403dSRicardo Garcia }
342*b290403dSRicardo Garcia if (bytePos != 0) {
343*b290403dSRicardo Garcia writeBytes(file, bytes, bytePos);
344*b290403dSRicardo Garcia }
345*b290403dSRicardo Garcia return file->failed;
346*b290403dSRicardo Garcia }
347