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/*
10This 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
19struct 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. */
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 */
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. */
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. */
240waveFile 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. */
265waveFile 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. */
290int 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. */
325int 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. */
349int 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