#ifndef _FASTFFT_CLASS_HPP
#define _FASTFFT_CLASS_HPP 1

/*
**
** $VER: FastFFTC.hpp 1.8 (21.8.2000)
**
*/

#ifndef  _PEGASECOND_HPP
#include "PegaseCond.hpp"
#endif

#ifndef  EXEC_TYPES_H
#include <exec/types.h>
#endif


#if _USE_ASM_FCT_ == 0 && !defined __PPC__
typedef double T_FFTDATAS;
#else
typedef float T_FFTDATAS;
#endif


#define _ASM_WINSAMPLES_    _USE_ASM_FCT_
#define _ASM_FFT_           _USE_ASM_FCT_

#ifdef DEBUG_FFT
#include "Debug_Tools.hpp"
#endif


//----------------------------------------------------------------------------------------------------
//========================================= Classe FastFFTC ==========================================
//----------------------------------------------------------------------------------------------------

const ULONG SINCOSSIZE  =  768;
const ULONG BLKSIZE     = 1024;
const ULONG HBLKSIZE    =  513;


//----------------------------------------------------------------------------------------------------
// Version n'utilisant pas de table pour les valeurs Sin/Cos

class FastFFTC
{
    private:

        // Fonctions nécessaires lorsqu'on n'utilise pas les versions optimisées en ASM.

        #if  _ASM_FFT_ == 0
        void FastFFT(T_FFTDATAS *pd_FFTIn, ULONG iFFTSize);
        void FixFFT_Stereo(T_FFTDATAS *pd_FFTIn, ULONG iFFTSize);
        void FixFFT_Mono(T_FFTDATAS *pd_FFTIn, ULONG iFFTSize);
        #endif

        ULONG   GetShuffleIndex_10(ULONG input);
        ULONG   GetShuffleIndex_8(ULONG input);

    protected:

        typedef T_FFTDATAS  T_HANN[BLKSIZE];
        typedef T_FFTDATAS  T_FFTBUFF[2][BLKSIZE][2];

        // Sélection du type de table Sin/Cos.
        // A partir du 68040, il est plus rapide de dériver les valeurs sin() et cos()
        // depuis les valeurs précédentes, plutôt que d'utiliser une table précalculée
        // (cette table demandant de nombreux accès mémoire). En contrepartie, les
        // calculs doivent être effectués en double précision.

        #if _USE_SINCOS_TABLE_ == 1
        typedef float T_SINCOS[SINCOSSIZE];
        T_SINCOS      FFT_dSinCos;
        #else
        double        FFT_dSinCos[2][10];
        #endif

        T_HANN       *FFT_pdHannWindowShuffled;
        T_FFTBUFF    *FFT_pdWinSmplShuffled;     // (wsamp_r[]/wsamp_i[])
        int           FFT_MemoryError;
        int           FFT_Pad;

    public:

        FastFFTC();
        ~FastFFTC();

        void ComputeFastFFT_Stereo(ULONG iFFTSize);
        void ComputeFastFFT_Mono(ULONG iFFTSize);
        void SetupWinSamples_1024(WORD *pSamples, UWORD wNumChannels);

        T_FFTBUFF       *FFT_pdWindowedSmpl;        // (wsamp_r[]/wsamp_i[])
        T_HANN          *FFT_pdHannWindow;
};

//----------------------------------------------------------------------------------------------------

#if _ASM_WINSAMPLES_ == 1
extern "C" {
void SetupWinSamples_1024(WORD *pSamples, UWORD wNumChannels, const T_FFTDATAS *pdHANNWindow, T_FFTDATAS *pdWSmpls);
};

inline void FastFFTC::SetupWinSamples_1024(WORD *pSamples, UWORD wNumChannels)
{
    ::SetupWinSamples_1024(pSamples, wNumChannels, (const T_FFTDATAS *) FFT_pdHannWindowShuffled, (T_FFTDATAS *) FFT_pdWinSmplShuffled);
}
#endif


#if _ASM_FFT_ == 1
extern "C" {
void FastFFT_Stereo_C(T_FFTDATAS *pdFFTIn, double dSinCos[2][10], ULONG iFFTSize);
void FastFFT_Stereo_T(T_FFTDATAS *pdFFTIn, float dSinCos[SINCOSSIZE], ULONG iFFTSize);
void FastFFT_Mono_C(T_FFTDATAS *pdFFTIn, double dSinCos[2][10], ULONG iFFTSize);
void FastFFT_Mono_T(T_FFTDATAS *pdFFTIn, float dSinCos[SINCOSSIZE], ULONG iFFTSize);
};
#endif


inline void FastFFTC::ComputeFastFFT_Stereo(ULONG iFFTSize)
{
    T_FFTDATAS *pd_FFTIn = (T_FFTDATAS *) FFT_pdWinSmplShuffled;

#if _ASM_FFT_ == 0
    FastFFT(pd_FFTIn, iFFTSize);
    FixFFT_Stereo(pd_FFTIn, iFFTSize);
#else
    // Sélection de la fonction à utiliser suivant le type de la table Sin/Cos.
    #if _USE_SINCOS_TABLE_ == 1
    ::FastFFT_Stereo_T(pd_FFTIn, FFT_dSinCos, iFFTSize);
    #else
    ::FastFFT_Stereo_C(pd_FFTIn, FFT_dSinCos, iFFTSize);
    #endif

#endif

#ifdef DEBUG_FFT
    for (int i = 0; i < iFFTSize; i++)
        debugFFT.UpdateBand(i, pd_FFTIn[i]);
#endif
}


inline void FastFFTC::ComputeFastFFT_Mono(ULONG iFFTSize)
{
    T_FFTDATAS *pd_FFTIn = (T_FFTDATAS *) FFT_pdWinSmplShuffled;

#if _ASM_FFT_ == 0
    iFFTSize >>= 1;
    FastFFT(pd_FFTIn, iFFTSize);
    FixFFT_Mono(pd_FFTIn, iFFTSize);
#else
    // Sélection de la fonction à utiliser suivant le type de la table Sin/Cos.
    #if _USE_SINCOS_TABLE_ == 1
    ::FastFFT_Mono_T(pd_FFTIn, FFT_dSinCos, iFFTSize);
    #else
    ::FastFFT_Mono_C(pd_FFTIn, FFT_dSinCos, iFFTSize);
    #endif
#endif

#ifdef DEBUG_FFT
    for (int i = 0; i < iFFTSize; i++)
        debugFFT.UpdateBand(i, pd_FFTIn[i]);
#endif
}

#endif  // _FASTFFT_CLASS_HPP

