#include #include #include #include #include #include #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); }