/*-------------- Telecommunications & Signal Processing Lab --------------- Routine: AFILE *AFgetAIpar (FILE *fp, const char Fname[], long int *Nsamp, long int *Nchan, float *Sfreq, FILE *fpout) Purpose: Get file format information from an AIFF or AIFF-C audio file Description: This routine reads the header for an AIFF or AIFF-C audio 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. AIFF or AIFF-C audio file: Offset Length Type Contents 0 4 char File identifier ("FORM") 4 4 int Form chunk length 8 4 char File identifier ("AIFF" or "AIFC") ... ... ... ... f 4 char COMM chunk identifier ("COMM") f+4 4 int COMM chunk length f+8 2 int Number of interleaved channels f+10 4 int Number of sample frames f+14 2 int Bits per sample f+16 10 float Sample frames per second f+26 4 int Compression type f+30 ... char Compression name ... ... ... ... d 4 char SSND chunk identifier ("SSND") d+4 4 int SSND chunk length d+8 4 int Data offset d+12 4 int Block size d+16 ... ... Audio data 8-bit mu-law, 8-bit A-law, 8-bit integer, and 16-bit integer data formats are supported. Parameters: <- AFILE *AFgetAIpar 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.11 $ $Date: 1996/08/15 23:02:15 $ -------------------------------------------------------------------------*/ static char rcsid [] = "$Id: AFgetAIpar.c 1.11 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) #define ICEILV(n, m) (((n) + ((m) - 1)) / (m)) /* int n,m >= 0 */ #define RUP(value) (2 * ICEILV (value, 2)) /* Round up */ static int AF_AIreadCOMM p_((FILE *fp, long int offs, int Ftype, struct AI_CkCOMM *CkCOMM)); AFILE * AFgetAIpar (fp, Fname, Nsamp, Nchan, Sfreq, fpout) FILE *fp; const char Fname[]; long int *Nsamp; long int *Nchan; float *Sfreq; FILE *fpout; { AFILE *AFp; long int offs; int Lw, Ftype, Format; long int Nbytes, Nsampx, Nchanx, Lhead, Sdata, Ldata; float ScaleF, Sfreqx; int len, nCOMM, nSSND; char *FtypeN; struct AI_CkPreamb CkHead; struct AI_CkCOMM CkCOMM; struct AI_CkFVER CkFVER; struct AI_CkSSND CkSSND; /* Get the size of the file */ Nbytes = FLfileSize (fp); if (Nbytes < AI_LHMIN) UThalt ("AFgetWVpar: WAVE file header too short"); /* Each chunk has an identifier and a length. The length gives the number of bytes in the chunk (not including the ckID or ckDataSize fields). If the ckDataSize field is odd, a zero byte fills the space before the start of the next chunk. Some AIFF-C files do not have this zero byte at the end of the file, i.e. the total file length is odd, yet the FORM chunk size indicates that the padding byte is there. */ /* Check the FORM chunk */ offs = 0; offs += RHEAD_S (fp, offs, CkHead.ckID); if (! SAME_CSTR (CkHead.ckID, FM_IFF)) UThalt ("AFgetWVpar: Invalid AIFF/AIFF-C file identifier"); offs += RHEAD_V (fp, offs, CkHead.ckDataSize, DS_EB); if (CkHead.ckDataSize < AI_LHMIN - offs || CkHead.ckDataSize > RUP (Nbytes - offs)) UTwarn ("AFgetAIpar - Invalid AIFF/AIFF-C file FORM chunk size"); offs += RHEAD_S (fp, offs, CkHead.ckID); if (SAME_CSTR (CkHead.ckID, FM_AIFF_C)) { Ftype = FT_AIFF_C; FtypeN = "AIFF-C"; } else if (SAME_CSTR (CkHead.ckID, FM_AIFF)) { Ftype = FT_AIFF; FtypeN = "AIFF"; } else UThalt ("AFgetAIpar: Invalid AIFF/AIFF-C file identifier"); /* Pick up the required chunks */ nCOMM = 0; nSSND = 0; while (offs < Nbytes && (nCOMM == 0 || nSSND == 0)) { /* Read the chunk preamble */ offs += RHEAD_S (fp, offs, CkHead.ckID); offs += RHEAD_V (fp, offs, CkHead.ckDataSize, DS_EB); if (CkHead.ckDataSize > Nbytes - offs) UTwarn ("AFgetAIpar - Invalid %s header chunk size", FtypeN); /* COMM chunk */ /* Some AIFF-C COMM chunks are declared to be of length 18 (the size for AIFF files) when they are in fact larger. Here we find the actual length of the COMM chunk and use the "actual" length. */ if (SAME_CSTR (CkHead.ckID, CKID_COMM)) { nCOMM++; len = AF_AIreadCOMM (fp, offs, Ftype, &CkCOMM); if (len != CkHead.ckDataSize) { UTwarn ("AFgetAIpar - Fixup for invalid %s COMM chunk size", FtypeN); CkHead.ckDataSize = len; } } /* FVER chunk */ else if (Ftype == FT_AIFF_C && SAME_CSTR (CkHead.ckID, CKID_FVER)) { RHEAD_V (fp, offs, CkFVER.timestamp, DS_EB); if (CkFVER.timestamp != AIFCVersion1) UTwarn ("AFgetAIpar - Unrecognized AIFF-C version number"); } /* SSND chunk */ else if (SAME_CSTR (CkHead.ckID, CKID_SSND)) { nSSND++; RHEAD_V (fp, offs, CkSSND.offset, DS_EB); Lhead = offs + 8 + CkSSND.offset; Sdata = CkHead.ckDataSize - 8 - CkSSND.offset; } /* Update the offset */ offs += RUP (CkHead.ckDataSize); } if (nCOMM != 1 || nSSND != 1) UThalt ("AFgetAIpar: Missing %s header information", FtypeN); /* Uncompressed */ if (SAME_CSTR (CkCOMM.compressionType, CT_NONE)) { if (CkCOMM.sampleSize == 16) { Lw = FDL_INT16; Format = FD_INT16; ScaleF = 1.; } else if (CkCOMM.sampleSize == 8) { Lw = FDL_INT8; Format = FD_INT8; ScaleF = 128.; } else UThalt ("AFgetAIpar: Unsupported %s sample size", FtypeN); } /* AIFF-C, compressed */ /* A-law and mu-law are compressed formats; for these formats CkCOMM.sampleSize = 16 (not checked) */ else if (SAME_CSTR (CkCOMM.compressionType, CT_ULAW)) { Lw = FDL_MULAW8; Format = FD_MULAW8; ScaleF = 1.; } else if (SAME_CSTR (CkCOMM.compressionType, CT_ALAW)) { Lw = FDL_ALAW8; Format = FD_ALAW8; ScaleF = 1.; } else UThalt ("AFgetAIpar: Unsupported AIFF-C compression type"); /* Set up the file data parameters */ Nchanx = CkCOMM.numChannels; Nsampx = CkCOMM.numSampleFrames * Nchanx; Sfreqx = UTdIEEE80 (CkCOMM.sampleRate); Ldata = Lw * Nsampx; /* Check the data length */ if (Sdata < Ldata) UThalt ("AFgetAIpar: Invalid data length"); /* Set the parameters for file access */ AFp = AFsetAFp (fp, FO_RO, Ftype, Format, DS_EB, ScaleF, Nchanx, Lhead, 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 int AF_AIreadCOMM (fp, offs, Ftype, CkCOMM) FILE *fp; long int offs; int Ftype; struct AI_CkCOMM *CkCOMM; { long int pos; char slen[1]; pos = offs; pos += RHEAD_V (fp, pos, CkCOMM->numChannels, DS_EB); pos += RHEAD_V (fp, pos, CkCOMM->numSampleFrames, DS_EB); pos += RHEAD_V (fp, pos, CkCOMM->sampleSize, DS_EB); pos += RHEAD_S (fp, pos, CkCOMM->sampleRate); /* Set the compressionType field even for both AIFF and AIFF-C files */ if (Ftype == FT_AIFF_C) { pos += RHEAD_S (fp, pos, CkCOMM->compressionType); pos += RHEAD_S (fp, pos, slen); /* 1 byte string length */ pos += (int) slen[0]; } else memcpy (CkCOMM->compressionType, CT_NONE, sizeof CT_NONE); return (int) (pos - offs); }