Home | History | Annotate | Download | only in sonic
      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