#ifndef  _CODER_CLASS_HPP
#define  _CODER_CLASS_HPP

/*
**
** CoderC.hpp
**
** $VER: CoderC.hpp 1.0 (19.04.98)
**
** $Revision: 1.4 $
** $State: Exp $
** $Date: 1998/08/16 19:03:38 $
**
** $Log: CoderC.hpp $
** Revision 1.4  1998/08/16 19:03:38  kakace
** Version Beta3+
**
** Revision 1.3  1998/08/02 15:17:05  kakace
** Fonctionnement OK (Layer 1/2 + Stereo/JStereo)
**
** Revision 1.2  1998/07/27 02:16:38  kakace
** Séparation de la classe PsychoC
**
** Revision 1.1  1998/07/24 14:26:53  kakace
** Automatic global CheckIn
**
*/

#define USE_QUICK_PUTBITS


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

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

#ifndef  _INPUTSTREAM_CLASS_HPP
#include "InputStreamC.hpp"
#endif

#ifndef  _FILEINFOS_CLASS_HPP
#include "FileInfosC.hpp"
#endif


//----------------------------------------------------------------------------------------------------
//=========================================== Définitions ============================================
//----------------------------------------------------------------------------------------------------

// Status de l'encodeur.

enum e_EncoderST {
        ESTAT_READY,                    // Encodeur prêt à fonctionner.
        ESTAT_WARNING,                  // Avertissement.
        ESTAT_NOTREADY,                 // Encodeur non prêt.
        ESTAT_NOMEMORY,                 // Plus assez de mémoire.
        ESTAT_IOERROR,                  // Erreur I/O
        ESTAT_NOTSUPPORTED              // Layer non supporté.
    };

// Codes d'erreur secondaires.

enum e_EncErrCodes {
        ERR_NONE,                       // Pas d'erreur.
        ERR_NOT_INITIALIZED,            // Encodeur pas encore initialisé.
        ERR_ISTREAM_FAILURE,            // Erreur pendant la création du flux d'entrée.
        ERR_INTERNAL_BUFFERS,           // Des tampons n'ont pas pu être alloués.
        ERR_OPEN_OSTREAM,               // Erreur pendant l'ouverture du fichier de sortie.
        ERR_SEEK_OSTREAM,               // Erreur lors du positionnement en fin de fichier.
        ERR_WRITE_OSTREAM,              // Erreur pendant l'écriture du tampon de sortie.
        ERR_CLOSE_OSTREAM,              // Erreur pendant la fermeture du fichier de sortie.
        ERR_BAD_FREQUENCY,              // Fréquence d'échantillonage non acceptée.
    };

// Constantes diverses.

const ULONG SCALE_BLOCK =   12;
const ULONG HAN_SIZE    =  512;
const ULONG SCALE_RANGE =   64;
const ULONG LOGBLKSIZE  =   10;
const ULONG CBANDS      =   63;
const ULONG SBLIMIT     =   32;
const ULONG MAX_CH      =    2;

const REAL  CB_FRACTION =    0.33;
const REAL  DBMIN       = -200.0;
const REAL  POWERNORM   =   90.308998699194;    // 20 × Log10(32768)
const REAL  LXMIN       =   32.0;
const REAL  LN_TO_LOG10 =    0.2302585092994;   // log(10) / 10
const REAL  LOG10_005   =   -1.301029995664;    // Log10(0.05)
const REAL  LOG10_05    =   -0.301029995664;    // Log10(0.5)
const REAL  LOG10_2     =    0.301029995664;    // Log10(2)
const REAL  LOG10_TO_LN =    4.3429448190325;   // 10 / log(10)
const REAL  SQRT_00005  =    0.022360679775;    // sqrt(0.0005)


// Constantes personnelles (certaines sont basées sur de simples suppositions de ma part...)

const ULONG FRAME_SIZE    = SCALE_BLOCK * SBLIMIT;      // =  384
const ULONG MAX_FRAMESETS = 3;
const ULONG BIGGEST_FRAME = FRAME_SIZE * MAX_FRAMESETS; // = 1152

const ULONG OUTPUT_BUFFSIZE = 64 * 1024;                // 64 Ko (Attention au MaxTransfert)


//----------------------------------------------------------------------------------------------------
//========================================== Classe CoderC ===========================================
//----------------------------------------------------------------------------------------------------
//
//  Classe de base, dérivée pour obtenir une classe spécialisée par Layer.

typedef float T_ANAWINDOW;


class PsychoC;

class CoderC : public EncoderConfigC
{
    public:

        // Fonctions de traitement virtuelles.

        virtual int     Encode() = 0;
        virtual ULONG   BitsForNoNoise() = 0;
        virtual void    OneBitAllocation() = 0;
        virtual int     GetAudio();

        // Fonctions d'accès aux membres privés.

        void SetErrorCodes(e_EncoderST iPrimaryError, e_EncErrCodes iSecondaryError)
        {
            enc_iEncoderStatus  = iPrimaryError;
            enc_iSecondaryError = iSecondaryError;
        }

        e_EncoderST   GetEncoderStatus()  const {return enc_iEncoderStatus;}
        e_EncErrCodes GetSecondaryError() const {return enc_iSecondaryError;}

        ULONG       GetProgression()   const    {return enc_pInputHandle->GetProgression();}
        ULONG       GetNumChannels()   const    {return enc_bStereo;}
        WORD       *GetSamplePtr()     const    {return enc_pwSampleBuffer;}

    protected:

        // Constructeur et destructeur.

        CoderC(FileInfosC &roFileInfos, ULONG iReserved = 0);
        virtual ~CoderC();                      // Destructeur virtuel.

        // Fonctions "privées"

        typedef REAL  T_SubBandSmplStereo[MAX_CH][SCALE_BLOCK][SBLIMIT];
        typedef REAL  T_SubBandSmplMono[SCALE_BLOCK][SBLIMIT];
        typedef LONG  T_2_SBLIMIT[MAX_CH][SBLIMIT];
        typedef UBYTE T_ScaleStereo[MAX_CH][SBLIMIT];
        typedef UBYTE T_ScaleMono[SBLIMIT];
        typedef REAL  T_AnaFilter[128];

        typedef LONG T_F2_32[MAX_CH][SBLIMIT];

        void  SubBandCoding_Stereo(T_SubBandSmplStereo *pdSample);
        void  SubBandCoding_Mono(T_SubBandSmplMono *pdSample);

        void  ScaleFactor(T_ScaleStereo *pScalar, T_SubBandSmplStereo *pSample);
        void  ScaleFactorCalc(UBYTE *pbScalar, T_SubBandSmplMono *pdSample);

        void  MixChannels(T_SubBandSmplStereo *pSample, T_SubBandSmplMono *pJSample);
        void  MainBitAllocation();

        void  InitSlotsPerFrame(UWORD wSamplesPerFrame, UWORD wNumBitsN);
        void  SetJSBound(UBYTE mode_ext) {enc_bJSBound = (mode_ext + 1) * 4;}

        void  UpdateCRC(ULONG iData, ULONG iLength);
        void  CRC_Calc();
        void  FillFrame();

        // Fonction d'écriture dans le fichier destination.

    #ifdef USE_QUICK_PUTBITS
        void    PutBits(ULONG iSource, ULONG iLength) {
            UWORD *p = BitBuffPtr;
            *p++ = iLength;
            *p++ = iSource;
            BitBuffPtr = p;
        }
        void    InitBitBuffer() {BitBuffPtr = bit_buffer;}

        UWORD   *BitBuffPtr;
    #else
        void    PutBits(ULONG iSource, ULONG iLength);
    #endif

        // Membres données.

        FileInfosC      &enc_roFileInfos;           // Objet FileInfosC.
        PsychoC         *enc_poPsychoC;             // Objet PsychoC.
        InputStreamC    *enc_pInputHandle;          // Handle vers le flux d'entrée.
        e_EncoderST      enc_iEncoderStatus;        // Statut de l'encodeur (codes d'erreur, etc)
        e_EncErrCodes    enc_iSecondaryError;       // Code d'erreur secondaire.

        // Variables générales.

        UWORD            enc_wSamplesPerFrame;      // Nombre d'échantillons par page.
        UBYTE            enc_bBitsPerSlot;          // Nombre de bits par Slot.
        UBYTE            enc_bBitsPerSlotN;         // Décalage en rapport avec le nombre de bits par Slot.

        LONG             enc_iAvgDataBits;          // (adb)
        UWORD            enc_wSlotsPerFrame;        // Nombre de Slots par page.
        UWORD            enc_wSPF_Reminder;         //
        UWORD            enc_wSlotFree;             // Place disponible dans le dernier slot utilisé.
        WORD             enc_wFrameSize;            // Nombre d'échantillons lus à chaque passe.

        WORD            *enc_pSamples;              // Pointeur vers le buffer contenant les échantillons.
        WORD            *enc_pwSampleBuffer;        // Pointeur vers les échantillons à encoder.
        T_AnaFilter     *enc_pdAnaFilter;
        WORD            *enc_pSB_ActualPtr;         // Pointeur de lecture dans le tampon d'entrée
        T_F2_32          enc_LTMin;                 // ltmin[][]
        T_ScaleStereo    enc_bBitAlloc;             // bit_alloc[][]
        ULONG            enc_iFrameNumber;          // Numéro de la page courante.
        UBYTE            enc_bStereo;               // 1 = Mono, 2 = Stéréo/Joint-stéréo/Dual-Channel
        UBYTE            enc_bPad;
        UBYTE            enc_bSBLimit;              // Valeur SBLIMIT suivant le type de Layer.
        UBYTE            enc_bJSBound;              // Valeur "jsbound".
        ULONG            enc_iMinNeededBits;        // Nombre de bits nécessaires pour l'entête et le CRC.
        UWORD            enc_wCRC;                  // Somme de contrôle.
        UWORD            enc_wPad;
                                                    // (évolue au fur et à mesure de la progression).

        // Bloc d'entête pour chaque "frame"

        struct BSHeader {LONGBITS bsh_SyncWord:12;
                         LONGBITS bsh_Version:1;
                         LONGBITS bsh_Layer:2;
                         LONGBITS bsh_CRC:1;
                         LONGBITS bsh_Bitrate:4;
                         LONGBITS bsh_SmplFreq:2;
                         LONGBITS bsh_Padding:1;
                         LONGBITS bsh_Extension:1;
                         LONGBITS bsh_Mode:2;
                         LONGBITS bsh_ModeExt:2;
                         LONGBITS bsh_Copyright:1;
                         LONGBITS bsh_Original:1;
                         LONGBITS bsh_Emphasis:2;
                        };

        union BitStreamHeader {ULONG    iBSHeader;
                               BSHeader sBSHeader;
                              } enc_BSHeader;

        double enc_adMultiples[64];

    private:

        void    InitAnaFilter();
        int     GetIndex(double real);
        int     GetIndex(float real);
        void    WriteBuffer();
        void    InitWrite();
        void    CleanupWrite();

        // Gestion du fichier encodé.
        // NE PAS TOUCHER A CETTE STRUCTURE SANS MODIFIER AUSSI LE SOURCE ASM !!!

        struct s_OutputStream {void   *pOutputBuffer;
                               ULONG   iOB_BitIndex;
                               ULONG   iOB_TotalBits;
                               ULONG   iOB_BitsFree;
                              };

        s_OutputStream   enc_sOutputStreamDatas;
        BPTR             enc_bpOB_FileLock;

    #ifdef USE_QUICK_PUTBITS
        UWORD   bit_buffer[6000];
    #endif

#if _USE_ASM_FCT_ == 1 || _CPUMODEL_ == 68020
        void PutBitsASM(s_OutputStream *pStreamDatas, ULONG iValue, ULONG iLength);
        void PutBitsASM2(s_OutputStream *pStreamDatas, UWORD *buffer, UWORD *lastPos);
#endif
};


//----------------------------------------------------------------------------------------------------
//========================================= Classe CoderL1C ==========================================
//----------------------------------------------------------------------------------------------------
//
//  Encodage Layer 1

class CoderL1C : public CoderC
{
    public:

        CoderL1C(FileInfosC &roFileInfos);
        ~CoderL1C();

        void    CRC_Calc();
        void    EncodeScale();          // encode_scale + encode_bit_alloc
        void    EncodeSamples();
        void    EncodeSubBand();

        // Fonctions virtuelles.

        int     GetAudio();
        ULONG   BitsForNoNoise();
        void    OneBitAllocation();
        int     Encode();

    private:

        // Tableaux et autres tampons globaux

        T_SubBandSmplStereo *enc_pdSB_Samples;          // Tampon des échantillons pré-analysés.
        T_SubBandSmplMono   *enc_pdJ_Samples;           // Tampon des échantillons pré-analysés (JOINTSTEREO)
        T_ScaleStereo        enc_bScalar;               // Tableau des indices vers le tableau multiple[].
        T_ScaleMono          enc_bJScale;               // Tableau des indices vers le tableau multiple[] (JOINTSTEREO)

        static LONG         snr[15];
        static REAL         quant_a[14];
};

LONG CoderL1C::snr[15] = {    0,  7000, 16000, 25280, 31590, 37750, 43840,
                          49890, 55930, 61960, 67980, 74010, 80030, 86050,
                          92010
                         };

REAL CoderL1C::quant_a[14] = {0.750000000, 0.875000000, 0.968750000, 0.984375000,
                              0.992187500, 0.996093750, 0.998046875, 0.999023438,
                              0.999511719, 0.999755859, 0.999877930, 0.999938965,
                              0.999969482, 0.999984741
                             };


//----------------------------------------------------------------------------------------------------
//========================================= Classe CoderL2C ==========================================
//----------------------------------------------------------------------------------------------------
//
//  Encodage Layer 2

#define MAX_ALLOC_TABLES     4
#define MAX_ALLOC_LINES     30
#define MAX_ALLOC_COLS      16


class CoderL2C : public CoderC
{
    public:

        CoderL2C(FileInfosC &roFileInfos);
        ~CoderL2C();

        void    CRC_Calc();
        void    EncodeScale();          // encode_scale() + encode_bit_alloc()
        void    EncodeSamples();
        void    EncodeSubBand();

        // Fonctions virtuelles.

        int     GetAudio();
        ULONG   BitsForNoNoise();
        void    OneBitAllocation();
        int     Encode();

    private:

        // Données nécessaires à la génération des tables d'encodage.

        struct s_InitTbl { UWORD wSteps;
                           UBYTE bBits;
                           UBYTE bGroup;
                           UBYTE bQuant;
                         };

        struct s_SBAlloc { UWORD  wSteps;                     // Valeurs = [0,65535]
                           UBYTE  bBits;                      // Valeurs = [2;16]
                           UBYTE  bGroup;                     // Valeurs = [0;3]
                           UWORD  wQuant;                     // valeurs = [0;16]
                           UWORD  wStepMask;
                           ULONG  iTotalBits;                 // SCALE_BLOCK * iGroup * iBits
                           ULONG  iBitMask;                   // 1 << iBits
                         };

        typedef s_SBAlloc T_AllocTable[MAX_ALLOC_LINES][MAX_ALLOC_COLS];

        UBYTE InitAllocTables(T_AllocTable *paTable, UBYTE iTable);
        void  TransmissionPattern();

        T_AllocTable        *enc_psAllocTable;      // Table courante.

        // Tableaux et autres tampons globaux

        typedef REAL  T_SB_Samples[MAX_FRAMESETS][MAX_CH][SCALE_BLOCK][SBLIMIT];
        typedef REAL  T_J_Samples[MAX_FRAMESETS][SCALE_BLOCK][SBLIMIT];
        typedef UBYTE T_Scalar[MAX_FRAMESETS][MAX_CH][SBLIMIT];
        typedef UBYTE T_JScale[MAX_FRAMESETS][SBLIMIT];

        T_SB_Samples    *enc_pdSB_Samples;          // Tampon des échantillons pré-analysés.
        T_J_Samples     *enc_pdJ_Samples;           // Tampon des échantillons pré-analysés (JOINTSTEREO)
        T_Scalar         enc_bScalar;               // Tableau des indices vers le tableau multiple[].
        T_JScale         enc_bJScale;               // Tableau des indices vers le tableau multiple[] (JOINTSTEREO)
        T_ScaleStereo    enc_bScaleFactorInfo;      // scfsi[][]

        static LONG         snr[18];
        static ULONG        sfsPerScfsi[4];
        static REAL         quant_a[17];
};

LONG CoderL2C::snr[18] = {    0,  7000, 11000, 16000, 20840, 25280,
                          31590, 37750, 43840, 49890, 55930, 61960,
                          67980, 74010, 80030, 86050, 92010, 98010
                         };

ULONG CoderL2C::sfsPerScfsi[] = {20, 14, 8, 14};

REAL CoderL2C::quant_a[17] = {0.750000000, 0.625000000, 0.875000000, 0.562500000, 0.937500000,
                              0.968750000, 0.984375000, 0.992187500, 0.996093750, 0.998046875,
                              0.999023438, 0.999511719, 0.999755859, 0.999877930, 0.999938965,
                              0.999969482, 0.999984741
                             };


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

#endif  // _CODER_CLASS_HPP

