Mercurial > hg > aim92
diff tools/racf.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/racf.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,321 @@ +/* + Autocorrelation function using recursive "leaky integrator" filter + ie. for the m'th lag, the n'th recursive update is: + + y[n] = a.y[n-1] + (1-a).f[n].f[n-m] + + where decay constant a = exp(-Ts/T) + and Ts is sample interval in seconds + T is decay time constant in seconds + + The recursion computes the mean value of the product f[n].f[n-m] + within an exponential window which weights past values. + The window width parameter (eg. corresponding with the width of the + fft analysis window) is the time constant parameter. + The half life of the exponential window is given by -T.ln(0.5) = 0.693T + ie. for a given time constant, T secs, the window decays to half of its + current value in a period of 0.693T secs. + So T can be set to accomodate an expected period. + As a rough guide, in a period of 0.5T the window decays to about 0.6 + (ie by 60%), so set T to twice the expected period of the waveform to + get 60% of the window over this period. + Eg. if the waveform has an expected period of t ms, then to set 60% of + the window to this period, set 0.5T = t ms, ie. T = 2t ms. + + The routine generates 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. + + 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). + + + 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, + plotting the 2nd frame in a sequence of frames with frameshift 12.8ms. + +acf samp=10kHz frstep=12.8ms frame=2 lag=12.8ms file | x11plot + +3. An animated plot of successive acf's of a waveform sampled at 10kHz, + shifted by 2 sample points. + +acf samp=10kHz 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, *sizestr ; +static char *lagstr, *tcstr, *scalestr, *normstr, *framestr, *echostr ; +static char *negstr, *upstr, *slopestr ; + +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" , "1ms" , &shiftstr , "Step between output frames." , VAL }, + { "lag" , "32ms" , &lagstr , "Maximum acf lag (output frame width)." , VAL }, + { "update" , "1p" , &upstr , "Step between recursive updates." , VAL }, + { "timeconst" , "3ms" , &tcstr , "Decay time constant" , VAL }, + { "normalize" , "off" , &normstr , "Normalized acf" , SETFLAG }, + { "scale" , "0.003" , &scalestr , "scale output" , SVAL }, + { "sigmoid" , "off" , &slopestr , "Sigmoidal weighting slope parameter" , 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 framestep ; +int updatestep ; +int maxlag ; +double scale ; +int normalize ; +int echo ; +int negative ; +double alpha ; /* decay constant */ + +float *vec ; +short *obuf ; + +main (argc, argv) +int argc; +char **argv; +{ + FILE *fp ; + int a, b ; + + fp = openopts( option,argc,argv ) ; + if ( !isoff( helpstr ) ) + helpopts( helpstr, argv[0], applic, option ) ; + + samplerate = to_Hz( sampstr ) ; + normalize = ison( normstr ) ; + echo = ison( echostr ) ; + negative = ison( negstr ) ; + + framestep = to_p( shiftstr, samplerate ) ; + updatestep = to_p( upstr , samplerate ) ; + maxlag = to_p( lagstr , samplerate ) ; + + scale = atof( scalestr ) ; + + alpha = exp( -(double)( 1. / ( samplerate * to_s( tcstr, samplerate ) ) ) ) ; + + if ( framestep % updatestep != 0 ) { + fprintf(stderr,"frstep [%dp] must be an integer multiple of update [%dp] \n", framestep, updatestep ) ; + exit( 1 ) ; + } + + /* frame size printout */ + + if ( ison( sizestr ) ) { + fprintf(stderr,"acf sizes in sample points:\n" ) ; + 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( maxlag * sizeof(float) ) ; + + /* 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 ) ; + } + + if ( isoff( slopestr ) ) + process( fp, a, b ) ; /* conventional acf recursion */ + else + process2( fp, a, b, atof( slopestr) ) ; /* sigmoidally weighted recursion */ + /* (recommend set sigmoid=.02) */ + + fclose( fp ) ; +} + + +process( fp, a, b ) +FILE *fp ; +int a, b ; +{ + register short *buf, *bptr, *endptr ; + register float *vptr ; + register double beta ; + register int update_count = 0 ; + register int frame_count = 0 ; + + /* Initialize process with the first maxlag samples */ + + if ( ( buf = getframe( fp, 1, maxlag, updatestep ) ) != (short *)0 ) { + for ( endptr = buf ; endptr < buf + maxlag ; endptr++ ) { + bptr = endptr ; + vptr = vec ; + beta = ( 1-alpha ) * *bptr ; + while ( bptr >= buf ) + *vptr++ = alpha * *vptr + beta * *bptr-- ; + } + + update_count = 1 ; + frame_count = 1 ; + + if ( frame_count >= a ) { + if ( echo ) + fwrite( buf, sizeof(short), maxlag, stdout ) ; + else + writeframe() ; + } + } + + /* Continue process with successive buffers of maxlag samples */ + + while ( ( buf = getframe( fp, ++update_count, maxlag, updatestep ) ) != (short *)0 && ( frame_count<=b || b==0 ) ) { + bptr = buf + maxlag - 1 ; + vptr = vec ; + beta = ( 1-alpha ) * *bptr ; + + while ( bptr >= buf ) /* recursive update */ + *vptr++ = alpha * *vptr + beta * *bptr-- ; + + if ( update_count % framestep == 1 ) { + if ( ++frame_count >= a && ( frame_count <= b || b == 0 ) ) { + if ( echo ) + fwrite( buf, sizeof(short), maxlag, stdout ) ; + else + writeframe() ; + } + } + } + if ( frame_count <= b && b > 0 ) + fprintf(stderr,"warning: not enough frames for request\n"); +} + + +writeframe() +{ + register float *vptr = vec ; + register short *optr = obuf ; + register short *endlag = obuf + maxlag ; + + if ( normalize ) + scale = 1000. / *vec ; + + while ( optr < endlag ) + *optr++ = (short)( scale * *vptr++ ) ; + + if ( !negative ) + fwrite( obuf, sizeof(short), maxlag, stdout ) ; + else { + for ( optr = endlag-1 ; optr >= obuf ; optr-- ) + fwrite( optr, sizeof(short), 1, stdout ) ; + } +} + + +/*************************************************************************/ +/* + same as `process', but with added sigmoidal weighting +*/ + +process2( fp, a, b, slope ) +FILE *fp ; +int a, b ; +float slope ; +{ + register short *buf, *bptr, *endptr ; + register float *vptr ; + register double beta ; + register int update_count = 0 ; + register int frame_count = 0 ; + double sigmoid() ; + + /* Initialize process with the first maxlag samples */ + + if ( ( buf = getframe( fp, 1, maxlag, updatestep ) ) != (short *)0 ) { + for ( endptr = buf ; endptr < buf + maxlag ; endptr++ ) { + bptr = endptr ; + vptr = vec ; + beta = ( 1-alpha ) * *bptr ; + while ( bptr >= buf ) + *vptr++ = alpha * *vptr + beta * sigmoid( fabs((double)*endptr) - fabs((double)*bptr ), slope ) * *bptr-- ; + } + + update_count = 1 ; + frame_count = 1 ; + + if ( frame_count >= a ) { + if ( echo ) + fwrite( buf, sizeof(short), maxlag, stdout ) ; + else + writeframe() ; + } + } + + /* Continue process with successive buffers of maxlag samples */ + + while ( ( buf = getframe( fp, ++update_count, maxlag, updatestep ) ) != (short *)0 && ( frame_count<=b || b==0 ) ) { + endptr = buf + maxlag - 1 ; + bptr = endptr ; + vptr = vec ; + beta = ( 1-alpha ) * *bptr ; + + while ( bptr >= buf ) /* recursive update */ + *vptr++ = alpha * *vptr + beta * sigmoid( fabs((double)*endptr) - fabs((double)*bptr ), slope ) * *bptr-- ; + + if ( update_count % framestep == 1 ) { + if ( ++frame_count >= a && ( frame_count <= b || b == 0 ) ) { + if ( echo ) + fwrite( buf, sizeof(short), maxlag, stdout ) ; + else + writeframe() ; + } + } + } + if ( frame_count <= b && b > 0 ) + fprintf(stderr,"warning: not enough frames for request\n"); +} + + +double sigmoid( x, slope ) +double x ; +float slope ; +{ + return (double)( 1. / ( exp( -(double)( 4.595 + slope * x ) ) + 1. ) ) ; +}