Logo Search packages:      
Sourcecode: yiff version File versions  Download package

wav.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "wav.h"


static void WavReportError(
      const char *filename, const char *reason, int how_bad
);

void WavDestroyData(wav_data_struct *wd);

static FILE *WavOpenFile(
      const char *filename, const char *mode, off_t *size_rtn
);

int WavIsFPWav(FILE *fp);
int WavIsFileWav(const char *filename);
int WavReadHeader(const char *filename, FILE *fp, wav_data_struct *wd);
int WavReadPartialData(
      wav_data_struct *wd,
      long offset,
      long max_chunk_size,
      int read_opt
);


#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#define MAX(a,b)        ((a) > (b) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


/*
 *    Error reporting routine.
 */
static void WavReportError(
      const char *filename, const char *reason, int how_bad
)
{
      if(filename != NULL)
      {
          fprintf(stderr, "%s: ", filename);
      }
      if(reason != NULL)
      {
          fprintf(stderr, "%s (Error level %i)", reason, how_bad);
      }
      fprintf(stderr, "\n");


      return;
}

/*
 *    Resets all members in the wd structure, deallocating any
 *    resources and closing any opened files.
 */
void WavDestroyData(wav_data_struct *wd)
{
      if(wd == NULL)
          return;

      /* Filename. */
      free(wd->filename);
      wd->filename = NULL;

      if(wd->fp != NULL)
      {
          fclose(wd->fp);
          wd->fp = NULL;
      }

      /* Format reading stuff for internal functions only. */
      wd->format_tag = 0;

      /* Header and parms. */
      wd->header_pos = 0;
      wd->header_len = 0;
      wd->channels = 0;
      wd->samples_per_sec = 0;
      wd->bytes_per_sec = 0;
      wd->block_align = 0;

      /* Format specific fields. */
      wd->bits_per_sample = 0;
      wd->samples_per_block = 0;
      wd->coefficients = 0;
      wd->comp_type = 0;
      wd->revision = 0;

      wd->data_starting_pos = 0;
      wd->data_chunk_size = 0;

      free(wd->data);
      wd->data = NULL;
      wd->data_len = 0;

      return;
}

/*
 *    Procedure to open a file and fetch its size.
 */
static FILE *WavOpenFile(
      const char *filename, const char *mode, off_t *size_rtn
)
{
      FILE *fp;
      struct stat stat_buf;


      if(filename == NULL)
      {
          fprintf(stderr, "Cannot open file with no name.\n");
          *size_rtn = 0;
          return(NULL);
      }
      if(mode == NULL)
      {
          fprintf(stderr, "%s: Open mode not givin.\n", filename);
          *size_rtn = 0;
          return(NULL);
      }

      if(stat(filename, &stat_buf))
      {
          fprintf(stderr, "%s: No such file.\n", filename);
          (*size_rtn) = 0;
          return(NULL);
      }
      (*size_rtn) = stat_buf.st_size;

      fp = fopen(filename, mode);
      if(fp == NULL)
      {
          fprintf(stderr, "%s: Cannot open.\n", filename);
          (*size_rtn) = 0;
          return(NULL);
      }

      return(fp);
}

/*
 *    Checks if the given wav file specified by fp really
 *    is a wav file by rewinding it and reading data at the fp.
 *
 *    Returns WavSuccess if it is or other value if it is not.
 */
int WavIsFPWav(FILE *fp)
{
      u_int32_t uix;
      u_int32_t internal_filesize;


      if(fp == NULL)
          return(WavErrorNoAccess);

      /* Rewind to beginning of file. */
      rewind(fp);

      /* Check for RIFF ID code. */
      fread(&uix, 1, 4, fp);
      if(uix != WavIDRIFF)
          return(WavErrorNotWave);

      /* Internal filesize. */
      fread(&internal_filesize, 1, 4, fp);
      internal_filesize += ftell(fp);

      /* Check for Wave ID code. */
      fread(&uix, 1, 4, fp);
      if(uix != WavIDWave)
          return(WavErrorNotWave);

      /* All checks passed, this is a wav file. */
      return(WavSuccess); 
}

/*
 *      Checks if the given wav file specified by filename really
 *      is a wav file by opening it and reading it.
 *
 *      Returns WavSuccess if it is or other value if it is not.
 */
int WavIsFileWav(const char *filename)
{
      int status;
      FILE *fp;
      off_t filesize;


      /* Open file. */
      fp = WavOpenFile(filename, "rb", &filesize);
      if(fp == NULL)
          return(WavErrorNoAccess);
      if(filesize == 0)
      {
          fclose(fp);
          return(WavErrorNoAccess);
      }

      /* Check if file is a wav file. */
      status = WavIsFPWav(fp);

      /* Close file. */
      fclose(fp);

      return(status);
}

/*
 *    Reads the header from the stream fp and initializes the given
 *    wd structure if fp is valid and a wav file.
 *
 *    The given filename is used only for referance purposes, it can be
 *    NULL.
 *
 *    If WavSuccess is returned then the given fp will be transfered
 *    to the wd structure and should not be referanced again. For all
 *    other return values, the calling function is responsible for
 *    closing the given fp.
 */
int WavReadHeader(const char *filename, FILE *fp, wav_data_struct *wd)
{
      int chunk_count;
      u_int16_t usx;
      u_int32_t uix;
      u_int32_t nextseek;
      u_int32_t ckid;
      u_int32_t cksize;
      u_int32_t ckpos;

      char idstr[5];
      u_int16_t extra_info_size;

      struct stat stat_buf;
      off_t filesize;
      u_int32_t internal_filesize;  /* Size indicated in file data. */


      /* Error checks. */
      if(wd == NULL)
          return(WavErrorNoBuffers);
      if(fp == NULL)
          return(WavErrorBadValue);

      /* Rewind to beginning of wav file. */
      rewind(fp);


      /* Reset values. */
      memset(wd, 0x00, sizeof(wav_data_struct));

      /* Get size of file. */
      if(fstat(fileno(fp), &stat_buf))
          return(WavErrorNoAccess);

      filesize = stat_buf.st_size;
      if(filesize == 0)
          return(WavErrorNoAccess);

      /* Is this a wav file? Check for RIFF ID code. */
      fread(&uix, 1, 4, fp);
      if(uix != WavIDRIFF)
          return(WavErrorNotWave);

      /* Get internal filesize. */
      fread(&internal_filesize, 1, 4, fp);
      internal_filesize += ftell(fp);

      /* Check for Wave ID code. */
      fread(&uix, 1, 4, fp);
      if(uix != WavIDWave)
          return(WavErrorNotWave);


      /* Begin reading wav file header. */

      /* Record file name. */
      if(filename != NULL)
          wd->filename = strdup(filename);


      /* Begin reading chunks. */

      nextseek = ftell(fp);
      chunk_count = 0;
      while(nextseek < internal_filesize)
      {
          fseek(fp, nextseek, SEEK_SET);
          fread(&ckid, 1, 4, fp);
          fread(&cksize, 1, 4, fp);
          ckpos = ftell(fp);
          nextseek = cksize + ckpos;
          memcpy(idstr, (void *)&ckid, 4);
          idstr[4] = '\0';

          /* Check chunk ID. */
          switch(ckid)
          {
            /* Format (header?) chunk. */
            case WavFormatChunkCode:

            /* Set header length. */
            wd->header_pos = ckpos;
            wd->header_len = cksize;

            /* Get format tag. */
            fread(&usx, 1, 2, fp);
            wd->format_tag = usx;

            /* Get channels. */
            fread(&usx, 1, 2, fp);
            wd->channels = usx;

            /* Get samples per second. */
            fread(&uix, 1, 4, fp);
            wd->samples_per_sec = uix;

            /* Get average bytes per second. */
            fread(&uix, 1, 4, fp);
            wd->bytes_per_sec = uix;

            /* Get block alignment. */
            fread(&usx, 1, 2, fp);
            wd->block_align = usx;


            /* Format specific fields. */

            /* Get bits per second. */
            fread(&usx, 1, 2, fp);
            wd->bits_per_sample = usx;

            if(wd->format_tag != 0x0001)
            {
                /* WARNING: THIS IS ALL EXPERIMENTAL!!! */

                /* Get size of extra info. */
                fread(&usx, 1, 2, fp);
                extra_info_size = usx;

                switch(wd->format_tag)
                {
                  case 0x0002:    /* MS ADPCM??? */
                  /* Get samples per block. */
                  fread(&usx, 1, 2, fp);
                  wd->samples_per_block = usx;

                  /* Get number of coefficients. */
                  fread(&usx, 1, 2, fp);
                  wd->coefficients = usx;
/* Need to write code to get each coefficient. */

                  break;

                  case 0x0011: /* WAVE_FORMAT_DVI_ADPCM??? */
                  /* Assuming same as WAVE_FORMAT_DSPGROUP_TRUESPEECH. */

                  case 0x0022: /* WAVE_FORMAT_DSPGROUP_TRUESPEECH */
                  /* Get samples per block. */
                  fread(&usx, 1, 2, fp);
                  wd->samples_per_block = usx;

                  break;

                  case 0x0021:  /* WAVE_FORMAT_SONARC */
                  /* Get compression type. */
                  fread(&usx, 1, 2, fp);
                  wd->comp_type = usx;

                  break;
                  case 0x0200: /* WAVE_FORMAT_CREATIVE_ADPCM */
                  /* Get revision. */
                  fread(&usx, 1, 2, fp);
                  wd->revision = usx;

                  break;

                }
            }    /* End format specific fields. */
            break;


            /* Data chunk. */
            case WavDataChunkCode:
            /* Record data chunk position and size. */
            wd->data_starting_pos = (off_t)ckpos;
            wd->data_chunk_size = (off_t)cksize;
            break;


            /* Unknown chunk. */
            default:
/*
            fprintf(stderr,
                "%s: Chunk %i: Unknown chunk ID 0x%.8x.\n",
                filename, chunk_count, ckid
            );
 */
            break;

          }        /* End check chunk ID. */

          /* Increment chunk count. */
          chunk_count++;

      }     /* End reading chunks. */


      /* Transfer given fp to the wd structure, the calling
       * function should not referance it again since we are returning
       * WavSuccess.
       */
      wd->fp = fp;

      return(WavSuccess);
}

/*
 *    Reads a segment of data from the wav file reffered to
 *    by the filename member on the given wd structure (which
 *    should have been initialized wuth a prior call to
 *    WavReadHeader().
 *
 *    File will be opened as needed if the fp member is NULL.
 *
 *    The member data and data_len will be reallocated as needed
 *    if the requested max_chunk_size is different than wd->data_len.
 */
int WavReadPartialData(
      wav_data_struct *wd,
      long offset,            /* In bytes. */
      long max_chunk_size,    /* In bytes. */
      int read_opt            /* Reading format. */
)
{
      int i;
      char *buf_ptr;


      if(wd == NULL)
          return(WavErrorBadValue);
      if(offset < 0)
          return(WavErrorBadValue);

      /* Read nothing? */
      if(max_chunk_size <= 0)
      {
          free(wd->data);
          wd->data = NULL;
          wd->data_len = 0;

          return(WavSuccess);
      }

      /* Sanitize offset and max_chunk_size. */
      if(offset >= wd->data_chunk_size)
      {
          return(WavErrorEndOfData);
      }
      if((offset + max_chunk_size) > wd->data_chunk_size)
      {
          /* Sanitize max_chunk_size. */
          max_chunk_size = wd->data_chunk_size - offset;
      }
      if(max_chunk_size <= 0)
      {
          return(WavErrorEndOfData);
      }


      /* Open file as needed. */
      if(wd->fp == NULL)
      {
          off_t filesize;

          if(wd->filename == NULL)
            return(WavErrorBadValue);

          wd->fp = WavOpenFile(wd->filename, "rb", &filesize);
          if(wd->fp == NULL)
            return(WavErrorNoAccess);
          if(filesize == 0)
            return(WavErrorNoAccess);
      }


      /* Reallocate buffer for data (as needed). */
      if(wd->data_len != max_chunk_size)
      {
          wd->data_len = max_chunk_size;

          wd->data = (char *)realloc(
            wd->data,
            wd->data_len * sizeof(char)
          );
      }
      if(wd->data == NULL)
      {
          wd->data_len = 0;

          fclose(wd->fp);
          wd->fp = NULL;

          return(WavErrorNoBuffers);
      }

      /* Set file pointer position. */
      fseek(wd->fp, wd->data_starting_pos + offset, SEEK_SET);

      /* Read the data from file, the wav data format is in
       * unsigned 8 values.
       */
      buf_ptr = wd->data;
      switch(read_opt)
      {
        /* Shift byte value by - 128 to make it signed 8. */
        case WavReadSigned8:
          for(i = 0; i < max_chunk_size; i++)
            *buf_ptr++ = (char)(fgetc(wd->fp) - 128);
          break;

        /* Read data as is (unsigned 8). */
        default:
          for(i = 0; i < max_chunk_size; i++)
            *buf_ptr++ = (char)(fgetc(wd->fp));
          break;
      }

      return(WavSuccess);
}

Generated by  Doxygen 1.6.0   Back to index