Mercurial > hg > aim92
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/ftgram.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,503 @@ +/* + 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; +} + +