view tools/sigproc.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line source
#include <stdio.h>
#include <math.h>
#include "sigproc.h"


/*
  C is n complex numbers: (real,imag), (ie 2n floats).
  Overwrite the first n floats with the modulus of the n complex numbers.
*/

Mod( C, n )
complex *C ;
int      n ;
{
    register complex *eptr = C+n ;
    register float   *V    = (float *)C ;

    for (  ; C < eptr ; C++ )
	*V++ = mod(C) ;
}

/*
  Return the modulus of the given complex number
*/

float mod( C )
complex *C ;
{
    return ( sqrt( sq(Re(C)) + sq(Im(C)) ) ) ;
}



/*
  C is n complex numbers: (real,imag), (ie 2n floats).
  Overwrite the first n floats with the argument of the n complex numbers.
*/

Arg( C, n )
complex *C ;
int      n ;
{
    register complex *eptr = C+n ;
    register float   *V    = (float *)C ;

    for (  ; C < eptr ; C++ )
	*V++ = arg(C) ;
}

/*
  Return the argument of the given complex number
*/

float arg( C )
complex *C;
{
    if (Im(C) >= 0) {
	if (Re(C) >= 0) return (         atan(Im(C)/Re(C)) );   /* 1st quad */
	else            return ( PI    + atan(Im(C)/Re(C)) );   /* 2nd quad */
    }
    else {
	if (Re(C) <  0) return ( PI    + atan(Im(C)/Re(C)) );   /* 1st quad */
	else            return ( TWOPI + atan(Im(C)/Re(C)) );   /* 2nd quad */
    }
}


/*
  C1 and C2 are both n complex numbers: (real,imag), (ie each 2n floats).
  Overwrite the C1 with product of C1 and the complex conjugate of C2.
  (When C1==C2, result is squared mod in the real parts, and 0 imag parts).
*/

conj( C1, C2, n )
complex *C1, *C2 ;
int   n ;
{
    register complex *eptr = C1+n ;
    float    ReC1, ReC2 ;

    for (  ; C1 < eptr ; C1++, C2++ ) {
	Re(C1) = ( ReC1=Re(C1) ) * ( ReC2=Re(C2) ) + Im(C1) * Im(C2) ;
	Im(C1) =   ReC2          *        Im(C1)   - ReC1   * Im(C2) ;
    }
}


/*
  Autocorrelation function via fft.
  A complex fft (of real data) is multiplied by its conjugate, and also scaled
  prior to the inverse fft.
*/



acf( V, m )
float *V ;
int    m ;
{
    register complex *C, *eptr ;
    register int  n = m>>1 ;
    float    tmp ;

    fft( V, m, 1 ) ;
    tmp = V[1] ; V[1] = 0 ;

    for ( C=(complex *)V, eptr=C+n ; C<eptr ; C++ ) {
	Re(C) = ( sq(Re(C)) + sq(Im(C)) ) / n ;
	Im(C) = 0 ;
    }
    V[1] = sq( tmp ) / n ;

    fft( V, m, -1 ) ;
}


/*
  Test x: return 1 if x is a power of 2, otherwise return 0.
*/

int ispower( x )
int  x ;
{
    return ( x > 1 && log2(x) == (int)log2(x) ) ;
}


/*
  Return a power of 2, either equal to x (if x is already a power of 2), or
  the next power of 2 larger than x.
*/

int getpower( x )
int x ;
{
    if ( log2(x) > (int)log2(x) )
	x = 1 << ( 1 + (int)log2(x) ) ;
    return ( x ) ;
}


/*
Copy float array x into short array y, both length n points.
Scale each point using the given scale factor.
Return 1 if no 16-bit over or underflow.
Otherwise return a scale factor to replace the given scale factor.

*/

float ftos( x, y, n, scale )
float *x ;
short *y ;
int    n ;
float  scale ;
{
    float  p ;
    float  min = MAXSHORT, fmin ;
    float  max = MINSHORT, fmax ;
    int    i ;

    fmax = fmin = 1. ;

    for ( i = 0 ; i < n ; i++ ) {
	p = x[i] * scale ;

	if ( p > max ) max = p ;
	if ( p < min ) min = p ;

	y[i] = (short)p ;
    }

    if ( max > MAXSHORT || min < MINSHORT ) {

	if ( max > MAXSHORT )  fmax = MAXSHORT / max ;
	if ( min < MINSHORT )  fmin = MINSHORT / min ;

	if ( fmax < fmin ) return ( scale * fmax ) ;
	else               return ( scale * fmin ) ;
    }

    return ( 1. ) ;
}




/*************************** convolution **********************************/

/*
Time-domain convolution. An input signal y of length n points is convolved
with a window x of length m points (where m is assumed odd for symmetry).
Return convolution signal z which must have allocated space of length n.

Discrete convolution is defined:
	z[i] = sum{j=-inf to inf}  x[i-j].y[j]          for i=-inf to inf
But the signal y and window x are assumed to be zero for all time outside
the given points so that:
	z[i] = sum{j=-m/2 to m/2}  x[i-j].y[j]          for i=0,1,...,n-1

Since the response of a linear filter to an arbitiary input signal is the
convolution of the signal with the filter's impulse response, the window may
be seen as the impulse response characterising the filter, and the return
z is then the filter output in response to input y.

Alternatively the convolution may be seen as a local averaging operation
on y with weights obtained by time-reversing and shifting x.
*/

convolve_time( y, n, x, m, z )
short  *y ;             /* input signal                                  */
int     n ;             /* length of array y                             */
float  *x ;             /* convolution window or filter impulse response */
int     m ;             /* length of array x                             */
float  *z ;             /* output signal of length n (same as input)     */
{
    register int  i, j, k, lim1, lim2 ;
    register float sum ;

    for ( i = 0 ; i < n ; i++ ) {

	k = m - 1 ;
	if ( ( lim1 = i - (m-1)/2 ) < 0 ) {
	    k += lim1 ;
	    lim1 = 0 ;
	}
	if ( ( lim2 = i + (m-1)/2 ) > n )
	    lim2 = n - 1 ;

	sum = 0 ;
	for ( j = lim1 ; j <= lim2 ; j++, --k )
	    sum += x[k] * y[j] ;

	z[i] = sum ;
    }
}


/*
Frequency-domain convolution.
*/

convolve_freq( y, n, x, m, z )
short  *y ;             /* input signal                                  */
int     n ;             /* length of array y                             */
float  *x ;             /* convolution window or filter impulse response */
int     m ;             /* length of array x                             */
float  *z ;             /* output signal of length n (same as input)     */
{
    int     i, j, n1 ;
    float  *data, *respns, *ans ;

    n1 = getpower( n + m ) ;    /* padded length of input signal */

    data   = (float *)malloc( ( n1     + 1 ) * sizeof(float) ) ;
    respns = (float *)malloc( ( n1     + 1 ) * sizeof(float) ) ;
    ans    = (float *)malloc( ( n1 * 2 + 1 ) * sizeof(float) ) ;

    /* copy and pad signal */

    for ( i = 0 ; i < n ; i++ )
	data[i] = y[i] ;
    for (  ; i < n1 ; i++ )
	data[i] = (float)0 ;

    /* copy window into wrap-around order */

    for ( i = m/2, j = 0 ; i < m ; i++, j++ )
	respns[i] = x[j] ;
    for ( i = 0 ; i < m/2 ; i++, j++ )
	respns[i] = x[j] ;

    /* Convolve and copy output */

    convlv( data-1, n1, respns-1, m, 1, ans-1 ) ;

    for ( i = 0 ; i < n ; i++ )
	z[i] = ans[i] ;

    free( data   ) ;
    free( respns ) ;
    free( ans    ) ;
}


/*
Convolution of two functions. [See NR: pp407-413].
Function 1 (the input signal) is in `data', length n, (n power of 2).
Function 2 (the response function) is in `respns', length m, (m<=n and odd).
(However respns must have n space available).
The response file should be stored in respns in "wrap-around order", ie with
its last half first and first half last in the array.
Return is the first n numbers in `ans', (though ans must have 2*n space).
Return convolution if isign==1, or deconvolution if isign==(-1).

The data array should be padded with zeroes to a power of 2.
The recommended amount of padding [NR: p411] is at least m/2, but need not be
more than m.
*/

static float sqrarg;
#define SQR(a) (sqrarg=(a),sqrarg*sqrarg)


convlv( data, n, respns, m, isign, ans )
float data[], respns[], ans[] ;
int   n, m, isign ;
{
    int   i, no2 ;
    float dum, mag2, *fftbuf ;

    fftbuf = (float *)malloc( 2 * n * sizeof(float) ) ;

    for (i=1;i<=(m-1)/2;i++)
	respns[n+1-i]=respns[m+1-i];
    for (i=(m+3)/2;i<=n-(m-1)/2;i++)
	respns[i]=0.0;

    twofft(data,respns,fftbuf,ans,n);
    no2=n/2;
    for (i=2;i<=n+2;i+=2) {
	if (isign == 1) {
	    ans[i-1]=(fftbuf[i-1]*(dum=ans[i-1])-fftbuf[i]*ans[i])/no2;
	    ans[i]=(fftbuf[i]*dum+fftbuf[i-1]*ans[i])/no2;
	} else if (isign == -1) {
	    if ((mag2=SQR(ans[i-1])+SQR(ans[i])) == 0.0)
		fprintf( stderr, "warning: deconvolving at response zero\n" ) ;
	    ans[i-1]=(fftbuf[i-1]*(dum=ans[i-1])+fftbuf[i]*ans[i])/mag2/no2;
	    ans[i]=(fftbuf[i]*dum-fftbuf[i-1]*ans[i])/mag2/no2;
	}
	else {
	    fprintf( stderr, "No meaning for ISIGN in CONVLV\n" ) ;
	    exit( 1 ) ;
	}
    }
    ans[2]=ans[n+1];
    realft(ans,no2,-1);

    free( fftbuf ) ;
}



/************************ fft ********************************************/

/*
 In-place FFT/IFFT routine for real-valued data.
 (Ref Numerical recipes, pp417).

 Arguments:
    data  = Input array of 2n floats,
	      also output data array of n complex numbers.
    n     = Number of data points, (must be a power of two).
	      Input data is 2n real numbers.
	      Output data is  n complex numbers.
    isign = FFT when 1; IFFT when -1.

 Each complex number occupies two consecutive locations: real and imag.
 The output data is n real and imag parts which are the positive frequency
 half of the symmetric Fourier transform.

 Indexing conversion:

 Both input and output data are indexed: 1,..,n.
 where:       Input data is 2n real numbers.
	      Output data is  n complex numbers, (ie 2n floats).
 To convert to conventional indexing:    0,..,m-1
 where:       Input data is m real numbers (eg. input framesize).
	      Output data is m/2 complex numbers, (ie m floats).

 allocated space for "data" of:

	      (m+1)*sizeof(float).

 call to routine "realft":

	      realft( data-1, m/2, isign ) ;

 for example via the define:

#define fft(data,m,isign)  ( realft((float *)(data)-1, m>>1, isign) )

 This works because the location (data-1) is never indexed in routine realft.
 Output data will then be data[0] to data[m/2-1] complex numbers,
 ie data[0] to data[m-1] floats, to be interpreted as m/2 (real,imag) pairs.

 The first of the m/2 points of the magnitude spectrum is then:
	      sqrt( sq( data[0] ) + sq( data[1] ) )


 The results of the IFFT are not normalized by multplication by 1/N.
 The user should multiply each returned element by 1/n.

 Routine realft returns the first and last real components in data[1] and
 data[2]. To make this correspond with the return from twofft, the mod below
 sets data[2]=0.
*/


realft(data,n,isign)
float data[];
int n,isign;
{
	int i,i1,i2,i3,i4,n2p3;
	float c1=0.5,c2,h1r,h1i,h2r,h2i;
	double wr,wi,wpr,wpi,wtemp,theta;

	theta=3.141592653589793/(double) n;
	if (isign == 1) {
		c2 = -0.5;
		four1(data,n,1);
	} else {
		c2=0.5;
		theta = -theta;
	}
	wtemp=sin(0.5*theta);
	wpr = -2.0*wtemp*wtemp;
	wpi=sin(theta);
	wr=1.0+wpr;
	wi=wpi;
	n2p3=2*n+3;
	for (i=2;i<=n/2;i++) {
		i4=1+(i3=n2p3-(i2=1+(i1=i+i-1)));
		h1r=c1*(data[i1]+data[i3]);
		h1i=c1*(data[i2]-data[i4]);
		h2r = -c2*(data[i2]+data[i4]);
		h2i=c2*(data[i1]-data[i3]);
		data[i1]=h1r+wr*h2r-wi*h2i;
		data[i2]=h1i+wr*h2i+wi*h2r;
		data[i3]=h1r-wr*h2r+wi*h2i;
		data[i4] = -h1i+wr*h2i+wi*h2r;
		wr=(wtemp=wr)*wpr-wi*wpi+wr;
		wi=wi*wpr+wtemp*wpi+wi;
	}
	if (isign == 1) {
		data[1] = (h1r=data[1])+data[2];
		data[2] = h1r-data[2];
	} else {
		data[1]=c1*((h1r=data[1])+data[2]);
		data[2]=c1*(h1r-data[2]);
		four1(data,n,-1);
	}
}


#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr

four1(data,nn,isign)
float data[];
int nn,isign;
{
	int n,mmax,m,j,istep,i;
	double wtemp,wr,wpr,wpi,wi,theta;
	float tempr,tempi;

	n=nn << 1;
	j=1;
	for (i=1;i<n;i+=2) {
		if (j > i) {
			SWAP(data[j],data[i]);
			SWAP(data[j+1],data[i+1]);
		}
		m=n >> 1;
		while (m >= 2 && j > m) {
			j -= m;
			m >>= 1;
		}
		j += m;
	}
	mmax=2;
	while (n > mmax) {
		istep=2*mmax;
		theta=6.28318530717959/(isign*mmax);
		wtemp=sin(0.5*theta);
		wpr = -2.0*wtemp*wtemp;
		wpi=sin(theta);
		wr=1.0;
		wi=0.0;
		for (m=1;m<mmax;m+=2) {
			for (i=m;i<=n;i+=istep) {
				j=i+mmax;
				tempr=wr*data[j]-wi*data[j+1];
				tempi=wr*data[j+1]+wi*data[j];
				data[j]=data[i]-tempr;
				data[j+1]=data[i+1]-tempi;
				data[i] += tempr;
				data[i+1] += tempi;
			}
			wr=(wtemp=wr)*wpr-wi*wpi+wr;
			wi=wi*wpr+wtemp*wpi+wi;
		}
		mmax=istep;
	}
}


twofft(data1,data2,fft1,fft2,n)
float data1[],data2[],fft1[],fft2[];
int n;
{
	int nn3,nn2,jj,j;
	float rep,rem,aip,aim;

	nn3=1+(nn2=2+n+n);
	for (j=1,jj=2;j<=n;j++,jj+=2) {
		fft1[jj-1]=data1[j];
		fft1[jj]=data2[j];
	}
	four1(fft1,n,1);
	fft2[1]=fft1[2];
	fft1[2]=fft2[2]=0.0;
	for (j=3;j<=n+1;j+=2) {
		rep=0.5*(fft1[j]+fft1[nn2-j]);
		rem=0.5*(fft1[j]-fft1[nn2-j]);
		aip=0.5*(fft1[j+1]+fft1[nn3-j]);
		aim=0.5*(fft1[j+1]-fft1[nn3-j]);
		fft1[j]=rep;
		fft1[j+1]=aim;
		fft1[nn2-j]=rep;
		fft1[nn3-j] = -aim;
		fft2[j]=aip;
		fft2[j+1] = -rem;
		fft2[nn2-j]=aip;
		fft2[nn3-j]=rem;
	}
}


/*********************** windows *******************************************/

/*
Allocate space for and compute an n-point Hann or raised cosine window,
returning array.
*/

float *raised_cosine( n )
int  n ;
{
    float            *W ;
    register int      i ;
    register double   k ;

    W = (float *)malloc( n * sizeof(float) ) ;

    k = TWOPI/(double)(n-1);
    for (i=0 ; i<n ; i++)
	W[i] = 0.5 * ( 1.0 - cos(k*i) ) ;

    return W ;
}


/*
Allocate space for and compute an n-point Hamming window, returning array.
*/

float *hamming( n )
int  n ;
{
    float            *W ;
    register int      i ;
    register double   k ;

    W = (float *)malloc( n * sizeof(float) ) ;

    k = TWOPI/(double)(n-1);
    for (i=0 ; i<n ; i++)
	W[i] = 0.54 - 0.46*cos(k*i) ;

    return W ;
}


/*
Allocate space for and compute a Gaussian window with given standard deviation
`s' (samples) over a range of `n' standard deviations either side of a zero
mean. The size of the returned window (returned via variable `num') will be:
	2 * n * s  +  1   [points]
(This is always odd as the window is symmetrical).
*/

float *gauss_window( s, n, num )
float  s, n ;
int   *num ;
{
    float  x, *y, var ;
    int    i, points  ;

    points = 2 * n * s + 1 ;
    y = (float *)malloc( points * sizeof(float) ) ;

    x   = ( - n * s ) ;
    var = s * s ;
    for ( i = 0 ; i < points ; i++, x++ )
	y[i] = exp( -(double)( 0.5 * x * x / var ) ) ;

    *num = points ;
    return y ;
}


/*********************** frames *******************************************/

/*
Return a ptr to the n'th frame (numbered 1,2,3,...,n), with given
framewidth (size) and frameshift (step). Return the null ptr if eof before
the n'th frame. Frame number n=0 refers to the last frame before eof.
Successive calls to getframe must request increasing frame numbers.

Data (binary shorts) is buffered in an array of "blocksize" shorts,
allocated internally, where:
    blocksize = size + ( frames_per_block - 1 ) * step
This means that the buffer can hold "frames_per_block" frames of the given
size and step, and ensures that no frame crosses the boundary between
successive buffers. At the buffer boundary, the overlap part
(where overlap = size - step) of the last frame in the buffer must
be shifted to the start of the buffer, and the step part of the next frame
must be read so as to follow it in the buffer, completing the first frame of
the next buffer.
To protect the last frame (in the event that the next step part is actually
the eof), the first frame of the next buffer must not corrupt the last frame
of the previous buffer. This is ensured by arranging that:
    size <= ( frames_per_block - 1 ) * step
This constrains frames_per_block to be:
    frames_per_block >= ( size / step ) + 1
Therefore the minimum number of frames_per_block (when step = size, and there
is no overlap) is 2.
The actual value you give to frames_per_block is an efficiency issue, being
a trade-off between space usage and the number of times a shift is necessary
at a buffer boundary. A reasonable guess would be (see define below):
    frames_per_block = ( size / step ) + 1 + FRAMES_PER_BLOCK

An example of a shifting frame buffer, from frame a to b inclusive in steps
of 1 frame, would be:

    while ( ( buf = getframe( fp, a, size, step ) ) != (short *)0  && ( a<=b || b==0 ) ) {
	process( buf, size ) ;
	a++ ;
    }
    if ( a<=b && b>0 )
	fprintf(stderr,"warning: not enough frames for request\n");

*/

#define FRAMES_PER_BLOCK    10

short *getframe( fp, n, size, step )
FILE *fp ;
int   n, size, step ;
{
    static int    first = 1 ;
    static short *buf, *endptr ;
    static short *bptr = (short *) 0 ;
    static int    blocksize, overlap, frames_per_block ;
    static int    m = 0 ;       /* frame counter */
    int           startsize, i ;

    if ( n < m )                /* catches invalid calls and also case of n==0 */
	return (short *) 0 ;

    if ( first ) {
	first = 0 ;
	frames_per_block = ( size / step ) + 1 + FRAMES_PER_BLOCK ;
	blocksize = size + ( frames_per_block - 1 ) * step ;
	overlap   = size - step ;
	bptr      = buf = (short *)malloc( blocksize * sizeof(short) ) ;
	endptr    = buf + blocksize ;

	if ( n > 0 ) {

	    /* seek the n'th frame in blocks of blocksize. */

	    startsize = ( n - 1 ) * step ;
	    for ( i = blocksize ; i < startsize ; i += blocksize )
		if ( fread( buf, sizeof(short), blocksize, fp ) < blocksize )
		    return (short *) 0 ;
	    if ( ( startsize -= ( i - blocksize ) ) > 0 )
		if ( fread( buf, sizeof(short), startsize, fp ) < startsize )
		    return (short *) 0 ;

	    if ( ( fread( bptr, sizeof(short), size, fp ) ) < size )
		return (short *) 0 ;
	    m = n ;
	}
	else {

	    /* seek the last frame in blocks of blocksize */

	    if ( fread( buf, sizeof(short), size, fp ) < size )
		return (short *) 0 ;
	    startsize = blocksize - size ;
	    for ( m = 1 ; ( i = fread( buf+size, sizeof(short), startsize, fp ) ) == startsize ; m += frames_per_block - 1 ) {
		bptr = endptr-size ;
		while ( bptr < endptr )
		    *buf++ = *bptr++ ;
		buf -= size ;
	    }
	    bptr = buf + ( i / step) * step ;
	    m += i / step ;
	}
	return ( bptr ) ;
    }

    for (  ; m < n || n == 0 ; m++ ) {

	if ( bptr + size >= endptr ) {  /* shift last overlap to buf start */
	    while ( bptr < endptr )
		*buf++ = *bptr++ ;
	    buf -= overlap ;
	    bptr = buf - step ;
	}

	if ( ( fread( bptr+size, sizeof(short), step, fp ) ) < step ) {
	    if ( n == 0 ) {
		if ( bptr < buf ) return ( endptr - size ) ;
		else              return ( bptr          ) ;
	    }
	    return (short *) 0 ;
	}
	bptr += step ;
    }
    return ( bptr ) ;
}


/*************************************************************************/

/*
Seek past n frames in blocks of blocksize.
Frames have width=size and shift=step.
When step == size, the seek reads ( n * size ) shorts from the i/p stream.
When step < size,  the seek reads ( size + ( n - 1 ) * step ) shorts.
Return 1, or otherwise 0 for an improper seek (insufficient data in stream).
*/

seekframes( fp, n, size, step )
FILE *fp ;
int   n, size, step ;
{
    static short *buf ;
    static int    first = 1 ;
    static int    blocksize, frames_per_block ;
    int           startsize = size + ( n - 1 ) * step ;
    int           i ;

    if ( first ) {
	first = 0 ;
	frames_per_block = ( size / step ) + 1 + FRAMES_PER_BLOCK ;
	blocksize = size + ( frames_per_block - 1 ) * step ;
	buf = (short *)malloc( blocksize * sizeof(short) ) ;
    }
    for ( i = blocksize ; i < startsize ; i += blocksize )
	if ( fread( buf, sizeof(short), blocksize, fp ) < blocksize )
	    return 0 ;
    if ( (startsize -= (i - blocksize)) > 0 )
	if ( fread( buf, sizeof(short), startsize, fp ) < startsize )
	    return 0 ;

    return 1 ;
}

/*************************************************************************/

/*
Seek past n bytes from current position in stream.
Return 1, or otherwise 0 for an improper seek (insufficient data in stream).
*/

seekbytes( fp, n )
FILE  *fp ;
int    n  ;
{
    static char  *buf ;
    static int    first = 1 ;

    if ( n > 0 ) {
	if ( first ) {
	    first = 0 ;
	    buf = (char *)malloc( n ) ;
	}

	if ( fread( buf, 1, n, fp ) < n )
	    return 0 ;
    }
    return 1 ;
}



/*********************** normalizing **************************************/

/*
Normalize the n-array y so that the sum of the points (ie the area) is unity.
*/

normalize_area( y, n )
float *y ;
int    n ;
{
    float  sum = 0 ;
    int    i ;

    for ( i=0 ; i < n ; i++ )
	sum += y[i] ;
    for ( i=0 ; i < n ; i++ )
	y[i] /= sum ;
}


/*
Normalize the n-array y so that the range of y[i] is [0,1].
*/

normalize_range( y, n )
float *y ;
int    n ;
{
    int   i ;
    float max = ( -256000. ) ;
    float min =    256000.   ;

    for ( i = 0 ; i < n ; i++ ) {       /* find max and min */
	if ( y[i] > max ) max = y[i] ;
	if ( y[i] < min ) min = y[i] ;
    }

    for ( i = 0 ; i < n ; i++ )
	y[i] = ( y[i] - min ) / ( max - min ) ;
}