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