Mercurial > hg > aim92
view 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 |
line wrap: on
line source
/* ftgram.c Row-wise fft program by Michael Allerhand. ---------- Short-time Fourier transform applied to each row of input frames. Output frames consist of row-wise magnitude spectra, computed using fft. Input/output: Read a header and frame in NAP format. Write a header, (constructed from the original input header), and a succession of frames in SAI format. The input file is interpreted as one large frame in NAP format to be divided into input frames, each to be processed and output in SAI format. The options "start" and "length" specify the input file. The special option value length=max specifies all the input file from the given start to its end. The options "width" and "frstep" specify the input frames. The width option is the framewidth of each input frame) and the frstep option is the frameshift between input frames in the input file. The special option value width=max specifies the input framewidth as equal to the given input file length, (and if this is also "max", then the input framewidth is the remainder of the input file). Most options in the input header are copied to the output header. This enables options which are needed for the eventual display to pass straight through. Some options are set so that they can override the input header. For example, the display option is set on to enable display even when input has display=off. The animate option can be set on even when the input has animate=off. Parts of the header are changed for the new sai format: (frames, frameshift, framewidth, frameheight, framebytes). Padding: The output framewidth is the next power-of-2 above twice the input framewidth. The rows of each input frame are padded with zeroes so that the input framewidth is at least twice its original size, and also a power of 2. This is because the fft method is "radix-2", and because each output spectrum is symmetrical so that just half the points are unique. It is advisable to pad input frames for the additional reason that padding counters frame end-effects. Therefore each row of each input frame is padded with zeroes to the next power of 2 larger than the original input framewidth. If necessary, extra padding can be enforced using the padding option to add extra zeroes, padding to a larger power of 2. The amount of extra padding is "exponential" since if the basic padded framesize is N, and the padding option is n, then extra padding is to: N * 2**n. (n=0 by default, so that N is not changed. When n=1 then N is doubled, and when n=2 then N is quadrupled, etc.). The output framewidth (and hence the frequency resolution) depends upon the input framewidth and the padding. Examples: 1. ftgram of a NAP, animated, with framewidth 16ms gennap len=128ms output=stdout display=off file | ftgram width=16ms anim=on > file.sai gensai useprev=on headr=5 top=1000 file -(for landscape plot) genspl useprev=on headr=5 top=1000 pensize=2 file -(for spiral plot) 2. ftgram of an SAI: (Note that gensai removes file.sai, so you must use some other name, eg foo.sai). gensai len=64 pwidth=64 nwidth=0 output=stdout display=off file | \ saitonap frame=3 | ftgram width=32ms frame=1 > foo.sai gensai useprev=on top=1000 headr=5 mag=2 foo */ #include <stdio.h> #include <math.h> #include "header.h" #include "options.h" #include "units.h" #include "strmatch.h" #include "sigproc.h" char applic[] = "Autocorrelogram auditory image." ; static char *helpstr , *debugstr, *dispstr , *anistr , *startstr ; static char *lengthstr, *widthstr, *shiftstr, *headerstr, *framestr ; static char *wstr , *scalestr, *padstr ; static Options option[] = { { "help" , "off" , &helpstr , "help" , DEBUG }, { "debug" , "off" , &debugstr , "debugging switch" , DEBUG }, { "display" , "on" , &dispstr , "Display output image." , SETFLAG }, { "animate" , "off" , &anistr , "Animate cartoon." , SETFLAG }, { "frames" , "1-max" , &framestr , "Select frames inclusively" , VAL }, { "header" , "on" , &headerstr , "Header (for gensai useprevious=on)." , VAL }, { "start" , "0" , &startstr , "Start point in i/p file." , VAL }, { "length" , "max" , &lengthstr , "Length of i/p file to process." , VAL }, { "frstep" , "16ms" , &shiftstr , "Step between input frames." , VAL }, { "width" , "32ms" , &widthstr , "Width of input frames." , VAL }, { "padding" , "0" , &padstr , "Exponential frame padding" , SVAL }, { "window" , "on" , &wstr , "Hamming window" , SETFLAG }, { "scale" , "0.08" , &scalestr , "Scale factor for output" , SVAL }, ( char * ) 0 } ; int samplerate ; int frameheight, framewidth ; int frames, framebytes ; int frameshift ; int Newframeheight, Newframewidth ; int Newframes, Newframebytes ; int Newframeshift ; int start, length ; /* Size of input file (num cols) */ int frameslength ; /* Length (num cols) of input file to process */ int zeroes ; int window ; /* Flag for Hamming window */ float scale ; /* Scale factor for output */ short *file ; /* Input file (NAP format) */ float *W ; /* Hamming window for fft */ float *buf ; /* fft working space */ main(argc, argv) int argc ; char *argv[] ; { FILE *fp ; char *header, *SaiHeader(); char *versionstr, c ; int startbytes, framebyteshift ; short *frame, *endframe ; int i, a, b ; fp = openopts( option,argc,argv ) ; if ( !isoff( helpstr ) ) helpopts( helpstr, argv[0], applic, option ) ; window = ison( wstr ) ; /**** parse bounds on number of frames ****/ if ( selector( framestr, &a, &b ) == 0 ) { fprintf(stderr,"ftgram: bad frame selector [%s]\n", framestr ) ; exit( 1 ) ; } /**** Input frame dimensions (from header and options) ****/ if ((header = ReadHeader(fp)) == (char *) 0 ) { fprintf(stderr,"ftgram: header not found\n"); exit(1); } if ( (versionstr = HeaderString( header, "Version" )) == (char *)0 ) { fprintf(stderr,"ftgram: model version-number not found in header\n"); exit(1); } samplerate = HeaderSamplerate( header ); /* In NAP format, the size of the 2-D image is given by: */ /* frameheight = number of rows (filterbank channels) */ /* frames = number of columns (sample points) */ frameheight = HeaderInt( header, "frameheight" ); frames = HeaderInt( header, "frames" ); if ( ( frameshift = to_p( shiftstr, samplerate ) ) <= 0 ) { fprintf(stderr,"ftgram: non-positive frstep [%d]\n", frameshift); exit(1); } /* calculate start of first frame (in cols) */ if ( ( start = to_p( startstr, samplerate ) + ( a-1 ) * frameshift ) >= frames ) { fprintf(stderr,"ftgram: input file too small (%dms) for given framing parameters\n", frames * 1000 / samplerate ); exit(1); } /* get length of input file (in cols) and framewidth of input frames */ if ( ismax( lengthstr ) ) length = frames - start ; else length = to_p( lengthstr, samplerate ) - start ; if ( ismax( widthstr ) ) framewidth = length ; else framewidth = to_p( widthstr, samplerate ) ; if ( length < framewidth ) { fprintf(stderr,"ftgram: input file too small (%dms) for given framing parameters\n", frames * 1000 / samplerate ); exit(1); } /* calculate length (num cols) to process, and the number of frames */ if ( b==0 ) { frameslength = length ; Newframes = 1 + ( length - framewidth ) / frameshift ; } else { frameslength = framewidth + ( b-a ) * frameshift ; Newframes = b - ( a-1 ) ; } if ( start + frameslength > frames ) { fprintf(stderr,"ftgram: input file too small (%dms) for requested start and length \n", frames * 1000 / samplerate ); exit(1); } framebytes = frameheight * frameslength * sizeof(short) ; startbytes = frameheight * start * sizeof(short) ; /**** Output frame dimensions ****/ Newframeheight = frameheight ; Newframewidth = ( framewidth + zeroes ) >> 1 ; /* spectral bins */ Newframeshift = frameshift ; Newframebytes = Newframeheight * Newframewidth * sizeof(short) ; /**** Padding and output scale factor ****/ zeroes = ( getpower( framewidth << 1 ) << atoi( padstr ) ) - framewidth ; scale = atof( scalestr ) ; /**** Debug ****/ if ( ison( debugstr ) ) { fprintf(stderr, "Input: frames=%d frameheight=%d frameshift=%d\n", frames, frameheight, frameshift ) ; fprintf(stderr, "Sizes: start=%d length=%d framewidth=%d frameslength=%d\n", start, length, framewidth, frameslength ) ; fprintf(stderr, "Output: zeroes=%d Newframewidth=%d Newframes=%d \n", zeroes, Newframewidth, Newframes ) ; exit( 1 ) ; } /**** Allocate space (input file, window coeffs, and fft working space) ****/ if ( ( file = (short *)malloc( framebytes ) ) == NULL ) { fprintf(stderr,"ftgram: malloc out of space\n"); exit(1); } if ( window ) W = hamming( framewidth ) ; buf = (float *)malloc( ( framewidth + zeroes ) * sizeof(float) ); /**** Write new sai header ****/ if ( ison( headerstr ) ) WriteHeader( SaiHeader(header), stdout ); /**** Seek to start of input file in blocks of framebytes ****/ for (i=framebytes ; i < startbytes ; i += framebytes) if ( fread( file, framebytes, 1, fp ) == NULL ) { fprintf(stderr,"ftgram: missing data after header\n"); exit(1); } if ( (startbytes -= (i - framebytes)) > 0 ) if ( fread( file, startbytes, 1, fp ) == NULL ) { fprintf(stderr,"ftgram: missing data after header\n"); exit(1); } /**** Read framebytes of i/p file ****/ if ( fread( file, framebytes, 1, fp ) == NULL ) { fprintf(stderr,"ftgram: missing data after header\n"); exit(1); } /**** Process frames ****/ framebyteshift = frameshift * frameheight ; endframe = file + Newframes * framebyteshift ; for ( frame = file, i=1 ; frame < endframe ; frame += framebyteshift, i++ ) { fprintf(stderr,"ftgram: %d / %d [%dms]\n", i, Newframes, start*1000/samplerate ); ftgram( frame ) ; start += frameshift ; } fclose(fp); fprintf(stderr,"ftgram: done\n" ) ; } /********************************** FFT ************************************/ /* Call fft and magnitude spectrum for each row in auditory image */ ftgram( frame ) short *frame ; { register int i, j, row, col ; for ( row=0 ; row < frameheight ; row++ ) { if ( window ) for ( col=0 , i=row ; col < framewidth ; col++, i+=frameheight ) buf[col] = frame[i] * W[col] ; else for ( col=0 , i=row ; col < framewidth ; col++, i+=frameheight ) buf[col] = frame[i] ; for ( j=0 ; j<zeroes ; j++ ) /* padding */ buf[col++] = 0 ; fft( buf, framewidth+zeroes, 1 ) ; mag( buf, framewidth+zeroes ) ; writebuf( buf, Newframewidth, scale ) ; } } /********************** Write o/p ****************************************/ writebuf( buf, n, scale ) /* write buffer as scaled shorts */ float *buf ; int n ; float scale ; { register int i ; short p ; for (i=0 ; i < n ; i++) { p = (short)( buf[i] * scale ) ; fwrite( &p, sizeof(short), 1, stdout ) ; } } /************************ Output header ************************************/ /* Copy the original nap header to a new sai header, changing in order: frames frameshift framewidth frameheight framebytes animate display Finally, update the new header_bytes, and return the new header. */ char *SaiHeader( napheader ) char *napheader ; { char *saiheader; char *p0, *p1, *p2, *s, str[64]; saiheader = (char *)malloc( strlen(napheader) + 64 ) ; p0 = saiheader ; p1 = napheader ; /** copy up to frames **/ p2 = HeaderString( napheader , "frames" ) ; while( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%d\n", Newframes); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to frameshift **/ p2 = HeaderString( napheader , "frameshift" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%d\n", Newframeshift); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to framewidth **/ p2 = HeaderString( napheader , "framewidth" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%d\n", Newframewidth); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to frameheight **/ p2 = HeaderString( napheader , "frameheight" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%d\n", Newframeheight); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to framebytes **/ p2 = HeaderString( napheader , "framebytes" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%d\n", Newframebytes); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to animate_ctn **/ p2 = HeaderString( napheader , "animate_ctn" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%s\n", anistr ); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy up to display **/ p2 = HeaderString( napheader , "display" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; sprintf(str,"%s\n", dispstr ); for (s = str ; *s != '\n' ; ) *p0++ = *s++; *p0++ = *s; while (*p1 != '\n') *p1++; *p1++; /** copy rest of header **/ p2 = HeaderString( napheader , "Version" ) ; while ( p1 < p2 ) *p0++ = *p1++ ; while (*p1 != '\n') *p0++ = *p1++ ; *p0++ = *p1++ ; /** update header_bytes **/ sprintf(str, "%0*d", 7, p0-saiheader); p0 = HeaderString( saiheader , "header_bytes" ) ; s = str; while(*p0 != '\n') *p0++ = *s++ ; return saiheader; }