Mercurial > hg > aim92
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/acf.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,229 @@ +/* + 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 ) ; + } +} + +