tomwalters@0
|
1 /*
|
tomwalters@0
|
2 Autocorrelation function using FFT routines adapted from routines given in
|
tomwalters@0
|
3 Numerical Recipes.
|
tomwalters@0
|
4 The routine acf() uses an fft to compute the array of lagged products
|
tomwalters@0
|
5 which is the autocovariance function. An optional normalization (dividing
|
tomwalters@0
|
6 each coefficient by the zeroth coefficient) produces the autocorrelation
|
tomwalters@0
|
7 function, (and this is then scaled up to the range [0-1000]).
|
tomwalters@0
|
8
|
tomwalters@0
|
9 The output framewidth is the given maximum acf lag, which must not be
|
tomwalters@0
|
10 greater than the input framewidth.
|
tomwalters@0
|
11
|
tomwalters@0
|
12 Padding with zeroes is recommended to counter end-effects (See NR, p416).
|
tomwalters@0
|
13 It is also necessary since:
|
tomwalters@0
|
14 1) framesize + padding must be a power of two (sample points at given rate).
|
tomwalters@0
|
15 2) max possible acf lag = ( framesize + padding ) / 2
|
tomwalters@0
|
16
|
tomwalters@0
|
17 Each input frame is padded with zeroes so that it is at least twice the
|
tomwalters@0
|
18 required max acf lag, and is also a power of 2.
|
tomwalters@0
|
19 This is because the fft method is "radix-2", and because the output acf is
|
tomwalters@0
|
20 symmetrical so that just half the points are unique.
|
tomwalters@0
|
21 Therefore each input frame is padded with zeroes to the next power of 2
|
tomwalters@0
|
22 larger than either the input framewidth, or twice the required max acf lag,
|
tomwalters@0
|
23 (whichever is the larger).
|
tomwalters@0
|
24
|
tomwalters@0
|
25 If necessary, extra padding can be enforced using the padding option to
|
tomwalters@0
|
26 add extra zeroes, padding to a larger power of 2.
|
tomwalters@0
|
27 The amount of extra padding is "exponential", expanding the basic size to:
|
tomwalters@0
|
28 ( framesize + padding ) * 2**n
|
tomwalters@0
|
29 where the padding option is n.
|
tomwalters@0
|
30 (n=0 by default, so that no extra padding is added. When n=1 then padding is
|
tomwalters@0
|
31 added to double the size, and when n=2 the size is quadrupled, etc.).
|
tomwalters@0
|
32
|
tomwalters@0
|
33 Frames are selected from the input stream using the "frames" option,
|
tomwalters@0
|
34 which has syntax: [[-]frame=a[-b]]. Input frames are numbered 1,2,...
|
tomwalters@0
|
35 For example:
|
tomwalters@0
|
36 frame=a Select just the a'th frame.
|
tomwalters@0
|
37 frame=a-b Select frames from the a'th to b'th inclusive.
|
tomwalters@0
|
38 The strings "min" and "max" can be used as specifiers, meaning eg:
|
tomwalters@0
|
39 frame=min Select the first frame.
|
tomwalters@0
|
40 frame=max Select the last frame.
|
tomwalters@0
|
41 frame=a-max Select frames from the a'th to the last inclusive.
|
tomwalters@0
|
42 frame=min-b Select frames from the first to the b'th inclusive.
|
tomwalters@0
|
43 The default selects all frames, (ie frame=min-max).
|
tomwalters@0
|
44
|
tomwalters@0
|
45 Other options (set using <option>=on):
|
tomwalters@0
|
46 size Print the input and output frame sizes in sample points.
|
tomwalters@0
|
47 echo Echo buffered input without processing.
|
tomwalters@0
|
48 negative Write negative half of acf (ie. with zeroth lag on right).
|
tomwalters@0
|
49
|
tomwalters@0
|
50
|
tomwalters@0
|
51 Examples:
|
tomwalters@0
|
52
|
tomwalters@0
|
53 1. To print the input and output frame sizes in sample points, eg for a
|
tomwalters@0
|
54 subsequent plotting program, use the size option:
|
tomwalters@0
|
55
|
tomwalters@0
|
56 acf ... size=on
|
tomwalters@0
|
57
|
tomwalters@0
|
58 2. An acf of a waveform sampled at 10kHz, computed to a max lag of 12.8ms
|
tomwalters@0
|
59 within a frame of 12.8ms, plotting the 2nd frame in a sequence of frames
|
tomwalters@0
|
60 with frameshift 12.8ms.
|
tomwalters@0
|
61
|
tomwalters@0
|
62 acf samp=10kHz width=12.8ms frstep=12.8ms frame=2 lag=12.8ms file | x11plot
|
tomwalters@0
|
63
|
tomwalters@0
|
64 3. An animated plot of successive acf's of a waveform sampled at 10kHz,
|
tomwalters@0
|
65 each computed within a frame of 12.8ms, and shifted by 2 sample points.
|
tomwalters@0
|
66
|
tomwalters@0
|
67 acf samp=10kHz width=12.8ms frstep=2p lag=12.8ms file | x11play -n128
|
tomwalters@0
|
68
|
tomwalters@0
|
69 */
|
tomwalters@0
|
70
|
tomwalters@0
|
71 #include <stdio.h>
|
tomwalters@0
|
72 #include <math.h>
|
tomwalters@0
|
73 #include "options.h"
|
tomwalters@0
|
74 #include "units.h"
|
tomwalters@0
|
75 #include "strmatch.h"
|
tomwalters@0
|
76 #include "sigproc.h"
|
tomwalters@0
|
77
|
tomwalters@0
|
78 char applic[] = "autocorrelation function of contiguous frames.\n i/p and o/p data in binary shorts." ;
|
tomwalters@0
|
79
|
tomwalters@0
|
80 static char *helpstr, *debugstr, *sampstr, *startstr, *shiftstr, *widthstr, *sizestr ;
|
tomwalters@0
|
81 static char *padstr, *wstr, *lagstr, *scalestr, *normstr, *framestr, *echostr, *negstr ;
|
tomwalters@0
|
82
|
tomwalters@0
|
83 static Options option[] = {
|
tomwalters@0
|
84 { "help" , "off" , &helpstr , "help" , DEBUG },
|
tomwalters@0
|
85 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG },
|
tomwalters@0
|
86 { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL },
|
tomwalters@0
|
87 { "start" , "0" , &startstr , "Start point in i/p file." , VAL },
|
tomwalters@0
|
88 { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL },
|
tomwalters@0
|
89 { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL },
|
tomwalters@0
|
90 { "width" , "32ms" , &widthstr , "Width of input frames." , VAL },
|
tomwalters@0
|
91 { "lag" , "32ms" , &lagstr , "Maximum acf lag (output frame width)." , VAL },
|
tomwalters@0
|
92 { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL },
|
tomwalters@0
|
93 { "window" , "on" , &wstr , "Hamming window" , SETFLAG },
|
tomwalters@0
|
94 { "normalize" , "off" , &normstr , "normalized acf" , SETFLAG },
|
tomwalters@0
|
95 { "scale" , "0.025" , &scalestr , "scale output" , SVAL },
|
tomwalters@0
|
96 { "size" , "off" , &sizestr , "print input/output frame size in samples" , SETFLAG },
|
tomwalters@0
|
97 { "echo" , "off" , &echostr , "echo buffered input without processing", SVAL },
|
tomwalters@0
|
98 { "negative" , "off" , &negstr , "write negative half of acf (zeroth lag on right)", SVAL },
|
tomwalters@0
|
99 ( char * ) 0 } ;
|
tomwalters@0
|
100
|
tomwalters@0
|
101
|
tomwalters@0
|
102 int samplerate ;
|
tomwalters@0
|
103 int width ;
|
tomwalters@0
|
104 int step ;
|
tomwalters@0
|
105 int zeroes ;
|
tomwalters@0
|
106 int maxlag ;
|
tomwalters@0
|
107 int dowindow ;
|
tomwalters@0
|
108 double scale ;
|
tomwalters@0
|
109 int normalize ;
|
tomwalters@0
|
110 int echo ;
|
tomwalters@0
|
111 int negative ;
|
tomwalters@0
|
112
|
tomwalters@0
|
113 float *vec, *window ;
|
tomwalters@0
|
114 short *obuf ;
|
tomwalters@0
|
115
|
tomwalters@0
|
116 main (argc, argv)
|
tomwalters@0
|
117 int argc;
|
tomwalters@0
|
118 char **argv;
|
tomwalters@0
|
119 {
|
tomwalters@0
|
120 FILE *fp ;
|
tomwalters@0
|
121 short *buf ;
|
tomwalters@0
|
122 int a, b ;
|
tomwalters@0
|
123
|
tomwalters@0
|
124 fp = openopts( option,argc,argv ) ;
|
tomwalters@0
|
125 if ( !isoff( helpstr ) )
|
tomwalters@0
|
126 helpopts( helpstr, argv[0], applic, option ) ;
|
tomwalters@0
|
127
|
tomwalters@0
|
128 samplerate = to_Hz( sampstr ) ;
|
tomwalters@0
|
129 dowindow = ison( wstr ) ;
|
tomwalters@0
|
130 normalize = ison( normstr ) ;
|
tomwalters@0
|
131 echo = ison( echostr ) ;
|
tomwalters@0
|
132 negative = ison( negstr ) ;
|
tomwalters@0
|
133
|
tomwalters@0
|
134 width = to_p( widthstr, samplerate ) ;
|
tomwalters@0
|
135 step = to_p( shiftstr, samplerate ) ;
|
tomwalters@0
|
136 maxlag = to_p( lagstr , samplerate ) ;
|
tomwalters@0
|
137
|
tomwalters@0
|
138 if ( ( maxlag << 1 ) < width )
|
tomwalters@0
|
139 zeroes = ( getpower( width ) << atoi( padstr ) ) - width ;
|
tomwalters@0
|
140 else if ( maxlag <= width )
|
tomwalters@0
|
141 zeroes = ( getpower( maxlag << 1 ) << atoi( padstr ) ) - width ;
|
tomwalters@0
|
142 else {
|
tomwalters@0
|
143 fprintf(stderr,"acf: required max lag [%dms] greater than framewidth [%dms]\n", maxlag*1000/samplerate, width*1000/samplerate);
|
tomwalters@0
|
144 exit( 1 ) ;
|
tomwalters@0
|
145 }
|
tomwalters@0
|
146
|
tomwalters@0
|
147 scale = ( 1./(double)(width+zeroes) ) * atof( scalestr ) ;
|
tomwalters@0
|
148
|
tomwalters@0
|
149 /* frame size printout */
|
tomwalters@0
|
150
|
tomwalters@0
|
151 if ( ison( sizestr ) ) {
|
tomwalters@0
|
152 fprintf(stderr,"acf sizes in sample points:\n" ) ;
|
tomwalters@0
|
153 fprintf(stderr," input frame size = %d (framewidth=%d + padding=%d)\n", width+zeroes, width, zeroes ) ;
|
tomwalters@0
|
154 fprintf(stderr," output frame size = %d \n", maxlag ) ;
|
tomwalters@0
|
155 exit( 0 ) ;
|
tomwalters@0
|
156 }
|
tomwalters@0
|
157
|
tomwalters@0
|
158 /* parse bounds on number of frames */
|
tomwalters@0
|
159
|
tomwalters@0
|
160 if ( selector( framestr, &a, &b ) == 0 ) {
|
tomwalters@0
|
161 fprintf(stderr,"acf: bad frame selector [%s]\n", framestr ) ;
|
tomwalters@0
|
162 exit( 1 ) ;
|
tomwalters@0
|
163 }
|
tomwalters@0
|
164
|
tomwalters@0
|
165 /* Allocate working space */
|
tomwalters@0
|
166
|
tomwalters@0
|
167 obuf = (short *)malloc( maxlag * sizeof(short) ) ;
|
tomwalters@0
|
168 vec = (float *)malloc( width+zeroes * sizeof(float) ) ;
|
tomwalters@0
|
169 if ( dowindow )
|
tomwalters@0
|
170 window = hamming( width ) ;
|
tomwalters@0
|
171
|
tomwalters@0
|
172 /* Compute acf for each frame of width shorts in the input stream */
|
tomwalters@0
|
173
|
tomwalters@0
|
174 if ( seekbytes( fp, (int)( to_p( startstr, samplerate ) * sizeof(short) ) ) == 0 ) {
|
tomwalters@0
|
175 fprintf(stderr,"improper seek\n") ;
|
tomwalters@0
|
176 exit( 1 ) ;
|
tomwalters@0
|
177 }
|
tomwalters@0
|
178
|
tomwalters@0
|
179 while ( ( buf = getframe( fp, a, width, step ) ) != (short *)0 && ( a<=b || b==0 ) ) {
|
tomwalters@0
|
180 if ( echo ) fwrite( buf, sizeof(short), width, stdout ) ;
|
tomwalters@0
|
181 else process( buf ) ;
|
tomwalters@0
|
182 a++ ;
|
tomwalters@0
|
183 }
|
tomwalters@0
|
184 if ( a<=b && b>0 )
|
tomwalters@0
|
185 fprintf(stderr,"warning: not enough frames for request\n");
|
tomwalters@0
|
186
|
tomwalters@0
|
187 fclose( fp ) ;
|
tomwalters@0
|
188 }
|
tomwalters@0
|
189
|
tomwalters@0
|
190
|
tomwalters@0
|
191 process( buf )
|
tomwalters@0
|
192 short *buf ;
|
tomwalters@0
|
193 {
|
tomwalters@0
|
194 short *bptr = buf ;
|
tomwalters@0
|
195 short *endptr = buf + width ;
|
tomwalters@0
|
196 short *optr = obuf ;
|
tomwalters@0
|
197 short *endlag = obuf + maxlag ;
|
tomwalters@0
|
198 float *wptr = window ;
|
tomwalters@0
|
199 float *vptr = vec ;
|
tomwalters@0
|
200 float *endvec = vec + width + zeroes ;
|
tomwalters@0
|
201
|
tomwalters@0
|
202 if ( dowindow )
|
tomwalters@0
|
203 while ( bptr < endptr )
|
tomwalters@0
|
204 *vptr++ = *bptr++ * *wptr++ ;
|
tomwalters@0
|
205 else
|
tomwalters@0
|
206 while ( bptr < endptr )
|
tomwalters@0
|
207 *vptr++ = *bptr++ ;
|
tomwalters@0
|
208
|
tomwalters@0
|
209 while ( vptr < endvec ) /* padding */
|
tomwalters@0
|
210 *vptr++ = 0 ;
|
tomwalters@0
|
211
|
tomwalters@0
|
212 acf( vec, width+zeroes ) ;
|
tomwalters@0
|
213
|
tomwalters@0
|
214 if ( normalize )
|
tomwalters@0
|
215 scale = 1000. / *vec ;
|
tomwalters@0
|
216
|
tomwalters@0
|
217 vptr = vec ;
|
tomwalters@0
|
218 while ( optr < endlag )
|
tomwalters@0
|
219 *optr++ = (short)( scale * *vptr++ ) ;
|
tomwalters@0
|
220
|
tomwalters@0
|
221 if ( !negative )
|
tomwalters@0
|
222 fwrite( obuf, sizeof(short), maxlag, stdout ) ;
|
tomwalters@0
|
223 else {
|
tomwalters@0
|
224 for ( optr = endlag-1 ; optr >= obuf ; optr-- ) /* write in reverse */
|
tomwalters@0
|
225 fwrite( optr, sizeof(short), 1, stdout ) ;
|
tomwalters@0
|
226 }
|
tomwalters@0
|
227 }
|
tomwalters@0
|
228
|
tomwalters@0
|
229
|