Mercurial > hg > aim92
comparison tools/ftgram.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 ftgram.c Row-wise fft program by Michael Allerhand. | |
3 ---------- | |
4 | |
5 Short-time Fourier transform applied to each row of input frames. | |
6 Output frames consist of row-wise magnitude spectra, computed using fft. | |
7 | |
8 Input/output: | |
9 Read a header and frame in NAP format. | |
10 Write a header, (constructed from the original input header), and a | |
11 succession of frames in SAI format. | |
12 | |
13 The input file is interpreted as one large frame in NAP format to be | |
14 divided into input frames, each to be processed and output in SAI format. | |
15 | |
16 The options "start" and "length" specify the input file. | |
17 The special option value length=max specifies all the input file from | |
18 the given start to its end. | |
19 | |
20 The options "width" and "frstep" specify the input frames. | |
21 The width option is the framewidth of each input frame) and the frstep | |
22 option is the frameshift between input frames in the input file. | |
23 The special option value width=max specifies the input framewidth as equal | |
24 to the given input file length, (and if this is also "max", then the | |
25 input framewidth is the remainder of the input file). | |
26 | |
27 Most options in the input header are copied to the output header. | |
28 This enables options which are needed for the eventual display | |
29 to pass straight through. Some options are set so that they can override | |
30 the input header. For example, the display option is set on to enable | |
31 display even when input has display=off. The animate option can be set on | |
32 even when the input has animate=off. | |
33 Parts of the header are changed for the new sai format: | |
34 (frames, frameshift, framewidth, frameheight, framebytes). | |
35 | |
36 Padding: | |
37 The output framewidth is the next power-of-2 above twice the input | |
38 framewidth. | |
39 The rows of each input frame are padded with zeroes so that the input | |
40 framewidth is at least twice its original size, and also a power of 2. | |
41 This is because the fft method is "radix-2", and because each output | |
42 spectrum is symmetrical so that just half the points are unique. | |
43 It is advisable to pad input frames for the additional reason that | |
44 padding counters frame end-effects. | |
45 | |
46 Therefore each row of each input frame is padded with zeroes to the next | |
47 power of 2 larger than the original input framewidth. | |
48 If necessary, extra padding can be enforced using the padding option to | |
49 add extra zeroes, padding to a larger power of 2. | |
50 The amount of extra padding is "exponential" since if the basic padded | |
51 framesize is N, and the padding option is n, then extra padding is to: | |
52 N * 2**n. | |
53 (n=0 by default, so that N is not changed. When n=1 then N is doubled, and | |
54 when n=2 then N is quadrupled, etc.). | |
55 | |
56 The output framewidth (and hence the frequency resolution) depends upon the | |
57 input framewidth and the padding. | |
58 | |
59 Examples: | |
60 | |
61 1. ftgram of a NAP, animated, with framewidth 16ms | |
62 | |
63 gennap len=128ms output=stdout display=off file | ftgram width=16ms anim=on > file.sai | |
64 gensai useprev=on headr=5 top=1000 file -(for landscape plot) | |
65 genspl useprev=on headr=5 top=1000 pensize=2 file -(for spiral plot) | |
66 | |
67 2. ftgram of an SAI: | |
68 (Note that gensai removes file.sai, so you must use some other name, eg foo.sai). | |
69 | |
70 gensai len=64 pwidth=64 nwidth=0 output=stdout display=off file | \ | |
71 saitonap frame=3 | ftgram width=32ms frame=1 > foo.sai | |
72 gensai useprev=on top=1000 headr=5 mag=2 foo | |
73 | |
74 | |
75 */ | |
76 | |
77 #include <stdio.h> | |
78 #include <math.h> | |
79 #include "header.h" | |
80 #include "options.h" | |
81 #include "units.h" | |
82 #include "strmatch.h" | |
83 #include "sigproc.h" | |
84 | |
85 char applic[] = "Autocorrelogram auditory image." ; | |
86 | |
87 static char *helpstr , *debugstr, *dispstr , *anistr , *startstr ; | |
88 static char *lengthstr, *widthstr, *shiftstr, *headerstr, *framestr ; | |
89 static char *wstr , *scalestr, *padstr ; | |
90 | |
91 static Options option[] = { | |
92 { "help" , "off" , &helpstr , "help" , DEBUG }, | |
93 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG }, | |
94 { "display" , "on" , &dispstr , "Display output image." , SETFLAG }, | |
95 { "animate" , "off" , &anistr , "Animate cartoon." , SETFLAG }, | |
96 { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL }, | |
97 { "header" , "on" , &headerstr , "Header (for gensai useprevious=on)." , VAL }, | |
98 { "start" , "0" , &startstr , "Start point in i/p file." , VAL }, | |
99 { "length" , "max" , &lengthstr , "Length of i/p file to process." , VAL }, | |
100 { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL }, | |
101 { "width" , "32ms" , &widthstr , "Width of input frames." , VAL }, | |
102 { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL }, | |
103 { "window" , "on" , &wstr , "Hamming window" , SETFLAG }, | |
104 { "scale" , "0.08" , &scalestr , "Scale factor for output" , SVAL }, | |
105 ( char * ) 0 } ; | |
106 | |
107 | |
108 int samplerate ; | |
109 | |
110 int frameheight, framewidth ; | |
111 int frames, framebytes ; | |
112 int frameshift ; | |
113 | |
114 int Newframeheight, Newframewidth ; | |
115 int Newframes, Newframebytes ; | |
116 int Newframeshift ; | |
117 | |
118 int start, length ; /* Size of input file (num cols) */ | |
119 int frameslength ; /* Length (num cols) of input file to process */ | |
120 | |
121 int zeroes ; | |
122 int window ; /* Flag for Hamming window */ | |
123 float scale ; /* Scale factor for output */ | |
124 | |
125 short *file ; /* Input file (NAP format) */ | |
126 float *W ; /* Hamming window for fft */ | |
127 float *buf ; /* fft working space */ | |
128 | |
129 | |
130 main(argc, argv) | |
131 int argc ; | |
132 char *argv[] ; | |
133 { | |
134 FILE *fp ; | |
135 char *header, *SaiHeader(); | |
136 char *versionstr, c ; | |
137 int startbytes, framebyteshift ; | |
138 short *frame, *endframe ; | |
139 int i, a, b ; | |
140 | |
141 fp = openopts( option,argc,argv ) ; | |
142 if ( !isoff( helpstr ) ) | |
143 helpopts( helpstr, argv[0], applic, option ) ; | |
144 | |
145 window = ison( wstr ) ; | |
146 | |
147 | |
148 /**** parse bounds on number of frames ****/ | |
149 | |
150 if ( selector( framestr, &a, &b ) == 0 ) { | |
151 fprintf(stderr,"ftgram: bad frame selector [%s]\n", framestr ) ; | |
152 exit( 1 ) ; | |
153 } | |
154 | |
155 | |
156 /**** Input frame dimensions (from header and options) ****/ | |
157 | |
158 if ((header = ReadHeader(fp)) == (char *) 0 ) { | |
159 fprintf(stderr,"ftgram: header not found\n"); | |
160 exit(1); | |
161 } | |
162 | |
163 if ( (versionstr = HeaderString( header, "Version" )) == (char *)0 ) { | |
164 fprintf(stderr,"ftgram: model version-number not found in header\n"); | |
165 exit(1); | |
166 } | |
167 | |
168 samplerate = HeaderSamplerate( header ); | |
169 | |
170 /* In NAP format, the size of the 2-D image is given by: */ | |
171 /* frameheight = number of rows (filterbank channels) */ | |
172 /* frames = number of columns (sample points) */ | |
173 | |
174 frameheight = HeaderInt( header, "frameheight" ); | |
175 frames = HeaderInt( header, "frames" ); | |
176 | |
177 if ( ( frameshift = to_p( shiftstr, samplerate ) ) <= 0 ) { | |
178 fprintf(stderr,"ftgram: non-positive frstep [%d]\n", frameshift); | |
179 exit(1); | |
180 } | |
181 | |
182 /* calculate start of first frame (in cols) */ | |
183 | |
184 if ( ( start = to_p( startstr, samplerate ) + ( a-1 ) * frameshift ) >= frames ) { | |
185 fprintf(stderr,"ftgram: input file too small (%dms) for given framing parameters\n", frames * 1000 / samplerate ); | |
186 exit(1); | |
187 } | |
188 | |
189 /* get length of input file (in cols) and framewidth of input frames */ | |
190 | |
191 if ( ismax( lengthstr ) ) length = frames - start ; | |
192 else length = to_p( lengthstr, samplerate ) - start ; | |
193 | |
194 if ( ismax( widthstr ) ) framewidth = length ; | |
195 else framewidth = to_p( widthstr, samplerate ) ; | |
196 | |
197 if ( length < framewidth ) { | |
198 fprintf(stderr,"ftgram: input file too small (%dms) for given framing parameters\n", frames * 1000 / samplerate ); | |
199 exit(1); | |
200 } | |
201 | |
202 /* calculate length (num cols) to process, and the number of frames */ | |
203 | |
204 if ( b==0 ) { | |
205 frameslength = length ; | |
206 Newframes = 1 + ( length - framewidth ) / frameshift ; | |
207 } | |
208 else { | |
209 frameslength = framewidth + ( b-a ) * frameshift ; | |
210 Newframes = b - ( a-1 ) ; | |
211 } | |
212 | |
213 if ( start + frameslength > frames ) { | |
214 fprintf(stderr,"ftgram: input file too small (%dms) for requested start and length \n", frames * 1000 / samplerate ); | |
215 exit(1); | |
216 } | |
217 | |
218 framebytes = frameheight * frameslength * sizeof(short) ; | |
219 startbytes = frameheight * start * sizeof(short) ; | |
220 | |
221 | |
222 /**** Output frame dimensions ****/ | |
223 | |
224 Newframeheight = frameheight ; | |
225 Newframewidth = ( framewidth + zeroes ) >> 1 ; /* spectral bins */ | |
226 Newframeshift = frameshift ; | |
227 Newframebytes = Newframeheight * Newframewidth * sizeof(short) ; | |
228 | |
229 | |
230 /**** Padding and output scale factor ****/ | |
231 | |
232 zeroes = ( getpower( framewidth << 1 ) << atoi( padstr ) ) - framewidth ; | |
233 scale = atof( scalestr ) ; | |
234 | |
235 | |
236 /**** Debug ****/ | |
237 | |
238 if ( ison( debugstr ) ) { | |
239 fprintf(stderr, "Input: frames=%d frameheight=%d frameshift=%d\n", frames, frameheight, frameshift ) ; | |
240 fprintf(stderr, "Sizes: start=%d length=%d framewidth=%d frameslength=%d\n", start, length, framewidth, frameslength ) ; | |
241 fprintf(stderr, "Output: zeroes=%d Newframewidth=%d Newframes=%d \n", zeroes, Newframewidth, Newframes ) ; | |
242 exit( 1 ) ; | |
243 } | |
244 | |
245 | |
246 /**** Allocate space (input file, window coeffs, and fft working space) ****/ | |
247 | |
248 if ( ( file = (short *)malloc( framebytes ) ) == NULL ) { | |
249 fprintf(stderr,"ftgram: malloc out of space\n"); | |
250 exit(1); | |
251 } | |
252 | |
253 if ( window ) | |
254 W = hamming( framewidth ) ; | |
255 | |
256 buf = (float *)malloc( ( framewidth + zeroes ) * sizeof(float) ); | |
257 | |
258 | |
259 /**** Write new sai header ****/ | |
260 | |
261 if ( ison( headerstr ) ) | |
262 WriteHeader( SaiHeader(header), stdout ); | |
263 | |
264 | |
265 /**** Seek to start of input file in blocks of framebytes ****/ | |
266 | |
267 for (i=framebytes ; i < startbytes ; i += framebytes) | |
268 if ( fread( file, framebytes, 1, fp ) == NULL ) { | |
269 fprintf(stderr,"ftgram: missing data after header\n"); | |
270 exit(1); | |
271 } | |
272 if ( (startbytes -= (i - framebytes)) > 0 ) | |
273 if ( fread( file, startbytes, 1, fp ) == NULL ) { | |
274 fprintf(stderr,"ftgram: missing data after header\n"); | |
275 exit(1); | |
276 } | |
277 | |
278 | |
279 /**** Read framebytes of i/p file ****/ | |
280 | |
281 if ( fread( file, framebytes, 1, fp ) == NULL ) { | |
282 fprintf(stderr,"ftgram: missing data after header\n"); | |
283 exit(1); | |
284 } | |
285 | |
286 | |
287 /**** Process frames ****/ | |
288 | |
289 framebyteshift = frameshift * frameheight ; | |
290 endframe = file + Newframes * framebyteshift ; | |
291 | |
292 for ( frame = file, i=1 ; frame < endframe ; frame += framebyteshift, i++ ) { | |
293 fprintf(stderr,"ftgram: %d / %d [%dms]\n", i, Newframes, start*1000/samplerate ); | |
294 ftgram( frame ) ; | |
295 start += frameshift ; | |
296 } | |
297 | |
298 fclose(fp); | |
299 fprintf(stderr,"ftgram: done\n" ) ; | |
300 } | |
301 | |
302 | |
303 | |
304 /********************************** FFT ************************************/ | |
305 | |
306 /* Call fft and magnitude spectrum for each row in auditory image */ | |
307 | |
308 ftgram( frame ) | |
309 short *frame ; | |
310 { | |
311 register int i, j, row, col ; | |
312 | |
313 for ( row=0 ; row < frameheight ; row++ ) { | |
314 | |
315 if ( window ) | |
316 for ( col=0 , i=row ; col < framewidth ; col++, i+=frameheight ) | |
317 buf[col] = frame[i] * W[col] ; | |
318 else | |
319 for ( col=0 , i=row ; col < framewidth ; col++, i+=frameheight ) | |
320 buf[col] = frame[i] ; | |
321 | |
322 for ( j=0 ; j<zeroes ; j++ ) /* padding */ | |
323 buf[col++] = 0 ; | |
324 | |
325 fft( buf, framewidth+zeroes, 1 ) ; | |
326 mag( buf, framewidth+zeroes ) ; | |
327 | |
328 writebuf( buf, Newframewidth, scale ) ; | |
329 } | |
330 } | |
331 | |
332 | |
333 /********************** Write o/p ****************************************/ | |
334 | |
335 writebuf( buf, n, scale ) /* write buffer as scaled shorts */ | |
336 float *buf ; | |
337 int n ; | |
338 float scale ; | |
339 { | |
340 register int i ; | |
341 short p ; | |
342 | |
343 for (i=0 ; i < n ; i++) { | |
344 p = (short)( buf[i] * scale ) ; | |
345 fwrite( &p, sizeof(short), 1, stdout ) ; | |
346 } | |
347 } | |
348 | |
349 | |
350 /************************ Output header ************************************/ | |
351 /* | |
352 Copy the original nap header to a new sai header, changing in order: | |
353 frames | |
354 frameshift | |
355 framewidth | |
356 frameheight | |
357 framebytes | |
358 animate | |
359 display | |
360 Finally, update the new header_bytes, and return the new header. | |
361 */ | |
362 | |
363 char *SaiHeader( napheader ) | |
364 char *napheader ; | |
365 { | |
366 char *saiheader; | |
367 char *p0, *p1, *p2, *s, str[64]; | |
368 | |
369 saiheader = (char *)malloc( strlen(napheader) + 64 ) ; | |
370 | |
371 p0 = saiheader ; | |
372 p1 = napheader ; | |
373 | |
374 | |
375 /** copy up to frames **/ | |
376 | |
377 p2 = HeaderString( napheader , "frames" ) ; | |
378 while( p1 < p2 ) | |
379 *p0++ = *p1++ ; | |
380 | |
381 sprintf(str,"%d\n", Newframes); | |
382 for (s = str ; *s != '\n' ; ) | |
383 *p0++ = *s++; | |
384 *p0++ = *s; | |
385 while (*p1 != '\n') | |
386 *p1++; | |
387 *p1++; | |
388 | |
389 | |
390 /** copy up to frameshift **/ | |
391 | |
392 p2 = HeaderString( napheader , "frameshift" ) ; | |
393 while ( p1 < p2 ) | |
394 *p0++ = *p1++ ; | |
395 | |
396 sprintf(str,"%d\n", Newframeshift); | |
397 for (s = str ; *s != '\n' ; ) | |
398 *p0++ = *s++; | |
399 *p0++ = *s; | |
400 while (*p1 != '\n') | |
401 *p1++; | |
402 *p1++; | |
403 | |
404 | |
405 /** copy up to framewidth **/ | |
406 | |
407 p2 = HeaderString( napheader , "framewidth" ) ; | |
408 while ( p1 < p2 ) | |
409 *p0++ = *p1++ ; | |
410 | |
411 sprintf(str,"%d\n", Newframewidth); | |
412 for (s = str ; *s != '\n' ; ) | |
413 *p0++ = *s++; | |
414 *p0++ = *s; | |
415 while (*p1 != '\n') | |
416 *p1++; | |
417 *p1++; | |
418 | |
419 | |
420 /** copy up to frameheight **/ | |
421 | |
422 p2 = HeaderString( napheader , "frameheight" ) ; | |
423 while ( p1 < p2 ) | |
424 *p0++ = *p1++ ; | |
425 | |
426 sprintf(str,"%d\n", Newframeheight); | |
427 for (s = str ; *s != '\n' ; ) | |
428 *p0++ = *s++; | |
429 *p0++ = *s; | |
430 while (*p1 != '\n') | |
431 *p1++; | |
432 *p1++; | |
433 | |
434 | |
435 /** copy up to framebytes **/ | |
436 | |
437 p2 = HeaderString( napheader , "framebytes" ) ; | |
438 while ( p1 < p2 ) | |
439 *p0++ = *p1++ ; | |
440 | |
441 sprintf(str,"%d\n", Newframebytes); | |
442 for (s = str ; *s != '\n' ; ) | |
443 *p0++ = *s++; | |
444 *p0++ = *s; | |
445 while (*p1 != '\n') | |
446 *p1++; | |
447 *p1++; | |
448 | |
449 | |
450 /** copy up to animate_ctn **/ | |
451 | |
452 p2 = HeaderString( napheader , "animate_ctn" ) ; | |
453 while ( p1 < p2 ) | |
454 *p0++ = *p1++ ; | |
455 | |
456 sprintf(str,"%s\n", anistr ); | |
457 for (s = str ; *s != '\n' ; ) | |
458 *p0++ = *s++; | |
459 *p0++ = *s; | |
460 while (*p1 != '\n') | |
461 *p1++; | |
462 *p1++; | |
463 | |
464 | |
465 /** copy up to display **/ | |
466 | |
467 p2 = HeaderString( napheader , "display" ) ; | |
468 while ( p1 < p2 ) | |
469 *p0++ = *p1++ ; | |
470 | |
471 sprintf(str,"%s\n", dispstr ); | |
472 for (s = str ; *s != '\n' ; ) | |
473 *p0++ = *s++; | |
474 *p0++ = *s; | |
475 while (*p1 != '\n') | |
476 *p1++; | |
477 *p1++; | |
478 | |
479 | |
480 /** copy rest of header **/ | |
481 | |
482 p2 = HeaderString( napheader , "Version" ) ; | |
483 while ( p1 < p2 ) | |
484 *p0++ = *p1++ ; | |
485 | |
486 while (*p1 != '\n') | |
487 *p0++ = *p1++ ; | |
488 *p0++ = *p1++ ; | |
489 | |
490 | |
491 /** update header_bytes **/ | |
492 | |
493 sprintf(str, "%0*d", 7, p0-saiheader); | |
494 p0 = HeaderString( saiheader , "header_bytes" ) ; | |
495 s = str; | |
496 while(*p0 != '\n') | |
497 *p0++ = *s++ ; | |
498 | |
499 | |
500 return saiheader; | |
501 } | |
502 | |
503 |