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