Sona 0.50 Source

Sona 0.50/tools/wav2swav/wav.c

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "wav.h"

// WAV header offsets
#define WAVHEADER_RIFF           0     // 32-bit - Always "RIFF"
#define WAVHEADER_RIFFSIZE       4     // 32-bit - Size of RIFF chunk
#define WAVHEADER_WAVE           8     // 32-bit - Always "WAVE"
#define WAVHEADER_FMT            12    // 32-bit - Always "fmt "
#define WAVHEADER_FMTSIZE        16    // 32-bit - Size of fmt chunk
#define WAVHEADER_ENCODING       20    // 16-bit - Encoding (1 = PCM)
#define WAVHEADER_NUMCHANNELS    22    // 16-bit - Number of channels
#define WAVHEADER_SAMPLERATE     24    // 32-bit - Samples per second
#define WAVHEADER_BYTESPERSEC    28    // 32-bit - Bytes per second
#define WAVHEADER_ALIGN          32    // 16-bit - Sample alignment
#define WAVHEADER_DEPTH          34    // 16-bit - Bits per sample
#define WAVHEADER_DATA           36    // 32-bit - Always "data"
#define WAVHEADER_DATASIZE       40    // 32-bit - Size of data chunk

#define SIZEOF_WAVHEADER         44    // Size of header

// Supported encoding formats
#define WAVENCODE_PCM            1     // Raw PCM

// Fixed values in the format
#define MAGIC(a,b,c,d) ((a)<<24 | (b)<<16 | (c)<<8 | (d))

#define MAGIC_RIFF               MAGIC('R','I','F','F')
#define MAGIC_WAVE               MAGIC('W','A','V','E')
#define MAGIC_FMT                MAGIC('f','m','t',' ')
#define MAGIC_DATA               MAGIC('d','a','t','a')

//***************************************************************************
// load_wav
// Parses and loads a WAV file into memory.
//---------------------------------------------------------------------------
// param filename ... file name to load from
//---------------------------------------------------------------------------
// return ........... pointer to waveform (NULL on failure)
//***************************************************************************

Waveform *load_wav(const char *filename)
{
   Waveform *wave = NULL;
   int errored = 0;
   
   // Open the WAV file
   FILE *file = fopen(filename, "rb");
   if (file == NULL) {
      fprintf(stderr, "Error: can't open WAV file \"%s\"\n", filename);
      return NULL;
   }
   
   // Read WAV header
   uint8_t header[SIZEOF_WAVHEADER];
   if (fread(header, 1, SIZEOF_WAVHEADER, file) != SIZEOF_WAVHEADER) {
      if (ferror(file))
         goto read_error;
      else
         goto not_valid;
   }
   
   // Check format
   // Note that since these are actually ASCII strings instead of 32-bit
   // integers we read them as big endian (unlike the rest of the format)
   uint32_t riff_text = header[WAVHEADER_RIFF+0] << 24 |
                        header[WAVHEADER_RIFF+1] << 16 |
                        header[WAVHEADER_RIFF+2] << 8 |
                        header[WAVHEADER_RIFF+3];
   
   uint32_t wave_text = header[WAVHEADER_WAVE+0] << 24 |
                        header[WAVHEADER_WAVE+1] << 16 |
                        header[WAVHEADER_WAVE+2] << 8 |
                        header[WAVHEADER_WAVE+3];
   
   uint32_t fmt_text  = header[WAVHEADER_FMT+0] << 24 |
                        header[WAVHEADER_FMT+1] << 16 |
                        header[WAVHEADER_FMT+2] << 8 |
                        header[WAVHEADER_FMT+3];
   
   uint32_t data_text = header[WAVHEADER_DATA+0] << 24 |
                        header[WAVHEADER_DATA+1] << 16 |
                        header[WAVHEADER_DATA+2] << 8 |
                        header[WAVHEADER_DATA+3];
   
   if (riff_text != MAGIC_RIFF ||
       wave_text != MAGIC_WAVE ||
       fmt_text  != MAGIC_FMT  ||
       data_text != MAGIC_DATA)
   {
      goto not_valid;
   }
   
   // Check encoding
   uint16_t encoding = header[WAVHEADER_ENCODING+1] << 8 |
                       header[WAVHEADER_ENCODING+0];
   
   if (encoding != WAVENCODE_PCM) {
      fprintf(stderr, "Error[\"%s\"]: unsupported encoding type %u "
                      "(only PCM is supported)\n",
                      filename, (unsigned)(encoding));
      fclose(file);
      return NULL;
   }
   
   // Check number of channels
   uint16_t num_channels = header[WAVHEADER_NUMCHANNELS+1] << 8 |
                           header[WAVHEADER_NUMCHANNELS+0];
   
   if (num_channels != 1 && num_channels != 2) {
      errored = 1;
      fprintf(stderr, "Error[\"%s\"]: unsupported number of channels "
                      "(is %u, must be 1 or 2)\n",
                      filename, (unsigned)(num_channels));
   }
   
   // Check sample depth
   uint16_t bit_depth = header[WAVHEADER_DEPTH+1] << 8 |
                        header[WAVHEADER_DEPTH+0];
   
   if (bit_depth != 8 && bit_depth != 16) {
      errored = 1;
      fprintf(stderr, "Error[\"%s\"]: unsupported sample bit depth "
                      "(is %u, must be 8 or 16)\n",
                      filename, (unsigned)(bit_depth));
   }
   
   // Retrieve sample rate
   uint32_t sample_rate = header[WAVHEADER_SAMPLERATE+3] << 24 |
                          header[WAVHEADER_SAMPLERATE+2] << 16 |
                          header[WAVHEADER_SAMPLERATE+1] << 8 |
                          header[WAVHEADER_SAMPLERATE+0];
   
   if (sample_rate == 0) {
      errored = 1;
      fprintf(stderr, "Error[\"%s\"]: invalid sample rate (0Hz?)\n",
                      filename);
   }
   
   // If any of the above failed let's return now
   // (we do it this way so all relevant messages are shown)
   if (errored) {
      fclose(file);
      return NULL;
   }
   
   // Compute number of samples
   uint32_t len = header[WAVHEADER_DATASIZE+3] << 24 |
                  header[WAVHEADER_DATASIZE+2] << 16 |
                  header[WAVHEADER_DATASIZE+1] << 8 |
                  header[WAVHEADER_DATASIZE+0];
   
   len /= num_channels;
   len /= bit_depth / 8;
   
   // Allocate memory to hold the waveform
   wave = malloc(sizeof(Waveform) + len);
   if (wave == NULL) {
      fprintf(stderr, "Error [\"%s\"]: out of memory!\n", filename);
      fclose(file);
      return NULL;
   }
   wave->len = len;
   wave->rate = sample_rate;
   
   // Read entire waveform into memory
   // Convert it to 8-bit mono in the process
   for (size_t pos = 0; pos < len; pos++) {
      uint16_t sample_l;
      uint16_t sample_r;
      
      // Read left channel
      if (bit_depth == 16) (void)(fgetc(file));
      sample_l = fgetc(file);
      
      // Read right channel, if any
      if (num_channels == 2) {
         if (bit_depth == 16) (void)(fgetc(file));
         sample_r = fgetc(file);
      } else {
         sample_r = sample_l;
      }
      
      // 8-bit WAVs are unsigned but 16-bit are signed
      // We want unsigned, so account for that now
      if (bit_depth == 16) {
         sample_l ^= 0x80;
         sample_r ^= 0x80;
      }
      
      // Did anything go wrong while reading?
      // (assume everything is bogus if so)
      if (ferror(file) || feof(file))
         goto read_error;
      
      // Mix and store
      wave->data[pos] = (sample_l + sample_r) / 2;
   }
   
   // We're done
   fclose(file);
   return wave;

// We reach here if something went wrong trying to read the file
// (though the most likely cause is that the file is truncated)
read_error:
   fprintf(stderr, "Error[\"%s\"]: problem reading WAV file\n", filename);
   fclose(file);
   free(wave);
   return NULL;

// We reach here if we determined it isn't a WAV file
not_valid:
   fprintf(stderr, "Error[\"%s\"]: not a valid WAV file\n", filename);
   fclose(file);
   return NULL;
}