diff tools/audim.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/audim.c	Fri May 20 15:19:45 2011 +0100
@@ -0,0 +1,973 @@
+/*
+  audim.c           auditory images
+ ---------
+
+   This program provides methods of constructing time-varying auditory
+   images from the output of the cochlear model which are alternatives to
+   the gensai (stabilized auditory image) program. Correlograms (ie. row-
+   wise autocorrelation) and row-wise Fourier transform are provided.  The
+   program reads an AIM header and the output from the genbmm (basilar mem-
+   brane motion) or gennap (neural activity pattern) programs. This is
+   divided into contiguous time frames and written on the stdout with an
+   appropriate header as if output from the gensai program. This enables
+   genbmm or gennap output to be divided into time frames and replayed as a
+   cartoon by gensai using the "useprevious" option.  Additional processing
+   to each frame is optionally available to compute alternative forms of
+   auditory image according to the "image" option.
+
+
+   The options "start" and "length" specify the size of the input.  The
+   options "width" and "frstep"  specify the frames which are output.  The
+   input is divided into frames according to the width option and the
+   frstep option.
+
+   Special option values are:
+
+     length=max      Read all the input from the given start to its end.
+     width=max       Output framewidth is set equal to the given input
+	       length, and if this is also "max", then the
+	       framewidth is the remainder of the input.
+     frames=1-max    Select the 1st to the last frame inclusively.
+     frame=4         Select the 4th frame only.
+
+     image=off       Divide the input into frames for replay as a cartoon.
+     image=acgram    Compute correlogram of each frame.
+     image=ftgram    Compute power spectrum of each channel of each frame.
+     image=phgram    Compute phase spectrum of each channel of each frame.
+
+   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 input nap is divided into frames according to the width option (called
+  framewidth internally) and the frstep option (called frameshift internally).
+  The framewidth is used as the basis for output image frame width:
+  the output width is actually the next power-of-2 above twice the framewidth.
+  This is because:
+  a. fft and acf output is always half the given frame size, due to symmetry,
+     so the input must be padded to twice its width.
+  b. To use fft, the input must also be a power of two.
+  So, each input row is padded out to the next power of two above twice its
+  width.
+
+  Certain display parameters have different default values for different
+  applications. The gensai display parameters should be set to the
+  appropriate values, in order to plot the cartoon on the same scale. For
+  example: when the source application is gennap, set gensai top=1000,
+  when the source application is genbmm, set gensai bottom=-100.
+
+
+Examples:
+
+1. An animated normalized autocorrelogram from a nap:
+
+gennap len=64ms output=stdout display=off file | audim norm=on anim=on > file.sai
+gensai useprev=on top=1000 cegc             -(for landscape plot)
+genspl useprev=on top=1000 pensize=2 cegc   -(for spiral plot)
+
+
+2. To convert a nap to multiple animated frames:
+
+gennap len=16ms display=off output=stdout file | audim image=off width=8ms frstep=0.2ms anim=on > file.sai
+gensai useprev=on top=1000 cegc             -(for landscape plot)
+genspl useprev=on top=1000 pensize=2 cegc   -(for spiral plot)
+
+    (Note: spirals look better in a square box, so you might use options:
+	   dencf=1 width=500 height=500 )
+
+3. To view the basilar membrane from a cross section, animating the waves on it.
+
+genbmm mincf=220 maxcf=660 len=8ms output=stdout display=off file | audim image=off width=1p frstep=1p display=on anim=on | edframe Tran=on > foo.sai
+gensai bott=-100 useprev=on mag=.2 foo
+
+or:
+
+genbmm mincf=220 maxcf=660 len=32ms output=stdout display=off file | \
+audim image=off width=1p frstep=1p display=on anim=on | \
+edframe Tran=on Header=off > foo
+
+x11play -n75 -a500 foo
+
+*/
+
+
+#include <stdio.h>
+#include <math.h>
+#include "header.h"
+#include "options.h"
+#include "units.h"
+#include "strmatch.h"
+#include "sigproc.h"
+#include "freqs.c"
+
+char applic[]     = "Auditory images of neural activity patterns (NAP). " ;
+
+static char *helpstr, *debugstr, *dispstr, *anistr, *startstr, *imstr ;
+static char *lengthstr, *widthstr, *shiftstr, *headerstr, *cfstr, *framestr ;
+static char *normstr, *wstr, *sacfstr, *smagstr, *sphstr ;
+static char *blstr, *erbstr ;
+
+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     },
+    {   "image"     ,   "acgram"    ,  &imstr       ,   "Form of auditory image."                    , VAL     },
+    {   "header"    ,   "on"        ,  &headerstr   ,   "Header (for gensai useprevious=on)."        , VAL     },
+    {   "start"     ,   "0"         ,  &startstr    ,   "Start point in i/p NAP"                     , VAL     },
+    {   "length"    ,   "max"       ,  &lengthstr   ,   "Length of i/p NAP to process."              , VAL     },
+    {   "frstep"    ,   "16ms"      ,  &shiftstr    ,   "Step between frames of the auditory image." , VAL     },
+    {   "width"     ,   "32ms"      ,  &widthstr    ,   "Width of auditory image."                   , VAL     },
+    {   "normalize" ,   "off"       ,  &normstr     ,   "Normalization"                              , SETFLAG },
+    {   "window"    ,   "off"       ,  &wstr        ,   "Hamming window"                             , SETFLAG },
+    {   "bandlimit" ,   "off"       ,  &blstr       ,   "Zero outside bandwidth."                    , SETFLAG },
+    {   "erbs"      ,   "2"         ,  &erbstr      ,   "Width (in ERBS) of band either side of cf." , SVAL    },
+    {   "align_cf"  ,   "off"       ,  &cfstr       ,   "Align on centre frequency."                 , SETFLAG },
+    {   "scale_acf" ,   "0.025"     ,  &sacfstr     ,   "Scale factor for acf output"                , SVAL    },
+    {   "scale_fft" ,   "0.08"      ,  &smagstr     ,   "Scale factor for fft magnitude output"      , SVAL    },
+    {   "scale_phs" ,   "100"       ,  &sphstr      ,   "Scale factor for fft phase output"          , SVAL    },
+   ( char * ) 0 } ;
+
+
+int     frameheight ;   /* Nap parameters read from header */
+int     frames, samplerate ;
+int     framewidth  ;   /* width option */
+
+int     start, length ;
+
+int     Newframeheight, Newframewidth ; /* Sai parameters to write  */
+int     Newframes ;
+int     Newframeshift ;
+int     Newframebytes ;
+
+
+char   *limitstr ;      /* Sai parameters used to get centre-frequencies */
+char   *qualstr  ;
+char   *minstr   ;
+char   *maxstr   ;
+char   *denstr   ;
+char   *chansstr ;
+double *frequencies ;
+int     nerbs ;         /* bandwidth defined as number of erbs either side of cf */
+int     bandlimit ;     /* flag for zeroing outside bandwidth around centre-freqs */
+int     align ;         /* flag for aligning centre-freqs */
+
+#define NONE    0       /* forms of auditory image */
+#define ACGRAM  1
+#define FTGRAM  2
+#define PHGRAM  3
+int     image ;
+
+float  *buf ;           /* fft working space */
+int     zeroes ;        /* padding */
+float  *W ;             /* Hamming window for fft */
+short   window ;        /* flag for window */
+
+short   debug ;         /* flag for debug printf's */
+
+main(argc, argv)
+int   argc ;
+char *argv[] ;
+{
+    FILE   *fp ;
+    int     i, framebytes, startbytes, frameshift, framebyteshift;
+    char   *header, *SaiHeader();
+    short  *nap, *frame, *endframe;
+    int     a, b ;
+    char   *val1, *val2 ;
+
+    fp = openopts( option,argc,argv ) ;
+    if ( !isoff( helpstr ) )
+	helpopts( helpstr, argv[0], applic, option ) ;
+
+    window = ison( wstr ) ;
+    nerbs  = atoi( erbstr ) ;
+    debug  = ison( debugstr ) ;
+
+    /**** Form of image ****/
+
+    if ( iststr( imstr, "acgram" ) )  {
+	image = ACGRAM ;
+    }
+    else if ( iststr( imstr, "ftgram" ) )  {
+	image = FTGRAM ;
+    }
+    else if ( iststr( imstr, "phgram" ) )  {
+	image = PHGRAM ;
+    }
+    else if ( isoff( imstr ) ) {
+	image = NONE ;
+    }
+    else {
+	fprintf(stderr, "audim: unknown form of auditory image [%s]\n", imstr ) ;
+	exit( 1 ) ;
+    }
+
+    align     = ison( cfstr ) ;
+    bandlimit = ison( blstr ) ;
+
+
+    /**** parse bounds on number of frames ****/
+
+    if ( getvals( framestr, &val1, &val2, "-" ) == BADVAL ) {
+	fprintf(stderr,"audim: bad frame selector [%s]\n", framestr ) ;
+	exit( 1 ) ;
+    }
+    a = atoi( val1 ) ;
+    if ( isempty( val2 ) )    b = a ;
+    else if ( ismax( val2 ) ) b = 0 ;
+    else                      b = atoi( val2 ) ;
+
+    if (b<a && b>0) fprintf(stderr,"audim warning: bad frame specifiers\n");
+
+
+    /**** Read header and options to find dimensions of auditory image ****/
+
+    if ( (header = ReadHeader(fp)) == (char *) 0 ) {
+	fprintf(stderr,"audim: header not found\n");
+	exit(1);
+    }
+
+    frameheight = HeaderInt( header, "frameheight" );
+    frames      = HeaderInt( header, "frames"      );
+    samplerate  = HeaderSamplerate( header );
+    start       = to_p( startstr,  samplerate );
+    frameshift  = to_p( shiftstr, samplerate ) ;
+
+    if ( ismax( lengthstr ) )   /* Special case for remainder of input */
+	length = frames - start ;
+    else
+	length = to_p( lengthstr, samplerate );
+
+    if ( start + length > frames ) {
+	fprintf(stderr,"audim: nap too small (%d ms) for requested start and length \n", to_ms( frames, samplerate ) );
+	exit(1);
+    }
+
+    framebytes = frameheight * length * sizeof(short) ;
+    startbytes = frameheight * start  * sizeof(short) ;
+
+    if ( ismax( widthstr ) )    /* Special case for single frame of max width */
+	framewidth = length ;
+    else
+	framewidth = to_p( widthstr, samplerate ) ;
+
+    frames = ( length - framewidth ) / frameshift ;
+    zeroes = getpower( framewidth << 1 ) - framewidth ;
+
+
+    /**** Get centre-frequencies information using info given in header ****/
+
+    limitstr =    HeaderStringOnly( header, "bwmin_afb"    );
+    qualstr =     HeaderStringOnly( header, "quality_afb"  );
+    minstr =      HeaderStringOnly( header, "mincf_afb"    );
+    maxstr =      HeaderStringOnly( header, "maxcf_afb"    );
+    denstr =      HeaderStringOnly( header, "dencf_afb"    );
+    chansstr =    HeaderStringOnly( header, "channels_afb" );
+
+    SetErbParameters( to_Hz( limitstr ), atof( qualstr ) ) ;
+    if( OptionInt( chansstr ) == 0 )
+	frequencies = GenerateCenterFrequencies( to_Hz( minstr ), to_Hz( maxstr ), atof( denstr ) )   ;
+    else
+	frequencies = NumberedCenterFrequencies( to_Hz( minstr ), to_Hz( maxstr ), OptionInt( chansstr ) ) ;
+
+
+    /**** Output frame dimensions (to write into new header) ****/
+
+    Newframeheight = frameheight ;
+
+    if ( !align ) {
+	if ( image == NONE )
+	    Newframewidth = framewidth ;
+	else
+	    Newframewidth = ( framewidth + zeroes ) >> 1 ;  /* spectral bins */
+    }
+    else  {      /* number of bins corresponding to the filter bandwidth */
+	switch ( image ) {
+	    case NONE   :   Newframewidth = framewidth ;
+			    break ;
+	    case ACGRAM :   Newframewidth = maxlagwidth( to_Hz( minstr ), nerbs ) ;
+			    break ;
+	    case FTGRAM :   Newframewidth = maxbandwidth( to_Hz( maxstr ), nerbs, (framewidth+zeroes)>>1 ) ;
+			    break ;
+	}
+    }
+    Newframeshift =  frameshift ;
+    Newframebytes =  Newframeheight * Newframewidth * sizeof(short) ;
+
+    if ( frameshift > 0 ) {
+	if ( b == 0 )
+	    Newframes = frames - (a-1) ;
+	else
+	    Newframes = b - (a-1) ;
+    }
+    else {
+	fprintf(stderr,"audim: non-positive frstep [%d]\n", frameshift);
+	exit(1);
+    }
+
+
+    /**** Check limits etc.. ****/
+
+    if ( b > frames ) {
+	fprintf(stderr,"audim: can't select frame %d out of %d frames\n", b, frames ) ;
+	exit(1);
+    }
+
+    if ( length < framewidth ) {
+	fprintf(stderr,"audim: nap too small (%d ms) for requested width\n", to_ms( length, samplerate ) );
+	exit(1);
+    }
+
+    if (frames<=0) {
+	if (frames==0)  fprintf(stderr,"audim: zero frames input\n");
+	if (frames<0)   fprintf(stderr,"audim: garbled number of frames, (start set too big?)\n");
+	exit(1);
+    }
+
+    if (a>frames || b>frames)
+	fprintf(stderr,"audim warning: bad frame selectors\n");
+
+
+
+    if ( debug ) {
+	fprintf(stderr, "Header:  frames=%d frameheight=%d\n", frames, frameheight ) ;
+	fprintf(stderr, "Options: a=%d b=%d start=%d length=%d frameshift=%d framewidth=%d\n", a, b, start, length, frameshift, framewidth ) ;
+	fprintf(stderr, "Output:  zeroes=%d Newframewidth=%d Newframes=%d \n", zeroes, Newframewidth, Newframes ) ;
+    }
+
+
+    /**** Write new sai header ****/
+
+    if ( ison( headerstr ) )
+	WriteHeader( SaiHeader(header), stdout );
+
+
+    /**** Allocate space for framebytes of nap data ****/
+
+    if ( (nap = (short *)malloc( framebytes )) == NULL ) {
+	fprintf(stderr,"audim: malloc out of space\n");
+	exit(1);
+    }
+
+
+    /**** Allocate and assign window coeffs ****/
+
+    if ( window )
+	W = hamming( framewidth ) ;
+
+
+    /**** Allocate fft working space ****/
+
+    buf = (float *)malloc( ( framewidth + zeroes ) * sizeof(float) );
+
+
+    /**** Seek to start in blocks of framebytes ****/
+
+    for (i=framebytes ; i < startbytes ; i += framebytes)
+	if ( fread( nap, framebytes, 1, fp ) == NULL ) {
+	    fprintf(stderr,"audim: missing data after header\n");
+	    exit(1);
+	}
+    if ( (startbytes -= (i - framebytes)) > 0 )
+	if ( fread( nap, startbytes, 1, fp ) == NULL ) {
+	    fprintf(stderr,"audim: missing data after header\n");
+	    exit(1);
+	}
+
+    /**** Read framebytes of i/p nap data ****/
+
+    if ( fread( nap, framebytes, 1, fp ) == NULL ) {
+	fprintf(stderr,"audim: missing data after header\n");
+	exit(1);
+    }
+
+    framebyteshift = frameshift * frameheight ;
+    nap = nap + (a-1) * framebyteshift ;
+    endframe = nap + Newframes * framebyteshift ;
+
+    switch ( image ) {
+	case NONE   : fprintf(stderr,"Output framewidth = %dp \n", Newframewidth ) ;  break ;
+	case ACGRAM : fprintf(stderr,"Output framewidth = %dp Lag range = 0-%dms\n", Newframewidth, (Newframewidth*1000)/samplerate ) ; break ;
+	case FTGRAM : fprintf(stderr,"Output framewidth = %dp Frequency range = 0-%dkHz\n", Newframewidth, samplerate/2000 ) ; break ;
+	case PHGRAM : break ;
+    }
+
+    for ( frame = nap ; frame < endframe ; frame += framebyteshift ) {
+
+	fprintf(stderr,"audim: %d / %d\n", a++, frames );
+	switch ( image ) {
+
+	    case NONE   : writeframe( frame ) ; break ;
+	    case ACGRAM : acgram( frame ) ;     break ;
+	    case FTGRAM : ftgram( frame ) ;     break ;
+	    case PHGRAM : phgram( frame ) ;     break ;
+
+	}
+    }
+    fprintf(stderr,"audim: done\n" );
+}
+
+
+
+
+
+int OptionInt( str )
+char *str ;
+{
+    if( strcmp( str, "on" ) == 0 )
+	return( 1 ) ;
+    else if( strcmp( str, "Not_used" ) == 0 )
+	return( 0 ) ;
+    else
+	return( atoi( str ) ) ;
+}
+
+
+/*********************** Read header and build new 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) + 256 ) ;
+
+    p0 = saiheader ;
+    p1 = napheader ;
+
+    /* copy to first item after the header_bytes */
+
+    p2 = HeaderString( napheader , "header_bytes" ) ;
+    while( p1 < p2 )
+	*p0++ = *p1++ ;
+    while (*p1 != '\n')
+	*p0++ = *p1++ ;
+    *p0++ = *p1++;
+
+    /* insert frstep_aid at top of new header */
+
+    sprintf( str,"frstep_aid=%s\n", shiftstr ) ;
+    for (s = str ; *s != '\n' ; )
+	*p0++ = *s++;
+    *p0++ = *s;
+
+    /** 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++ ;
+
+    if ( ( p2 = ApplicString( napheader ) ) == (char *)0 )  {
+	fprintf(stderr,"audim: application name not found in header\n");
+	exit( 1 ) ;
+    }
+    while ( p1 < p2 )
+	*p0++ = *p1++ ;
+
+    *p0++ = 's' ; *p0++ = 'a' ; *p0++ = 'i' ;   /* copy sai into applic name */
+    p1 += 3 ;
+
+    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;
+}
+
+
+/************************** Autocorrelation function ***********************/
+
+/* Call acf for each row in auditory image */
+
+acgram( frame )
+short *frame ;
+{
+    static   float  scale ;
+    static   int    normalize ;
+    static   int    first = 1 ;
+    register int    i, j, row, col ;
+    short    p ;
+
+    if (first) {
+	normalize = ison( normstr ) ;
+	scale = 1./(double)( framewidth + zeroes ) * atof( sacfstr ) ;
+	first=0;
+    }
+
+    for ( row=0 ; row < frameheight ; row++ ) {
+	for ( col=0 , i=row ; col < framewidth ; col++, i+=frameheight )
+	    buf[col] = frame[i] ;
+	for ( j=0 ; j<zeroes ; j++ )    /* padding */
+	    buf[col++] = 0 ;
+
+	acf( buf, framewidth+zeroes ) ;
+
+	if ( align || bandlimit )
+	    writelags( buf, row, (framewidth+zeroes)>>1, Newframewidth, scale ) ;
+	else {
+	    if ( normalize == 0 )
+		writebuf( buf, Newframewidth, scale ) ;
+	    else
+		writebuf( buf, Newframewidth, 5000./buf[0] ) ;
+	}
+    }
+}
+
+
+/********************************** FFT ************************************/
+
+/* Call fft and magnitude spectrum for each row in auditory image */
+
+ftgram( frame )
+short *frame ;
+{
+    static   float  scale ;
+    static   int    first = 1 ;
+    register int    i, j, row, col ;
+
+    if (first) {
+	scale = atof( smagstr ) ;
+	first=0;
+    }
+
+    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 ) ;
+
+	if ( align || bandlimit )
+	    writebins( buf, row, (framewidth+zeroes)>>1, Newframewidth, scale ) ;
+	else
+	    writebuf( buf, Newframewidth, scale ) ;
+    }
+}
+
+
+/********************************** Phase **********************************/
+
+/* Call fft and phase spectrum for each row in auditory image */
+
+phgram( frame )
+short *frame ;
+{
+    static   float  scale ;
+    static   int    first = 1 ;
+    register int    i, j, row, col ;
+
+    if (first) {
+	scale = atof( sphstr ) ;
+	first=0;
+    }
+
+    for ( row=0 ; row < frameheight ; row++ ) {
+	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 ) ;
+	phase( buf, framewidth+zeroes ) ;
+
+	writebuf( buf, Newframewidth, scale ) ;
+    }
+}
+
+
+/**** Write o/p routines ****/
+
+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 ) ;
+    }
+}
+
+
+
+
+writeframe( frame )     /* Write a nap frame in sai format */
+short  *frame ;
+{
+    int    i, row, col;
+
+    for (row=0 ; row < frameheight ; row++)
+	for (col=0 , i=row ; col < framewidth ; col++, i+=frameheight)
+	    fwrite( &frame[i], sizeof(short), 1, stdout );
+}
+
+
+/*
+  Write a band of spectral bins around the centre-freq of the given channel.
+
+  The o/p spectrum is 0 to samplerate/2 Hz, resolved into spectral bins:
+	bins = (framewidth + zeroes) / 2
+
+  For the i'th channel, the centre-frequency cf = frequencies[i].
+  Filters are symmetrical bandpass, so considering a bandwidth of 4 ERB
+  the side-frequencies at the band limits are:
+
+    side1 = cf - 2*ERB     [Hz]
+    side2 = cf + 2*ERB     [Hz]
+
+  Note: side1 < side2
+
+  Convert frequency to bin number as follows:
+  Let centre-frequency cf have bin number j, so that ratios:
+	cf/(samplerate/2) = j/bins
+  and so:
+	j  = ( cf*bins ) / ( samplerate/2 )
+
+  (Similarly for each of the side-frequencies at the 2-ERB band limits).
+
+*/
+
+writebins( buf, chan, bins, maxwidth, scale )
+float *buf ;
+int    chan ;
+int    bins ;       /* number of spectral bins */
+int    maxwidth ;
+float  scale ;
+{
+    double ERB, cf, side1, side2, max ;
+    register int  i, j, s1, s2 ;
+    short         p ;
+
+    ERB   = frequencies[chan]/9.26449 + 24.7 ;
+    cf    = frequencies[chan]          ;
+    side1 = frequencies[chan] - nerbs*ERB  ;
+    side2 = frequencies[chan] + nerbs*ERB  ;
+
+    j  = (int)( ( cf    * bins ) / ( samplerate >> 1 ) + 0.5 ) ;   /* rounding */
+    s1 = (int)( ( side1 * bins ) / ( samplerate >> 1 ) + 0.5 ) ;
+    s2 = (int)( ( side2 * bins ) / ( samplerate >> 1 ) + 0.5 ) ;
+
+    if ( debug )
+	fprintf(stderr,"chan=%d bins=%d maxwidth=%d j=%d side1=%d side2=%d\n",chan,bins,maxwidth,j,s1,s2);
+
+    /*  Plot the given bandwidth, zeroing elsewhere:     */
+
+    if ( bandlimit && !align ) {
+
+	for ( i=0, p=0 ; i < s1 ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+
+	for (  ; i < s2 && i < bins ; i++ )  {
+	    p = (short)( buf[i] * scale ) ;
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	}
+
+	for ( p=0 ; i < bins ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+    }
+
+    /* Plot the given bandwidth, shifting it to the centre */
+
+    if ( align ) {
+
+/* align on max bin in band
+	max = 0 ;
+	for ( i=s1 ; i<s2 ; i++ )
+	    if ( buf[i] >= max ) {
+		max = buf[i] ;
+		j = i ;
+	    }
+	for ( i=0, p=0 ; i < s1+(maxwidth/2-j) ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	for ( j = s1 ; j < s2 && i < maxwidth ; i++, j++ )  {
+	    p = (short)( buf[j] * scale ) ;
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	}
+	for ( p=0 ; i < maxwidth ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+*/
+
+	j = (maxwidth+s1-s2)/2 ;
+	for ( i=0, p=0 ; i < j ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+
+	for ( j = s1 ; j < s2 ; i++, j++ )  {
+	    p = (short)( buf[j] * scale ) ;
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	}
+	for ( p=0 ; i < maxwidth ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+
+    }
+}
+
+
+
+/*
+  Write a band of acf lags around the centre-period of the given channel.
+
+  For the i'th channel, the centre-frequency
+	Centre-frequency   cf  = frequencies[i]     [Hz]
+	Bandwidth          ERB = cf/9.26449 + 24.7  [Hz]
+	Centre-period       cp = samplerate / cf    [samples]
+
+  Filters are symmetrical bandpass, so considering a bandwidth of 4 ERB
+
+    side1 = samplerate / ( cf - 2*ERB )    [samples]
+    side2 = samplerate / ( cf + 2*ERB )    [samples]
+
+  Note: side2 < side1 since periods
+
+  Also, successive bins ARE successive lags (in samples).
+*/
+
+
+
+writelags( buf, chan, lags, maxwidth, scale )
+float *buf ;
+int    chan ;
+int    lags ;       /* number of acf lags */
+int    maxwidth ;
+float  scale ;
+{
+    double ERB, cp, side1, side2 ;
+    register int  i, j, s1, s2 ;
+    short         p ;
+
+    ERB   = frequencies[chan]/9.26449 + 24.7 ;
+    cp    = (double)samplerate /   frequencies[chan]           ;
+    side1 = (double)samplerate / ( frequencies[chan] - nerbs*ERB ) ;
+    side2 = (double)samplerate / ( frequencies[chan] + nerbs*ERB ) ;
+
+    j  = (int)( cp    + 0.5 ) ;         /* rounding */
+    s1 = (int)( side1 + 0.5 ) ;
+    s2 = (int)( side2 + 0.5 ) ;
+
+    if ( debug )
+	fprintf(stderr,"chan=%d lags=%d side1=%d side2=%d\n",chan,lags,s1,s2);
+
+    /*  Plot the given bandwidth, zeroing elsewhere:     */
+
+    if ( bandlimit && !align ) {
+
+	for ( i=0, p=0 ; i < s2 ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+
+	for (  ; i < s1 && i < lags ; i++ )  {
+	    p = (short)( buf[i] * scale ) ;
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	}
+
+	for ( p=0 ; i < lags ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+    }
+
+    /* Plot the given bandwidth, shifting it to the centre */
+
+    if ( align ) {
+
+	j = (maxwidth+s2-s1)/2 ;
+	for ( i=0, p=0 ; i < j ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+
+	for ( j = s2 ; j < s1 ; i++, j++ )  {
+	    p = (short)( buf[j] * scale ) ;
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+	}
+	for ( p=0 ; i < maxwidth ; i++ )
+	    fwrite( &p, sizeof(short), 1, stdout ) ;
+    }
+}
+
+
+/*
+  Return the number of lags corresponding to a bandwidth of nerbs ERBS either
+  side of a given mincf. This will be the max required framewidth.
+*/
+
+maxlagwidth( mincf, nerbs )
+double mincf ;
+int    nerbs ;
+{
+    double ERB, side1, side2 ;
+
+    ERB   = mincf/9.26449 + 24.7 ;
+    side1 = (double)samplerate / ( mincf - nerbs*ERB ) ;
+    side2 = (double)samplerate / ( mincf + nerbs*ERB ) ;
+
+    return (int)( side1 - side2 ) ;
+}
+
+
+/*
+  Return the number of bins corresponding to a bandwidth of nerbs ERBS either
+  side of a given maxcf. This will be the max required framewidth.
+*/
+
+maxbandwidth( maxcf, nerbs, bins )
+double maxcf ;
+int    nerbs ;
+int    bins  ;
+{
+    double ERB, side1, side2 ;
+    int    s1, s2 ;
+
+    ERB   = maxcf/9.26449 + 24.7 ;
+    side1 = maxcf - nerbs*ERB  ;
+    side2 = maxcf + nerbs*ERB  ;
+
+    /* convert frequencies to bin numbers */
+
+    s1 = (int)( ( side1 * bins ) / ( samplerate >> 1 ) + 0.5 ) ;
+    s2 = (int)( ( side2 * bins ) / ( samplerate >> 1 ) + 0.5 ) ;
+
+    return (int)( s2 - s1 ) ;
+}
+
+