blob: 7dd6f636546ed124986bd50d4c8141cc9f9e35c8 [file] [log] [blame]
Bill Coxca02d872010-11-02 15:10:52 -04001/* Sonic library
2 Copyright 2010
3 Bill Cox
4 This file is part of the Sonic Library.
5
Bill Coxa9999872010-11-11 14:36:59 -05006 The Sonic Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
Bill Coxca02d872010-11-02 15:10:52 -040010
Bill Coxa9999872010-11-11 14:36:59 -050011 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
Bill Coxca02d872010-11-02 15:10:52 -040015
Bill Coxa9999872010-11-11 14:36:59 -050016 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
Bill Coxca02d872010-11-02 15:10:52 -040020
21/*
22This file supports read/write wave files.
23*/
Bill Coxec23ae02011-10-21 03:02:47 -040024#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040025#include <stdlib.h>
Bill Cox0c4c0602010-11-08 11:46:30 -050026#include <string.h>
Bill Coxca02d872010-11-02 15:10:52 -040027#include "wave.h"
28
Bill Coxec23ae02011-10-21 03:02:47 -040029#define WAVE_BUF_LEN 4096
30
Bill Coxca02d872010-11-02 15:10:52 -040031struct waveFileStruct {
Bill Cox2081ea42010-11-05 05:49:47 -040032 int numChannels;
Bill Coxec23ae02011-10-21 03:02:47 -040033 int sampleRate;
34 FILE *soundFile;
35 int bytesWritten; /* The number of bytes written so far, including header */
36 int failed;
37 int isInput;
Bill Coxca02d872010-11-02 15:10:52 -040038};
39
Bill Coxec23ae02011-10-21 03:02:47 -040040/* Write a string to a file. */
41static void writeBytes(
42 waveFile file,
43 void *bytes,
44 int length)
45{
46 size_t bytesWritten;
47
48 if(file->failed) {
49 return;
50 }
51 bytesWritten = fwrite(bytes, sizeof(char), length, file->soundFile);
52 if(bytesWritten != length) {
53 fprintf(stderr, "Unable to write to output file");
54 file->failed = 1;
55 }
56 file->bytesWritten += bytesWritten;
57}
58
59/* Write a string to a file. */
60static void writeString(
61 waveFile file,
62 char *string)
63{
64 writeBytes(file, string, strlen(string));
65}
66
67/* Write an integer to a file in little endian order. */
68static void writeInt(
69 waveFile file,
70 int value)
71{
72 char bytes[4];
73 int i;
74
75 for(i = 0; i < 4; i++) {
76 bytes[i] = value;
77 value >>= 8;
78 }
79 writeBytes(file, bytes, 4);
80}
81
82/* Write a short integer to a file in little endian order. */
83static void writeShort(
84 waveFile file,
85 short value)
86{
87 char bytes[2];
88 int i;
89
90 for(i = 0; i < 2; i++) {
91 bytes[i] = value;
92 value >>= 8;
93 }
94 writeBytes(file, bytes, 2);
95}
96
97/* Read bytes from the input file. Return the number of bytes actually read. */
98static int readBytes(
99 waveFile file,
100 void *bytes,
101 int length)
102{
103 if(file->failed) {
104 return 0;
105 }
106 return fread(bytes, sizeof(char), length, file->soundFile);
107}
108
109/* Read an exact number of bytes from the input file. */
110static void readExactBytes(
111 waveFile file,
112 void *bytes,
113 int length)
114{
115 int numRead;
116
117 if(file->failed) {
118 return;
119 }
120 numRead = fread(bytes, sizeof(char), length, file->soundFile);
121 if(numRead != length) {
122 fprintf(stderr, "Failed to read requested bytes from input file\n");
123 file->failed = 1;
124 }
125}
126
127/* Read an integer from the input file */
128static int readInt(
129 waveFile file)
130{
131 unsigned char bytes[4];
132 int value = 0, i;
133
134 readExactBytes(file, bytes, 4);
135 for(i = 3; i >= 0; i--) {
136 value <<= 8;
137 value |= bytes[i];
138 }
139 return value;
140}
141
142/* Read a short from the input file */
143static int readShort(
144 waveFile file)
145{
146 unsigned char bytes[2];
147 int value = 0, i;
148
149 readExactBytes(file, bytes, 2);
150 for(i = 1; i >= 0; i--) {
151 value <<= 8;
152 value |= bytes[i];
153 }
154 return value;
155}
156
157/* Read a string from the input and compare it to an expected string. */
158static void expectString(
159 waveFile file,
160 char *expectedString)
161{
162 char buf[11]; /* Be sure that we never call with a longer string */
163 int length = strlen(expectedString);
164
165 if(length > 10) {
166 fprintf(stderr, "Internal error: expected string too long\n");
167 file->failed = 1;
168 } else {
169 readExactBytes(file, buf, length);
170 buf[length] = '\0';
171 if(strcmp(expectedString, buf)) {
172 fprintf(stderr, "Unsupported wave file format\n");
173 file->failed = 1;
174 }
175 }
176}
177
178/* Write the header of the wave file. */
179static void writeHeader(
180 waveFile file,
181 int sampleRate)
182{
183 /* write the wav file per the wav file format */
184 writeString(file, "RIFF"); /* 00 - RIFF */
185 /* We have to fseek and overwrite this later when we close the file because */
186 /* we don't know how big it is until then. */
187 writeInt(file, 36 /* + dataLength */); /* 04 - how big is the rest of this file? */
188 writeString(file, "WAVE"); /* 08 - WAVE */
189 writeString(file, "fmt "); /* 12 - fmt */
190 writeInt(file, 16); /* 16 - size of this chunk */
191 writeShort(file, 1); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
192 writeShort(file, 1); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
193 writeInt(file, sampleRate); /* 24 - samples per second (numbers per second) */
194 writeInt(file, sampleRate * 2); /* 28 - bytes per second */
195 writeShort(file, 2); /* 32 - # of bytes in one sample, for all channels */
196 writeShort(file, 16); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
197 writeString(file, "data"); /* 36 - data */
198 writeInt(file, 0); /* 40 - how big is this data chunk */
199}
200
201/* Read the header of the wave file. */
202static int readHeader(
203 waveFile file)
204{
205 int data;
206
207 expectString(file, "RIFF");
208 data = readInt(file); /* 04 - how big is the rest of this file? */
209 expectString(file, "WAVE"); /* 08 - WAVE */
210 expectString(file, "fmt "); /* 12 - fmt */
Matthew Albright72ae5452013-04-17 22:33:51 -0700211 int chunkSize = readInt(file); /* 16 or 18 - size of this chunk */
212 if(chunkSize != 16 && chunkSize != 18) {
Bill Coxec23ae02011-10-21 03:02:47 -0400213 fprintf(stderr, "Only basic wave files are supported\n");
214 return 0;
215 }
216 data = readShort(file); /* 20 - what is the audio format? 1 for PCM = Pulse Code Modulation */
217 if(data != 1) {
218 fprintf(stderr, "Only PCM wave files are supported\n");
219 return 0;
220 }
221 file->numChannels = readShort(file); /* 22 - mono or stereo? 1 or 2? (or 5 or ???) */
222 file->sampleRate = readInt(file); /* 24 - samples per second (numbers per second) */
223 readInt(file); /* 28 - bytes per second */
224 readShort(file); /* 32 - # of bytes in one sample, for all channels */
225 data = readShort(file); /* 34 - how many bits in a sample(number)? usually 16 or 24 */
226 if(data != 16) {
227 fprintf(stderr, "Only 16 bit PCM wave files are supported\n");
228 return 0;
229 }
Matthew Albright72ae5452013-04-17 22:33:51 -0700230 if (chunkSize == 18) { /* ffmpeg writes 18, and so has 2 extra bytes here */
231 data = readShort(file);
232 }
Bill Coxec23ae02011-10-21 03:02:47 -0400233 expectString(file, "data"); /* 36 - data */
234 readInt(file); /* 40 - how big is this data chunk */
235 return 1;
236}
237
238/* Close the input or output file and free the waveFile. */
239static void closeFile(
240 waveFile file)
241{
242 FILE *soundFile = file->soundFile;
243
244 if(soundFile != NULL) {
245 fclose(soundFile);
246 file->soundFile = NULL;
247 }
248 free(file);
249}
250
251/* Open a 16-bit little-endian wav file for reading. It may be mono or stereo. */
Bill Coxca02d872010-11-02 15:10:52 -0400252waveFile openInputWaveFile(
253 char *fileName,
Bill Cox1a299bb2010-11-19 15:07:17 -0500254 int *sampleRate,
255 int *numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400256{
Bill Coxca02d872010-11-02 15:10:52 -0400257 waveFile file;
Bill Coxec23ae02011-10-21 03:02:47 -0400258 FILE *soundFile = fopen(fileName, "rb");
Bill Coxca02d872010-11-02 15:10:52 -0400259
Bill Coxca02d872010-11-02 15:10:52 -0400260 if(soundFile == NULL) {
Bill Coxec23ae02011-10-21 03:02:47 -0400261 fprintf(stderr, "Unable to open wave file %s for reading\n", fileName);
Bill Coxca02d872010-11-02 15:10:52 -0400262 return NULL;
263 }
264 file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
265 file->soundFile = soundFile;
Bill Coxec23ae02011-10-21 03:02:47 -0400266 file->isInput = 1;
267 if(!readHeader(file)) {
268 closeFile(file);
269 return NULL;
270 }
271 *sampleRate = file->sampleRate;
272 *numChannels = file->numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400273 return file;
274}
275
Bill Coxec23ae02011-10-21 03:02:47 -0400276/* Open a 16-bit little-endian wav file for writing. It may be mono or stereo. */
Bill Coxca02d872010-11-02 15:10:52 -0400277waveFile openOutputWaveFile(
278 char *fileName,
Bill Cox1a299bb2010-11-19 15:07:17 -0500279 int sampleRate,
280 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400281{
Bill Coxca02d872010-11-02 15:10:52 -0400282 waveFile file;
Bill Coxec23ae02011-10-21 03:02:47 -0400283 FILE *soundFile = fopen(fileName, "wb");
Bill Coxca02d872010-11-02 15:10:52 -0400284
Bill Coxca02d872010-11-02 15:10:52 -0400285 if(soundFile == NULL) {
Bill Coxec23ae02011-10-21 03:02:47 -0400286 fprintf(stderr, "Unable to open wave file %s for writing\n", fileName);
Bill Coxca02d872010-11-02 15:10:52 -0400287 return NULL;
288 }
289 file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
290 file->soundFile = soundFile;
Bill Coxec23ae02011-10-21 03:02:47 -0400291 file->sampleRate = sampleRate;
Bill Coxf8071892010-11-19 17:57:40 -0500292 file->numChannels = numChannels;
Bill Coxec23ae02011-10-21 03:02:47 -0400293 writeHeader(file, sampleRate);
294 if(file->failed) {
295 closeFile(file);
296 return NULL;
297 }
Bill Coxca02d872010-11-02 15:10:52 -0400298 return file;
299}
300
301/* Close the sound file. */
Bill Coxec23ae02011-10-21 03:02:47 -0400302int closeWaveFile(
Bill Coxca02d872010-11-02 15:10:52 -0400303 waveFile file)
304{
Bill Coxec23ae02011-10-21 03:02:47 -0400305 FILE *soundFile = file->soundFile;
306 int passed = 1;
Bill Coxca02d872010-11-02 15:10:52 -0400307
Bill Coxec23ae02011-10-21 03:02:47 -0400308 if(!file->isInput) {
309 if(fseek(soundFile, 4, SEEK_SET) != 0) {
310 fprintf(stderr, "Failed to seek on input file.\n");
311 passed = 0;
312 } else {
313 /* Now update the file to have the correct size. */
314 writeInt(file, file->bytesWritten - 8);
315 if(file->failed) {
316 fprintf(stderr, "Failed to write wave file size.\n");
317 passed = 0;
318 }
319 if(fseek(soundFile, 40, SEEK_SET) != 0) {
320 fprintf(stderr, "Failed to seek on input file.\n");
321 passed = 0;
322 } else {
323 /* Now update the file to have the correct size. */
324 writeInt(file, file->bytesWritten - 48);
325 if(file->failed) {
326 fprintf(stderr, "Failed to write wave file size.\n");
327 passed = 0;
328 }
329 }
330 }
331 }
332 closeFile(file);
333 return passed;
Bill Coxca02d872010-11-02 15:10:52 -0400334}
335
Bill Coxec23ae02011-10-21 03:02:47 -0400336/* Read from the wave file. Return the number of samples read. */
Bill Coxca02d872010-11-02 15:10:52 -0400337int readFromWaveFile(
338 waveFile file,
Bill Cox0c4c0602010-11-08 11:46:30 -0500339 short *buffer,
Bill Coxca02d872010-11-02 15:10:52 -0400340 int maxSamples)
341{
Bill Coxec23ae02011-10-21 03:02:47 -0400342 int i, bytesRead, samplesRead;
343 int bytePos = 0;
344 unsigned char bytes[WAVE_BUF_LEN];
345 short sample;
Bill Coxca02d872010-11-02 15:10:52 -0400346
Bill Coxec23ae02011-10-21 03:02:47 -0400347 if(maxSamples*file->numChannels*2 > WAVE_BUF_LEN) {
348 maxSamples = WAVE_BUF_LEN/(file->numChannels*2);
Bill Coxca02d872010-11-02 15:10:52 -0400349 }
Bill Coxec23ae02011-10-21 03:02:47 -0400350 bytesRead = readBytes(file, bytes, maxSamples*file->numChannels*2);
351 samplesRead = bytesRead/(file->numChannels*2);
352 for(i = 0; i < samplesRead*file->numChannels; i++) {
353 sample = bytes[bytePos++];
354 sample |= (unsigned int)bytes[bytePos++] << 8;
355 *buffer++ = sample;
356 }
357 return samplesRead;
Bill Coxca02d872010-11-02 15:10:52 -0400358}
359
360/* Write to the wave file. */
361int writeToWaveFile(
362 waveFile file,
Bill Cox0c4c0602010-11-08 11:46:30 -0500363 short *buffer,
Bill Coxca02d872010-11-02 15:10:52 -0400364 int numSamples)
365{
Bill Coxec23ae02011-10-21 03:02:47 -0400366 int i;
367 int bytePos = 0;
368 unsigned char bytes[WAVE_BUF_LEN];
369 short sample;
370 int total = numSamples*file->numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400371
Bill Coxec23ae02011-10-21 03:02:47 -0400372 for(i = 0; i < total; i++) {
373 if(bytePos == WAVE_BUF_LEN) {
374 writeBytes(file, bytes, bytePos);
375 bytePos = 0;
376 }
377 sample = buffer[i];
378 bytes[bytePos++] = sample;
379 bytes[bytePos++] = sample >> 8;
Bill Coxca02d872010-11-02 15:10:52 -0400380 }
Bill Coxec23ae02011-10-21 03:02:47 -0400381 if(bytePos != 0) {
382 writeBytes(file, bytes, bytePos);
383 }
384 return file->failed;
Bill Coxca02d872010-11-02 15:10:52 -0400385}