Logo Search packages:      
Sourcecode: mas version File versions

wav.c

/*
 * Copyright (c) 2001-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */
/*
 * $Id: wav.c,v 1.1.1.1 2003/01/22 14:21:30 rocko Exp $
 *
 * Copyright (c) 2000, 2001 by Shiman Associates Inc., Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions: The above
 * copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the names of the authors or
 * copyright holders shall not be used in advertising or otherwise to
 * promote the sale, use or other dealings in this Software without
 * prior written authorization from the authors or copyright holders,
 * as applicable.
 *
 * All trademarks and registered trademarks mentioned herein are the
 * property of their respective owners. No right, title or interest in
 * or to any trademark, service mark, logo or trade name of the
 * authors or copyright holders or their licensors is granted.
 *
 */

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mas/mas.h"
#include "wav.h"

/******* PRIVATE FUNCTIONS *********************************************/

static int cmp_fourcc(FourCC , char* );
static int riff_read_chunk_header( FILE * , struct riff_chunk_header*  );
static int print_wav_file_info( struct wave_info* );
static int parse_small_chunks( FILE * , struct wave_info* , u_int32 );
static int32 alloc_and_read(FILE * , u_int32 , void** );

static struct wave_file_handle_node* new_wave_file_handle( void );
static int delete_wave_file_handle( struct wave_file_handle_node* ); 
static int append_wave_file_handle(struct wave_file_handle_node* ,
                           struct wave_file_handle_node* );
static struct wave_file_handle_node* find_wave_file_handle(
    struct wave_file_handle_node* , FILE* );

/****** PRIVATE VARIABLES *********************************************/
/*** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ******************************/
/*** !!!! THIS IS NOT THREADSAFE !!!!!! ******************************/
/*** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ******************************/
static struct wave_file_handle_node* wave_file_handle_list_head = 0;

/***********************************************************************/

/*** wave file handle linked list handling routines *******************/
struct wave_file_handle_node*
new_wave_file_handle( void )
{
    struct wave_file_handle_node* node;

    if ( (node = masc_rtalloc(sizeof(struct wave_file_handle_node))) == 0)
    {
      return 0;
    }

    node->fp   = 0;
    node->info = 0;
    node->next = 0;
    node->prev = 0;
    return node;
}

int32
delete_wave_file_handle( struct wave_file_handle_node* node )
{
    if (node)
    {
      if (node->info) 
      {
          masc_rtfree(node->info);
          node->info = 0;
      }
      
      /* connect nodes on either side in list */
      if (node->prev != 0)
          node->prev->next = node->next;
      if (node->next != 0)
          node->next->prev = node->prev;

      masc_rtfree(node);
    }
    else return MERR_MEMORY;

    return 0;
}

int32
append_wave_file_handle(struct wave_file_handle_node* head, struct
                      wave_file_handle_node* node)
{
    struct wave_file_handle_node* tail = head;

    while( tail->next != NULL ) tail = tail->next;

    tail->next = node;
    node->prev = tail;

    return 0;
}

struct wave_file_handle_node*
find_wave_file_handle( struct wave_file_handle_node* head, FILE *fp )
{
    struct wave_file_handle_node* node = head;
    
    while ( node != NULL )
    {
      if ( node->fp == fp) return node;

      node = node->next;
    }
    
    return 0;
}


/***********************************************************************/

int32
alloc_and_read(FILE *fp, u_int32 size, void** object)
{
    if (size == 0)
      return MERR_INVALID;

    /* allocate space for the object */
    if ( (*object = masc_rtalloc( size )) == 0)
    {
      fclose(fp);
      return MERR_MEMORY;
    }
    
    /* read in the data */
    if ( fread(*object, 1, size, fp) != size )
    {
      fclose(fp);
      return MERR_IO;
    }
   
    return size;
}

int32
cmp_fourcc(FourCC id, char* test_string)
{
    char id_string[FOURCC_SIZE+1];
    int  i;

    /* Cast each byte of the "id" into a char in a string... */
    for (i=0; i<FOURCC_SIZE; i++)
    {
      id_string[i] = ((char *) &id)[i];
    }
    id_string[FOURCC_SIZE] = 0; /* ...and null-terminate it. */
    
    return strcmp(id_string, test_string); /* test for equivalence */
}


/* returns the number of bytes read */
int32
riff_read_chunk_header( FILE *fp, struct riff_chunk_header* header )
{
    int32 error = 0;

    error = fread(header, 1, SIZEOF_RIFF_CHUNK_HEADER, fp);
    if (error == SIZEOF_RIFF_CHUNK_HEADER)
      header->length = mas_letohl(header->length);

    return error;
}

int32
riff_begin_read( char* fname, FILE** fp_addr )
{
    FILE*                    fp;
    struct riff_chunk_header header;
    int32                    error;
    struct wave_info*        info;
    FourCC                   wave_id;
    struct wave_file_handle_node* node;

    if ( (fp = fopen(fname, "rb")) == NULL)
      return MERR_FILE_CANNOT_OPEN;

    *fp_addr = fp;

    /** Check RIFF header */
    if ( (error = riff_read_chunk_header( fp, &header )) == 0)
    {
      fclose(fp);
      return error;
    }
    
    if (cmp_fourcc(header.id, "RIFF") != 0)
    {
      fclose(fp);
      return MERR_FILE_TYPE;
    }
    
    if ( (info = masc_rtalloc( sizeof(struct wave_info) )) == 0)
    {
      fclose(fp);
      return MERR_MEMORY;
    }
    
    if ( (info->name = masc_rtalloc( strlen(fname)+1 ) ) == 0)
    {
      fclose(fp);
      return MERR_MEMORY;
    }
    
    /** Initialize wave_info data structure */
    strcpy(info->name, fname);
    info->length = header.length;
    info->fmt               = 0;
    info->cue_header        = 0;
    info->cue_points        = 0;
    info->playlist_header   = 0;
    info->play_segments     = 0;
    info->label_header      = 0;
    info->label_data        = 0;
    info->note_header       = 0;
    info->note_data         = 0;
    info->label_text_header = 0;
    info->label_text_data   = 0;
    info->file_header       = 0;
    info->file_data         = 0;
    info->pcm_fields        = 0;
    info->offset_data_start = 0;
    info->data_length       = 0;
    /***************************************/

    if ( (error = fread(&wave_id, 1, FOURCC_SIZE, fp)) == 0 )
    {
      fclose(fp);
      return MERR_IO;
    }
        
    if (cmp_fourcc(wave_id, WAV_WAVE_ID) != 0)
    {
      fclose(fp);
      return MERR_FILE_TYPE;
    }
    
    if ( ( error = parse_small_chunks(fp, info, header.length) ) < 0)
    {
      fclose(fp);
      return MERR_IO;
    }
    print_wav_file_info( info );

    /* seek to start of data */
    if (info->offset_data_start)
    {
      if (fseek(fp, (long)info->offset_data_start, SEEK_SET) < 0)
      {
          fclose(fp);
          return MERR_IO;
      }
    }

    node       = new_wave_file_handle();
    if (node == 0) return MERR_MEMORY;
    node->fp   = fp;
    node->info = info;
    
    if (wave_file_handle_list_head == 0)
    {
      wave_file_handle_list_head = node;
    }
    else
    {
      append_wave_file_handle(wave_file_handle_list_head, node);
    }
    
    return 0;
}

int32
print_wav_file_info( struct wave_info* info )
{
    int i;

    printf("RIFF chunk length: %d\n", info->length);
    
    if (info->fmt)
    {
      printf("[format]\n");
      printf("              format tag: %s\n", wav_format_string(info->fmt->format_tag));
      printf("                channels: %d\n", info->fmt->channels);
      printf("      samples per second: %d\n", info->fmt->samples_per_second);
      printf("average bytes per second: %d\n",
             info->fmt->average_bytes_per_second);
      printf("       block align bytes: %d\n",
             info->fmt->block_align_bytes);
      if(info->pcm_fields)
          printf("     PCM bits per sample: %d\n",
               info->pcm_fields->bits_per_sample);
    }
    
    if (info->offset_data_start)
    {
      printf("\n[data]\n");
      printf("data chunk begins at: %d bytes in\n", info->offset_data_start);
      printf("     data chunk size: %d bytes\n", info->data_length);
    }
    
    if (info->label_text_header)
    {
      printf("\n[label text]\n");
      printf("         name: %d\n", info->label_text_header->name);
      printf("sample length: %d\n", info->label_text_header->sample_length);
      printf("      purpose: %d\n", info->label_text_header->purpose);
      printf("      country: %d\n", info->label_text_header->country);
      printf("     language: %d\n", info->label_text_header->language);
      printf("      dialect: %d\n", info->label_text_header->dialect);
      printf("     codepage: %d\n", info->label_text_header->codepage);
      if (info->label_text_data) printf("data: %s\n", info->label_text_data);
      
    }

    if (info->label_header)
    {
      printf("\n[label]\n");
      printf("name: %d\n", info->label_header->name);
      if (info->label_data) printf("data: %s\n", info->label_data);
    }

    if (info->cue_header)
    {
      printf("\n[cue points]\n");
      printf(" number: %d\n", info->cue_header->num_cue_points);
      for (i=0; i<info->cue_header->num_cue_points; i++)
      {
          printf("  [cue %d]\n", i);
          printf("             name: %d\n", info->cue_points[i].name);
          printf("         position: %d\n", info->cue_points[i].position);
          printf("       chunk name: %c%c%c%c\n",
               ((char *) &(info->cue_points[i].chunk_name))[0],
               ((char *) &(info->cue_points[i].chunk_name))[1],
               ((char *) &(info->cue_points[i].chunk_name))[2], 
               ((char *) &(info->cue_points[i].chunk_name))[3]);
      
          printf("      chunk start: %d\n", info->cue_points[i].chunk_start);
          printf("      block start: %d\n", info->cue_points[i].block_start);
          printf("    sample offset: %d\n", info->cue_points[i].sample_offset);
      }
    }
    return 0;
}


/* This function is called recursively for list-style chunks */
int32
parse_small_chunks( FILE *fp, struct wave_info* info, u_int32 chunk_length )
{
    struct riff_chunk_header header;
    int32                    read_data=1;
    int                      recurse_read_data=0;
    FourCC                   label;
    u_int32                  skip;
    u_int32                  chunk_countdown = chunk_length;
    int                      i;

    /* loop until EOF or the end of chunk*/
    while (read_data && (chunk_countdown > 0))
    {
      if ( (read_data = riff_read_chunk_header( fp, &header )) <= 0)
          return read_data;

      printf("chunk: %c%c%c%c ", ((char *) &(header.id))[0],
             ((char *) &(header.id))[1], ((char *) &(header.id))[2],
             ((char *) &(header.id))[3] );  
      
      printf("%d\n", header.length);
      skip = header.length;

      /*** Parse individual chunks, if they're small enough **/
      /*** (this is everything except WAV_DATA_ID) for now   */

      /* if we can't parse a chunk, we pass over it. */

      /***** FORMAT CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_FORMAT_ID) == 0)
      {
          if ( ( read_data = alloc_and_read(fp,
                                    SIZEOF_FMT_COMMON_FIELDS,
                                    (void **)&info->fmt) ) <= 0 )
            return read_data;

          /* do byte-sex conversion */
          info->fmt->format_tag = mas_letohs(info->fmt->format_tag);
          info->fmt->channels   = mas_letohs(info->fmt->channels);
          info->fmt->samples_per_second =
            mas_letohl(info->fmt->samples_per_second);
          info->fmt->average_bytes_per_second =
            mas_letohl(info->fmt->average_bytes_per_second);
          info->fmt->block_align_bytes =
            mas_letohs(info->fmt->block_align_bytes);
          
          skip -= SIZEOF_FMT_COMMON_FIELDS;

          /* read in PCM-specific fields */
          if (info->fmt->format_tag == WAVE_FORMAT_PCM)
          {
            if ( ( read_data = 
                   alloc_and_read(fp, SIZEOF_FMT_PCM_FIELDS,
                              (void *)&info->pcm_fields) ) <= 0 )
                return read_data;

            info->pcm_fields->bits_per_sample =
                mas_letohs(info->pcm_fields->bits_per_sample);
            skip -= SIZEOF_FMT_PCM_FIELDS;
          }
      }

      /***** DATA CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_DATA_ID) == 0)
      {
          /* get current position, right after the data chunk header */
          info->offset_data_start = ftell(fp);
          info->data_length = header.length;
          /* don't modify skip */
      }
      
      /***** CUE CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_CUE_ID) == 0)
      {
          /** get number of cue points */
          if ( ( read_data = alloc_and_read(fp, SIZEOF_CUE_CHUNK_HEADER,
                                    (void *)&info->cue_header) ) <= 0 )
            return read_data;
          info->cue_header->num_cue_points =
            mas_letohl(info->cue_header->num_cue_points);
          skip -= read_data;

          if ((info->cue_header->num_cue_points*SIZEOF_CUE_POINT)
            <= (header.length - read_data) )
          {
            if ( ( read_data =
                   alloc_and_read(fp, info->cue_header->num_cue_points *
                              SIZEOF_CUE_POINT, 
                              (void *)&info->cue_points ) ) <= 0 )
                return read_data;
            skip -= read_data;
            for (i=0; i<info->cue_header->num_cue_points; i++)
            {
                info->cue_points[i].name =
                  mas_letohl(info->cue_points[i].name); 
                info->cue_points[i].position =
                  mas_letohl(info->cue_points[i].position); 
                info->cue_points[i].chunk_start =
                  mas_letohl(info->cue_points[i].chunk_start); 
                info->cue_points[i].block_start =
                  mas_letohl(info->cue_points[i].block_start); 
                info->cue_points[i].sample_offset =
                  mas_letohl(info->cue_points[i].sample_offset); 
            }
          }
      }
      
      /***** LABEL CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_LABEL_ID) == 0)
      {
          if ( ( read_data = alloc_and_read(fp,
                                    SIZEOF_LABEL_CHUNK_HEADER, 
                                    (void *)&info->label_header) ) <= 0)
            return read_data;
          if (read_data < header.length) /* is there a data section? */
          {
            if ( ( read_data =
                   alloc_and_read(fp, 
                              header.length - SIZEOF_LABEL_CHUNK_HEADER,
                              (void *)&info->label_data) ) <= 0 )
                return read_data;
          }
          skip -= header.length;
      }

      /***** NOTE CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_NOTE_ID) == 0)
      {
          if ( ( read_data = alloc_and_read(fp, 
                                    SIZEOF_NOTE_CHUNK_HEADER,
                                    (void *)&info->note_header) ) <= 0 )
            return read_data;
          if (read_data < header.length) /* is there a data section? */
          {
            if ( ( read_data =
                   alloc_and_read(fp, header.length - SIZEOF_NOTE_CHUNK_HEADER,
                              (void *)&info->note_data) ) <= 0 )
                return read_data;
          }
          skip -= header.length;
      }

      /***** LABEL TEXT CHUNK *****************************************/
      if ( cmp_fourcc(header.id, WAV_TEXT_ID) == 0)
      {
          if ( ( read_data = 
               alloc_and_read(fp, 
                          SIZEOF_LABEL_TEXT_CHUNK_HEADER,
                          (void *)&info->label_text_header) ) <= 0 )
            return read_data;
          if (read_data < header.length) /* is there a data section? */
          {
            if ( ( read_data = 
                   alloc_and_read(fp,
                              header.length - SIZEOF_LABEL_TEXT_CHUNK_HEADER,
                              (void *)&info->label_text_data) ) <= 0)
                return read_data;
          }
          skip -= header.length;
      }

      /***** LIST CHUNK ********************************************/
      if ( cmp_fourcc(header.id, WAV_LIST_ID) == 0)
      {
          if ( (read_data = fread(&label, 1, FOURCC_SIZE, fp)) != FOURCC_SIZE )
          {
            fclose(fp);
            return MERR_IO;
          }
          skip -= FOURCC_SIZE;
 
          printf("%c%c%c%c\n", ((char *) &(label))[0],
               ((char *) &(label))[1], ((char *) &(label))[2],
               ((char *) &(label))[3] );  
          
          /** if this is an associated data LIST chunk, we'll recurse
            into parse_small_chunks() to read this chunk's
            contents. */
          if ( cmp_fourcc(label, "adtl") == 0 )
          {
            recurse_read_data =
                parse_small_chunks(fp, info, header.length - FOURCC_SIZE );
            skip -= recurse_read_data; /* should have read the
                                    rest of this chunk */
          }
      }

      /** skip ahead to the start of the next chunk, discounting the
          number of bytes we've already read. */
      if (fseek(fp, skip, SEEK_CUR) < 0) return MERR_IO;

      /* The file position counter is now just past this chunk.  */
      chunk_countdown -= header.length;
    }

    return chunk_length - chunk_countdown; /* number of bytes read */
}

char*
wav_format_string( int format )
{
    switch (format)
    {
    case WAVE_FORMAT_UNKNOWN: return "Unknown";
    case WAVE_FORMAT_PCM: return "Microsoft PCM";
    case WAVE_FORMAT_ADPCM: return "Microsoft ADPCM";
    case WAVE_FORMAT_IEEE_FLOAT: return "IEEE Floating Point";
    case WAVE_FORMAT_ALAW: return "Microsoft A-law";
    case WAVE_FORMAT_MULAW: return "Microsoft mu-law";
    case WAVE_FORMAT_OKI_ADPCM: return "OKI ADPCM";
    case WAVE_FORMAT_IMA_ADPCM: return "IMA ADPCM";
    case WAVE_FORMAT_DIGISTD: return "Digistd";
    case WAVE_FORMAT_DIGIFIX: return "Digifix";
    case WAVE_FORMAT_DOLBY_AC2: return "Dolby AC-2";
    case WAVE_FORMAT_GSM610: return "GSM 06.10";
    case WAVE_FORMAT_ROCKWELL_ADPCM: return "Rockwell ADPCM";
    case WAVE_FORMAT_ROCKWELL_DIGITALK: return "Rockwell Digitalk";
    case WAVE_FORMAT_G721_ADPCM: return "G.721 ADPCM";
    case WAVE_FORMAT_G728_CELP: return "G.728 CELP";
    case WAVE_FORMAT_MPEG: return "MPEG";
    case WAVE_FORMAT_MPEGLAYER3: return "MPEG2 layer 3 (MP3)";
    case WAVE_FORMAT_G726_ADPCM: return "G.726 ADPCM";
    case WAVE_FORMAT_G722_ADPCM: return "G.722 ADPCM";
    default: break;
    }
    return "";
}


int32
wav_get_format(FILE *fp, u_int16* format)
{
    struct wave_file_handle_node* node;

    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *format = node->info->fmt->format_tag;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_get_channels(FILE *fp, u_int16* channels)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *channels = node->info->fmt->channels;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_get_samples_per_second(FILE *fp, u_int32* samples_per_second)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *samples_per_second = node->info->fmt->samples_per_second;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_get_average_bytes_per_second(FILE *fp, u_int32* average_bytes_per_second)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *average_bytes_per_second =
          node->info->fmt->average_bytes_per_second;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_get_block_align_bytes(FILE *fp, u_int16* block_align_bytes)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *block_align_bytes = node->info->fmt->block_align_bytes;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_pcm_get_bits_per_sample(FILE *fp, u_int16* bits_per_sample)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      if (node->info->pcm_fields)
      {
          *bits_per_sample =
            node->info->pcm_fields->bits_per_sample;
          return 0;
      }
      else return MERR_INVALID;
    }
    else return MERR_INVALID;
}

int32
wav_get_length(FILE *fp, u_int32* length)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *length = node->info->data_length;
      return 0;
    }
    else return MERR_INVALID;
}

int32
wav_get_wave_info(FILE *fp, struct wave_info** info)
{
    struct wave_file_handle_node* node;
    
    node = find_wave_file_handle(wave_file_handle_list_head, fp);
    if (node)
    {
      *info = node->info;
      return 0;
    }
    else return MERR_INVALID;
}


Generated by  Doxygen 1.6.0   Back to index