blob: 05bbf1f12db5d5f4e6c56599dda874157e10a097 [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 Cox60eeb062015-02-27 10:17:45 -08006 This file is licensed under the Apache 2.0 license.
7*/
Bill Coxca02d872010-11-02 15:10:52 -04008
9/*
10This file supports read/write wave files.
11*/
Bill Coxec23ae02011-10-21 03:02:47 -040012#include <stdio.h>
Bill Coxca02d872010-11-02 15:10:52 -040013#include <stdlib.h>
Bill Cox0c4c0602010-11-08 11:46:30 -050014#include <string.h>
Bill Coxca02d872010-11-02 15:10:52 -040015#include "wave.h"
16
Bill Coxec23ae02011-10-21 03:02:47 -040017#define WAVE_BUF_LEN 4096
18
Bill Coxca02d872010-11-02 15:10:52 -040019struct waveFileStruct {
Bill Cox2081ea42010-11-05 05:49:47 -040020 int numChannels;
Bill Coxec23ae02011-10-21 03:02:47 -040021 int sampleRate;
22 FILE *soundFile;
23 int bytesWritten; /* The number of bytes written so far, including header */
24 int failed;
25 int isInput;
Bill Coxca02d872010-11-02 15:10:52 -040026};
27
Bill Coxec23ae02011-10-21 03:02:47 -040028/* Write a string to a file. */
29static 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. */
48static 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. */
56static 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. */
71static 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. */
86static 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. */
98static 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 */
116static 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 */
131static 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. */
146static 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. */
167static 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. */
190static 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 */
Matthew Albright72ae5452013-04-17 22:33:51 -0700199 int chunkSize = readInt(file); /* 16 or 18 - size of this chunk */
200 if(chunkSize != 16 && chunkSize != 18) {
Bill Coxec23ae02011-10-21 03:02:47 -0400201 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 }
Matthew Albright72ae5452013-04-17 22:33:51 -0700218 if (chunkSize == 18) { /* ffmpeg writes 18, and so has 2 extra bytes here */
219 data = readShort(file);
220 }
Bill Coxec23ae02011-10-21 03:02:47 -0400221 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. */
227static 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. */
Bill Coxca02d872010-11-02 15:10:52 -0400240waveFile openInputWaveFile(
241 char *fileName,
Bill Cox1a299bb2010-11-19 15:07:17 -0500242 int *sampleRate,
243 int *numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400244{
Bill Coxca02d872010-11-02 15:10:52 -0400245 waveFile file;
Bill Coxec23ae02011-10-21 03:02:47 -0400246 FILE *soundFile = fopen(fileName, "rb");
Bill Coxca02d872010-11-02 15:10:52 -0400247
Bill Coxca02d872010-11-02 15:10:52 -0400248 if(soundFile == NULL) {
Bill Coxec23ae02011-10-21 03:02:47 -0400249 fprintf(stderr, "Unable to open wave file %s for reading\n", fileName);
Bill Coxca02d872010-11-02 15:10:52 -0400250 return NULL;
251 }
252 file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
253 file->soundFile = soundFile;
Bill Coxec23ae02011-10-21 03:02:47 -0400254 file->isInput = 1;
255 if(!readHeader(file)) {
256 closeFile(file);
257 return NULL;
258 }
259 *sampleRate = file->sampleRate;
260 *numChannels = file->numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400261 return file;
262}
263
Bill Coxec23ae02011-10-21 03:02:47 -0400264/* Open a 16-bit little-endian wav file for writing. It may be mono or stereo. */
Bill Coxca02d872010-11-02 15:10:52 -0400265waveFile openOutputWaveFile(
266 char *fileName,
Bill Cox1a299bb2010-11-19 15:07:17 -0500267 int sampleRate,
268 int numChannels)
Bill Coxca02d872010-11-02 15:10:52 -0400269{
Bill Coxca02d872010-11-02 15:10:52 -0400270 waveFile file;
Bill Coxec23ae02011-10-21 03:02:47 -0400271 FILE *soundFile = fopen(fileName, "wb");
Bill Coxca02d872010-11-02 15:10:52 -0400272
Bill Coxca02d872010-11-02 15:10:52 -0400273 if(soundFile == NULL) {
Bill Coxec23ae02011-10-21 03:02:47 -0400274 fprintf(stderr, "Unable to open wave file %s for writing\n", fileName);
Bill Coxca02d872010-11-02 15:10:52 -0400275 return NULL;
276 }
277 file = (waveFile)calloc(1, sizeof(struct waveFileStruct));
278 file->soundFile = soundFile;
Bill Coxec23ae02011-10-21 03:02:47 -0400279 file->sampleRate = sampleRate;
Bill Coxf8071892010-11-19 17:57:40 -0500280 file->numChannels = numChannels;
Bill Coxec23ae02011-10-21 03:02:47 -0400281 writeHeader(file, sampleRate);
282 if(file->failed) {
283 closeFile(file);
284 return NULL;
285 }
Bill Coxca02d872010-11-02 15:10:52 -0400286 return file;
287}
288
289/* Close the sound file. */
Bill Coxec23ae02011-10-21 03:02:47 -0400290int closeWaveFile(
Bill Coxca02d872010-11-02 15:10:52 -0400291 waveFile file)
292{
Bill Coxec23ae02011-10-21 03:02:47 -0400293 FILE *soundFile = file->soundFile;
294 int passed = 1;
Bill Coxca02d872010-11-02 15:10:52 -0400295
Bill Coxec23ae02011-10-21 03:02:47 -0400296 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;
Bill Coxca02d872010-11-02 15:10:52 -0400322}
323
Bill Coxec23ae02011-10-21 03:02:47 -0400324/* Read from the wave file. Return the number of samples read. */
Bill Coxca02d872010-11-02 15:10:52 -0400325int readFromWaveFile(
326 waveFile file,
Bill Cox0c4c0602010-11-08 11:46:30 -0500327 short *buffer,
Bill Coxca02d872010-11-02 15:10:52 -0400328 int maxSamples)
329{
Bill Coxec23ae02011-10-21 03:02:47 -0400330 int i, bytesRead, samplesRead;
331 int bytePos = 0;
332 unsigned char bytes[WAVE_BUF_LEN];
333 short sample;
Bill Coxca02d872010-11-02 15:10:52 -0400334
Bill Coxec23ae02011-10-21 03:02:47 -0400335 if(maxSamples*file->numChannels*2 > WAVE_BUF_LEN) {
336 maxSamples = WAVE_BUF_LEN/(file->numChannels*2);
Bill Coxca02d872010-11-02 15:10:52 -0400337 }
Bill Coxec23ae02011-10-21 03:02:47 -0400338 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;
Bill Coxca02d872010-11-02 15:10:52 -0400346}
347
348/* Write to the wave file. */
349int writeToWaveFile(
350 waveFile file,
Bill Cox0c4c0602010-11-08 11:46:30 -0500351 short *buffer,
Bill Coxca02d872010-11-02 15:10:52 -0400352 int numSamples)
353{
Bill Coxec23ae02011-10-21 03:02:47 -0400354 int i;
355 int bytePos = 0;
356 unsigned char bytes[WAVE_BUF_LEN];
357 short sample;
358 int total = numSamples*file->numChannels;
Bill Coxca02d872010-11-02 15:10:52 -0400359
Bill Coxec23ae02011-10-21 03:02:47 -0400360 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;
Bill Coxca02d872010-11-02 15:10:52 -0400368 }
Bill Coxec23ae02011-10-21 03:02:47 -0400369 if(bytePos != 0) {
370 writeBytes(file, bytes, bytePos);
371 }
372 return file->failed;
Bill Coxca02d872010-11-02 15:10:52 -0400373}