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