ftp.nice.ch/pub/next/unix/audio/cmusic.bs.N.tar.gz#/src/frm/spect.c

This is spect.c in view mode; [Download] [Up]

/*
 * Here is the source for a new version of spect.  It is very similar to the
 * old one but with lots of little internal improvements, most notably, it
 * does not rely anymore on the IEEE software for FFTs and LPC (now replaced
 * with C code from my book), so it all works well and perfectly.  There is
 * a new plot option ("-IN", which iterates N times producing multiple
 * SUCCESSIVE plots rather than a perspective plot, the "-mN" flag syntax
 * has changed, and there is only one kind of linear prediction (the good
 * kind).  It works dandy with the new sunplot, especially on a Sun-3 (don't
 * forget the "-fsingle" and "-f68881" flags).
 */

/*
 * General Purpose Spectral Display Program
 * cc -O spect.c -lcarl -lplot -lm -o spect
 */
#include <stdio.h>
#include <strings.h>
#include <math.h>
#include <ctype.h>
#include <carl/carl.h>
#include <carl/defaults.h>
#include <carl/procom.h>

#define XMIN 0
#define XMAX 4096
#define YMIN 0
#define YMAX 4096
#define MAP(x,x1,x2,y1,y2)(((y2)-(y1))*((x)-(x1))/((x2)-(x1))+(y1))
#define MX(x)(int)(MAP(x,xmin,xmax,pxmin,pxmax)+.5)
#define L10(x)log10((double)x)
#define LMX(x)(int)(pow(10.,(double)MAP(L10(x),lxmin,lxmax,lpxmin,lpxmax))+.5)
#define MLX(x)lf?(LMX(x)):(MX(x))
#define MY(y)(int)(MAP(y,ymin,ymax,pymin,pymax)+.5)
#define MOX(x)(int)(MAP(x,xmin,xmax,pxmino,pxmaxo)+.5)
#define LMOX(x)(int)(pow(10.,(double)MAP(L10(x),lxmin,lxmax,lpxmino,lpxmaxo))+.5)
#define MOLX(x)lf?(LMOX(x)):(MOX(x))
#define MOY(y)(int)(MAP(y,ymin,ymax,pymino,pymaxo)+.5)

float TWOPI ;		/* global value for FFT routines */
float PI ;		/* global value for FFT routines */

long	sampa,		/* input starting sample */
	N,		/* FFT length */
	N2,		/* 1/2 FFT length */
	npoles,		/* number of poles used in linear prediction */
	skip,		/* hop size for window */
	w ;		/* analysis window size (<=N) */
int	ampspec,	/* amplitude spectrum flag */
	powspec = 1,	/* power spectrum flag */
	comspec,	/* complex spectrum flag */
	phaspec,	/* phase spectrum flag */
	dbspec,		/* power spectrum in dB flag */
	rect,		/* rectangular (instead of Hamming) window flag */
	un,		/* suppress normalization flag */
	aspec,		/* average power spectrum (over niter iterations) */
	gflag,		/* generate device-independent plot flag */
	lpspec,		/* linear prediction flag */
	fspec,		/* filter evaluation flag */
	phadelspec, 	/* phase delay flag */
	grpdelspec,	/* group delay flag */
	autoflag,	/* autocorrelation flag */
	Wflag,		/* display windowed waveform flag */
	lf,		/* log frequency flag */
	yminflag,	/* ranged y flag */
	ymaxflag,	/* ranged y flag */
	xminflag,	/* ranged x flag */
	xmaxflag,	/* ranged x flag */
	enhancer	/* special plot enhancer for amp or power modes */
	;
int 	iter,
	aiter,
	Mplot,
	niter = 1 ;
float	srate = 1. ;
char	plotlabel[BUFSIZ] ;
float miny, maxy ;

typedef struct { float re ; float im ; } complex ;
#define CABS(x) hypot( (x).re, (x).im )
complex cadd(), csub(), cmult(), smult(), cdiv(), conjg(), csqrt() ;
complex zero = { 0., 0. } ;
complex one = { 1., 0. } ;
extern char *malloc(), *calloc() ;

main( argc, argv ) int argc ; char *argv[] ; {
 int i, j, v, exhausted = 0 ;
 float	*ibuf, *sbuf, *delbuf, *wind, *abuf, *cbuf, *grc, alpha,
        fac, norm = 1., sum = 0., pmax, minfreq, maxfreq ;
 float lpa() ;
 char	ch ;
 PROP	*proplist ;	/* from header on stdin */
 char	*dbuf ;		/* buffer for strings read from header */
 char	chbuf[72] ;	/* buffer for strings to write to header */

/*
 * read header from stdin
 */
    if ( isatty(0) )
	usage( 1 ) ;
    if ( (proplist =  getheader( stdin ) ) != NULL) {
        if ( ( dbuf = getprop( stdin, H_SRATE ) ) != NULL )
            srate = atof( dbuf ) ;        /* get input srate */
        if ( ( dbuf = getprop( stdin, H_FILENAME ) ) != NULL )
            strcpy( plotlabel, rindex( dbuf, '/' ) + 1 ) ;
    }
/*
 * call crack to interpet commandline
 */
    while ( ( ch = crack( argc, argv, "R|", 1 ) ) != NULL ) {
	switch ( ch ) {
	    case 'R': srate = sfexpr( arg_option, 1.0 ) ; break ;
	}
    }
    minfreq = 0. ;
    maxfreq = srate/2 ;
    arg_index = 0 ;    /* reset for second call to crack() */
    while (
	( ch = crack( 
	    argc, argv,
	    "I|W|n|y|Y|x|X|g|G|c|v|l|k|b|w|s|i|R|m|aAdeEfptruhz^", 0 
	    ) ) != NULL )
	{
	switch (ch) {
	    case 'R': break ;
	    case 'a': powspec = 0 ; ampspec = 1 ; break ;
	    case 'A': powspec = 0 ; autoflag = 1 ; break ;
	    case 'b': sampa = sfexpr( arg_option, srate ) ; break ;
	    case 'd': powspec = 0 ; dbspec = 1 ; break ;
	    case 'e': powspec = 0 ; phadelspec = 1 ; break ;
	    case 'E': powspec = 0 ; grpdelspec = 1 ; break ;
	    case 'f': fspec = 1 ; rect = 1 ; un = 1 ; break ;
	    case 'G': lf = 1 ;
	    case 'g': gflag = 1 ;
		      if ( strlen( arg_option ) )
			  strcpy( plotlabel, arg_option ) ;
		      if ( isatty(1) ) {
			  fprintf( stderr,
		          "spect: with -g flag output must be a file or pipe\n"
			  ) ;
			  exit( 1 ) ;
		      }
		      break ;
	    case 'h': usage(0) ;
	    case 'i': gflag = 1 ; niter = sfexpr( arg_option, 1. ) ; break ;
	    case 'I': gflag = Mplot = 1 ; niter = sfexpr( arg_option, 1. ) ; 
		      break ;
	    case 'l': npoles = sfexpr( arg_option, 1. ) ; lpspec = 1 ;
		      break ;
	    case 'm': powspec = 0 ; aspec = 1 ;
		      niter = sfexpr( arg_option, 1. ) ; break ;
	    case 'n': N = sfexpr( arg_option, 1. ) ; break ;
	    case 'p': powspec = 1 ; break ;
	    case 'r': rect = 1 ; break ;
	    case 's': skip = sfexpr( arg_option, srate ) ; break ;
	    case 't': powspec = 0 ; phaspec = 1 ; break ;
	    case 'u': un = 1 ; break ;
	    case 'w': w = sfexpr( arg_option, srate ) ; break ;
	    case 'W': powspec = 0 ; Wflag = 1 ; break ;
	    case 'x': xminflag = 1 ; gflag = 1 ;
		      minfreq = sfexpr( arg_option, 1. ) ; break ;
	    case 'X': xmaxflag = 1 ; gflag = 1 ; 
		      maxfreq = sfexpr( arg_option, 1. ) ; break ;
	    case 'y': yminflag = gflag = 1 ; 
		      miny = sfexpr( arg_option, 1. ) ; break ;
	    case 'Y': ymaxflag = gflag = 1 ; 
		      maxy = sfexpr( arg_option, 1. ) ; break ;
	    case 'z': powspec = 0 ; comspec = 1 ; break ;
	    case '^': enhancer = 1 ; break ;
	    default:  usage(1) ;
	}
	if ( exprerr ) {
	    fprintf( stderr,"spect: expression error:'%s'\n",argv[arg_index] ) ;
	    exit(1) ;
	}
    }
    if ( w == 0. )
	w = 1024. ;
    if ( N == 0 )
/*
 * N is the smallest power of two greater than or equal to w by default
 */
	for ( N = 2 ; N <= 32*1024 ; N *= 2 )
	    if ( N >= w )
		break ;
/*
 * check N for validity
 */
    if ( N >= 32*1024 ) {
	fprintf( stderr, "spect: n too large!\n" ) ;
	exit(1) ;
    }
    for ( i = 2 ; ; i *= 2 ) {
	if ( i == N )
	    break ;
	if ( i > N ) {
	    fprintf( stderr, "spect: n not a power of two!\n" ) ;
	    exit(1) ;
	}
    }
    if ( N < w )
	fprintf( stderr, "spect: warning--n (%d) < w (%d)\n", N, (int) w ) ;
    N2 = N>>1 ;
    if ( autoflag || Wflag ) {
	if ( !xminflag )
	    minfreq = 0 ;
	if ( !xmaxflag )
	    maxfreq = N ;
    }
    if ( skip == 0 )
	skip = w/2 ;
    if ( ( ibuf = (float *) calloc( w, sizeof(float) ) ) == NULL )
        malerr( "spect: insufficient memory", 1 ) ;
    if ( ( sbuf = (float *) calloc( N+2, sizeof(float) ) ) == NULL )
        malerr( "spect: insufficient memory", 1 ) ;
    if ( ( wind = (float *) calloc(w, sizeof(float) ) ) == NULL )
        malerr( "spect: insufficient memory", 1 ) ;
    if ( grpdelspec )
        if ( (delbuf = (float *) calloc( N2, sizeof(float) ) ) == NULL )
            malerr( "spect: insufficient memory", 1 ) ;
    if ( lpspec )
        if ( ( cbuf = (float *) calloc( npoles, sizeof(float) ) ) == NULL )
            malerr( "spect: insufficient memory", 1 ) ;
    if ( aspec )
        if ( ( abuf = (float *) calloc( N2, sizeof(float) ) ) == NULL )
            malerr( "spect: insufficient memory", 1 ) ;
/*
 * write header to stdout
 */
    if ( !isatty(1) ) {
	sprintf( chbuf,"%d",N) ;
	addprop( stdin,"BlockSize", chbuf ) ;
	addprop( stdin, H_SNDOUT_FORMAT, H_FLOATSAM ) ;    /* -of */
	cpoheader( proplist, stdout ) ;
	putheader( stdout ) ;            /* write header to stdout */
    }
/*
 * skip over sampa input samples
 */
    for ( i = 0 ; i < sampa ; i++ )
	if ( getfloat( ibuf ) <= 0 ) {
	    fprintf( stderr, "spect: EOF while skipping to first sample\n" ) ;
	    exit( -1 ) ;
	}
/*
 * set up normalized Hamming window
 */
    fac = 8.*atan(1.)/(w - 1.) ;
    for ( i = 0 ; i < w ; i++ )
	wind[i] = .54 -.46*cos( fac*i ) ;
    for ( i = 0 ; i < w ; i++ )
	sum += wind[i] ;
    sum = 1./sum ;
    for ( i = 0 ; i < w ; i++ )
	wind[i] *= sum ;
    if ( !fspec )
	norm = 1./w ;
/*
 * main loop: step through niter times, skipping by skip samples
 */
    for ( iter = 0 ; iter < niter ; iter++ ) {
        if ( iter == 0 ) {
	    for ( i = 0 ; i < w ; i++ )
		if ( getfloat( &ibuf[i] ) <= 0) {
		    if ( i == 0 ) {
			fprintf( stderr, "spect: can't process 0 samples\n" ) ;
			exit( -1 ) ;
		    }
		    ibuf[i] = 0. ;
		    niter = iter + 1 ;
		}
	} else {
            if ( skip >= w ) {
                for ( i = 0 ; i < skip - w ; i++ )
		    getfloat( ibuf ) ;
                for ( i = 0 ; i < w ; i++ )
		    if ( getfloat( &ibuf[i] ) <= 0) {
			niter = iter + 1 ;
			if ( i == 0 ) {
			    exhausted = 1 ;
			    niter = iter-- ;
			    goto done ;
			}
			ibuf[i] = 0. ;
		    }
            } else {
                for ( i = 0 ; i < skip ; i++ )
		    ibuf[i] = ibuf[skip+i] ;
                for ( i = skip ; i < w ; i++ )
		    if ( getfloat( &ibuf[i] ) <= 0 ) {
			ibuf[i] = 0. ;
			niter = iter + 1 ;
		    }
            }
        }
/*
 * apply window
 */
        if ( rect )
	    if ( norm != 1. )
		for ( i = 0 ; i < w ; i++ )
		    sbuf[i] = norm*ibuf[i] ;
	    else
		for ( i = 0 ; i < w ; i++ )
		    sbuf[i] = ibuf[i] ;
	else
	    for ( i = 0 ; i < w ; i++ )
		sbuf[i] = ibuf[i]*wind[i] ;
        for ( i = w ; i < N+2 ; i++)
	    sbuf[i] = 0. ;
/*
 * optional linear prediction: replace signal to be transformed by lp estimate
 */
        if ( lpspec ) {
	    lpa( sbuf, w, cbuf, npoles, 0 ) ;
            for ( sbuf[0] = i = 1 ; i < N ; i++ )
                for ( sbuf[i] = 0., j = 1 ; j <= npoles ; j++ )
                    if ( j <= i )
			sbuf[i] += cbuf[j - 1]*sbuf[i - j] ;
	}
/*
 * if -W then write out windowed waveform
 */
        if ( Wflag ) {
	    output( sbuf, N, minfreq, maxfreq ) ;
	    continue ;
	}
/*
 * call rfft to perform FFT
 */
        rfft( sbuf, N2, 1 ) ;
	sbuf[N] = sbuf[1] ;
	sbuf[1] = sbuf[N+1] = 0. ;
/*
 * if -z then write out complex values
 */
        if ( comspec ) {
	    output( sbuf, N, minfreq, maxfreq ) ;
	    continue ;
/*
 * if -t then write out phase values (no unwraping)
 */
        } else if ( phaspec ) {
            for ( i = 0 ; i < N2 ; i++ )
		sbuf[i] =
		    atan2( (double) sbuf[(i<<1)+1], (double) sbuf[i<<1] ) ;
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
/*
 * if -e then write out phase delay values
 */
        } else if ( phadelspec ) { register float fac = 4.*atan(1.)/N2 ;
            for ( i = 0 ; i < N2 ; i++ )
		sbuf[i] =
		    atan2( (double) sbuf[(i<<1)+1], (double) sbuf[i<<1] ) ;
            for ( i = 1 ; i < N2 ; i++ )
		sbuf[i] /= i*fac ;
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
/*
 * if -E then write out group delay values
 */
        } else if ( grpdelspec ) { register float fac = N2/(4.*atan(1.)) ;
            for ( i = 0 ; i < N2 ; i++ )
		sbuf[i] = 
		    atan2( (double) sbuf[(i<<1)+1], (double) sbuf[i<<1] ) ;
            for ( i = 1 ; i < N2 ; i++ )
		delbuf[i] = sbuf[i] - sbuf[i-1] ;
            for ( i = 1 ; i < N2 ; i++ )
		sbuf[i] = delbuf[i]*fac ;
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
/*
 * if -A then calculate autocorrelation
 */
        } else if ( autoflag ) {
	    for ( i = 0 ; i <= N2 ; i++ ) {
		sbuf[i<<1] =
		    sbuf[i<<1]*sbuf[i<<1] + sbuf[(i<<1)+1]*sbuf[(i<<1)+1] ;
		sbuf[(i<<1)+1] = 0. ;
	    }
	    sbuf[1] = sbuf[N] ;
	    rfft( sbuf, N2, 0 ) ;
	    output( sbuf, N, minfreq, maxfreq ) ;
	    continue ;
	}
/*
 * otherwise calculate power spectrum
 */
        for ( i = 0 ; i < N2 ; i++ )
	    sbuf[i] = sbuf[i<<1]*sbuf[i<<1] + sbuf[(i<<1)+1]*sbuf[(i<<1)+1] ;
/*
 * if -p then write out power values
 */
        if ( powspec ) {
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
        }
/*
 * if -a then write out amplitude values
 */
        if ( ampspec ) {
            for ( i = 0 ; i < N2 ; i++ )
		sbuf[i] = sqrt( (double) sbuf[i] ) ;
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
        }
/*
 * if -m then accumulate spectral values for later averaged output
 */
        if ( aspec ) {
            for ( i = 0 ; i < N2 ; i++ )
		abuf[i] += sbuf[i] ;
	    continue ;
        }
/*
 * if -d then write out log-magnitude (decibel) values
 */
        if ( dbspec ) {
	    if ( !un )
		normalize( sbuf, N2 ) ;
	    for ( i = 0 ; i < N2 ; i++ ) {
		if ( sbuf[i] > 1.e-20 )
		    sbuf[i] = 10.* log10( (double) sbuf[i] ) ;
		else sbuf[i] = -200. ;
	    }
	    output( sbuf, N2, minfreq, maxfreq ) ;
	    continue ;
        }
    }
    done:
    if ( exhausted && !aspec )
	output( sbuf, 0, minfreq, maxfreq ) ;
/*
 * if -m then output averaged spectral values
 */
    if ( aspec ) {
	for ( i = 0 ; i < N2 ; i++ )
	    sbuf[i] = abuf[i]/niter ;
	if ( !un )
	    normalize( sbuf, N2 ) ;
	for ( i = 0 ; i < N2 ; i++ ) {
	    if ( sbuf[i] > 1.e-20 )
		sbuf[i] = 10.* log10( (double) sbuf[i] ) ;
	    else sbuf[i] = -200. ;
	}
	iter = 0 ;
	aiter = niter ;
	niter = 1 ;
	output( sbuf, N2, minfreq, maxfreq ) ;
    }
    exit(0) ;
}
/*
 * take care of various forms of output (printable, floatsam, and graphic)
 */
output( array, length, minfreq, maxfreq ) 
 float array[], minfreq, maxfreq ; int length ; {
 register int i ;
    if ( !gflag ) {
/*
 * print complex values two per output line
 */
	if ( comspec )
	    for ( i = 0 ; i < length ; i += 2 )
		printf( "%f %f\n", array[i], array[i+1] ) ;
	else
	    for ( i = 0 ; i < length ; i++ )
		printf( "%f\n", array[i] ) ;
/*
 * generate graphic output
 */
    } else {
     static float xmin, xmax, ymin, ymax, pxmin, pxmax, pymin, pymax,
	dmax, dmin, yrange, xrange, pxmino, pxmaxo, pymino, pymaxo,
	lxmin, lxmax, lpxmin, lpxmax, lpxmino, lpxmaxo,
        xtick[100], ytick[100], *obuf, Nyquist ;
     static int normlen, *peakmark, first = 1 ;
     float x, tick, dig ;
     char svalue[BUFSIZ], s2[BUFSIZ], *malloc() ;
     int j, nxticks, nyticks ;
/*
 * allocate output buffer large enough to hold the works,
 * otherwise scaling of plot is unknown
 */
	if ( first ) {
	    Nyquist = srate/2 ;
	    first = 0 ;
	    normlen = length ;
	    if (
	     ( obuf = (float *) malloc( niter*length*sizeof(float) ) ) == NULL
	    ) {
		fprintf( stderr,
		    "spect: unable to obtain %d bytes for plot memory\n",
			niter*length*sizeof(float)
		) ;
		exit( -1 ) ;
	    }
	    if ( (powspec || ampspec) && enhancer ) {
		if (
		 ( peakmark = (int *) malloc( length*sizeof(int) ) ) == NULL
		) {
		    fprintf( stderr,
			"spect: unable to obtain %d bytes for peak memory\n",
			    length*sizeof(float)
		    ) ;
		    exit( -1 ) ;
		}
	    }
	    dmax = dmin = array[0] ;
	}
/*
 * squirrel away plot data, ranging as we go
 */
	for ( i = 0 ; i < length ; i++ ) {
	 register float temp = array[i] ;
	 register int k = iter*length ;
	    if ( temp > dmax ) dmax = temp ;
	    if ( temp < dmin ) dmin = temp ;
	    obuf[k+i] = temp ;
	}
/*
 * generate plot output on last iteration only
 */
	if ( iter != niter - 1 )
	    return ;
	else for ( iter = 0 ; iter < niter ; iter++ ) {
	 register int k = iter*normlen ;
	    if ( iter == 0 || Mplot ) {
		pxmino = pxmin = .2*( XMAX - XMIN ) ;
		pymino = pymin = .2*( YMAX - YMIN ) ;
		pxmaxo = pxmax = .85*( XMAX - XMIN ) ;
		pymaxo = pymax = .85*( YMAX - YMIN ) ;
		xmin = minfreq ;
/*
 * can't use 0 frequency on log frequency plot
 */
		if ( lf && xmin <= 0. )
		    xmin = 0.01 ;
		xmax = maxfreq ;
		ymin = dmin ;
/*
 * normally force ymin to 0 for amplitude and (linear) power spectral plots
 */
		if ( ampspec || powspec ) 
		    ymin = 0 ;
		ymax = dmax ;
/*
 * normally force phase spectrum plots to range from -pi/2 to pi/2
 */
		if ( phaspec ) {
		    ymin = -4.*atan(1.) ;
		    ymax = 4.*atan(1.) ;
		}
/*
 * give user enough rope to coerce ymin and ymax values
 */
		if ( ymaxflag )
		    ymax = maxy ;
		if ( yminflag )
		    ymin = miny ;
/*
 * set up log frequency plot constants
 */
		if ( lf ) {	
		    lxmin = log10( (double) xmin ) ;
		    lxmax = log10( (double) xmax ) ;
		    lpxmin = log10( (double) pxmin ) ;
		    lpxmax = log10( (double) pxmax ) ;
		    lpxmino = log10( (double) pxmino ) ;
		    lpxmaxo = log10( (double) pxmaxo ) ;
		}
		xrange = fabs( xmax - xmin ) ;
		yrange = fabs( ymax - ymin ) ;
/*
 * open plot and draw coordinates
 */
		openpl() ;
		space( XMIN, YMIN, XMAX, YMAX ) ;
		erase() ;
/*
 * use box with grid lines for single or successive plots
 * use left and bottom ticked axes only for iterated perspective plots
 */
		line( MX(xmin), MY(ymin), MX(xmin), MY(ymax) ) ;
		line( MX(xmax), MY(ymin), MX(xmin), MY(ymin) ) ;
		if ( niter == 1 || Mplot ) {
		    line( MX(xmin), MY(ymax), MX(xmax), MY(ymax) ) ;
		    line( MX(xmax), MY(ymax), MX(xmax), MY(ymin) ) ;
		}
/*
 * generate extreme ticks
 */
		tick = .02 ;
		dig = .025 ;
		line( MX(xmin-tick*xrange), MY(ymin), MX(xmin), MY(ymin) ) ;
		line( MX(xmin-tick*xrange ), MY(ymax), MX(xmin), MY(ymax) ) ;
		line( MX(xmin), MY(ymin-tick*yrange), MX(xmin), MY(ymin) ) ;
		line( MX(xmax), MY(ymin-tick*yrange), MX(xmax), MY(ymin) ) ;
/*
 * generate extreme value labels
 */
		sprintf( svalue, "%g", ymin ) ;
		move( MX(xmin-tick*xrange-strlen(svalue)*dig*xrange), MY(ymin) ) ;
		label( svalue ) ;

		sprintf( svalue, "%g", ymax ) ;
		move( MX(xmin-tick*xrange-strlen(svalue)*dig*xrange), MY(ymax) ) ;
		label( svalue ) ;

		sprintf( svalue, "%g", xmin ) ;
		move( MX(xmin-strlen(svalue)*dig*xrange/2.), MY(ymin-3.*tick*yrange) ) ;
		label( svalue ) ;

		sprintf( svalue, "%g", xmax ) ;
		move( MX(xmax-strlen(svalue)*dig*xrange/2.), MY(ymin-3.*tick*yrange) ) ;
		label( svalue ) ;
/*
 * label plot fully
 */
		if ( dbspec || aspec ) {
		    move( MX(xmin-.15*xrange), MY(ymin+.5*yrange) ) ;
		    label( "(dB)" ) ;
		}
		if ( phadelspec || grpdelspec ) {
		    move( MX(xmin-.19*xrange), MY(ymin+.5*yrange) ) ;
		    label( "(samples)" ) ;
		}

		if ( lf )
		    strcpy( svalue, "log f ->" ) ;
		else
		    if ( autoflag || Wflag )
			strcpy( svalue, "T ->" ) ;
		    else
			strcpy( svalue, "f ->" ) ;
		move( MX(xmax), MY(ymin-.16*yrange) ) ;
		label( svalue ) ;

		if ( powspec || dbspec ) strcpy( svalue, "Power Spectrum" ) ;
		if ( aspec ) strcpy( svalue, "Averaged Power Spectrum" ) ;
		if ( ampspec ) strcpy( svalue, "Amplitude Spectrum" ) ;
		if ( phaspec ) strcpy( svalue, "Phase Spectrum" ) ;
		if ( comspec ) strcpy( svalue, "Complex Spectrum" ) ;
		if ( autoflag ) strcpy( svalue, "Autocorrelation" ) ;
		if ( Wflag ) strcpy( svalue, "Waveform" ) ;
		if ( phadelspec ) strcpy( svalue, "Phase Delay Spectrum" ) ;
		if ( grpdelspec ) strcpy( svalue, "Group Delay Spectrum" ) ;
		if ( fspec && !Wflag )
		    strcat( svalue, " (filter response)" ) ;
		if ( fspec && Wflag )
		    strcat( svalue, " (impulse response)" ) ;
		if ( ( dbspec || aspec ) && un )
		    strcat( svalue, " (unnormalized)" ) ;
		if ( ( dbspec || aspec ) && !un )
		    strcat( svalue, " (normalized)" ) ;
		move( MX(xmin+.1*xrange), MY(ymin-.06*yrange) ) ;
		label( svalue ) ;

		if ( rect )
		    sprintf( svalue, "R=%dHz, Rectangular window=%dS", 
			(int) srate, (int) w ) ;
		else
		    sprintf( svalue, "R=%dHz, Hamming window=%dS", 
			(int) srate, (int) w ) ;
		move( MX(xmin+.11*xrange), MY(ymin-.12*yrange) ) ;
		label( svalue ) ;

		if ( Mplot ) {
		    sprintf( svalue, "%d of %d iterations, skip=%dS", 
			iter + 1, niter, (int) skip ) ;
		    move( MX(xmin+.12*xrange), MY(ymin-.18*yrange) ) ;
		    label( svalue ) ;
		}
		if ( !Mplot && ( niter > 1 || aspec ) ) {
		    sprintf( svalue, "%d iterations, skip=%dS", 
			aspec ? aiter : niter, (int) skip ) ;
		    move( MX(xmin+.12*xrange), MY(ymin-.18*yrange) ) ;
		    label( svalue ) ;
		}

		if ( lpspec ) {
		    move( MX(xmin+.13*xrange), MY(ymin-.24*yrange) ) ;
		    sprintf( svalue, "%d-pole Linear Prediction", npoles ) ;
		    label( svalue ) ;
		}

		if( strlen( plotlabel ) ) {
		 int ok = 1 ; char *cptr = plotlabel ;
		    while ( *cptr )
			if ( !isascii( *cptr++ ) )
			    ok = 0 ;
		    if ( ok ) {
			move( MX(xmin+.14*xrange), MY(ymin-.30*yrange) ) ;
			label( plotlabel ) ;
		    }
		}
/*
 * generate a good set of y tick (or grid) values
 */
		nyticks = maketicks( ymin, ymax, ytick ) ;
		if ( niter > 1 ) {
		    linemod( "solid" ) ;
		    for ( j = 0 ; j < nyticks ; j++ )
			if ( fabs( ytick[j] ) < 1.e-10 ) {
			    line( MX(xmin-.05*xrange), MY(ytick[j]),
				MX(xmin), MY(ytick[j]) ) ;
			} else
			    line( MX(xmin-.02*xrange), MY(ytick[j]),
				MX(xmin), MY(ytick[j]) ) ;
		} else {
		    linemod( "dotted" ) ;
		    for ( j = 0 ; j < nyticks ; j++ )
			if ( fabs( ytick[j] ) < 1.e-10 ) {
			    linemod( "solid" ) ;
			    line( MX(xmin-.05*xrange), MY(ytick[j]),
				MLX(xmax), MY(ytick[j]) ) ;
			    linemod( "dotted" ) ;
			} else
			    line( MLX(xmin), MY(ytick[j]), 
				MLX(xmax), MY(ytick[j]) ) ;
		}
/*
 * generate a good set of x tick (or grid) values
 */
		nxticks = maketicks( xmin, xmax, xtick ) ;
		if ( niter > 1 ) {
		    linemod( "solid" ) ;
		    for ( j = 0 ; j < nxticks ; j++ )
			line( MLX(xtick[j]), MY(ymin-.02*yrange),
			    MLX(xtick[j]), MY(ymin) ) ;
		} else {
		    linemod( "dotted" ) ;
		    for ( j = 0 ; j < nxticks ; j++ )
		      line( MLX(xtick[j]), MY(ymin), MLX(xtick[j]), MY(ymax) ) ;
		}
	    } else {
/*
 * make iterated plots by moving picture up and right a little on each 
 * iteration dragging axes and ticks as we go
 */
	     float delx, dely ;
	     int xx, yy ;
		delx = .01*( XMAX - XMIN ) ;
		dely = .01*( YMAX - YMIN ) ;
		if ( (powspec || ampspec) && enhancer ) {
		    delx = .005*( XMAX - XMIN ) ;
		    dely = .005*( YMAX - YMIN ) ;
		}
		move( MX(xmin), MY(ymin) ) ;
		pxmin += delx ;
		pxmax += delx ;
		if ( lf ) {
		    lpxmin = log10( (double) pxmin ) ;
		    lpxmax = log10( (double) pxmax ) ;
		}
		pymin += dely ;
		pymax += dely ;
		cont( MX(xmin), MY(ymin) ) ;
		linemod( "dotted" ) ;
		xx = MX(xmax) ;
		xx = xx >= XMAX ? XMAX-1 : xx ;
		cont( xx, MY(ymin) ) ;
		move( MX(xmin), MY(ymin) ) ;
		yy = MY(ymax) ;
		yy = yy >= YMAX ? YMAX-1 : yy ;
		cont( MX(xmin), yy ) ;
		for ( j = 0 ; j < nyticks ; j++ )
		   line( MOLX(xmin), MOY(ytick[j]), MLX(xmin), MY(ytick[j]) ) ;
		for ( j = 0 ; j < nxticks ; j++ )
		   line( MOLX(xtick[j]), MOY(ymin), MLX(xtick[j]), MY(ymin) ) ;
		pxmino = pxmin ;
		pxmaxo = pxmax ;
		if ( lf ) {
		    lpxmino = log10( (double) pxmino ) ;
		    lpxmaxo = log10( (double) pxmaxo ) ;
		}
		pymino = pymin ;
		pymaxo = pymax ;
	    }
	    linemod( "solid" ) ;
/*
 * optional peak enhancer for power and amplitude plots
 */
	    if ( (powspec || ampspec) && enhancer ) {
		for ( i = 0 ; i < normlen ; i++ )
		    peakmark[i] = 0 ;
		for ( i = 1 ; i < normlen - 1 ; i++ )
		    if ( obuf[k+i-1] < obuf[k+i] && obuf[k+i] > obuf[k+i+1] )
			peakmark[i] = 1 ;
		for ( i = 0 ; i < normlen ; i++ )
		    if ( !peakmark[i] )
			obuf[k+i] = 0. ;
	    }
/*
 * output plot through plot optimizers
 */
/*
 * show complex spectrum as solid (real) and dashed (imaginary) lines
 */
	    if ( comspec ) {
		optmove( MLX(xmin), MY(obuf[k]) ) ;
		for ( i = 1 ; i < normlen/2 ; i++ ) {
		    x = MAP((i<<1),0,normlen,0,Nyquist) ;
		    if ( x < xmin )
			optmove( MLX(x), MY(obuf[k+(i<<1)]) ) ;
		    else if ( x <= xmax ) { int xx ;
			xx = MLX(x) ;
			if ( xx < XMAX )
			    if ( obuf[k+(i<<1)] >= ymin &&
			     obuf[k+(i<<1)] <= ymax )
				optcont( xx, MY(obuf[k+(i<<1)]) ) ;
			    else
				optmove( xx, MY(obuf[k+(i<<1)]) ) ;
			else break ;
		    } else break ;
		}
		linemod( "longdashed" ) ;
		optmove( MLX(xmin), MY(obuf[k+1]) ) ;
		for ( i = 1 ; i < normlen/2 ; i++ ) {
		    x = MAP((i<<1),0,normlen,0,Nyquist) ;
		    if ( x < xmin )
			optmove( MLX(x), MY(obuf[k+(i<<1)+1]) ) ;
		    else if ( x <= xmax ) { int xx ;
			xx = MLX(x) ;
			if ( xx < XMAX )
			    if ( obuf[k+(i<<1)+1] >= ymin &&
			     obuf[k+(i<<1)+1] <= ymax )
				optcont( xx, MY(obuf[k+(i<<1)+1]) ) ;
			    else
				optmove( xx, MY(obuf[k+(i<<1)+1]) ) ;
			else break ;
		    } else break ;
		}
		linemod( "solid" ) ;
/*
 * delay spectra have one fewer component
 */
	    } else if ( phadelspec || grpdelspec ) {
		optmove( MLX(xmin), MY(obuf[k+1]) ) ;
		for ( i = 1 ; i < normlen ; i++ ) {
		    x = MAP(i,0,normlen,0,Nyquist) ;
		    if ( x < xmin )
			optmove( MLX(x), MY(obuf[k+i]) ) ;
		    else if ( x <= xmax ) { int xx ;
			xx = MLX(x) ;
			if ( xx < XMAX )
			    if ( obuf[k+i] >= ymin &&
			     obuf[k+i] <= ymax )
				optcont( xx, MY(obuf[k+i]) ) ;
			    else
				optmove( xx, MY(obuf[k+i]) ) ;
			else break ;
		    } else break ;
		}
/*
 * normal plot output:
 */
	    } else {
/*
 * on each iteration, move initially to first plot value
 */
		optmove( MLX(xmin), MY(obuf[k]) ) ;
/*
 * loop through plotted function values
 */
		for ( i = 1 ; i < normlen ; i++ ) {
/*
 * compute x position on plot
 */
		    if ( autoflag || Wflag )
			x = i ;
		    else
			x = MAP(i,0,normlen,0,Nyquist) ;
/*
 * skip over x positions less than xmin and greater than xmax
 */
		    if ( x < xmin )
			optmove( MLX(x), MY(obuf[k+i]) ) ;
		    else if ( x <= xmax ) { int xx ;
/*
 * try not to fall off right edge of plot
 */
			xx = MLX(x) ;
/*
 * if we're in the plot x window, skip y values outside range
 */
			if ( xx < XMAX )
			    if ( obuf[k+i] >= ymin &&
			     obuf[k+i] <= ymax )
				if ( (powspec || ampspec) && enhancer ) {
				    optmove( xx, MY(ymin) ) ;
				    optcont( xx, MY(obuf[k+i]) ) ;
				    optmove( xx, MY(ymin) ) ;
				} else
				    optcont( xx, MY(obuf[k+i]) ) ;
			    else
				optmove( xx, MY(obuf[k+i]) ) ;
			else break ;
		    } else break ;
		}
	    }
/*
 * extra optmove to flush last plot point for each iteration
 */
	    optmove( XMIN, YMIN ) ;
	    if ( iter == niter - 1 ) {
		move( XMIN, YMIN ) ;
		closepl() ;
	    }
	}
    }
}
normalize( array, length ) float array[] ; int length ; {
 register int i ;
 register float max ;
    max = array[0] ;
    for ( i = 0 ; i < length ; i++ )
	if ( array[i] > max)
	    max = array[i] ;
    if ( max <= 0. ) {
	fprintf(stderr,"spect: zero input") ;
	exit(1) ;
    }
    max = 1./max ;
    for ( i = 0 ; i < length ; i++ )
	array[i] *= max ;
}
/*
 * maketicks finds a reasonable way to put tick marks between
 * min and max at decimal subdivisions
 */
maketicks( min, max, ticks ) float min, max, ticks[] ; {
 int order, n ;
 float forder, delta, tick ;
    forder = log10( fabs( (double) max - min ) ) ;
    order = forder ;
    if ( forder - order < .302 )
	order -= 1 ;
    delta = pow( 10., (double) order ) ;
    tick = delta ;
    while ( tick > min ) 
	tick -= delta ;
    while ( tick < min ) 
	tick += delta ;
    for ( n = 0 ; tick < max ; tick += delta ) {
	ticks[n++] = tick ;
    }
    return( n ) ;
}
usage( x ) {
 fprintf( stderr, 
"USAGE: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
"spect [flags] < floatsams > output (input must be file or pipe)\n",
"---------------OUTPUT SPECTRUM FLAG (CHOOSE ONE):-----------------\n",
" -p   power [default]        -a   amplitude       -t   phase\n",
" -d   power (in dB)                               -e   phase delay\n",
" -mN  power (in dB                                -E   group delay\n",
"      averaged over\n",
"      N iterations)                               -z   complex\n",
" -A   output autocorrelation of input (instead of spectrum)\n",
" -W   output windowed input (instead of spectrum)\n",
"-------------------OPTIONAL CONTROL FLAGS:------------------------\n",
" -bN  begin at time N seconds [0S]\n",
" -f   input is filter impulse response\n",
" -nN  FFT is N samples long, N must be power of 2 [n >= wS]\n",
" -r   rectangular window [Hamming window]\n",
" -RN  sample rate [set from input header or 1 if no header]\n",
" -sN  skip window by N seconds between iterations [w/2]\n",
" -u   don't normalize [normalize to 0 dB]\n",
" -wN  window is N seconds long [1024S]\n",
"---------------OPTIONAL LINEAR PREDICTION FLAG:-------------------\n",
" -lN  replace input with N-pole linear prediction (MEM)\n",
"--------------------OPTIONAL PLOT FLAGS:--------------------------\n",
" -gL  generate linear frequency device-independent plot of output\n",
" -GL  generate log frequency device-independent plot of output\n",
"      (L is optional plot label [set from input filename if header])\n",
" -iN  iterate N times (generate perspective plot) [1]\n",
" -IN  iterate N times (generate successive plots) [1]\n",
" -xN  minimum plot frequency is N [0]\n",
" -XN  maximum plot frequency is N [R/2]\n",
" -yN  minimum plot data value is N [data minimum]\n",
" -YN  maximum plot data value is N [data maximum]\n",
" -^   special peak enhancer for -a or -p plot output\n"
) ;
    exit( x ) ;
}
malerr(str, ex) char *str ; int ex ; {
    fprintf( stderr, "%s\n", str ) ;
    exit( ex ) ;
}
/*
 * optimal drawing routines that avoid redundant plotting calls
 */
int lastx, lasty, movelast, horiz ;
optcont( x, y ) int x, y ; {
    if ( movelast ) {
	move( lastx, lasty ) ;
	movelast = 0 ;
    }
    if ( lasty == y ) {
	lastx = x ;
	horiz = 1 ;
    } else {
	if ( horiz ) {
	    cont( lastx, lasty ) ;
	    horiz = 0 ;
	}
	lastx = x ;
	lasty = y ;
	cont( x, y ) ;
    }
}
optmove( x, y ) int x, y ; {
    if ( horiz ) {
	cont( lastx, lasty ) ;
	horiz = 0 ;
    }
    movelast = 1 ;
    lastx = x ;
    lasty = y ;
}
/*
 * If forward is true, rfft replaces 2*N real data points in x with
 * N complex values representing the positive frequency half of their
 * Fourier spectrum, with x[1] replaced with the real part of the Nyquist
 * frequency value.  If forward is false, rfft expects x to contain a
 * positive frequency spectrum arranged as before, and replaces it with
 * 2*N real values.  N MUST be a power of 2.
 */
rfft( x, N, forward ) float x[] ; int N, forward ; {
 float c1, c2, h1r, h1i, h2r, h2i, wr, wi, wpr, wpi, temp, theta ;
 float xr, xi ;
 int i, i1, i2, i3, i4, N2p1 ;
 int first = 1 ;
    if ( first ) {
	first = 0 ;
	TWOPI = 8.*atan( 1. ) ;
	PI = 4.*atan( 1. ) ;
    }
    theta = PI/N ;
    wr = 1. ;
    wi = 0. ;
    c1 = 0.5 ;
    if ( forward ) {
	c2 = -0.5 ;
	cfft( x, N, forward ) ;
	xr = x[0] ;
	xi = x[1] ;
    } else {
	c2 = 0.5 ;
	theta = -theta ;
	xr = x[1] ;
	xi = 0. ;
	x[1] = 0. ;
    }
    wpr = -2.*pow( sin( 0.5*theta ), 2. ) ;
    wpi = sin( theta ) ;
    N2p1 = (N<<1) + 1 ;
    for ( i = 0 ; i <= N>>1 ; i++ ) {
	i1 = i<<1 ;
	i2 = i1 + 1 ;
	i3 = N2p1 - i2 ;
	i4 = i3 + 1 ;
	if ( i == 0 ) {
	    h1r =  c1*(x[i1] + xr ) ;
	    h1i =  c1*(x[i2] - xi ) ;
	    h2r = -c2*(x[i2] + xi ) ;
	    h2i =  c2*(x[i1] - xr ) ;
	    x[i1] =  h1r + wr*h2r - wi*h2i ;
	    x[i2] =  h1i + wr*h2i + wi*h2r ;
	    xr =  h1r - wr*h2r + wi*h2i ;
	    xi = -h1i + wr*h2i + wi*h2r ;
	} else {
	    h1r =  c1*(x[i1] + x[i3] ) ;
	    h1i =  c1*(x[i2] - x[i4] ) ;
	    h2r = -c2*(x[i2] + x[i4] ) ;
	    h2i =  c2*(x[i1] - x[i3] ) ;
	    x[i1] =  h1r + wr*h2r - wi*h2i ;
	    x[i2] =  h1i + wr*h2i + wi*h2r ;
	    x[i3] =  h1r - wr*h2r + wi*h2i ;
	    x[i4] = -h1i + wr*h2i + wi*h2r ;
	}
	wr = (temp = wr)*wpr - wi*wpi + wr ;
	wi = wi*wpr + temp*wpi + wi ;
    }
    if ( forward )
	x[1] = xr ;
    else
	cfft( x, N, forward ) ;
}
/*
 * cfft replaces float array x containing NC complex values
 * (2*NC float values alternating real, imagininary, etc.)
 * by its Fourier transform if forward is true, or by its
 * inverse Fourier transform if forward is false, using a
 * recursive Fast Fourier transform method due to Danielson
 * and Lanczos.  NC MUST be a power of 2.
 */
cfft( x, NC, forward ) float x[] ; int NC, forward ; {
 float wr, wi, wpr, wpi, theta, scale ;
 int mmax, ND, m, i, j, delta ;
    ND = NC<<1 ;
    bitreverse( x, ND ) ;
    for ( mmax = 2 ; mmax < ND ; mmax = delta ) {
	delta = mmax<<1 ;
	theta = TWOPI/( forward? mmax : -mmax ) ;
	wpr = -2.*pow( sin( 0.5*theta ), 2. ) ;
	wpi = sin( theta ) ;
	wr = 1. ;
	wi = 0. ;
	for ( m = 0 ; m < mmax ; m += 2 ) {
	 register float rtemp, itemp ;
	    for ( i = m ; i < ND ; i += delta ) {
		j = i + mmax ;
		rtemp = wr*x[j] - wi*x[j+1] ;
		itemp = wr*x[j+1] + wi*x[j] ;
		x[j] = x[i] - rtemp ;
		x[j+1] = x[i+1] - itemp ;
		x[i] += rtemp ;
		x[i+1] += itemp ;
	    }
	    wr = (rtemp = wr)*wpr - wi*wpi + wr ;
	    wi = wi*wpr + rtemp*wpi + wi ;
	}
    }
/*
 * scale output
 */
/*
    scale = forward ? 1./ND : 2. ;
    for ( i = 0 ; i < ND ; i++ )
	x[i] *= scale ;
    { register float *xi=x, *xe=x+ND ;
	while ( xi < xe )
	    *xi++ *= scale ;
    }
*/
}
/*
 * bitreverse places float array x containing N/2 complex values
 * into bit-reversed order
 */
bitreverse( x, N ) float x[] ; int N ; {
 float rtemp, itemp ;
 int i, j, m ;
    for ( i = j = 0 ; i < N ; i += 2, j += m ) {
	if ( j > i ) {
	    rtemp = x[j] ; itemp = x[j+1] ; /* complex exchange */
	    x[j] = x[i] ; x[j+1] = x[i+1] ;
	    x[i] = rtemp ; x[i+1] = itemp ;
	}
	for ( m = N>>1 ; m >= 2 && j >= m ; m >>= 1 )
	    j -= m ;
    }
}
/*
 * given N samples of digital waveform x, lpa computes M coefficients
 * by maximum entropy method for spectral estimation--these are returned
 * in b[].  lpa itself returns the a0 coefficient.
 */
float lpa( x, N, b, M ) float x[], b[] ; int N, M ; {
 float neum, denom, a0 ;
 int i, j, k ;
 static int first = 1 ;
 static float *t1, *t2, *t3 ;
    if ( first ) {    /* first time only */
	first = 0 ;
/*
 * allocate working memory: temp arrays t1, t2 and t3
 */
	t1 = (float *) malloc( N*sizeof( float ) ) ;
	t2 = (float *) malloc( N*sizeof( float ) ) ;
	t3 = (float *) malloc( M*sizeof( float ) ) ;
    }
    a0 = 0. ; 
    { register float *xj=x, *xe=x+N ;
	while ( xj < xe ) {
	    a0 += *xj * *xj ;
	    xj++ ;
	}
    }
/*
 * return zeros on zero input
 */
    if ( a0 == 0. ) { register float *bk=b, *be=b+M ;
	while ( bk < be )
	    *bk++ = 0. ;
	return( 0. ) ;
    }
    a0 /= N ;
/*
 * solve autocorrelation matrix for coefficients
 */
    *t1 = *x ;
    *(t2+N-2) = *(x+N-1) ;
    { register float *xj=x+1, *xe=x+N-1, *t1j=t1+1, *t2j=t2 ;
	while ( xj < xe )
	    *t2j++ = *t1j++ = *xj++ ;
    }
    { register float *bk=b ;
	for ( k = 0 ; k < M ; k++, bk++ ) {
	    denom = neum = 0. ;
	    { register float *t1j=t1, *t1e=t1+N-k-1, *t2j=t2 ;
		while ( t1j < t1e ) {
		    neum += *t1j * *t2j ;
		    denom += *t1j * *t1j + *t2j * *t2j ;
		    t1j++, t2j++ ;
		}
	    }
	    *bk = 2.*neum/denom ;
	    a0 *= 1. - *bk * *bk ;
	    if ( k ) {
	     register float *bi=b, *bie=b+k, *t3i=t3, *t3ki=t3+k-1 ;
		while ( bi < bie )
			*bi++ = *t3i++ - *bk * *t3ki-- ;
	    }
	    if ( k < M - 1 ) {
		{ register float *bi=b, *bie=b+k, *t3i=t3 ;
		    while ( bi <= bie )
			*t3i++ = *bi++ ;
		}
		{ register float *t1j=t1, *t1e=t1+N-k-1, *t2j=t2,
	 	   *t3k=t3+k, *t1j1=t1+1, *t2j1=t2+1 ;
		    while ( t1j < t1e ) {
			*t1j++ -= *t3k * *t2j ;
			*t2j++ = *t2j1++ - *t3k * *t1j1++ ;
		    }
		}
	    }
	}
    }
    return( a0 ) ;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.