/*-------------- Telecommunications & Signal Processing Lab --------------- Routine: AFILE *AFgetWVpar (FILE *fp, const char Fname[], long int *Nsamp, long int *Nchan, float *Sfreq, FILE *fpout) Purpose: Get file format information from a RIFF WAVE file Description: This routine reads the header for a RIFF WAVE file. The header information is used to set the file data format information in the audio file pointer structure and to set the returned argument values. A banner identifying the audio file and its parameters is printed. RIFF WAVE file: Offset Length Type Contents 0 4 char File identifier ("RIFF") 4 4 int RIFF chunk length 8 4 char File identifier ("WAVE") ... ... ... ... f 4 char Format chunk identifier ("fmt ") f+4 4 int Format chunk length f+8 2 int Audio data type f+10 2 int Number of interleaved channels f+12 4 int Sample rate f+16 4 int Average bytes/sec f+20 2 int Block align f+22 2 int Data word length (bits) ... ... ... ... d 4 char Data chunk identifier ("data") d+4 4 int Data chunk length d+8 ... ... Audio data 8-bit mu-law, 8-bit A-law, offset-binary 8-bit integer, and 16-bit integer data formats are supported. Parameters: <- AFILE *AFgetWVpar Audio file pointer for the audio file -> FILE *fp File pointer for the file -> const char Fname[] File name <- long int *Nsamp Total number of samples in the file (all channels) <- long int *Nchan Number of channels <- float *Sfreq Sampling frequency from the file header -> FILE *fpout File pointer for printing the audio file identification information. If fpout is NULL, no information is printed. Author / revision: P. Kabal Copyright (C) 1996 $Revision: 1.26 $ $Date: 1996/08/15 23:26:15 $ -------------------------------------------------------------------------*/ static char rcsid [] = "$Id: AFgetWVpar.c 1.26 1996/08/15 AFsp-V2R1 $"; #include #include #include #include #include #include #define RHEAD_S(fp,offs,string) \ AFreadHead (fp, (long int) (offs), (void *) (string), \ 1, sizeof (string), DS_NATIVE) #define RHEAD_V(fp,offs,value,swap) \ AFreadHead (fp, (long int) (offs), (void *) &(value), \ sizeof (value), 1, swap) #define SAME_CSTR(str,ref) (memcmp (str, ref, sizeof (str)) == 0) static uint4_t AF_WVfindChunk p_((FILE *fp, char Id[], long int *offs)); static int AF_WVreadfmt p_((FILE *fp, long int offs, uint4_t cksize, struct WV_PCMformat *CkPCM)); AFILE * AFgetWVpar (fp, Fname, Nsamp, Nchan, Sfreq, fpout) FILE *fp; const char Fname[]; long int *Nsamp; long int *Nchan; float *Sfreq; FILE *fpout; { AFILE *AFp; uint4_t cksize; long int offs, Nbytes, Nsampx, Ldata, Nchanx; int Lw, Format; float ScaleF, Sfreqx; struct WV_CkPreamb CkHead; struct WV_PCMformat CkPCM; /* Get the size of the file */ Nbytes = FLfileSize (fp); if (Nbytes < WV_LHMIN) UThalt ("AFgetWVpar: WAVE file header too short"); /* Check the file magic */ offs = 0; offs += RHEAD_S (fp, offs, CkHead.ckid); if (! SAME_CSTR (CkHead.ckid, FM_RIFF)) UThalt ("AFgetWVpar: Invalid RIFF file identifier"); offs += RHEAD_V (fp, offs, CkHead.cksize, DS_EL); if (CkHead.cksize < WV_LHMIN - offs || CkHead.cksize > Nbytes - offs) UTwarn ("AFgetWVpar - Invalid RIFF chunk size"); offs += RHEAD_S (fp, offs, CkHead.ckid); if (! SAME_CSTR (CkHead.ckid, FM_WAVE)) UThalt ("AFgetWVpar: Invalid RIFF WAVE file identifier"); /* Read the fmt chunk */ cksize = AF_WVfindChunk (fp, "fmt ", &offs); if (cksize < WV_FMT_CKSIZE || cksize > Nbytes - offs) UThalt ("AFgetWVpar: Invalid PCM fmt chunk size"); offs += AF_WVreadfmt (fp, offs, cksize, &CkPCM); /* Get the data format */ if (CkPCM.FormatTag == WAVE_FORMAT_PCM) { if (CkPCM.BitsPerSample == 16) { Lw = FDL_INT16; Format = FD_INT16; ScaleF = 1.; } else if (CkPCM.BitsPerSample == 8) { Lw = FDL_UINT8; Format = FD_UINT8; ScaleF = 128.; } else UThalt ("AFgetWVpar: Unsupported RIFF WAVE PCM data size (%d bits)", (int) CkPCM.BitsPerSample); } else if (CkPCM.FormatTag == WAVE_FORMAT_MULAW) { if (CkPCM.BitsPerSample != 8) UThalt ("AFgetWVpar: Invalid mu-law data size (%d bits)", (int) CkPCM.BitsPerSample); Lw = FDL_MULAW8; Format = FD_MULAW8; ScaleF = 1.; } else if (CkPCM.FormatTag == WAVE_FORMAT_ALAW) { if (CkPCM.BitsPerSample != 8) UThalt ("AFgetWVpar: Invalid A-law data size (%d bits)", (int) CkPCM.BitsPerSample); Lw = FDL_ALAW8; Format = FD_ALAW8; ScaleF = 1.; } else UThalt ("AFgetWVpar: Unsupported RIFF WAVE data format"); Nchanx = CkPCM.Channels; Sfreqx = CkPCM.SamplesPerSec; /* Find the data chunk */ Ldata = AF_WVfindChunk (fp, "data", &offs); if (Ldata > Nbytes - offs) { UTwarn ("AFgetWVpar - Fixup for invalid RIFF WAVE data chunk size"); Ldata = Lw * ((Nbytes - offs) / Lw); } /* Set the parameters for file access */ Nsampx = Ldata / Lw; AFp = AFsetAFp (fp, FO_RO, FT_WAVE, Format, DS_EL, ScaleF, Nchanx, offs, Ldata, Nsampx); /* Check and print the header information */ AFprintAFh (AFp, Fname, "", Sfreqx, fpout); /* Set the return parameters */ *Nsamp = Nsampx; *Nchan = Nchanx; *Sfreq = Sfreqx; return AFp; } static uint4_t AF_WVfindChunk (fp, Id, offs) FILE *fp; char Id[]; long int *offs; { struct WV_CkPreamb CkHead; long int offsx; offsx = *offs; while (1) { offsx += RHEAD_S (fp, offsx, CkHead.ckid); offsx += RHEAD_V (fp, offsx, CkHead.cksize, DS_EL); if (SAME_CSTR (CkHead.ckid, Id)) { *offs = offsx; return CkHead.cksize; } offsx += CkHead.cksize; /* Point to next chunk */ } } static int AF_WVreadfmt (fp, offs, cksize, CkPCM) FILE *fp; long int offs; uint4_t cksize; struct WV_PCMformat *CkPCM; { long int pos; pos = offs; pos += RHEAD_V (fp, pos, CkPCM->FormatTag, DS_EL); pos += RHEAD_V (fp, pos, CkPCM->Channels, DS_EL); pos += RHEAD_V (fp, pos, CkPCM->SamplesPerSec, DS_EL); /* skip two values */ pos += sizeof (CkPCM->AvgBytesPerSec); pos += sizeof (CkPCM->BlockAlign); pos += RHEAD_V (fp, pos, CkPCM->BitsPerSample, DS_EL); /* Skip over any extra data at the end of the fmt chunk */ if (pos > offs + cksize) UTwarn ("AFgetWVpar - Invalid RIFF WAVE fmt chunk size"); else pos = offs + cksize; return (int) (pos - offs); }