#ifndef  _CODER_CLASS_HPP
#define  _CODER_CLASS_HPP

/*
**
** $VER: CoderC.hpp 1.8 (15.8.2000)
**
*/


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

#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.
    ERR_DISKFULL,                   // Place insuffisante sur le disque.
    ERR_WRITEPROTECTED,             // Disque/Fichier inaccessible en écriture.
};

// 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 MAX_CH      =    2;

#define SBLIMIT 32

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 = 127 * 1024;               // 127 Ko


//----------------------------------------------------------------------------------------------------
//========================================== 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_NumChannels;}
        WORD       *GetSamplePtr()     const    {return enc_pwSampleBuffer;}
        void        CloseIStream();

    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(ULONG wSamplesPerFrame, ULONG wNumBitsN);
        void  SetJSBound(ULONG mode_ext) {enc_JSBound = (mode_ext + 1) * 4;}

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

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

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

        // 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.

        ULONG            enc_SamplesPerFrame;       // Nombre d'échantillons par page.
        ULONG            enc_BitsPerSlot;           // Nombre de bits par Slot.
        ULONG            enc_BitsPerSlotN;          // Décalage en rapport avec le nombre de bits par Slot.

        ULONG            enc_AvgDataBits;           // (adb)
        ULONG            enc_SlotsPerFrame;         // Nombre de Slots par page.
        ULONG            enc_SPF_Reminder;
        ULONG            enc_SlotLag;
        ULONG            enc_SlotRem;
        ULONG            enc_FrameSize;             // 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[][]
        ULONG            enc_FrameNumber;           // Numéro de la page courante.
        ULONG            enc_isStereo;              // 0 = Mono, ~0 = 2 channels
        ULONG            enc_MinNeededBits;         // 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).

        // Données utilisées par la version ASM d'EncodeSamples()

        ULONG            enc_SBLimit;               // Valeur SBLIMIT suivant le type de Layer.
        ULONG            enc_JSBound;               // Valeur "jsbound".
        ULONG            enc_NumChannels;           // 1 = Mono, 2 = Stéréo/Joint-stéréo/Dual-Channel
        UWORD           *BitBuffPtr;
        T_ScaleStereo    enc_bBitAlloc;             // bit_alloc[][]
        double enc_adMultiples[64];

        // 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;

    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;
        UWORD   bit_buffer[6000];

        void PutBitsASM(s_OutputStream *pStreamDatas, UWORD *buffer, UWORD *lastPos);
};


//----------------------------------------------------------------------------------------------------
//========================================= 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[16];
        static REAL         quant_a[15];
};

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

REAL CoderL1C::quant_a[15] = {
    0.750000000, 0.875000000, 0.937500000, 0.968750000, 0.984375000,    // 2^n - 1 / 2^n
    0.992187500, 0.996093750, 0.998046875, 0.999023438, 0.999511719,    // 1 < n < 17
    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];

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

        UBYTE                enc_BitSum[MAX_ALLOC_LINES + 2];
        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

