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