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

voc.c

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

#include "voc.h"


static FILE *VocOpenFile(
      const char *filename, const char *mode, off_t *size_rtn
);
int VocIsFileVoc(const char *filename);
unsigned int VocGetEnvSampleRate(voc_data_struct *vd);

void VocDestroyData(voc_data_struct *vd);
int VocAddDataBlock(voc_data_struct *vd, int type);

int VocReadHeader(const char *filename, FILE *fp, voc_data_struct *vd);
int VocReadPartialData(
      voc_data_struct *vd,
      off_t offset,
      off_t 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))


/*
 *    Procedure to open a voc file and return its entire file size.
 */
static FILE *VocOpenFile(
      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 specified file is a VOC file.
 */
int VocIsFileVoc(const char *filename)
{
      int i;
      char magicname[0x14];
      FILE *fp;
      off_t filesize;


      /* Open file. */
      fp = VocOpenFile(filename, "rb", &filesize);
      if(fp == NULL)
          return(VocErrorNoAccess);
      if(filesize == 0)
          return(VocErrorNoAccess);

      /* Bytes 0x00 to 0x13: Get magic name. */
      for(i = 0; i < 0x13; i++)
          magicname[i] = (char)fgetc(fp);

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

      /* Check magic name. */
      magicname[0x13] = '\0';
      if(strcmp(magicname, VocMagicName))
          return(VocErrorNotVoc);
      else
          return(VocSuccess);
}

/*
 *    Returns the sample rate of that of the first data block
 *    of type VocBlockTypeSoundData that is found on the blocks
 *    in the vd structure.
 *
 *    If vd is invalid or no blocks of type VocBlockTypeSoundData exist
 *    then the value VocDefaultSampleRate will be returned.
 */
unsigned int VocGetEnvSampleRate(voc_data_struct *vd)
{
      int i;
      unsigned int sample_rate = VocDefaultSampleRate;
      voc_block_struct **ptr, *block_ptr;


      if(vd == NULL)
          return(sample_rate);

      /* Go through each data block. */
      for(i = 0, ptr = vd->datablock;
          i < vd->total_datablocks;
          i++, ptr++)
      {
          block_ptr = *ptr;
          if(block_ptr == NULL)
            continue;

          /* Check if this type is a VocBlockTypeSoundData. */
          if(block_ptr->type == VocBlockTypeSoundData)
          {
            sample_rate = block_ptr->sample_rate;
            break;
          }
      }

      /* Sanitize sample rate. */
      if(sample_rate == 0)
          sample_rate = VocDefaultSampleRate;

      return(sample_rate);
}

/*
 *    Deallocates all resources and closes any opened files specified
 *    in the given vd structure and resets its values.
 */
void VocDestroyData(voc_data_struct *vd)
{
      int i;
      voc_block_struct **ptr, *block_ptr;


      if(vd == NULL)
          return;

      /* Data blocks. */
      for(i = 0, ptr = vd->datablock;
          i < vd->total_datablocks;
          i++, ptr++
      )
      {
          block_ptr = *ptr;
          if(block_ptr == NULL)
            continue;

          free(block_ptr->str);

          free(block_ptr);
      }

      free(vd->datablock);
      vd->datablock = NULL;

      vd->total_datablocks = 0;


      /* Reset values. */
      free(vd->filename);
      vd->filename = NULL;
      vd->filesize = 0;

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

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

      vd->total_data_len = 0;

      vd->major_version = 0;
      vd->minor_version = 0;
      vd->first_data_block = 0;


      return;
}

/*
 *    Allocates a new data block on the vd structure by the specified
 *    type.
 */
int VocAddDataBlock(voc_data_struct *vd, int type)
{
      int n;


      if(vd == NULL)
          return(-1);

      if(vd->total_datablocks < 0)
          vd->total_datablocks = 0;

      n = vd->total_datablocks;
      vd->total_datablocks++;

      vd->datablock = (voc_block_struct **)realloc(
          vd->datablock,
          vd->total_datablocks * sizeof(voc_block_struct *)
      );
      if(vd->datablock == NULL)
      {
          vd->total_datablocks = 0;
          return(-1);
      }

      vd->datablock[n] = (voc_block_struct *)calloc(
          1,
          sizeof(voc_block_struct)
      );
      if(vd->datablock[n] == NULL)
      {
          vd->total_datablocks = n;
          return(-1);
      }

      /* Set type. */
      vd->datablock[n]->type = type;

      return(n);
}

/*
 *      Reads the header from the stream fp and initializes the given
 *      vd structure if fp is valid and a voc file.
 *
 *      The given filename is used only for referance purposes, it can be
 *    NULL.
 *
 *      If VocSuccess is returned then the given fp will be transfered
 *      to the vd structure and should not be referanced again. For all
 *      other return values, the calling function is responsible for
 *      closing the given fp.
 */
int VocReadHeader(const char *filename, FILE *fp, voc_data_struct *vd)
{
      int x, y, z;
      unsigned char type;
      off_t nextseek;

      /* To identify that this is a VOC file. */
      char magicname[0x14];

      /* For calculating that pesky 3 byte unsigned int block offset. */
      unsigned int size_x, size_y, size_z;

      int bytes_read = 0;
      struct stat stat_buf;
      off_t filesize;


      /* Error checks. */
      if(vd == NULL)
          return(VocErrorNoBuffers);
      if(fp == NULL)
          return(VocErrorBadValue);

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


      /* Reset values. */
      memset(vd, 0x00, sizeof(voc_data_struct));

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

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

      /* Is this is a voc file? Check 0x00 to 0x12 for magic number. */
      for(x = 0; x < 0x13; x++)
      {
          magicname[x] = (char)fgetc(fp);
          bytes_read++;
      }
      magicname[0x13] = '\0';
      /* Magic name does not match? If so then this is not a voc
       * file.
       */
      if(strcmp(magicname, VocMagicName))
          return(VocErrorNotVoc);


      /* Begin reading voc file header. */

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

      /* Record file size. */
      vd->filesize = filesize;


      /* 0x13: Read but skip abort print byte. */
      fgetc(fp);
      bytes_read++;


      /* 0x14 to 0x15: Get offset to first data block. */
      x = fgetc(fp);
      y = fgetc(fp);
      bytes_read += 2;
      /* Calculate, LSBF. */
      vd->first_data_block = x + (y * 256);


      /* 0x16 to 0x17: Minor and major version numbers. */
      vd->minor_version = (unsigned char)fgetc(fp);
      vd->major_version = (unsigned char)fgetc(fp);
      bytes_read += 2;


      /* 0x18 to 0x19: Extended stuff. */
      x = fgetc(fp);
      y = fgetc(fp);
      bytes_read += 2;


      /* Move fp to start of first block. */
      fseek(fp, vd->first_data_block, SEEK_SET);
      nextseek = ftell(fp);

      while(nextseek < vd->filesize)
      {
          fseek(fp, nextseek, SEEK_SET);

          /* Read type. */
          type = (unsigned char)fgetc(fp);
          bytes_read++;
          switch(type)
          {
            case VocBlockTypeTerminator:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);
            /* *** Terminator type has no size! *** */

            /* Get size. */
            vd->datablock[y]->size = 1;
            /* Set nextseek. */
            nextseek += 1;

            break;


            case VocBlockTypeSoundData:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Calculate and set data_start_pos. */
            vd->datablock[y]->data_start_pos = nextseek + 6;

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get data_len. */
            if((long)vd->datablock[y]->size >= 2)
                vd->datablock[y]->data_len = vd->datablock[y]->size - 2;
            else
                vd->datablock[y]->data_len = 2;

            /* Get sample rate. */
            z = fgetc(fp);
            bytes_read++;
            if(z >= 256)
                z = 255;
            vd->datablock[y]->sample_rate = (unsigned int)(
                (double)(-1000000) / (double)(z - 256)
            );

            /* Get compresion type. */
            vd->datablock[y]->compression_type = (unsigned char)fgetc(fp);
            bytes_read++;

            /* Calculate bits. */
            switch(vd->datablock[y]->compression_type)
            {
              case 0x00:
                vd->datablock[y]->bits = 8;
                break;

              case 0x01:
                vd->datablock[y]->bits = 4;
                break;

              case 0x02:
                vd->datablock[y]->bits = 2.6;
                break;

              case 0x03:
                vd->datablock[y]->bits = 2;
                break;

              case 0x04:      /* Multi DAC, not standard */
                vd->datablock[y]->bits = 8;
                break;

              default:
                vd->datablock[y]->bits = 8;
                break;
            }

            break;


            case VocBlockTypeSoundDataContinue:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Calculate and set data_start_pos. */
            vd->datablock[y]->data_start_pos = nextseek + 4;
                
            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get data_len. */
            vd->datablock[y]->data_len = vd->datablock[y]->size;
            break;


            case VocBlockTypeSilence:
            /* Allocate data block. */  
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get silence length. */
            fread(&(vd->datablock[y]->silence_length), 1, 2, fp);
            bytes_read += 2;

            /* Get sample rate. */
            z = fgetc(fp);
            bytes_read++;
            if(z >= 256)
                z = 255;
            vd->datablock[y]->sample_rate = (unsigned int)(
                (double)(-1000000) / (double)(z - 256)
            );
            break;

            case VocBlockTypeMarker:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get marker number. */
            fread(&(vd->datablock[y]->marker_num), 1, 2, fp);
            bytes_read += 2;
            break;

            case VocBlockTypeASCII:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get string. */
            vd->datablock[y]->str_len = vd->datablock[y]->size;
            vd->datablock[y]->str = (char *)calloc(1,
                (vd->datablock[y]->str_len + 1) * sizeof(char)
            );
            for(z = 0; z < vd->datablock[y]->size; z++)
            {
                vd->datablock[y]->str[z] = (char)fgetc(fp);
                bytes_read++;
            }
            vd->datablock[y]->str[z] = '\0';
            break;


            case VocBlockTypeRepeat:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get repeat count. */
            fread(&(vd->datablock[y]->repeat_count), 1, 2, fp);
            bytes_read += 2;
            break;


            case VocBlockTypeEndRepeat:
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* There is no info for this field. */
            break;


            case VocBlockTypeExtended: 
            /* Allocate data block. */
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            /* Get time constant. */
            fread(&(vd->datablock[y]->time_constant), 1, 2, fp);
            bytes_read += 2;

            /* Get pack. */
            vd->datablock[y]->pack = (unsigned char)fgetc(fp);
            bytes_read++;

            /* Get mode (0 = mono, 1 = stereo). */
            vd->datablock[y]->mode = (unsigned char)fgetc(fp);
            bytes_read++;
            break;


            /* Unknown, assume field is just 1 byte line. */
            default:
            /* Allocate data block. */  
            y = VocAddDataBlock((voc_data_struct *)vd, type);
            if(y < 0)
                return(VocErrorNoBuffers);

            /* Get size. */
            size_x = (unsigned int)fgetc(fp);
            size_y = (unsigned int)fgetc(fp);
            size_z = (unsigned int)fgetc(fp);
            bytes_read += 3;
            vd->datablock[y]->size = size_x + (size_y << 8) +
                (size_z << 16);
            /* Set nextseek. */
            nextseek += (vd->datablock[y]->size + 4);

            break;
          }
      }

      /* Add audio lengths from all data blocks containing audio data. */
      vd->total_data_len = 0;
      for(x = 0; x < vd->total_datablocks; x++)
      {
          if(vd->datablock[x] == NULL)
            continue;

          if( (vd->datablock[x]->type != VocBlockTypeSoundData) &&
            (vd->datablock[x]->type != VocBlockTypeSoundDataContinue)
          )
            continue;

          vd->total_data_len += vd->datablock[x]->data_len;
      }


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

      return(VocSuccess);
}

/*
 *    Reads a segment of data from the VOC file reffered to
 *      by the filename member on the given wd structure (which
 *      should have been initialized wuth a prior call to
 *      VocReadHeader().
 *
 *      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 data_len.
 */
int VocReadPartialData(
      voc_data_struct *vd,
      off_t offset,
      off_t max_chunk_size,
      int read_opt            /* Reading format. */
)
{
      int i, datablock_num;
      int total_datablocks;
      voc_block_struct *block_ptr;

      off_t src_buf_pos;
      int tar_buf_pos;

      off_t offset_count;


      if(vd == NULL)
          return(VocErrorBadValue);
      if(offset < 0)
          return(VocErrorBadValue);

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

          return(VocSuccess);
      }

      /* Get total_datablocks. */
      total_datablocks = vd->total_datablocks;

      /* Free and allocate data buffer as needed. */
      if(vd->data_len != max_chunk_size)
      {
          vd->data_len = max_chunk_size;

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

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

          if(vd->filename == NULL)
            return(VocErrorBadValue);
       
          vd->fp = VocOpenFile(vd->filename, "rb", &filesize);
          if(vd->fp == NULL)
            return(VocErrorNoAccess);
          if(filesize == 0)
            return(VocErrorNoAccess);
      }

      /* Go through each data block. */
      for(datablock_num = 0, tar_buf_pos = 0, offset_count = 0;
          datablock_num < total_datablocks;
          datablock_num++
      )
      {
          if(tar_buf_pos >= max_chunk_size)
            break;

          block_ptr = vd->datablock[datablock_num];
          if(block_ptr == NULL)
            continue;

          /* Skip data blocks not of type audio or audio continue. */
          if((block_ptr->type != VocBlockTypeSoundData) &&
             (block_ptr->type != VocBlockTypeSoundDataContinue)
          )
            continue;

          /* Skip if offset would not put us in this block. */
          if((offset_count + block_ptr->data_len) < offset)
          {
            offset_count += block_ptr->data_len;
            continue;
          }

          /* Seek beginning of audio data block in file. */
          src_buf_pos = (off_t)((int)offset - (int)offset_count);
          if(src_buf_pos < 0)
            src_buf_pos = 0;
          fseek(
            vd->fp,
            block_ptr->data_start_pos + src_buf_pos,
            SEEK_SET
          );

          offset_count += block_ptr->data_len;
          offset = offset_count;
          while((tar_buf_pos < max_chunk_size) &&
              (src_buf_pos < block_ptr->data_len)
          )
          {
            vd->data[tar_buf_pos] = (char)fgetc(vd->fp);

            tar_buf_pos++;
            src_buf_pos++;
          }
      }


      /* Sanitize data length. */
      if(tar_buf_pos < vd->data_len)
          vd->data_len = tar_buf_pos;


      /* Shift the DSP data that was read, voc file format stores the
       * data as unsigned char.
       */
      switch(read_opt)
      {
        case VocReadSigned8:
          for(i = 0; i < vd->data_len; i++)
            vd->data[i] = (char)((int)vd->data[i] - (int)128);
          break;

        default:
          break;
      }

      return(VocSuccess);
}

Generated by  Doxygen 1.6.0   Back to index