xref: /aosp_15_r20/external/sonic/wave.c (revision b290403dc9d28f89f133eb7e190ea8185d440ecd)
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