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 <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include "wave.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. */ 29 static 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. */ 48 static 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. */ 56 static 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. */ 71 static 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. */ 86 static 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. */ 98 static 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 */ 116 static 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 */ 131 static 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. */ 146 static 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. */ 167 static 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. */ 190 static 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. */ 227 static 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. */ 240 waveFile 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. */ 265 waveFile 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. */ 290 int 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. */ 325 int 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. */ 349 int 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