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