Mercurial > hg > aim92
view tools/acf.c @ 0:5242703e91d3 tip
Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author | tomwalters |
---|---|
date | Fri, 20 May 2011 15:19:45 +0100 |
parents | |
children |
line wrap: on
line source
/* Autocorrelation function using FFT routines adapted from routines given in Numerical Recipes. The routine acf() uses an fft to compute the array of lagged products which is the autocovariance function. An optional normalization (dividing each coefficient by the zeroth coefficient) produces the autocorrelation function, (and this is then scaled up to the range [0-1000]). The output framewidth is the given maximum acf lag, which must not be greater than the input framewidth. Padding with zeroes is recommended to counter end-effects (See NR, p416). It is also necessary since: 1) framesize + padding must be a power of two (sample points at given rate). 2) max possible acf lag = ( framesize + padding ) / 2 Each input frame is padded with zeroes so that it is at least twice the required max acf lag, and is also a power of 2. This is because the fft method is "radix-2", and because the output acf is symmetrical so that just half the points are unique. Therefore each input frame is padded with zeroes to the next power of 2 larger than either the input framewidth, or twice the required max acf lag, (whichever is the larger). If necessary, extra padding can be enforced using the padding option to add extra zeroes, padding to a larger power of 2. The amount of extra padding is "exponential", expanding the basic size to: ( framesize + padding ) * 2**n where the padding option is n. (n=0 by default, so that no extra padding is added. When n=1 then padding is added to double the size, and when n=2 the size is quadrupled, etc.). Frames are selected from the input stream using the "frames" option, which has syntax: [[-]frame=a[-b]]. Input frames are numbered 1,2,... For example: frame=a Select just the a'th frame. frame=a-b Select frames from the a'th to b'th inclusive. The strings "min" and "max" can be used as specifiers, meaning eg: frame=min Select the first frame. frame=max Select the last frame. frame=a-max Select frames from the a'th to the last inclusive. frame=min-b Select frames from the first to the b'th inclusive. The default selects all frames, (ie frame=min-max). Other options (set using <option>=on): size Print the input and output frame sizes in sample points. echo Echo buffered input without processing. negative Write negative half of acf (ie. with zeroth lag on right). Examples: 1. To print the input and output frame sizes in sample points, eg for a subsequent plotting program, use the size option: acf ... size=on 2. An acf of a waveform sampled at 10kHz, computed to a max lag of 12.8ms within a frame of 12.8ms, plotting the 2nd frame in a sequence of frames with frameshift 12.8ms. acf samp=10kHz width=12.8ms frstep=12.8ms frame=2 lag=12.8ms file | x11plot 3. An animated plot of successive acf's of a waveform sampled at 10kHz, each computed within a frame of 12.8ms, and shifted by 2 sample points. acf samp=10kHz width=12.8ms frstep=2p lag=12.8ms file | x11play -n128 */ #include <stdio.h> #include <math.h> #include "options.h" #include "units.h" #include "strmatch.h" #include "sigproc.h" char applic[] = "autocorrelation function of contiguous frames.\n i/p and o/p data in binary shorts." ; static char *helpstr, *debugstr, *sampstr, *startstr, *shiftstr, *widthstr, *sizestr ; static char *padstr, *wstr, *lagstr, *scalestr, *normstr, *framestr, *echostr, *negstr ; static Options option[] = { { "help" , "off" , &helpstr , "help" , DEBUG }, { "debug" , "off" , &debugstr , "debugging switch" , DEBUG }, { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL }, { "start" , "0" , &startstr , "Start point in i/p file." , VAL }, { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL }, { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL }, { "width" , "32ms" , &widthstr , "Width of input frames." , VAL }, { "lag" , "32ms" , &lagstr , "Maximum acf lag (output frame width)." , VAL }, { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL }, { "window" , "on" , &wstr , "Hamming window" , SETFLAG }, { "normalize" , "off" , &normstr , "normalized acf" , SETFLAG }, { "scale" , "0.025" , &scalestr , "scale output" , SVAL }, { "size" , "off" , &sizestr , "print input/output frame size in samples" , SETFLAG }, { "echo" , "off" , &echostr , "echo buffered input without processing", SVAL }, { "negative" , "off" , &negstr , "write negative half of acf (zeroth lag on right)", SVAL }, ( char * ) 0 } ; int samplerate ; int width ; int step ; int zeroes ; int maxlag ; int dowindow ; double scale ; int normalize ; int echo ; int negative ; float *vec, *window ; short *obuf ; main (argc, argv) int argc; char **argv; { FILE *fp ; short *buf ; int a, b ; fp = openopts( option,argc,argv ) ; if ( !isoff( helpstr ) ) helpopts( helpstr, argv[0], applic, option ) ; samplerate = to_Hz( sampstr ) ; dowindow = ison( wstr ) ; normalize = ison( normstr ) ; echo = ison( echostr ) ; negative = ison( negstr ) ; width = to_p( widthstr, samplerate ) ; step = to_p( shiftstr, samplerate ) ; maxlag = to_p( lagstr , samplerate ) ; if ( ( maxlag << 1 ) < width ) zeroes = ( getpower( width ) << atoi( padstr ) ) - width ; else if ( maxlag <= width ) zeroes = ( getpower( maxlag << 1 ) << atoi( padstr ) ) - width ; else { fprintf(stderr,"acf: required max lag [%dms] greater than framewidth [%dms]\n", maxlag*1000/samplerate, width*1000/samplerate); exit( 1 ) ; } scale = ( 1./(double)(width+zeroes) ) * atof( scalestr ) ; /* frame size printout */ if ( ison( sizestr ) ) { fprintf(stderr,"acf sizes in sample points:\n" ) ; fprintf(stderr," input frame size = %d (framewidth=%d + padding=%d)\n", width+zeroes, width, zeroes ) ; fprintf(stderr," output frame size = %d \n", maxlag ) ; exit( 0 ) ; } /* parse bounds on number of frames */ if ( selector( framestr, &a, &b ) == 0 ) { fprintf(stderr,"acf: bad frame selector [%s]\n", framestr ) ; exit( 1 ) ; } /* Allocate working space */ obuf = (short *)malloc( maxlag * sizeof(short) ) ; vec = (float *)malloc( width+zeroes * sizeof(float) ) ; if ( dowindow ) window = hamming( width ) ; /* Compute acf for each frame of width shorts in the input stream */ if ( seekbytes( fp, (int)( to_p( startstr, samplerate ) * sizeof(short) ) ) == 0 ) { fprintf(stderr,"improper seek\n") ; exit( 1 ) ; } while ( ( buf = getframe( fp, a, width, step ) ) != (short *)0 && ( a<=b || b==0 ) ) { if ( echo ) fwrite( buf, sizeof(short), width, stdout ) ; else process( buf ) ; a++ ; } if ( a<=b && b>0 ) fprintf(stderr,"warning: not enough frames for request\n"); fclose( fp ) ; } process( buf ) short *buf ; { short *bptr = buf ; short *endptr = buf + width ; short *optr = obuf ; short *endlag = obuf + maxlag ; float *wptr = window ; float *vptr = vec ; float *endvec = vec + width + zeroes ; if ( dowindow ) while ( bptr < endptr ) *vptr++ = *bptr++ * *wptr++ ; else while ( bptr < endptr ) *vptr++ = *bptr++ ; while ( vptr < endvec ) /* padding */ *vptr++ = 0 ; acf( vec, width+zeroes ) ; if ( normalize ) scale = 1000. / *vec ; vptr = vec ; while ( optr < endlag ) *optr++ = (short)( scale * *vptr++ ) ; if ( !negative ) fwrite( obuf, sizeof(short), maxlag, stdout ) ; else { for ( optr = endlag-1 ; optr >= obuf ; optr-- ) /* write in reverse */ fwrite( optr, sizeof(short), 1, stdout ) ; } }