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