Mercurial > hg > aim92
comparison tools/fft.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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:5242703e91d3 |
---|---|
1 /* | |
2 FFT program adapted from routines given in Numerical Recipes. | |
3 | |
4 The output framewidth = ( input framewidth + padding ) / 2 spectral points. | |
5 | |
6 Padding with zeroes is recommended to counter end-effects (See NR, p416). | |
7 It is also necessary since framewidth + padding must be a power of 2 | |
8 (sample points at given rate). | |
9 This is because the fft method is "radix-2", and because the output is | |
10 symmetrical so that just half the points are unique. | |
11 Therefore each input frame is padded with zeroes to the next power of 2 | |
12 larger than the input framewidth. | |
13 | |
14 If necessary, extra padding can be enforced using the padding option to | |
15 add extra zeroes, padding to a larger power of 2. | |
16 The amount of extra padding is "exponential", expanding the basic size to: | |
17 ( framewidth + padding ) * 2**n | |
18 where the padding option is n. | |
19 (n=0 by default, so that no extra padding is added. When n=1 then padding is | |
20 added to double the size, and when n=2 the size is quadrupled, etc.). | |
21 | |
22 Frames are selected from the input stream using the "frames" option, | |
23 which has syntax: [[-]frame=a[-b]]. Input frames are numbered 1,2,... | |
24 For example: | |
25 frame=a Select just the a'th frame. | |
26 frame=a-b Select frames from the a'th to b'th inclusive. | |
27 The strings "min" and "max" can be used as specifiers, meaning eg: | |
28 frame=min Select the first frame. | |
29 frame=max Select the last frame. | |
30 frame=a-max Select frames from the a'th to the last inclusive. | |
31 frame=min-b Select frames from the first to the b'th inclusive. | |
32 The default selects all frames, (ie frame=min-max). | |
33 | |
34 Several forms of fft processing are provided, according to the "spectrum" | |
35 option: log/magnitude/phase/complex/inverse/verbose. | |
36 | |
37 Log and magnitude are both magnitude spectra (log is log10 of magnitude). | |
38 Phase is the phase spectrum. | |
39 Complex is the full complex spectrum, in <real,imag> pairs. | |
40 Inverse transform reads framewidth numbers which are interpreted as | |
41 <real,imag> pairs, (ie framewidth/2 complex numbers), | |
42 and outputs the inverse transform scaled by 1/framewidth. | |
43 Verbose prints the spectrum in ASCII on the stdout. | |
44 | |
45 | |
46 Examples: | |
47 | |
48 1. To print the input and output frame sizes in sample points, eg for a | |
49 subsequent plotting program, use the size option: | |
50 | |
51 fft ... size=on | |
52 | |
53 2. An fft of a waveform sampled at 10kHz, computed within a frame of 12.8ms, | |
54 plotting the 2nd frame in a sequence of frames with half-frame overlap. | |
55 | |
56 fft samp=10kHz width=12.8ms frstep=6.4ms frame=2 file | x11plot | |
57 | |
58 3. An animated plot of successive fft spectra of a waveform sampled at 10kHz, | |
59 each computed within a frame of 12.8ms, and shifted by 2 sample points. | |
60 | |
61 fft samp=10kHz width=12.8ms frstep=2p file | x11play -n64 | |
62 | |
63 4. Using the complex output from fft, and inverse transform without windowing | |
64 to recover original input. | |
65 | |
66 fft samp=10kHz frame=2 spec=complex window=off file > foo | |
67 fft samp=10kHz frame=1 spec=inverse window=off foo | x11plot | |
68 | |
69 */ | |
70 | |
71 #include <stdio.h> | |
72 #include <math.h> | |
73 #include "options.h" | |
74 #include "units.h" | |
75 #include "strmatch.h" | |
76 #include "sigproc.h" | |
77 | |
78 char applic[] = "fast Fourier transform of contiguous frames.\n i/p and o/p data in binary shorts." ; | |
79 | |
80 static char *helpstr, *debugstr, *sampstr, *widthstr, *padstr, *wstr, *sstr, *sizestr ; | |
81 static char *startstr, *shiftstr, *framestr, *smagstr, *slogstr, *sphasestr, *echostr ; | |
82 | |
83 static Options option[] = { | |
84 { "help" , "off" , &helpstr , "help" , DEBUG }, | |
85 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG }, | |
86 { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL }, | |
87 { "start" , "0" , &startstr , "Start point in i/p file." , VAL }, | |
88 { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL }, | |
89 { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL }, | |
90 { "width" , "32ms" , &widthstr , "Width of input frames." , VAL }, | |
91 { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL }, | |
92 { "window" , "on" , &wstr , "Hamming window" , SETFLAG }, | |
93 { "spectrum" , "magnitude" , &sstr , "log/magnitude/phase/complex/inverse/verbose" , SETFLAG }, | |
94 { "scalemag" , "0.08" , &smagstr , "scale magnitude spectrum" , SVAL }, | |
95 { "scalelog" , "10" , &slogstr , "scale log spectrum" , SVAL }, | |
96 { "scalephase", "100" , &sphasestr , "scale phase spectrum" , SVAL }, | |
97 { "size" , "off" , &sizestr , "print input/output frame size in samples" , SETFLAG }, | |
98 { "echo" , "off" , &echostr , "echo buffered input without processing" , SVAL }, | |
99 ( char * ) 0 } ; | |
100 | |
101 | |
102 int samplerate ; | |
103 int width ; | |
104 int step ; | |
105 int zeroes ; | |
106 int dowindow ; | |
107 double scale ; | |
108 int echo ; | |
109 | |
110 float *vec, *window ; | |
111 short *obuf ; | |
112 | |
113 int allbins ; | |
114 int halfbins ; | |
115 | |
116 #define MAG 1 /* magnitude spectrum */ | |
117 #define PHASE 2 /* phase spectrum */ | |
118 #define INV 3 /* inverse transform */ | |
119 #define VERB 4 /* verbose (ASCII) */ | |
120 #define LOG 5 /* log magnitude */ | |
121 #define COMP 6 /* complex spectrum */ | |
122 | |
123 int output ; | |
124 | |
125 main (argc, argv) | |
126 int argc; | |
127 char **argv; | |
128 { | |
129 FILE *fp ; | |
130 short *buf ; | |
131 int a, b ; | |
132 int isign ; | |
133 | |
134 fp = openopts( option,argc,argv ) ; | |
135 if ( !isoff( helpstr ) ) | |
136 helpopts( helpstr, argv[0], applic, option ) ; | |
137 | |
138 samplerate = to_Hz( sampstr ) ; | |
139 dowindow = ison( wstr ) ; | |
140 echo = ison( echostr ) ; | |
141 width = to_p( widthstr, samplerate ) ; | |
142 step = to_p( shiftstr, samplerate ) ; | |
143 zeroes = ( getpower( width ) << atoi( padstr ) ) - width ; | |
144 | |
145 allbins = width + zeroes ; | |
146 halfbins = allbins / 2 ; | |
147 | |
148 if ( iststr( sstr, "magnitude" ) ) { | |
149 output = MAG ; | |
150 scale = atof( smagstr ) ; | |
151 isign = 1 ; | |
152 } | |
153 else if ( iststr( sstr, "log" ) ) { | |
154 output = LOG ; | |
155 scale = atof( slogstr ) ; | |
156 isign = 1 ; | |
157 } | |
158 else if ( iststr( sstr, "phase" ) ) { | |
159 output = PHASE ; | |
160 scale = atof( sphasestr ) ; | |
161 isign = 1 ; | |
162 } | |
163 else if ( iststr( sstr, "complex" ) ) { | |
164 output = COMP ; | |
165 isign = 1 ; | |
166 } | |
167 else if ( iststr( sstr, "inverse" ) ) { | |
168 output = INV ; | |
169 scale = 1./(double)width ; | |
170 isign = (-1) ; | |
171 } | |
172 else if ( iststr( sstr, "verbose" ) ) { | |
173 output = VERB ; | |
174 isign = 1 ; | |
175 } | |
176 else { | |
177 fprintf(stderr,"unknown spectrum \n"); | |
178 exit( 1 ) ; | |
179 } | |
180 | |
181 /* frame size printout */ | |
182 | |
183 if ( ison( sizestr ) ) { | |
184 fprintf(stderr,"fft sizes in sample points:\n" ) ; | |
185 fprintf(stderr," input frame size = %d (framewidth=%d + padding=%d)\n", width+zeroes, width, zeroes ) ; | |
186 fprintf(stderr," output frame size = %d \n", (width + zeroes)/2 ) ; | |
187 exit( 0 ) ; | |
188 } | |
189 | |
190 /* parse bounds on number of frames */ | |
191 | |
192 if ( selector( framestr, &a, &b ) == 0 ) { | |
193 fprintf(stderr,"fft: bad frame selector [%s]\n", framestr ) ; | |
194 exit( 1 ) ; | |
195 } | |
196 | |
197 /* Allocate working space */ | |
198 | |
199 obuf = (short *)malloc( halfbins * sizeof(short) ) ; | |
200 vec = (float *)malloc( allbins * sizeof(float) ) ; | |
201 if ( dowindow ) | |
202 window = hamming( width ) ; | |
203 | |
204 | |
205 /* Compute fft for each frame of width shorts in the input stream */ | |
206 | |
207 if ( seekbytes( fp, (int)( to_p( startstr, samplerate ) * sizeof(short) ) ) == 0 ) { | |
208 fprintf(stderr,"improper seek\n") ; | |
209 exit( 1 ) ; | |
210 } | |
211 | |
212 while ( ( buf = getframe( fp, a, width, step ) ) != (short *)0 && ( a<=b || b==0 ) ) { | |
213 if ( echo ) fwrite( buf, sizeof(short), width, stdout ) ; | |
214 else process( buf, isign ) ; | |
215 a++ ; | |
216 } | |
217 if ( a<=b && b>0 ) | |
218 fprintf(stderr,"warning: not enough frames for request\n"); | |
219 | |
220 fclose( fp ) ; | |
221 } | |
222 | |
223 | |
224 process( buf, isign ) | |
225 short *buf ; | |
226 int isign ; | |
227 { | |
228 short *bptr = buf ; | |
229 short *endptr = buf + width ; | |
230 short *optr = obuf ; | |
231 short *endbin = obuf + halfbins ; | |
232 float *wptr = window ; | |
233 float *vptr = vec ; | |
234 float *endvec = vec + allbins ; | |
235 | |
236 if ( dowindow ) | |
237 while ( bptr < endptr ) | |
238 *vptr++ = *bptr++ * *wptr++ ; | |
239 else | |
240 while ( bptr < endptr ) | |
241 *vptr++ = *bptr++ ; | |
242 | |
243 while ( vptr < endvec ) /* padding */ | |
244 *vptr++ = 0 ; | |
245 | |
246 fft( vec, width+zeroes, isign ) ; | |
247 | |
248 vptr = vec ; | |
249 switch ( output ) { | |
250 | |
251 case COMP : while ( vptr < endvec ) | |
252 *optr++ = (short)( *vptr++ ) ; | |
253 fwrite( obuf, sizeof(short), allbins, stdout ) ; | |
254 break; | |
255 case PHASE : phase( vec, allbins ) ; | |
256 while ( optr < endbin ) | |
257 *optr++ = (short)( scale * *vptr++ ) ; | |
258 fwrite( obuf, sizeof(short), halfbins, stdout ) ; | |
259 break; | |
260 case MAG : mag( vec, allbins ) ; | |
261 while ( optr < endbin ) | |
262 *optr++ = (short)( scale * *vptr++ ) ; | |
263 fwrite( obuf, sizeof(short), halfbins, stdout ) ; | |
264 break; | |
265 case LOG : mag( vec, allbins ) ; | |
266 while ( optr < endbin ) | |
267 *optr++ = (short)( scale * 10 * log10(*vptr++) ) ; | |
268 fwrite( obuf, sizeof(short), halfbins, stdout ) ; | |
269 break; | |
270 case INV : while ( vptr < endvec ) | |
271 *optr++ = (short)( scale * *vptr++ ) ; | |
272 fwrite( obuf, sizeof(short), allbins, stdout ) ; | |
273 break; | |
274 case VERB : print_complex_spectrum( (complex *)vec, halfbins, samplerate ) ; | |
275 break; | |
276 | |
277 } | |
278 } | |
279 | |
280 | |
281 /* | |
282 Pretty-print complex k-spectrum on the stdout. | |
283 */ | |
284 | |
285 print_complex_spectrum(C,k,samplerate) | |
286 complex *C; | |
287 int k, samplerate; | |
288 { | |
289 float res; /* frequency resolution. */ | |
290 int i; | |
291 | |
292 res = (float)(samplerate>>1) / (float)k; | |
293 for (i=0; i<k ; i++) { | |
294 printf ("%3d. %6.1fHz. Re=%9.2f Im=%9.2f mod=%8.2f arg=%4.2f\n", | |
295 i, i*res, Re(&C[i]), Im(&C[i]), mod(&C[i]), arg(&C[i]) ); | |
296 } | |
297 } | |
298 |