/*----------------------------------------------------------------------------
  ChucK Strongly-timed Audio Programming Language
    Compiler and Virtual Machine

  Copyright (c) 2003 Ge Wang and Perry R. Cook. All rights reserved.
    http://chuck.cs.princeton.edu/
    http://soundlab.cs.princeton.edu/

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  U.S.A.
-----------------------------------------------------------------------------*/

//-----------------------------------------------------------------------------
// name: util_xform.c
// desc: transforms
//
// authors: Ge Wang (gewang@cs.princeton.edu)
//          Perry R. Cook (prc@cs.princeton.edu)
//          FFT: CARL music distribution
// date: 11.27.2003
//-----------------------------------------------------------------------------
#include "util_xforms.h"
#include <stdlib.h>
#include <math.h>




//-----------------------------------------------------------------------------
// name: hanning()
// desc: make window
//-----------------------------------------------------------------------------
void hanning( FLOAT_XFORM * window, unsigned long length )
{
    unsigned long i;
    double pi, phase = 0, delta;

    pi = 4.*atan(1.0);
    delta = 2 * pi / (double) length;

    for( i = 0; i < length; i++ )
    {
        window[i] = (FLOAT_XFORM)(0.5 * (1.0 - cos(phase)));
        phase += delta;
    }
}




//-----------------------------------------------------------------------------
// name: hamming()
// desc: make window
//-----------------------------------------------------------------------------
void hamming( FLOAT_XFORM * window, unsigned long length )
{
    unsigned long i;
    double pi, phase = 0, delta;

    pi = 4.*atan(1.0);
    delta = 2 * pi / (double) length;

    for( i = 0; i < length; i++ )
    {
        window[i] = (FLOAT_XFORM)(0.54 - .46*cos(phase));
        phase += delta;
    }
}




//-----------------------------------------------------------------------------
// name: blackman()
// desc: make window
//-----------------------------------------------------------------------------
void blackman( FLOAT_XFORM * window, unsigned long length )
{
    unsigned long i;
    double pi, phase = 0, delta;

    pi = 4.*atan(1.0);
    delta = 2 * pi / (double) length;

    for( i = 0; i < length; i++ )
    {
        window[i] = (FLOAT_XFORM)(0.42 - .5*cos(phase) + .08*cos(2*phase));
        phase += delta;
    }
}




//-----------------------------------------------------------------------------
// name: bartlett()
// desc: make window
//-----------------------------------------------------------------------------
void bartlett( FLOAT_XFORM * window, unsigned long length )
{
    unsigned long i;
    FLOAT_XFORM half = (FLOAT_XFORM)length / 2;

    for( i = 0; i < length; i++ )
    {
        if( i < half ) window[i] = i / half;
        else window[i] = (length - i) / half;
    }
}




//-----------------------------------------------------------------------------
// name: apply_window()
// desc: apply a window to data
//-----------------------------------------------------------------------------
void apply_window( FLOAT_XFORM * data, FLOAT_XFORM * window, unsigned long length )
{
    unsigned long i;

    for( i = 0; i < length; i++ )
        data[i] *= window[i];
}

static FLOAT_XFORM PI ;
static FLOAT_XFORM TWOPI ;
void bit_reverse( FLOAT_XFORM * x, long N );

//-----------------------------------------------------------------------------
// name: rfft()
// desc: real value fft
//
//   these routines from the CARL software, spect.c
//   check out the CARL CMusic distribution for more source code
//
//   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.
//
//-----------------------------------------------------------------------------
void rfft( FLOAT_XFORM * x, long N, unsigned int forward )
{
    static int first = 1 ;
    FLOAT_XFORM c1, c2, h1r, h1i, h2r, h2i, wr, wi, wpr, wpi, temp, theta ;
    FLOAT_XFORM xr, xi ;
    long i, i1, i2, i3, i4, N2p1 ;

    if( first )
    {
        PI = (FLOAT_XFORM) (4.*atan( 1. )) ;
        TWOPI = (FLOAT_XFORM) (8.*atan( 1. )) ;
        first = 0 ;
    }

    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 = (FLOAT_XFORM) (-2.*pow( sin( 0.5*theta ), 2. )) ;
    wpi = (FLOAT_XFORM) 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 ) ;
}




//-----------------------------------------------------------------------------
// name: cfft()
// desc: complex value fft
//
//   these routines from CARL software, spect.c
//   check out the CARL CMusic distribution for more software
//
//   cfft replaces FLOAT_XFORM array x containing NC complex values
//   (2*NC FLOAT_XFORM values alternating real, imagininary, etc.)
//   by its Fourier transform; if forward is true, or by its inverse
//   Fourier transform ifforward is false, using a recursive Fast Fourier
//   transform method due to Danielson and Lanczos.
//
//   NC MUST be a power of 2.
//-----------------------------------------------------------------------------
void cfft( FLOAT_XFORM * x, long NC, unsigned int forward )
{
    FLOAT_XFORM wr, wi, wpr, wpi, theta, scale ;
    long mmax, ND, m, i, j, delta ;
    ND = NC<<1 ;
    bit_reverse( x, ND ) ;
    
    for( mmax = 2 ; mmax < ND ; mmax = delta )
    {
        delta = mmax<<1 ;
        theta = TWOPI/( forward? mmax : -mmax ) ;
        wpr = (FLOAT_XFORM) (-2.*pow( sin( 0.5*theta ), 2. )) ;
        wpi = (FLOAT_XFORM) sin( theta ) ;
        wr = 1. ;
        wi = 0. ;

        for( m = 0 ; m < mmax ; m += 2 )
        {
            /* register */ FLOAT_XFORM 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 = (FLOAT_XFORM)(forward ? 1./ND : 2.) ;
    {
        /* register */ FLOAT_XFORM *xi=x, *xe=x+ND ;
        while( xi < xe )
            *xi++ *= scale ;
    }
}




//-----------------------------------------------------------------------------
// name: bit_reverse()
// desc: bitreverse places FLOAT_XFORM array x containing N/2 complex values
//       into bit-reversed order
//-----------------------------------------------------------------------------
void bit_reverse( FLOAT_XFORM * x, long N )
{
    FLOAT_XFORM rtemp, itemp ;
    long 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 ;
    }
}




//-----------------------------------------------------------------------------
// name: the_dct()
// desc: type ii dct on N reals
//-----------------------------------------------------------------------------
void the_dct( FLOAT_XFORM * x, unsigned long N, FLOAT_XFORM * out, unsigned long Nout )
{
    unsigned long k, n;

    // sanity check
    assert( Nout <= N );
    // zero out
    memset( out, 0, sizeof(FLOAT_XFORM)*Nout );

    // go for it
    for( k = 0; k < Nout; k++ )
    {
        for( n = 0; n < N; n++ )
        {
            out[k] += x[n] * cos( CK_ONE_PI / N * k * (n + .5) );
        }
    }
}





//-----------------------------------------------------------------------------
// name: the_dct_matrix()
// desc: type ii dct matrx on N reals
//-----------------------------------------------------------------------------
void the_dct_matrix( FLOAT_XFORM ** out, unsigned long N )
{
    unsigned long k, n;

    // go for it
    for( k = 0; k < N; k++ )
    {
        for( n = 0; n < N; n++ )
        {
            out[k][n] = cos( CK_ONE_PI / N * k * (n + .5) );
        }
    }
}


//-----------------------------------------------------------------------------
// name: the_dct_now()
// desc: apply dct matrx on N reals
//-----------------------------------------------------------------------------
void the_dct_now( FLOAT_XFORM * x, FLOAT_XFORM ** matrix, unsigned long N,
                  FLOAT_XFORM * out, unsigned long Nout )
{
    unsigned long k, n;

    // sanity check
    assert( Nout <= N );
    // zero out
    memset( out, 0, sizeof(FLOAT_XFORM)*Nout );

    // go for it
    for( k = 0; k < Nout; k++ )
    {
        for( n = 0; n < N; n++ )
        {
            out[k] = x[n] * matrix[k][n];
        }
    }
}




//-----------------------------------------------------------------------------
// name: the_inverse_dct()
// desc: type iii dct, or inverse dct
//-----------------------------------------------------------------------------
void the_inverse_dct( FLOAT_XFORM * x, unsigned long N, FLOAT_XFORM * out, unsigned long Nout )
{
}


//-----------------------------------------------------------------------------
// name: the_inverse_dct_matrix()
// desc: generates the NxN DCT matrix
//-----------------------------------------------------------------------------
void the_inverse_dct_matrix( FLOAT_XFORM ** out, unsigned long N )
{
    unsigned long k, n;

    // go for it
    for( k = 0; k < N; k++ )
    {
        for( n = 0; n < N; n++ )
        {
            out[k][n] = cos( CK_ONE_PI / N * n * (k + .5) );
        }
    }
}




//-----------------------------------------------------------------------------
// name: the_inverse_dct_now()
// desc: apply inverse dct matrx on N reals
//-----------------------------------------------------------------------------
void the_inverse_dct_now( FLOAT_XFORM * x, FLOAT_XFORM ** matrix, unsigned long N,
                  FLOAT_XFORM * out, unsigned long Nout )
{
    unsigned long k, n;

    // sanity check
    assert( Nout <= N );
    // zero out
    memset( out, 0, sizeof(FLOAT_XFORM)*Nout );

    // go for it
    for( k = 0; k < Nout; k++ )
    {
        for( n = 0; n < N; n++ )
        {
            out[k] = x[0] / 2 + x[n] * matrix[k][n];
        }
    }
}
