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