tomwalters@0
|
1 /*
|
tomwalters@0
|
2 Autocorrelation function using recursive "leaky integrator" filter
|
tomwalters@0
|
3 ie. for the m'th lag, the n'th recursive update is:
|
tomwalters@0
|
4
|
tomwalters@0
|
5 y[n] = a.y[n-1] + (1-a).f[n].f[n-m]
|
tomwalters@0
|
6
|
tomwalters@0
|
7 where decay constant a = exp(-Ts/T)
|
tomwalters@0
|
8 and Ts is sample interval in seconds
|
tomwalters@0
|
9 T is decay time constant in seconds
|
tomwalters@0
|
10
|
tomwalters@0
|
11 The recursion computes the mean value of the product f[n].f[n-m]
|
tomwalters@0
|
12 within an exponential window which weights past values.
|
tomwalters@0
|
13 The window width parameter (eg. corresponding with the width of the
|
tomwalters@0
|
14 fft analysis window) is the time constant parameter.
|
tomwalters@0
|
15 The half life of the exponential window is given by -T.ln(0.5) = 0.693T
|
tomwalters@0
|
16 ie. for a given time constant, T secs, the window decays to half of its
|
tomwalters@0
|
17 current value in a period of 0.693T secs.
|
tomwalters@0
|
18 So T can be set to accomodate an expected period.
|
tomwalters@0
|
19 As a rough guide, in a period of 0.5T the window decays to about 0.6
|
tomwalters@0
|
20 (ie by 60%), so set T to twice the expected period of the waveform to
|
tomwalters@0
|
21 get 60% of the window over this period.
|
tomwalters@0
|
22 Eg. if the waveform has an expected period of t ms, then to set 60% of
|
tomwalters@0
|
23 the window to this period, set 0.5T = t ms, ie. T = 2t ms.
|
tomwalters@0
|
24
|
tomwalters@0
|
25 The routine generates the autocovariance function.
|
tomwalters@0
|
26 An optional normalization (dividing each coefficient by the zeroth
|
tomwalters@0
|
27 coefficient) produces the autocorrelation function,
|
tomwalters@0
|
28 (and this is then scaled up to the range [0-1000]).
|
tomwalters@0
|
29
|
tomwalters@0
|
30 The output framewidth is the given maximum acf lag.
|
tomwalters@0
|
31
|
tomwalters@0
|
32 Frames are selected from the input stream using the "frames" option,
|
tomwalters@0
|
33 which has syntax: [[-]frame=a[-b]]. Input frames are numbered 1,2,...
|
tomwalters@0
|
34 For example:
|
tomwalters@0
|
35 frame=a Select just the a'th frame.
|
tomwalters@0
|
36 frame=a-b Select frames from the a'th to b'th inclusive.
|
tomwalters@0
|
37 The strings "min" and "max" can be used as specifiers, meaning eg:
|
tomwalters@0
|
38 frame=min Select the first frame.
|
tomwalters@0
|
39 frame=max Select the last frame.
|
tomwalters@0
|
40 frame=a-max Select frames from the a'th to the last inclusive.
|
tomwalters@0
|
41 frame=min-b Select frames from the first to the b'th inclusive.
|
tomwalters@0
|
42 The default selects all frames, (ie frame=min-max).
|
tomwalters@0
|
43
|
tomwalters@0
|
44
|
tomwalters@0
|
45 Examples:
|
tomwalters@0
|
46
|
tomwalters@0
|
47 1. To print the input and output frame sizes in sample points, eg for a
|
tomwalters@0
|
48 subsequent plotting program, use the size option:
|
tomwalters@0
|
49
|
tomwalters@0
|
50 acf ... size=on
|
tomwalters@0
|
51
|
tomwalters@0
|
52 2. An acf of a waveform sampled at 10kHz, computed to a max lag of 12.8ms,
|
tomwalters@0
|
53 plotting the 2nd frame in a sequence of frames with frameshift 12.8ms.
|
tomwalters@0
|
54
|
tomwalters@0
|
55 acf samp=10kHz frstep=12.8ms frame=2 lag=12.8ms file | x11plot
|
tomwalters@0
|
56
|
tomwalters@0
|
57 3. An animated plot of successive acf's of a waveform sampled at 10kHz,
|
tomwalters@0
|
58 shifted by 2 sample points.
|
tomwalters@0
|
59
|
tomwalters@0
|
60 acf samp=10kHz frstep=2p lag=12.8ms file | x11play -n128
|
tomwalters@0
|
61
|
tomwalters@0
|
62 */
|
tomwalters@0
|
63
|
tomwalters@0
|
64 #include <stdio.h>
|
tomwalters@0
|
65 #include <math.h>
|
tomwalters@0
|
66 #include "options.h"
|
tomwalters@0
|
67 #include "units.h"
|
tomwalters@0
|
68 #include "strmatch.h"
|
tomwalters@0
|
69 #include "sigproc.h"
|
tomwalters@0
|
70
|
tomwalters@0
|
71 char applic[] = "autocorrelation function of contiguous frames.\n i/p and o/p data in binary shorts." ;
|
tomwalters@0
|
72
|
tomwalters@0
|
73 static char *helpstr, *debugstr, *sampstr, *startstr, *shiftstr, *sizestr ;
|
tomwalters@0
|
74 static char *lagstr, *tcstr, *scalestr, *normstr, *framestr, *echostr ;
|
tomwalters@0
|
75 static char *negstr, *upstr, *slopestr ;
|
tomwalters@0
|
76
|
tomwalters@0
|
77 static Options option[] = {
|
tomwalters@0
|
78 { "help" , "off" , &helpstr , "help" , DEBUG },
|
tomwalters@0
|
79 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG },
|
tomwalters@0
|
80 { "samplerate", "20kHz" , &sampstr , "Samplerate " , VAL },
|
tomwalters@0
|
81 { "start" , "0" , &startstr , "Start point in i/p file." , VAL },
|
tomwalters@0
|
82 { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL },
|
tomwalters@0
|
83 { "frstep" , "1ms" , &shiftstr , "Step between output frames." , VAL },
|
tomwalters@0
|
84 { "lag" , "32ms" , &lagstr , "Maximum acf lag (output frame width)." , VAL },
|
tomwalters@0
|
85 { "update" , "1p" , &upstr , "Step between recursive updates." , VAL },
|
tomwalters@0
|
86 { "timeconst" , "3ms" , &tcstr , "Decay time constant" , VAL },
|
tomwalters@0
|
87 { "normalize" , "off" , &normstr , "Normalized acf" , SETFLAG },
|
tomwalters@0
|
88 { "scale" , "0.003" , &scalestr , "scale output" , SVAL },
|
tomwalters@0
|
89 { "sigmoid" , "off" , &slopestr , "Sigmoidal weighting slope parameter" , SVAL },
|
tomwalters@0
|
90 { "size" , "off" , &sizestr , "Print input/output frame size in samples" , SETFLAG },
|
tomwalters@0
|
91 { "echo" , "off" , &echostr , "echo buffered input without processing", SVAL },
|
tomwalters@0
|
92 { "negative" , "off" , &negstr , "write negative half of acf (zeroth lag on right)", SVAL },
|
tomwalters@0
|
93 ( char * ) 0 } ;
|
tomwalters@0
|
94
|
tomwalters@0
|
95
|
tomwalters@0
|
96 int samplerate ;
|
tomwalters@0
|
97 int framestep ;
|
tomwalters@0
|
98 int updatestep ;
|
tomwalters@0
|
99 int maxlag ;
|
tomwalters@0
|
100 double scale ;
|
tomwalters@0
|
101 int normalize ;
|
tomwalters@0
|
102 int echo ;
|
tomwalters@0
|
103 int negative ;
|
tomwalters@0
|
104 double alpha ; /* decay constant */
|
tomwalters@0
|
105
|
tomwalters@0
|
106 float *vec ;
|
tomwalters@0
|
107 short *obuf ;
|
tomwalters@0
|
108
|
tomwalters@0
|
109 main (argc, argv)
|
tomwalters@0
|
110 int argc;
|
tomwalters@0
|
111 char **argv;
|
tomwalters@0
|
112 {
|
tomwalters@0
|
113 FILE *fp ;
|
tomwalters@0
|
114 int a, b ;
|
tomwalters@0
|
115
|
tomwalters@0
|
116 fp = openopts( option,argc,argv ) ;
|
tomwalters@0
|
117 if ( !isoff( helpstr ) )
|
tomwalters@0
|
118 helpopts( helpstr, argv[0], applic, option ) ;
|
tomwalters@0
|
119
|
tomwalters@0
|
120 samplerate = to_Hz( sampstr ) ;
|
tomwalters@0
|
121 normalize = ison( normstr ) ;
|
tomwalters@0
|
122 echo = ison( echostr ) ;
|
tomwalters@0
|
123 negative = ison( negstr ) ;
|
tomwalters@0
|
124
|
tomwalters@0
|
125 framestep = to_p( shiftstr, samplerate ) ;
|
tomwalters@0
|
126 updatestep = to_p( upstr , samplerate ) ;
|
tomwalters@0
|
127 maxlag = to_p( lagstr , samplerate ) ;
|
tomwalters@0
|
128
|
tomwalters@0
|
129 scale = atof( scalestr ) ;
|
tomwalters@0
|
130
|
tomwalters@0
|
131 alpha = exp( -(double)( 1. / ( samplerate * to_s( tcstr, samplerate ) ) ) ) ;
|
tomwalters@0
|
132
|
tomwalters@0
|
133 if ( framestep % updatestep != 0 ) {
|
tomwalters@0
|
134 fprintf(stderr,"frstep [%dp] must be an integer multiple of update [%dp] \n", framestep, updatestep ) ;
|
tomwalters@0
|
135 exit( 1 ) ;
|
tomwalters@0
|
136 }
|
tomwalters@0
|
137
|
tomwalters@0
|
138 /* frame size printout */
|
tomwalters@0
|
139
|
tomwalters@0
|
140 if ( ison( sizestr ) ) {
|
tomwalters@0
|
141 fprintf(stderr,"acf sizes in sample points:\n" ) ;
|
tomwalters@0
|
142 fprintf(stderr," output frame size = %d \n", maxlag ) ;
|
tomwalters@0
|
143 exit( 0 ) ;
|
tomwalters@0
|
144 }
|
tomwalters@0
|
145
|
tomwalters@0
|
146 /* parse bounds on number of frames */
|
tomwalters@0
|
147
|
tomwalters@0
|
148 if ( selector( framestr, &a, &b ) == 0 ) {
|
tomwalters@0
|
149 fprintf(stderr,"acf: bad frame selector [%s]\n", framestr ) ;
|
tomwalters@0
|
150 exit( 1 ) ;
|
tomwalters@0
|
151 }
|
tomwalters@0
|
152
|
tomwalters@0
|
153 /* Allocate working space */
|
tomwalters@0
|
154
|
tomwalters@0
|
155 obuf = (short *)malloc( maxlag * sizeof(short) ) ;
|
tomwalters@0
|
156 vec = (float *)malloc( maxlag * sizeof(float) ) ;
|
tomwalters@0
|
157
|
tomwalters@0
|
158 /* Compute acf for each frame of width shorts in the input stream */
|
tomwalters@0
|
159
|
tomwalters@0
|
160 if ( seekbytes( fp, (int)( to_p( startstr, samplerate ) * sizeof(short) ) ) == 0 ) {
|
tomwalters@0
|
161 fprintf(stderr,"improper seek\n") ;
|
tomwalters@0
|
162 exit( 1 ) ;
|
tomwalters@0
|
163 }
|
tomwalters@0
|
164
|
tomwalters@0
|
165 if ( isoff( slopestr ) )
|
tomwalters@0
|
166 process( fp, a, b ) ; /* conventional acf recursion */
|
tomwalters@0
|
167 else
|
tomwalters@0
|
168 process2( fp, a, b, atof( slopestr) ) ; /* sigmoidally weighted recursion */
|
tomwalters@0
|
169 /* (recommend set sigmoid=.02) */
|
tomwalters@0
|
170
|
tomwalters@0
|
171 fclose( fp ) ;
|
tomwalters@0
|
172 }
|
tomwalters@0
|
173
|
tomwalters@0
|
174
|
tomwalters@0
|
175 process( fp, a, b )
|
tomwalters@0
|
176 FILE *fp ;
|
tomwalters@0
|
177 int a, b ;
|
tomwalters@0
|
178 {
|
tomwalters@0
|
179 register short *buf, *bptr, *endptr ;
|
tomwalters@0
|
180 register float *vptr ;
|
tomwalters@0
|
181 register double beta ;
|
tomwalters@0
|
182 register int update_count = 0 ;
|
tomwalters@0
|
183 register int frame_count = 0 ;
|
tomwalters@0
|
184
|
tomwalters@0
|
185 /* Initialize process with the first maxlag samples */
|
tomwalters@0
|
186
|
tomwalters@0
|
187 if ( ( buf = getframe( fp, 1, maxlag, updatestep ) ) != (short *)0 ) {
|
tomwalters@0
|
188 for ( endptr = buf ; endptr < buf + maxlag ; endptr++ ) {
|
tomwalters@0
|
189 bptr = endptr ;
|
tomwalters@0
|
190 vptr = vec ;
|
tomwalters@0
|
191 beta = ( 1-alpha ) * *bptr ;
|
tomwalters@0
|
192 while ( bptr >= buf )
|
tomwalters@0
|
193 *vptr++ = alpha * *vptr + beta * *bptr-- ;
|
tomwalters@0
|
194 }
|
tomwalters@0
|
195
|
tomwalters@0
|
196 update_count = 1 ;
|
tomwalters@0
|
197 frame_count = 1 ;
|
tomwalters@0
|
198
|
tomwalters@0
|
199 if ( frame_count >= a ) {
|
tomwalters@0
|
200 if ( echo )
|
tomwalters@0
|
201 fwrite( buf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
202 else
|
tomwalters@0
|
203 writeframe() ;
|
tomwalters@0
|
204 }
|
tomwalters@0
|
205 }
|
tomwalters@0
|
206
|
tomwalters@0
|
207 /* Continue process with successive buffers of maxlag samples */
|
tomwalters@0
|
208
|
tomwalters@0
|
209 while ( ( buf = getframe( fp, ++update_count, maxlag, updatestep ) ) != (short *)0 && ( frame_count<=b || b==0 ) ) {
|
tomwalters@0
|
210 bptr = buf + maxlag - 1 ;
|
tomwalters@0
|
211 vptr = vec ;
|
tomwalters@0
|
212 beta = ( 1-alpha ) * *bptr ;
|
tomwalters@0
|
213
|
tomwalters@0
|
214 while ( bptr >= buf ) /* recursive update */
|
tomwalters@0
|
215 *vptr++ = alpha * *vptr + beta * *bptr-- ;
|
tomwalters@0
|
216
|
tomwalters@0
|
217 if ( update_count % framestep == 1 ) {
|
tomwalters@0
|
218 if ( ++frame_count >= a && ( frame_count <= b || b == 0 ) ) {
|
tomwalters@0
|
219 if ( echo )
|
tomwalters@0
|
220 fwrite( buf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
221 else
|
tomwalters@0
|
222 writeframe() ;
|
tomwalters@0
|
223 }
|
tomwalters@0
|
224 }
|
tomwalters@0
|
225 }
|
tomwalters@0
|
226 if ( frame_count <= b && b > 0 )
|
tomwalters@0
|
227 fprintf(stderr,"warning: not enough frames for request\n");
|
tomwalters@0
|
228 }
|
tomwalters@0
|
229
|
tomwalters@0
|
230
|
tomwalters@0
|
231 writeframe()
|
tomwalters@0
|
232 {
|
tomwalters@0
|
233 register float *vptr = vec ;
|
tomwalters@0
|
234 register short *optr = obuf ;
|
tomwalters@0
|
235 register short *endlag = obuf + maxlag ;
|
tomwalters@0
|
236
|
tomwalters@0
|
237 if ( normalize )
|
tomwalters@0
|
238 scale = 1000. / *vec ;
|
tomwalters@0
|
239
|
tomwalters@0
|
240 while ( optr < endlag )
|
tomwalters@0
|
241 *optr++ = (short)( scale * *vptr++ ) ;
|
tomwalters@0
|
242
|
tomwalters@0
|
243 if ( !negative )
|
tomwalters@0
|
244 fwrite( obuf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
245 else {
|
tomwalters@0
|
246 for ( optr = endlag-1 ; optr >= obuf ; optr-- )
|
tomwalters@0
|
247 fwrite( optr, sizeof(short), 1, stdout ) ;
|
tomwalters@0
|
248 }
|
tomwalters@0
|
249 }
|
tomwalters@0
|
250
|
tomwalters@0
|
251
|
tomwalters@0
|
252 /*************************************************************************/
|
tomwalters@0
|
253 /*
|
tomwalters@0
|
254 same as `process', but with added sigmoidal weighting
|
tomwalters@0
|
255 */
|
tomwalters@0
|
256
|
tomwalters@0
|
257 process2( fp, a, b, slope )
|
tomwalters@0
|
258 FILE *fp ;
|
tomwalters@0
|
259 int a, b ;
|
tomwalters@0
|
260 float slope ;
|
tomwalters@0
|
261 {
|
tomwalters@0
|
262 register short *buf, *bptr, *endptr ;
|
tomwalters@0
|
263 register float *vptr ;
|
tomwalters@0
|
264 register double beta ;
|
tomwalters@0
|
265 register int update_count = 0 ;
|
tomwalters@0
|
266 register int frame_count = 0 ;
|
tomwalters@0
|
267 double sigmoid() ;
|
tomwalters@0
|
268
|
tomwalters@0
|
269 /* Initialize process with the first maxlag samples */
|
tomwalters@0
|
270
|
tomwalters@0
|
271 if ( ( buf = getframe( fp, 1, maxlag, updatestep ) ) != (short *)0 ) {
|
tomwalters@0
|
272 for ( endptr = buf ; endptr < buf + maxlag ; endptr++ ) {
|
tomwalters@0
|
273 bptr = endptr ;
|
tomwalters@0
|
274 vptr = vec ;
|
tomwalters@0
|
275 beta = ( 1-alpha ) * *bptr ;
|
tomwalters@0
|
276 while ( bptr >= buf )
|
tomwalters@0
|
277 *vptr++ = alpha * *vptr + beta * sigmoid( fabs((double)*endptr) - fabs((double)*bptr ), slope ) * *bptr-- ;
|
tomwalters@0
|
278 }
|
tomwalters@0
|
279
|
tomwalters@0
|
280 update_count = 1 ;
|
tomwalters@0
|
281 frame_count = 1 ;
|
tomwalters@0
|
282
|
tomwalters@0
|
283 if ( frame_count >= a ) {
|
tomwalters@0
|
284 if ( echo )
|
tomwalters@0
|
285 fwrite( buf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
286 else
|
tomwalters@0
|
287 writeframe() ;
|
tomwalters@0
|
288 }
|
tomwalters@0
|
289 }
|
tomwalters@0
|
290
|
tomwalters@0
|
291 /* Continue process with successive buffers of maxlag samples */
|
tomwalters@0
|
292
|
tomwalters@0
|
293 while ( ( buf = getframe( fp, ++update_count, maxlag, updatestep ) ) != (short *)0 && ( frame_count<=b || b==0 ) ) {
|
tomwalters@0
|
294 endptr = buf + maxlag - 1 ;
|
tomwalters@0
|
295 bptr = endptr ;
|
tomwalters@0
|
296 vptr = vec ;
|
tomwalters@0
|
297 beta = ( 1-alpha ) * *bptr ;
|
tomwalters@0
|
298
|
tomwalters@0
|
299 while ( bptr >= buf ) /* recursive update */
|
tomwalters@0
|
300 *vptr++ = alpha * *vptr + beta * sigmoid( fabs((double)*endptr) - fabs((double)*bptr ), slope ) * *bptr-- ;
|
tomwalters@0
|
301
|
tomwalters@0
|
302 if ( update_count % framestep == 1 ) {
|
tomwalters@0
|
303 if ( ++frame_count >= a && ( frame_count <= b || b == 0 ) ) {
|
tomwalters@0
|
304 if ( echo )
|
tomwalters@0
|
305 fwrite( buf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
306 else
|
tomwalters@0
|
307 writeframe() ;
|
tomwalters@0
|
308 }
|
tomwalters@0
|
309 }
|
tomwalters@0
|
310 }
|
tomwalters@0
|
311 if ( frame_count <= b && b > 0 )
|
tomwalters@0
|
312 fprintf(stderr,"warning: not enough frames for request\n");
|
tomwalters@0
|
313 }
|
tomwalters@0
|
314
|
tomwalters@0
|
315
|
tomwalters@0
|
316 double sigmoid( x, slope )
|
tomwalters@0
|
317 double x ;
|
tomwalters@0
|
318 float slope ;
|
tomwalters@0
|
319 {
|
tomwalters@0
|
320 return (double)( 1. / ( exp( -(double)( 4.595 + slope * x ) ) + 1. ) ) ;
|
tomwalters@0
|
321 }
|