/*-------------- Telecommunications & Signal Processing Lab --------------- McGill University Routine: FiltAudio [options] -f FilterFile AFileIn AFileOut Purpose: Filter data from an audio file Description: This program takes an audio file and a filter coefficient file as input and produces a filtered output audio file. Subsampling and interpolation factors can be optionally specified. This program supports three types of filters, FIR, all-pole, and general IIR. Filters are specified in filter files. Filter Files: The first record of a filter file indicates the type of filter. !FIR - FIR filter, direct form !IIR - IIR filter, cascade of biquad sections !ALL - All-pole filter, direct form Subsequent records contain filter coefficients. Comment records ('!' in the first position of the record) can be interspersed amongst the data. Data records are free form, with data values separated by white space (blanks, tabs and newlines). Commas can also be used to separate data values, but only within records, i.e. a comma should not appear at the end of a record. FIR filters are specified by the direct-form coefficients h[i], N-1 -i H(z) = SUM h[i] z . i=0 IIR filters are implemented as the cascade of biquadratic filter sections, where each section has a z-transform, h(i,0)*z^2 + h(i,1)*z + h(i,2) H(i,z) = ------------------------------ . z^2 + h(i,3)*z + h(i,4) All-pole filters are specified by direct-form feeback coefficients, 1 N-1 -i H(z) = ---- , where C(z) = SUM h[i] z . C(z) i=0 For FIR filters, a sample rate change may be affected with interpolation and subsampling. Let Ir and Nsub be the interpolation and subsampling factors, respectively. Conceptually, the operations for FIR filters are as follows. 1: Ir-1 zeros are inserted between adjacent samples of the frequency shifted input to increase the sampling rate by a factor of Ir. 2: The increased rate signal is filtered. 3: The result of the filtering is subsampled by a factor of Nsub to form the output signal. The initial filter alignment and the number of output samples can be specified with options. The filter alignment specifies the position of the filter relative to the input date for calculating the first output sample. For FIR filters, this alignment is relative to the increased rate input sequence. Specifically, let the number of samples in the input file be Nin. The input can be considered to be an array x(0),...,x(Nin-1). The increased rate sequence is xi(.), with xi(k*Ir)=x(k). The first output sample is calculated with the beginning of the impulse response of the filter aligned with xi(idoffs). The array xi(.) can be considered to be of length Nin*Ir; the first non-zero sample is xi(0)=x(0), the last non-zero sample is xi((Nin-1)*Ir). Conceptually, the impulse impulse response is moved in steps of Nsub to create the output samples. The intent is that the output samples be a subset of the values the would be obtained if the infinite length sequence formed by padding out the input data on either end with zeros were to be filtered. To this end, the filter calculations need warm-up points, particularly for the case that the initial filter alignment is not at the beginning of the input data. For FIR filters, this is taken into account by reading previous input values into the filter memory. For IIR filters, previous outputs are also needed as warm-up points. If the initial alignment is near the beginning of the data, the IIR filter is run from the beginning of the data to generate the warm-up points. For larger alignment offsets, the IIR filter is backed up for a maximum of 1000 samples to provide the warm-up points. If the initial filter alignment is not explicitly specified it is chosen to be zero, except for odd-length symmetric or anti-symmetric FIR filters for which it is is chosen to be (Ncof-1)/2. If the number of output samples is not explicitly set, it is chosen to be Ir*Nin/Nsub. For the case of Ir=1 and Nsub=1, this results in the same number of output samples as input samples. If the initial filter alignment, offs, is explicitly specified, the number of output samples is chosen to be (Ir*Nin-offs)/Nsub. This value can be overridden by explicitly setting the number of output samples. Options: The command line specifies options and the input and output file names. -f FILTFILE, --filter_file=FILTFILE Filter file name. -i IR/NSUB, --interpolate=IR/NSUB Interpolation ratio, default 1. The interpolation and subsampling factors are specified as a fraction Ir/Nsub. Interpolation and subsampling can only be used with FIR filters. -a OFFS, --alignment=OFFS Alignment of data relative to the filter. The first output sample is calculated with the beginning of the filter response aligned with the specified sample of the interpolated data sequence. The interpolated data sequence is formed from the data in the input file, by inserting Ir-1 zeros between each input sample. Offset zero corresponds to the first sample from the input file; offset Ir corresponds to the second sample from the input file. For most filter types, the default alignment is 0. For symmetric or anti-symmetric FIR filters with an odd number of coefficients, the default is (Ncof-1)/2, corresponding to no delay between the input and output files. Note that with this value of offset, part of the start-up transient is inevitably lost. -n NOUT, --number_samples=NOUT Number of output samples to be calculated. -D DFORMAT, --data_format=DFORMAT Data format for the output file. "mu-law8" - 8-bit mu-law data "A-law8" - 8-bit A-law data "unsigned8" - offset-binary 8-bit integer data "integer8" - two's-complement 8-bit integer data "integer16" - two's-complement 16-bit integer data "float32" - 32-bit IEEE floating-point data "text" - text data The data formats available depend on the output file type. AFsp (Sun) audio files: mu-law, A-law, 8-bit integer, 16-bit integer, float RIFF WAVE files: mu-law, A-law, offset-binary 8-bit integer, 16-bit integer AIFF-C audio files: mu-law, A-law, 8-bit integer, 16-bit integer Headerless files: all data formats -F FTYPE, --file_type=FTYPE File type, default "AFsp". "AFsp", "Sun" or "sun" - AFsp (Sun) audio file "WAVE" or "wave" - RIFF WAVE file "AIFF-C" - AIFF-C audio file "raw" or "raw_native" - Headerless file (native byte order) "raw_swap" - Headerless file (byte swapped) "raw_big-endian" - Headerless file (big-endian byte order) "raw_little-endian" - Headerless file (little-endian byte order) -P PARMS, --parameters=PARMS Parameters to be used for headerless input files. See the description of the environment variable RAWAUDIOFILE below for the format of the parameter specification. -I INFO, --info=INFO Header information string. -h, --help Print a list of options and exit. -v, --version Print the version number and exit. For AFsp output files, the audio file header contains an information string. Standard Header Information: date:1994/01/25 19:19:39 UTC date user:kabal@aldebaran user program:FiltAudio program name This information can be changed with the header information string which is specified as one of the command line options. Structured information records should adhere to the above format with a named field terminated by a colon, followed by numeric data or text. Comments can follow as unstructured information. For the purpose of this program, records are terminated by newline characters. However in the header itself, the newline characters are replaced by nulls. To place a newline character into the header, escape the newline character by preceding it with a '\' character. If the first character of the user supplied header information string is a newline character, the header information string is appended to the standard header information. If not, the user supplied header information string replaces the standard header information. Environment variables: RAWAUDIOFILE: This environment variable defines the data format for headerless or non-standard input audio files. The string consists of a list of parameters separated by commas. The form of the list is "Format, Start, Sfreq, Swapb, Nchan, ScaleF" Format: File data format The lowercase versions of these format specifiers cause a headerless file to be accepted only after checking for standard file headers; the uppercase versions cause a headerless file to be accepted without checking the file header. "undefined" - Headerless files will be rejected "mu-law8" or "MU-LAW8" - 8-bit mu-law data "A-law8" or "A-LAW8" - 8-bit A-law data "unsigned8" or "UNSIGNED8" - offset-binary 8-bit integer data "integer8" or "INTEGER8" - two's-complement 8-bit integer data "integer16" or "INTEGER16" - two's-complement 16-bit integer data "float32" or "FLOAT32" - 32-bit floating-point data "text" or "TEXT" - text data Start: byte offset to the start of data (integer value) Sfreq: sampling frequency in Hz (floating point number) Swapb: Data byte swap parameter "native" - no byte swapping "little-endian" - file data is in little-endian byte order "big-endian" - file data is in big-endian byte order "swap" - swap the data bytes as the data is read Nchan: number of channels The data consists of interleaved samples from Nchan channels ScaleF: Scale factor Scale factor applied to the data from the file The default values for the audio file parameters correspond to the following string. "undefined, 0, 8000., native, 1, 1.0" AUDIOPATH: This environment variable specifies a list of directories to be searched when opening the input audio files. Directories in the list are separated by colons (semicolons for MS-DOS). Author / version: P. Kabal / v1r12 1996/08/13 Copyright (C) 1996 -------------------------------------------------------------------------*/ static char rcsid[] = "$Id: FiltAudio.c 1.49 1996/08/16 AFsp-V2R1 $"; #include /* LONG_MIN */ #include #include /* EXIT_SUCCESS */ #include #include #include #include "FiltAudio.h" #include "AO.h" #ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 /* Normally in stdlib.h */ #endif int main (argc, argv) int argc; const char *argv[]; { int DformatI, Fformat; float SfreqI; double SfreqO; const char *NHparms; const char *Hinfo; const char *Fname[3]; char Fn[FILENAME_MAX+1]; long int Nsamp, Nchan; AFILE *AFpI; AFILE *AFpO; long int idoffs, Nout; int FiltType; int Nsub, Ir; int Dalign; int Ncof, Nsec; float h[MAXCOF]; /* Get the input parameters */ FAoptions (argc, argv, &Fformat, &Nout, &idoffs, &Nsub, &Ir, &Hinfo, &NHparms, Fname); /* Open the input audio file */ if (NHparms != NULL) AFsetNH (NHparms); else AFsetNH ("$RAWAUDIOFILE"); FLpathList (Fname[0], "$AUDIOPATH", Fn); AFpI = AFopenRead (Fn, &Nsamp, &Nchan, &SfreqI, stdout); if (Nchan != 1) UThalt ("%s: Multiple input channels not supported", PROGRAM); DformatI = AFpI->Format; /* Read the coefficient file */ FiltType = FIreadFilt (Fname[2], MAXCOF, h, &Ncof, stdout); switch (FiltType) { case FI_IIR: Nsec = Ncof / 5; break; case FI_FIR: case FI_ALL: break; default: UThalt ("%s: Invalid filter type", PROGRAM); break; } if (Ncof <= 0) UThalt ("%s: No coeffients specified", PROGRAM); if (FiltType != FI_FIR && (Nsub != 1 || Ir != 1)) UThalt ("%s: Sample rate change only supported for FIR filters", PROGRAM); /* Open the output audio file */ SfreqO = (Ir * (double) SfreqI) / Nsub; if (Hinfo != NULL) AFsetHinfo (Hinfo); printf ("\n"); FLbackup (Fname[1]); Fformat = AOsetDFormat (Fformat, &DformatI, 1); AFpO = AFopenWrite (Fname[1], Fformat, 1L, SfreqO, stdout); /* Default alignment */ Dalign = (idoffs == LONG_MIN); if (Dalign) { if (FiltType == FI_FIR && Ncof % 2 != 0 && VRfCheckSym (h, Ncof) != 0) idoffs = (Ncof - 1)/2; else idoffs = 0; } if (Nout == 0) { if (Dalign) Nout = (Ir * Nsamp) / Nsub; else Nout = (Ir * Nsamp - idoffs) / Nsub; } /* Filtering */ if (FiltType == FI_FIR) { if (Nsub == 1 && Ir == 1) FAfiltFIR (AFpI, AFpO, Nout, h, Ncof, idoffs); else FAfiltSI (AFpI, AFpO, Nout, h, Ncof, Nsub, Ir, idoffs); } else if (FiltType == FI_IIR) FAfiltIIR (AFpI, AFpO, Nout, (const float (*)[5]) h, Nsec, idoffs); else if (FiltType == FI_ALL) FAfiltAP (AFpI, AFpO, Nout, h, Ncof, idoffs); else UThalt ("%s: Unsupported filter type", PROGRAM); /* Close the audio files */ AFclose (AFpI); AFclose (AFpO); return EXIT_SUCCESS; }