/*
**
** CoderC.cpp
**
** $VER: CoderC.cpp 1.0 (05.06.99)
**
** $Revision: 1.5 $
** $State: Exp $
** $Date: 1998/08/16 19:03:39 $
**
** $Log: CoderC.cpp $
** Revision 1.5  1998/08/16 19:03:39  kakace
** Version Beta3+
**
** Revision 1.4  1998/08/02 15:17:05  kakace
** Fonctionnement OK (Layer 1/2 + Stereo/JStereo)
**
** Revision 1.3  1998/08/02 02:13:45  kakace
** Modes stereo Ok (?)
**
*/


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

/// Includes

#ifndef  _INCLUDE_STDLIB_H
#include <stdlib.h>
#endif

#ifndef  _INCLUDE_MATH_H
#include <math.h>
#endif

#ifndef  _INCLUDE_IOSTREAM_H
#include <iostream.h>
#endif

#ifndef  CLIB_DOS_PROTOS_H
#include <clib/dos_protos.h>
#endif

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

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

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

#ifndef  _COMMON_HPP
#include "common.hpp"
#endif

#ifndef  _MEMORY_HPP
#include "Memory.hpp"
#endif

#ifndef  _CODER_CLASS_HPP
#include "CoderC.hpp"
#endif

#ifndef  _PSYCHO_CLASS_HPP
#include "PsychoC.hpp"
#endif

#ifndef  _ENCODERCONFIG_CLASS_HPP
#include "EncoderConfigC.hpp"
#endif

///


//----------------------------------------------------------------------------------------------------
//====================================== Classe EncoderConfigC =======================================
//----------------------------------------------------------------------------------------------------

/// EncoderConfigC::COPY CONSTRUCTOR
/****** Class EncoderConfigC/COPY CONSTRUCTOR *******************************
*
*   NAME
*   EncoderConfigC::EncoderConfigC -- Constructeur de recopie
*
*   SYNOPSIS
*   EncoderConfigC::EncoderConfigC(roConfig)
*
*   EncoderConfigC::EncoderConfigC(const EncoderConfigC &);
*
*   FUNCTION
*   Recopie les réglages de l'encodeur.
*
*   INPUTS
*   roConfig - Réglages à recopier.
*
*   NOTES
*   Cette fonctionnalité est utilisée pour initialiser les réglages locaux
*   de l'encodeur pour chaque fichier, en partant des réglages par défaut.
*
*****************************************************************************
*
*/


EncoderConfigC::EncoderConfigC(const EncoderConfigC &roConfig)
{
    const EncoderConfigC *const poConfig = &roConfig;

    // Recopier les réglages par défaut.

    ecfg_iLayer       = poConfig->ecfg_iLayer;
    ecfg_wSampling    = poConfig->ecfg_wSampling;
    ecfg_wBitrate     = poConfig->ecfg_wBitrate;
    ecfg_sFlags       = poConfig->ecfg_sFlags;
    ecfg_sModes       = poConfig->ecfg_sModes;
    ecfg_wTargetType  = poConfig->ecfg_wTargetType;
    ecfg_iIntelFormat = poConfig->ecfg_iIntelFormat;
    ecfg_CDDAFmt      = poConfig->ecfg_CDDAFmt;

    SetOutputName(poConfig->GetOutputName());
}
///
/// EncoderConfigC::SetBitRate()
/****** Class EncoderConfigC/SetBitRate ****************************************
*
*   NAME
*   EncoderConfigC::SetBitRate -- Définir le taux de sortie de l'encodeur.
*
*   SYNOPSIS
*   EncoderConfigC::SetBitRate(wBitRate)
*
*   void EncoderConfigC::SetBitRate(UWORD);
*
*   FUNCTION
*   Mémorise le taux de sortie de l'encodeur, ainsi que l'index correspondant.
*   Si le taux de sortie demandé n'existe pas dans la table, la fonction sélec-
*   tionne la valeur inférieure la plus proche.
*   Si le taux de sortie demandé est inférieur à la plus petite valeur de la
*   table, la fonction sélectionne la valeur par défaut (128 kbits/s)/
*
*   INPUTS
*   wBitRate - Taux de sortie souhaité (kbits/s)
*
*****************************************************************************
*
*/


void EncoderConfigC::SetBitRate(UWORD wBitRate)
{
    static UWORD a_wBitrate[3][14] =
    {
          {32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, // Layer I
          {32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384}, // Layer II
          {32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320}  // Layer III
    };

    int iBrate = 0;
    int ibr;
    int iLayer = ecfg_iLayer;
    ecfg_wBitrate = 0;

    // Utiliser le taux de sortie légal immédiatement inférieur à la valeur indiquée.

    while (wBitRate >= (ibr = a_wBitrate[iLayer][iBrate]) && iBrate < 14)
    {
        if (wBitRate == ibr)
        {
            ecfg_wBitrate = a_wBitrate[iLayer][iBrate];
            ecfg_sModes.bitrate = iBrate;
            break;
        }
        ++iBrate;
    }

    // Si le taux de sortie choisi n'a pas été trouvé, sélectionner la valeur inférieure
    // la plus proche, ou la valeur par défaut.

    if (ecfg_wBitrate == 0)
    {
        if (iBrate > 0 && iBrate < 14)          // Prendre la valeur inférieure.
        {
            ecfg_sModes.bitrate = --iBrate;
            ecfg_wBitrate       = a_wBitrate[iLayer][iBrate];
        }
        else                                    // Prendre la valeur par défaut.
        {
            ecfg_wBitrate = 160;

            if      (iLayer == LAYER_I)     ecfg_sModes.bitrate = 4;
            else if (iLayer == LAYER_II)    ecfg_sModes.bitrate = 8;
            else                            ecfg_sModes.bitrate = 9;
        }
    }
}
///
/// EncoderConfigC::SetSampleFreq()
/****** Class EncoderConfigC/SetSampleFreq *************************************
*
*   NAME
*   EncoderConfigC::SetSampleFreq -- Définir la fréquence d'échantillonage.
*
*   SYNOPSIS
*   success = EncoderConfigC::SetSampleFreq(wSampleFreq)
*
*   bool EncoderConfigC::SetSampleFreq(UWORD);
*
*   FUNCTION
*   Mémorise la fréquence d'échantillonage choisie par l'utilisateur ou
*   spécifiée par le fichier audio à encoder.
*   La tolérance pour cette fréquence d'échantillonage est de +/- 4% par
*   rapport aux valeurs autorisées (32 KHz, 44.1 KHz et 48 KHz). Toutes les
*   fréquences hors de ces limites sont rejetées.
*
*   INPUTS
*   wSampleFreq - Fréquence d'échantillonage à utiliser par défaut (Hz)
*
*   RESULT
*   success - FALSE si la fréquence est incorrecte.
*
*****************************************************************************
*
*/


int EncoderConfigC::SetSampleFreq(UWORD wSampleFreq)
{
    if      (wSampleFreq >= 30720 && wSampleFreq <= 33280)
    {
            ecfg_wSampling = 32000;
            ecfg_sModes.sampfreq = F_32000;
    }
    else if (wSampleFreq >= 42336 && wSampleFreq <= 45864)
    {
            ecfg_wSampling = 44100;
            ecfg_sModes.sampfreq = F_44100;
    }
    else if (wSampleFreq >= 46080 && wSampleFreq <= 49920)
    {
            ecfg_wSampling = 48000;
            ecfg_sModes.sampfreq = F_48000;
    }
    else
    {
            ecfg_wSampling = 0;
            ecfg_sModes.sampfreq = F_ILLEGAL;
            return FALSE;
    }

    return TRUE;
}
///

//----------------------------------------------------------------------------------------------------
//========================================== Classe CoderC ===========================================
//----------------------------------------------------------------------------------------------------
//
//  CoderC est la classe de base, dérivée en classes spécialisées pour chaque Layer.
//  Les fonctions supportées par cette classe sont des fonctions générales, communes à chaque type
//  de layer (chaque classe spécialisée ayant la possibilité de re-définir en partie ces fonctions).

// Constructeur et destructeur.

/// CoderC::CoderC()
CoderC::CoderC(FileInfosC &roFileInfos, ULONG iReserved) : EncoderConfigC(roFileInfos), enc_roFileInfos(roFileInfos)
{
    int iInitError;

    SetErrorCodes(ESTAT_NOTREADY, ERR_NOT_INITIALIZED);     // Indiquer que l'encodeur n'est pas encore prêt.
    enc_pInputHandle   = NULL;

    SetOutputName(roFileInfos.GetOutputName());             // Récupérer le nom du fichier encodé.

    // Ouvrir le canal d'entrée (InputStream)

    if ( (enc_pInputHandle = CreateAudioC(roFileInfos)) )
    {
        ULONG SpecialOffset = (GetEncodingMode() == MONO) ? iReserved : iReserved * 2;
        enc_pInputHandle->SetRollGap(SpecialOffset);

        if (enc_pInputHandle->OpenStream() == IOERR_NONE && enc_pInputHandle->AllocStreamBuffer() == IOERR_NONE)
        {
            iInitError  = !(enc_pdAnaFilter = (T_AnaFilter *) AllocAlignedBuffer(sizeof(T_AnaFilter)));
            iInitError |= !(enc_sOutputStreamDatas.pOutputBuffer = AllocAlignedBuffer(OUTPUT_BUFFSIZE));

            if (!iInitError)
            {
                SetErrorCodes(ESTAT_READY, ERR_NONE);

                enc_bStereo  = (GetEncodingMode() == MONO) ? 1 : 2;
                enc_bSBLimit = SBLIMIT;

                enc_iMinNeededBits = (sizeof(BSHeader) * 8) + (IsCRC() ? 16 : 0);
                enc_sOutputStreamDatas.iOB_BitIndex  = 0;
                enc_sOutputStreamDatas.iOB_TotalBits = enc_sOutputStreamDatas.iOB_BitsFree = OUTPUT_BUFFSIZE * 8;
                enc_bpOB_FileLock  = NULL;

                InitAnaFilter();
                InitWrite();            // Initialiser le fichier destination.

                // Initialiser le tableau "multiple" inverse (pour EncodeSamples()).

                REAL s = 1.25992104989487;          // = sqrt³(2)
                REAL old;
                enc_adMultiples[0] = old = 0.5;

                for (int i = 1; i < 63; i++)
                {
                    old = enc_adMultiples[i] = old * s;
                }
                enc_adMultiples[63] = 1e20;

                // Initialiser le bloc d'entête pour le fichier encodé.

                enc_BSHeader.sBSHeader.bsh_SyncWord  = 0xFFF;
                enc_BSHeader.sBSHeader.bsh_Version   = 1;        // MPEG-1
                enc_BSHeader.sBSHeader.bsh_CRC       = !IsCRC();
                enc_BSHeader.sBSHeader.bsh_Bitrate   = GetBitrateIndex() + 1;
                enc_BSHeader.sBSHeader.bsh_SmplFreq  = GetSmplFrqIndex();
                enc_BSHeader.sBSHeader.bsh_Padding   = 0;
                enc_BSHeader.sBSHeader.bsh_Extension = 0;
                enc_BSHeader.sBSHeader.bsh_Mode      = GetEncodingMode();
                enc_BSHeader.sBSHeader.bsh_ModeExt   = 0;
                enc_BSHeader.sBSHeader.bsh_Copyright = IsCopyrighted();
                enc_BSHeader.sBSHeader.bsh_Original  = IsOriginal();
                enc_BSHeader.sBSHeader.bsh_Emphasis  = GetDeemphasis();
            }
            else SetErrorCodes(ESTAT_NOMEMORY, ERR_INTERNAL_BUFFERS);
        }
        else SetErrorCodes(ESTAT_NOTREADY, ERR_ISTREAM_FAILURE);
    }
}
///
/// CoderC::~CoderC() (virtual)
CoderC::~CoderC()
{
    void *pOutBuffer = enc_sOutputStreamDatas.pOutputBuffer;

    if (enc_pdAnaFilter) FreeAlignedBuffer(enc_pdAnaFilter);
    if (pOutBuffer)
    {
        CleanupWrite();
        FreeAlignedBuffer(pOutBuffer);
    }

    if (enc_poPsychoC)  delete enc_poPsychoC;

    // Fermer le fichier à encoder.

    if (enc_pInputHandle)
    {
        enc_pInputHandle->FreeStreamBuffer();
        enc_pInputHandle->CloseStream();
        delete enc_pInputHandle;                      // Fermer le flux d'entrée.
    }
}
///


//----------------------------------------------------------------------------------------------------
//=========================== Parties communes à tous les types de Layers ============================
//----------------------------------------------------------------------------------------------------

// Fonctions privées.

/// CoderC::InitAnaFilter()
/****** Class CoderC/InitAnaFilter ******************************************
*
*   NAME
*   CoderC::InitAnaFilter -- Initialiser la table AnaFilter
*
*   SYNOPSIS
*   CoderC::InitAnaFilter()
*
*   void CoderC::InitAnaFilter();
*
*   FUNCTION
*   Initialise la table AnaFilter().
*
*   NOTES
*   Cette table est calculée en utilisant les formules de trigonométrie
*   classiques plutôt qu'en faisant appel de multiples fois à cos().
*
*****************************************************************************
*
*/


void CoderC::InitAnaFilter()
{
    REAL sin_alpha, cos_alpha;
    REAL sin_delta, cos_delta;
    REAL t;

    t = PI / 64.0;
    sin_delta = sin(t);
    cos_delta = cos(t);

    sin_alpha = cos_alpha = sqrt(0.5);

    for (int i = 0; i < 128 ; i++)
    {
        t = cos_alpha * 1e9;
        (*enc_pdAnaFilter)[i] = 1e-9 * ( (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5) );

        // Calculer les valeurs angulaires suivantes.

        t = cos_alpha;
        cos_alpha = t * cos_delta + sin_alpha * sin_delta;
        sin_alpha = sin_alpha * cos_delta - t * sin_delta;
    }
}
///
/// CoderC::GetIndex()   (double : ASM)
/****** Class CoderC/GetIndex ***********************************************
*
*   NAME
*   CoderC::GetIndex -- Retourne un index du tableau multiple[]
*
*   SYNOPSIS
*   Index = CoderC::GetIndex(real)
*
*   int CoderC::GetIndex(double);
*   int CoderC::GetIndex(float);
*
*   FUNCTION
*   Sépare la mantisse et l'exposant d'un nombre flottant, puis retourne
*   l'index du tableau multiple[] contenant la valeur immédiatement
*   supérieure.
*   Les deux variantes de la fonction permettent d'employer indifféremment
*   des "double" ou des "float".
*
*   INPUTS
*   real - Nombre flottant à traiter.
*   e    - Valeur de l'exposant.
*
*   RESULT
*   Mantisse - Valeur de la mantisse.
*
*   BUGS
*   La valeur retournée pour la mantisse ne prend pas en compte la totalité
*   des chiffres significatifs (6 chiffres en FLOAT, 9 chiffres en DOUBLE).
*
*   SEE ALSO
*   CoderC::ScaleFactor()
*
*****************************************************************************
*
* Le tableau "multiple[]" original est composé des 63 premiers termes d'une suite
* géométrique :
* U0 = 2, r = SQR³(1/2), U63 = 1e-20;
*
*  n   : Un                =>    exp        mantisse
* [00] : 2.00000000000000  => + 0x400   0x0000000000000
* [01] : 1.58740105196820  => + 0x3FF   0x965FEA53D6E3D
* [02] : 1.25992104989487  => + 0x3FF   0x428A2F98D728B
* [03] : 1.00000000000000  => + 0x3FF   0x0000000000000
* [04] : 0.79370052598410  => + 0x3FE   0x965FEA53D6E3D
* ...
*/


inline int CoderC::GetIndex(double real)
{
    // Format d'un "double" :
    // seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm
    // Bias of biased exponent = 1023

    union lfloat {double f;
                  short  s[4];
                 } u;

    const int NEW_EXP = 0x3FF0 + (30 * 16);                // On n'utilise que 30 bits de la mantisse.
    int e, m;

    u.f = real;
    e = ( 1024 - ((u.s[0] & 0x7FF0) >> 4) ) * 3;            // Extraire l'exposant.
    u.s[0] = (u.s[0] & 0x000F) | NEW_EXP;                   // Modifier l'exposant.
    m = (int) u.f;                                          // Convertir la mantisse (flottante) en entier.

    if      (m >= 1704458901)  e -= 3;
    else if (m >= 1352829926)  e -= 2;
    else if (m >= 1073741824)  e -= 1;

    if (e < 0)               e = 0;
    if (e > SCALE_RANGE - 2) e = SCALE_RANGE - 2;

    return (e);
}


inline int CoderC::GetIndex(float real)
{
    // Format d'un "float" :
    // seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
    // Bias of biased exponent = 127

    union sfloat {float f;
                  long  l;
                 } u;

    int e, m;

    u.f = real;
    e = ( 128 - ((u.l & 0x7F800000) >> 23) ) * 3;           // Extraire l'exposant.
    m = u.l & 0x007FFFFF;

    if      (m >= 4927477)  e -= 3;
    else if (m >= 2180376)  e -= 2;
    else if (m >= 0)        e -= 1;

    if (e < 0)               e = 0;
    if (e > SCALE_RANGE - 2) e = SCALE_RANGE - 2;

    return (e);
}
///
/// CoderC::InitWrite()
/****** Class CoderC/InitWrite **********************************************
*
*   NAME
*   CoderC::InitWrite -- Initialiser le fichier de destination
*
*   SYNOPSIS
*   CoderC::InitWrite()
*
*   void CoderC::InitWrite();
*
*   FUNCTION
*   Initialise le fichier destination en l'effaçant s'il existe déjà.
*
*   NOTES
*   Il est nécessaire d'effacer ce fichier avant de commencer l'encodage, du
*   fait que la fonction WriteBuffer() ajoute les données au fur et à mesure
*   dans le fichier de sortie.
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderC::InitWrite()
{
    BPTR bpFileHandle;

    // Effacer le fichier, tout en le re-créant avec une taille de ZERO.

    if ( (bpFileHandle = ::Open(GetOutputName(), MODE_NEWFILE)) )
    {
        // Création OK. Fermer le fichier (il doit maintenant avoir une taille de ZERO)

        if (::Close(bpFileHandle) != DOSFALSE)
        {
            if ( (enc_bpOB_FileLock = ::Lock(GetOutputName(), SHARED_LOCK)) )
            {
                return;     // On a le Lock, donc poursuivre.
                            // Ce "lock" permettra de réouvrir ce fichier chaque fois qu'il
                            // faudra vider le tampon de sortie, et interdit en même temps
                            // la destruction du fichier tant que l'encodage n'est pas
                            // terminé.
            }
        }
    }
    SetErrorCodes(ESTAT_IOERROR, ERR_OPEN_OSTREAM);
}
///
/// CoderC::CleanupWrite()
/****** Class CoderC/CleanupWrite *******************************************
*
*   NAME
*   CoderC::CleanupWrite -- Fermer définitivement le fichier destination.
*
*   SYNOPSIS
*   CoderC::CleanupWrite()
*
*   void CoderC::CleanupWrite();
*
*   FUNCTION
*   Vide le tampon et ferme définitivement le fichier destination en libérant
*   le 'Lock' sur ce fichier.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderC::CleanupWrite()
{
    WriteBuffer();

    if (enc_bpOB_FileLock != 0)
    {
        ::UnLock(enc_bpOB_FileLock);
        enc_bpOB_FileLock = 0;
    }
}
///
/// CoderC::WriteBuffer()
/****** Class CoderC/WriteBuffer ********************************************
*
*   NAME
*   CoderC::WriteBuffer -- Créer/compléter le fichier encodé.
*
*   SYNOPSIS
*   CoderC::WriteBuffer()
*
*   void CoderC::WriteBuffer();
*
*   FUNCTION
*   Ajoute le tampon de sortie au fichier encodé. Lors du premier appel, le
*   fichier est crée ou remplacé s'il existe déjà. Les appels suivants
*   ajoutent les données du tampon en fin de fichier.
*
*   NOTES
*   Le fichier est immédiatement refermé après avoir inscrit les nouvelles
*   données.
*
*   Lors du premier accès, le fichier est ouvert normalement, puis un 'Lock'
*   est obtenu sur ce fichier. Lorsque le fichier est refermé après avoir
*   inscrit les données, le 'Lock' est maintenu protégeant ainsi le fichier.
*   Lors des appels suivants, c'est ce 'Lock' qui est utilisé pour ouvrir
*   le fichier.
*
*   BUGS
*   Le mode READWRITE présentait l'avantage de créer un fichier vide s'il
*   n'existait pas, mais cette méthode semble poser des problèmes à AFS.
*   La méthode actuelle consiste donc à initialiser le fichier grâce au mode
*   NEWFILE, puis à obtenir directement un SHARED_LOCK après avoir fermé ce
*   fichier vide.
*
*****************************************************************************
*
*/

// La fonction Seek() ne fonctionne pas correctement avec le FileSystem v36/37.
// Voir les Autodocs pour le détail.

LONG NewSeek(BPTR file, LONG position, LONG offset);
// Fonction définie dans InputStream.cpp


void CoderC::WriteBuffer()
{
    BPTR  file_handle;
    ULONG num_bytes     = ((enc_sOutputStreamDatas.iOB_BitIndex + 7) >> 3);
    e_EncErrCodes error = ERR_NONE;

    // Avorter s'il y a eu une erreur, ou si le tampon est vide.

    if (enc_iEncoderStatus != ESTAT_READY || num_bytes == 0)  return;

    // Ouvrir le fichier à partir du Lock obtenu à l'initialisation de l'encodeur.

    if (  (file_handle = ::OpenFromLock(enc_bpOB_FileLock))
       && (enc_bpOB_FileLock = ::DupLockFromFH(file_handle)) )
    {
        if (NewSeek(file_handle, 0, OFFSET_END) >= 0)       // Se positionner en fin de fichier.
        {
            BYTE *buffer = (BYTE *) enc_sOutputStreamDatas.pOutputBuffer;

            // Boucler sur l'écriture dans le fichier tant qu'il reste
            // des données dans le tampon.

            while (num_bytes != 0)
            {
                LONG bytes_read = ::Write(file_handle, buffer, num_bytes);

                if (bytes_read == -1)
                {
                    error = ERR_WRITE_OSTREAM;              // Erreur d'écriture.
                    break;
                }

                num_bytes -= bytes_read;
                buffer    += bytes_read;
            }
        }
        else error = ERR_SEEK_OSTREAM;                      // Erreur de positionnement.

        if (::Close(file_handle) == DOSFALSE)
            error = ERR_CLOSE_OSTREAM;                      // Erreur de fermeture du fichier.
    }
    else error = ERR_OPEN_OSTREAM;                          // Erreur d'ouverture du fichier.

    // S'il y a eu une erreur, placer les indicateurs globaux.

    if (error != ERR_NONE)  SetErrorCodes(ESTAT_IOERROR, error);
}
///

// Fonctions virtuelles.

/// CoderC::GetAudio()
/****** Class CoderC/GetAudio ***********************************************
*
*   NAME
*   CoderC::GetAudio -- Préparer la passe suivante de l'encodeur.
*
*   SYNOPSIS
*   Result = CoderC::GetAudio()
*
*   bool CoderC::GetAudio();
*
*   FUNCTION
*   Obtient l'adresse de la page suivante. Les échantillons au format Intel
*   sont convertis au format Motorola.
*
*   RESULT
*   Result - FALSE s'il y a eu une erreur pendant la lecture du tampon d'
*            entrée.
*
*****************************************************************************
*
*/


int CoderC::GetAudio()
{
    if ( (enc_pSamples = enc_pInputHandle->ReadSamples(enc_wFrameSize)) )
    {
        // Réordonner les échantillons si nécessaire (conversion Intel => Motorola).
        // (On converti la totalité du tampon de lecture).

        if (ecfg_iIntelFormat)     // Format Intel ?
        {
            char *p = (char *) enc_pSamples;
            ULONG FrameSize = enc_wFrameSize;
            char  temp;

            for (int i = 0; i < FrameSize; i++)
            {
                temp = *p;
                *p++ = *(p+1);
                *p++ = temp;
            }
        }

        // Ajouter un slot si nécessaire.

        if (enc_wSPF_Reminder)
        {
            if (enc_wSlotFree >= ecfg_wSampling)
            {
                // Le dernier slot utilisé peut contenir les données.
                enc_wSlotFree -= ecfg_wSampling;
                enc_iAvgDataBits = 0;
                enc_BSHeader.sBSHeader.bsh_Padding = 0;
            }
            else
            {
                // Slot partiel (non entièrement rempli).
                enc_wSlotFree += enc_wSPF_Reminder;             // Calculer la place restante dans ce slot.
                enc_iAvgDataBits = enc_bBitsPerSlot;
                enc_BSHeader.sBSHeader.bsh_Padding = 1;
            }
        }

        enc_iAvgDataBits += enc_wSlotsPerFrame << enc_bBitsPerSlotN;
        ++enc_iFrameNumber;

        return TRUE;
    }

    return FALSE;
}
///

// Fonctions protégées.

///CoderC::InitSlotsPerFrame()
/****** Class CoderC/InitSlotsPerFrame **************************************
*
*   NAME
*   CoderC::InitSlotsPerFrame --
*
*   SYNOPSIS
*   CoderC::InitSlotsPerFrame(wSamplesPerFrame, wNumBitsN)
*
*   void CoderC::InitSlotsPerFrame(UWORD, UWORD);
*
*   FUNCTION
*
*   INPUTS
*   wSamplesPerFrame - Nombre d'échantillons par "page".
*   wNumBitsN        - Nombre de bits (Log2) par slot.
*
*   NOTES
*   L'encodeur original (Musicin) effectue ces calculs en flottant, ce qui
*   peut induire des erreurs d'arrondis par rapport aux calculs sur des
*   entiers comme c'est fait ici.
*   La différence de taille entre un fichier encodé avec Pegase et un fichier
*   encodé avec Musicin vient très probablement de là.
*
*****************************************************************************
*
*/


void CoderC::InitSlotsPerFrame(UWORD wSamplesPerFrame, UWORD wNumBitsN)
{
    enc_wSamplesPerFrame = wSamplesPerFrame;
    enc_bBitsPerSlot     = 1 << wNumBitsN;
    enc_bBitsPerSlotN    = wNumBitsN;
    enc_wFrameSize       = enc_roFileInfos.GetNumChannels() * wSamplesPerFrame;

    div_t t = div( (enc_wSamplesPerFrame >> enc_bBitsPerSlotN) * 1000 * ecfg_wBitrate, ecfg_wSampling);
    enc_wSlotsPerFrame = t.quot;
    enc_wSPF_Reminder  = enc_wSlotFree = ecfg_wSampling - t.rem;

    enc_iAvgDataBits = enc_wSlotsPerFrame << enc_bBitsPerSlotN;
}
///
///CoderC::SubBandCoding_Stereo()  (ASM)
/****** Class CoderC/SubBandCoding_Stereo ***********************************
*
*   NAME
*   CoderC::SubBandCoding_Stereo -- Prétraitement des échantillons.
*
*   SYNOPSIS
*   CoderC::SubBandCoding_Stereo(pdSample)
*
*   void CoderC::SubBandCoding_Stereo(T_SubBandSmplStereo *);
*   void CoderC::SubBandCoding_Stereo(T_SubBandSmplMono *);
*
*   FUNCTION
*   Filtre la bande courante.
*
*   INPUTS
*   - pdSample = Pointeur vers le tableau a initialiser.
*
*   NOTES
*   Cette fonction est constituée de plusieurs sous-ensembles des sources
*   originaux (boucles, window_subband et filter_subband). L'intégration de
*   ces blocs dans une seule et unique fonction permet de supprimer les
*   différents appels de fonction, et autorise une meilleure gestion des
*   ressources.
*
*   L'implémentation actuelle de l'algorithme est basée sur une optimisation
*   des accès mémoire. Elle n'est pas forcément la plus efficace lorsque l'
*   encodeur est utilisée sur une machine dépourvue de cache (68020).
*
*****************************************************************************
*
*   22.06.98 :
*       · Modification du source entraînant une meilleure génération de code
*         du StormC.
*
*   10.09.98 :
*       · Nouvel algorithme plus performant. Les données sont systématiquement
*         utilisées dans tous les calculs dès la première lecture. De cette façon,
*         on ne modifie plus sans cesse le cache (tableaux y[]), et donc on
*         élimine un bon nombre de "flushes".
*
*   Combinaison des fonctions "window_subband" et "filter_subband" des Layers 1 et 2.
*
*   PROFILER :
*       Date        Fichier             Durée       Layer II (double)       Layer II (float)
*       ------------------------------------------------------------------------------------
*       09.05.98    Europe2.1           00:02           00:03.02                 00:02.82
*       12.05.98    07_AudioNormal      03:27           04:47
*       30.05.98    07_AudioNormal      03:27           04:17
*       14.06.98    07_AudioNormal      03:27           04:04
*       10.09.98    07_AudioNormal      03:27           03:13
*/

#if _USE_ASM_FCT_ == 1

extern "C"{
void SubBandCoding_Stereo(REAL pdSample[2][12][32], WORD **pActualPtr, REAL *pAnaFilter);
}

inline void CoderC::SubBandCoding_Stereo(CoderC::T_SubBandSmplStereo *pdSample)
{
    ::SubBandCoding_Stereo(*pdSample, &enc_pSB_ActualPtr, &(*enc_pdAnaFilter)[0]);
}

#else

void CoderC::SubBandCoding_Stereo(CoderC::T_SubBandSmplStereo *pdSample)
{
    extern const T_ANAWINDOW a_dAnaWindow[];

    ULONG j, n, m;
    T_ANAWINDOW *py;
    T_ANAWINDOW y[SBLIMIT*2];
    T_ANAWINDOW  ysumL, ysumR;

    for (j = 0; j < SCALE_BLOCK; j++)
    {
        //-------------------------------------------------------------------
        //========== Collationnement et application des paramètres ==========
        //-------------------------------------------------------------------

        // Première partie : Calculer yx[0]

        const T_ANAWINDOW *pAnaWindow = &a_dAnaWindow[0];

        {
            T_ANAWINDOW wincoeff;
            WORD *p1, *p2;

            p2 = enc_pSB_ActualPtr;
            p1 = &p2[31 * 2];
            p2 = &p2[479 * 2];

            ysumL = ysumR = 0;

            for (n = 3; n > 0; --n)
            {
                wincoeff = *pAnaWindow++;
                ysumL += wincoeff * ((LONG) *p2    - (LONG) *p1);
                ysumR += wincoeff * ((LONG)  p2[1] - (LONG)  p1[1]);
                p1 += 32 * 2; p2 += -(32 * 2);

                wincoeff = *pAnaWindow++;
                ysumL += wincoeff * ((LONG) *p2    + (LONG) *p1);
                ysumR += wincoeff * ((LONG)  p2[1] + (LONG)  p1[1]);
                p1 += 32 * 2; p2 += -(32 * 2);
            }
            wincoeff = *pAnaWindow++;
            ysumL += wincoeff * ((LONG) *p2    - (LONG) *p1);
            ysumR += wincoeff * ((LONG)  p2[1] - (LONG)  p1[1]);

            wincoeff = *pAnaWindow++;
            y[0]  = ysumL + wincoeff * p1[32 * 2];
            y[32] = ysumR + wincoeff * p1[32 * 2 + 1];
        }

        // Deuxième partie : Calculer yx[1;15] et yx[17;31]

        {
            T_ANAWINDOW wincoeff;
            T_ANAWINDOW ysumL2, ysumR2;
            WORD *p1, *p2;

            ULONG stp  = 30 * 2;
            ULONG next = (64 - 30) * 2;

            n = 0;

            while (n < 15)
            {
                p2 = enc_pSB_ActualPtr;

                p1 = &p2[n+n];
                p2 = &p2[478 * 2 - (n+n)];

                ysumL = ysumL2 = ysumR = ysumR2 = 0;

                for (m = 4; m > 0; --m)
                {
                    wincoeff = *pAnaWindow++;
                    ysumL2 += wincoeff * p1[0];             // b4
                    ysumR2 += wincoeff * p1[1];
                    ysumL  += wincoeff * p2[32 * 2];        // a1
                    ysumR  += wincoeff * p2[32 * 2 + 1];

                    wincoeff = *pAnaWindow++;
                    ysumL2 += wincoeff * p2[0];             // b1
                    ysumR2 += wincoeff * p2[1];
                    ysumL  -= wincoeff * p1[32 * 2];        // a4
                    ysumR  -= wincoeff * p1[32 * 2 + 1];

                    p1 = &p1[stp]; p2 = &p2[-stp];

                    wincoeff = *pAnaWindow++;
                    ysumL2 -= wincoeff * p1[0];             // b3
                    ysumR2 -= wincoeff * p1[1];
                    ysumL  += wincoeff * p2[32 * 2];        // a2
                    ysumR  += wincoeff * p2[32 * 2 + 1];

                    wincoeff = *pAnaWindow++;
                    ysumL2 -= wincoeff * p2[0];             // b2
                    ysumR2 -= wincoeff * p2[1];
                    ysumL  -= wincoeff * p1[32 * 2];        // a3
                    ysumR  -= wincoeff * p1[32 * 2 + 1];

                    p1 = &p1[next]; p2 = &p2[-next];
                }

                stp  -= 4; next += 4;

                ++n; py = &y[n];

                py[0]  = ysumL;  py[32]  = ysumR;
                py[15] = ysumL2; py[47] = ysumR2;           // On continue à partir de y[16]
            }
        }

        // Dernière partie : Calculer yx[16]

        {
            T_ANAWINDOW wincoeff;
            WORD *pL = &enc_pSB_ActualPtr[47 * 2];

            ysumL = ysumR = 0;

            for (n = 4; n > 0; --n)
            {
                wincoeff = *pAnaWindow++;
                ysumL -= wincoeff * *pL;
                ysumR -= wincoeff *  pL[1];

                wincoeff = *pAnaWindow++;
                ysumL += wincoeff * pL[256 * 2];
                ysumR += wincoeff * pL[256 * 2 + 1];

                pL += (64 * 2);
            }
            // ysumL et ysumR contiennent les valeurs yR[16] et yL[16]
        }

        enc_pSB_ActualPtr += (32 * 2);                      // Pointer la sous-bande suivante.

        //---------------------------------------------
        //========== Application des filtres ==========
        //---------------------------------------------

        {
            double *SampleL1 = &(*pdSample)[0][j][0];
            double *pSB_SampleL1 = SampleL1;
            double *pSB_SampleL2;
            const  REAL *pFilter = &(*enc_pdAnaFilter)[0];

            #define K (32*12)       // Offset pour le second canal.

            ULONG max = 4, lstep = 16, lpos = 8;
            ULONG i, fidx, fidx2, fstep, i32;

            pSB_SampleL1[0]   = y[0]  * *pFilter;
            pSB_SampleL1[K]   = y[32] * *pFilter;
            pSB_SampleL1[1]   = ysumL - pSB_SampleL1[0];
            pSB_SampleL1[K+1] = ysumR - pSB_SampleL1[K];
            pSB_SampleL1[0] = pSB_SampleL1[0] + ysumL;
            pSB_SampleL1[K] = pSB_SampleL1[K] + ysumR;

            while (max <= 32) {
                i32 = 0;
                pSB_SampleL1 = SampleL1;
                pSB_SampleL2 = &pSB_SampleL1[max];

                for (i = lpos; i < 32; i += lstep) {
                    fstep = i;
                    fidx  = fstep - i32;
                    i32 += 32;
                    fidx2 = fstep + i32;

                    ysumL = ysumR = 0;
                    fstep += fstep;
                    py = &y[lpos];

                    while (py < &y[16]) {
                        double f1 = pFilter[fidx & 127];
                        double f2 = pFilter[fidx2 & 127];
                        ysumL += py[0]  * f1 + py[15] * f2;
                        ysumR += py[32] * f1 + py[47] * f2;
                        fidx += fstep; fidx2 += fstep; py += lstep;
                    }
                    *--pSB_SampleL2 = pSB_SampleL1[0] - ysumL;
                    pSB_SampleL2[K] = pSB_SampleL1[K] - ysumR;
                    pSB_SampleL1[K] = pSB_SampleL1[K] + ysumR;
                    *pSB_SampleL1++ = *pSB_SampleL1 + ysumL;
                }
                lstep -= lpos;
                lpos >>= 1;
                max += max;
            }
        }
    }
}

#endif  // _USE_ASM_FCT_
///
/// CoderC::SubBandCoding_Mono()  (ASM)

#if _USE_ASM_FCT_ == 1

extern "C"{
void SubBandCoding_Mono(REAL pdSample[12][32], WORD **pActualPtr, REAL *pAnaFilter);
}

inline void CoderC::SubBandCoding_Mono(CoderC::T_SubBandSmplMono *pdSample)
{
    ::SubBandCoding_Mono(*pdSample, &enc_pSB_ActualPtr, &(*enc_pdAnaFilter)[0]);
}

#else

void CoderC::SubBandCoding_Mono(CoderC::T_SubBandSmplMono *pdSample)
{
    extern const T_ANAWINDOW a_dAnaWindow[];

    ULONG j, m, n;
    T_ANAWINDOW  y[SBLIMIT], *py;
    T_ANAWINDOW  ysum;

    for (j = 0; j < SCALE_BLOCK; j++)
    {
        //-------------------------------------------------------------------
        //========== Collationnement et application des paramètres ==========
        //-------------------------------------------------------------------

        // Première partie : Calculer yx[0]

        const T_ANAWINDOW *pAnaWindow = &a_dAnaWindow[0];

        {
            WORD *p1, *p2;

            p2 = enc_pSB_ActualPtr;
            p1 = &p2[31];
            p2 = &p2[479];

            ysum = 0;

            for (n = 3; n > 0; --n)
            {
                ysum += *pAnaWindow++ * ((LONG) *p2 - (LONG) *p1);
                p1 += 32; p2 += -32;

                ysum += *pAnaWindow++ * ((LONG) *p2 + (LONG) *p1);
                p1 += 32; p2 += -32;
            }
            ysum += *pAnaWindow++ * ((LONG) *p2 - (LONG) *p1);

            y[0] = ysum + *pAnaWindow++ * p1[32];
        }

        // Deuxième partie : Calculer yx[1;15] et yx[17;31]

        {
            T_ANAWINDOW ysum2, wincoeff;

            WORD  *p1, *p2;
            ULONG stp  = 30;
            ULONG next = 64 - 30;

            n = 0;

            while (n < 15)
            {
                p2 = enc_pSB_ActualPtr;

                p1 = &p2[n];
                p2 = &p2[478 - n];

                ysum = ysum2 = 0;

                for (m = 4; m > 0; --m)
                {
                    wincoeff = *pAnaWindow++;
                    ysum2 += wincoeff * p1[0];          // b4
                    ysum  += wincoeff * p2[32];         // a1

                    wincoeff = *pAnaWindow++;
                    ysum2 += wincoeff * p2[0];          // b1
                    ysum  -= wincoeff * p1[32];         // a4

                    p1 = &p1[stp]; p2 = &p2[-stp];

                    wincoeff = *pAnaWindow++;
                    ysum2 -= wincoeff * p1[0];          // b3
                    ysum  += wincoeff * p2[32];         // a2

                    wincoeff = *pAnaWindow++;
                    ysum2 -= wincoeff * p2[0];          // b2
                    ysum  -= wincoeff * p1[32];         // a3

                    p1 = &p1[next]; p2 = &p2[-next];
                }

                stp  -= 2; next += 2;

                ++n; py = &y[n];

                py[0] = ysum; py[15] = ysum2;           // On continue à partir de y[16]
            }
        }

        // Dernière partie : Calculer yx[16]

        {
            WORD *p = &enc_pSB_ActualPtr[47];

            ysum = 0;

            for (n = 4; n > 0; --n)
            {
                ysum -= *pAnaWindow++ * p[0];
                ysum += *pAnaWindow++ * p[256];

                p += 64;
            }
            // ysum contient la valeur y[16]
        }

        enc_pSB_ActualPtr += 32;                // Pointer la sous-bande suivante.

        //---------------------------------------------
        //========== Application des filtres ==========
        //---------------------------------------------

        {
            double *Sample = &(*pdSample)[j][0];
            double *pSB_Sample1 = Sample;
            double *pSB_Sample2;

            const REAL *pFilter = &(*enc_pdAnaFilter)[0];

            ULONG max = 4, lstep = 16, lpos = 8;
            ULONG i, fidx, fidx2, fstep, i32;

            pSB_Sample1[0] = y[0] * pFilter[0];
            pSB_Sample1[1] = ysum - pSB_Sample1[0];
            pSB_Sample1[0] = pSB_Sample1[0] + ysum;

            while (max <= 32) {
                i32 = 0;
                pSB_Sample1 = Sample;
                pSB_Sample2 = &pSB_Sample1[max];

                for (i = lpos; i < 32; i += lstep) {
                    fstep = i;
                    fidx  = fstep - i32;
                    i32 += 32;
                    fidx2 = fstep + i32;

                    ysum = 0;
                    fstep += fstep;
                    py = &y[lpos];

                    while (py < &y[16]) {
                        ysum += py[0] * pFilter[fidx & 127] + py[15] * pFilter[fidx2 & 127];
                        fidx += fstep; fidx2 += fstep; py += lstep;
                    }
                    *--pSB_Sample2 = *pSB_Sample1 - ysum;
                    *pSB_Sample1++ = *pSB_Sample1 + ysum;
                }
                lstep -= lpos;
                lpos >>= 1;
                max += max;
            }
        }
    }
}

#endif  // _USE_ASM_FCT_
///
/// CoderC::ScaleFactor()
/****** Class CoderC/ScaleFactor ********************************************
*
*   NAME
*   CoderC::ScaleFactor -- Rechercher le facteur d'échelle.
*
*   SYNOPSIS
*   CoderC::ScaleFactor(pScalar, pSample)
*   CoderC::ScaleFactorCalc(pbScalar, pdSample)
*
*   void CoderC::ScaleFactor(T_ScaleStereo *, T_SubBandSmplStereo *);
*   void CoderC::ScaleFactor(T_ScaleMono *, T_SubBandSmplMono *);
*
*   FUNCTION
*   Recherche le facteur d'échelle pour chaque sous-bande.
*
*   INPUTS
*   - pScalar : Tableau des index vers le tableau "Multiple[]"
*   - pSample : Tableau des bandes de filtres.
*
*   BUGS
*   La boucle originale ne prend pas en compte la valeur "multiple[63]", ce
*   qui semble curieux (cependant cette valeur est utilisée pour initialiser
*   le reste du tableau "scalar[]").
*   La méthode d'obtention de l'index n'est pas forcément strictement comp-
*   atible avec la méthode originale (du fait que le nombre de chiffres
*   significatifs utilisés pour la mantisse est peut-être trop faible...)
*
*   SEE ALSO
*   CoderC::GetIndex()
*
*****************************************************************************
*
*/

// Rechercher le facteur d'échelle de chaque canal.

inline void CoderC::ScaleFactor(CoderC::T_ScaleStereo *pScalar, CoderC::T_SubBandSmplStereo *pSample)
{
    for (ULONG k = 0; k < enc_bStereo; k++)
    {
        ScaleFactorCalc(&(*pScalar)[k][0], (T_SubBandSmplMono *) &(*pSample)[k][0][0]);
    }
}
///
/// CoderC::ScaleFactorCalc()  (ASM)
// Rechercher le facteur d'échelle du canal.

#if _USE_ASM_FCT_ == 1

extern "C"{
void ScaleFactorCalc_double(UBYTE *pbScalar, REAL pdSample[12][32], ULONG iSBLimit);
}

void CoderC::ScaleFactorCalc(UBYTE *pbScalar, CoderC::T_SubBandSmplMono *pdSample)
{
    ::ScaleFactorCalc_double(pbScalar, *pdSample, enc_bSBLimit);
}

#else

void CoderC::ScaleFactorCalc(UBYTE *pbScalar, CoderC::T_SubBandSmplMono *pdSample)
{
    LONG  i, j;
    REAL  ds, d, *pSamples;

    for (i = 0; i < enc_bSBLimit; i++)
    {
        pSamples = &(*pdSample)[0][i];
        ds = fabs(*pSamples);

        // Rechercher la valeur maximale dans chaque bande.

        for (j = 1; j < SCALE_BLOCK; j++)
        {
            pSamples += SBLIMIT;                // Passer à la bande suivante.
            d = fabs(*pSamples);
            if (ds < d) ds = d;
        }

        *pbScalar++ = GetIndex(ds);
    }
}

#endif
///
/// CoderC::MixChannels()
/****** Class CoderC/MixChannels ********************************************
*
*   NAME
*   CoderC::MixChannels -- Mixe les canaux stéréo en un canal mono.
*
*   SYNOPSIS
*   CoderC::MixChannels()
*
*   void CoderC::MixChannels();
*
*   FUNCTION
*   Mélange les deux canaux stéréo en un canal mono (JOINTSTEREO)
*
*   NOTES
*   Le mixage s'effectue APRES le calcul des filtres. Il serait plus inter-
*   ressant (et plus rapide) de mixer les canaux stéréo AVANT de calculer
*   les filtres...
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderC::MixChannels(CoderC::T_SubBandSmplStereo *pSample, CoderC::T_SubBandSmplMono *pJSample)
{
    ULONG sb, smp, offset;
    REAL  *pSample0, *pSample1, *pJS;
    REAL  dOneHalf = 0.5;

    offset = SBLIMIT - enc_bSBLimit;        // Layer I : offset = 0

    // Initialiser les pointeurs temporaires.

    pJS      = &(*pJSample)[0][0];
    pSample0 = &(*pSample)[0][0][0];
    pSample1 = &(*pSample)[1][0][0];

    for (smp = 0; smp < SCALE_BLOCK; smp++)
    {
        for (sb = 0; sb < enc_bSBLimit; sb++)
        {
            *pJS++ = dOneHalf * (*pSample0++ + *pSample1++);
        }
        pJS      += offset;             // Adresser la "page" suivante.
        pSample0 += offset;             // (Le Layer II n'utilise pas toutes les pages)
        pSample1 += offset;
    }
}
///
/// CoderC::MainBitAllocation()
/****** Class CoderC/MainBitAllocation **************************************
*
*   NAME
*   CoderC::MainBitAllocation -- Traiter l'encodage en Joint-Stéréo.
*
*   SYNOPSIS
*   CoderC::MainBitAllocation()
*
*   void CoderC::MainBitAllocation();
*
*   FUNCTION
*   Détermine quel mode Joint-Stéréo (parmis les 4) utiliser pour l'encodage.
*   S'il y a assez de bits pour encoder en stéréo pure, l'encodeur utilise ce
*   mode plutôt que le Joint-Stéréo.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderC::MainBitAllocation()
{
    enc_BSHeader.sBSHeader.bsh_Mode    = STEREO;
    enc_BSHeader.sBSHeader.bsh_ModeExt = 0;
    enc_bJSBound = enc_bSBLimit;

    if (BitsForNoNoise() > enc_iAvgDataBits)
    {
        ULONG mode_ext = 4;
        enc_BSHeader.sBSHeader.bsh_Mode = JOINTSTEREO;

        do
        {
            SetJSBound(--mode_ext);
        } while (BitsForNoNoise() > enc_iAvgDataBits && mode_ext > 0);

        enc_BSHeader.sBSHeader.bsh_ModeExt = mode_ext;
    }
}
///
/// CoderC::UpdateCRC()  (ASM)
/****** Class CoderC/UpdateCRC **********************************************
*
*   NAME
*   CoderC::UpdateCRC -- Calcule la somme de contrôle.
*
*   SYNOPSIS
*   CoderC::UpdateCRC(iData, iLength)
*
*   void CoderC::UpdateCRC(ULONG, ULONG);
*
*   FUNCTION
*   Ajuste la somme de contrôle.
*
*   INPUTS
*   iData   - Donnée à prendre en charge.
*   iLength - Nombre de bits de cette donnée.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


#if _USE_ASM_FCT_ == 1 || _CPUMODEL_ == 68020

extern "C"{
UWORD UpdateCRC(register __d0 ULONG iData, register __d1 UWORD wLength, register __d2 UWORD wCRC);
}

inline void CoderC::UpdateCRC(ULONG iData, ULONG iLength)
{
    enc_wCRC = ::UpdateCRC(iData, iLength, enc_wCRC);
}

#else

void CoderC::UpdateCRC(ULONG iData, ULONG iLength)
{
    ULONG iMasking = 1 << iLength;
    ULONG iCarry;

    while (iMasking >>= 1)
    {
        iCarry = enc_wCRC & 0x8000;
        enc_wCRC *= 2;
        if (!iCarry ^ !(iData & iMasking)) enc_wCRC ^= 0x8005;
    }
}
#endif
///
/// CoderC::CRC_Calc()
/****** Class CoderC/CRC_Calc ***********************************************
*
*   NAME
*   CoderC::CRC_Calc -- Calcule le CRC du bloc d'entête.
*
*   SYNOPSIS
*   CoderC::CRC_Calc()
*
*   void CoderC::CRC_Calc();
*
*   FUNCTION
*   Initialise la somme de contrôle avec les informations du bloc d'entête.
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderC::CRC_Calc()
{
    enc_wCRC = 0xFFFF;

    UpdateCRC(GetBitrateIndex() + 1, 4);
    UpdateCRC(GetSmplFrqIndex(), 2);
    UpdateCRC(enc_BSHeader.sBSHeader.bsh_Padding, 1);
    UpdateCRC(enc_BSHeader.sBSHeader.bsh_Extension, 1);
    UpdateCRC(enc_BSHeader.sBSHeader.bsh_Mode, 2);
    UpdateCRC(enc_BSHeader.sBSHeader.bsh_ModeExt, 2);
    UpdateCRC((ULONG) IsCopyrighted(), 1);
    UpdateCRC((ULONG) IsOriginal(), 1);
    UpdateCRC(GetDeemphasis(), 2);
}
///
/// CoderC::PutBits()  (ASM)
/****** Class CoderC/PutBits ************************************************
*
*   NAME
*   CoderC::PutBits -- Placer un groupe de bits dans le tampon de sortie.
*
*   SYNOPSIS
*   CoderC::PutBits(iSource, iLength)
*
*   void CoderC::PutBits(ULONG, ULONG);
*
*   FUNCTION
*   Envoie le groupe de bits passé en argument vers le tampon de sortie.
*
*   INPUTS
*   iSource - Valeur à inscrire dans le tampon.
*   iLength - Nombre de bits significatifs.
*
*   BUGS
*   La fonction "SetBitField()" n'existe qu'en assembleur.
*
*****************************************************************************
*
*/

#ifndef USE_QUICK_PUTBITS
#if _USE_ASM_FCT_ == 1 || _CPUMODEL_ == 68020
inline void CoderC::PutBits(ULONG iSource, ULONG iLength)
{
    PutBitsASM(&enc_sOutputStreamDatas, iSource, iLength);
}

#else

void CoderC::PutBits(ULONG source, ULONG length)
{
    UWORD *output     = (UWORD *) enc_sOutputStreamDatas.pOutputBuffer;
    ULONG bit_index   = enc_sOutputStreamDatas.iOB_BitIndex;
    LONG  bits_free   = enc_sOutputStreamDatas.iOB_BitsFree;
    ULONG offset      = bit_index >> 4;
    LONG  remaining_bits;
    ULONG bit_field;

    remaining_bits = 32 - (bit_index & 15) - length;        // E [1;31]
    bit_field      = (output[offset] * 65536) | (source << remaining_bits);

    output[offset] = bit_field >> 16;
    bits_free     -= length;

    // Si le tampon est plein, l'écrire sur disque.

    if (bits_free <= 0)
    {
        bits_free = enc_sOutputStreamDatas.iOB_TotalBits;
        enc_sOutputStreamDatas.iOB_BitIndex = bits_free;

        WriteBuffer();

        output[0] = bit_field;

        bit_index  = 16 - remaining_bits;
        bits_free -= bit_index;
    }
    else
    {
        output[offset + 1] = bit_field;
        bit_index += length;
    }

    enc_sOutputStreamDatas.iOB_BitIndex = bit_index;
    enc_sOutputStreamDatas.iOB_BitsFree = bits_free;
}
#endif
#endif  // USE_QUICK_PUTBITS
///
/// CoderC::FillFrame()
void CoderC::FillFrame()
{
    while (enc_iAvgDataBits >= 16)
    {
        PutBits(0, 16);
        enc_iAvgDataBits -= 16;
    }

    if (enc_iAvgDataBits)
    {
        PutBits(0, enc_iAvgDataBits);
    }

#ifdef USE_QUICK_PUTBITS
#if _USE_ASM_FCT_ == 1 || _CPUMODEL_ == 68020
    PutBitsASM2(&enc_sOutputStreamDatas, &bit_buffer[0], BitBuffPtr);
#else
    register UWORD *output     = (UWORD *) enc_sOutputStreamDatas.pOutputBuffer;
    register UWORD *bitpos     = &bit_buffer[0];

    ULONG bit_index = enc_sOutputStreamDatas.iOB_BitIndex;
    LONG  bits_free = enc_sOutputStreamDatas.iOB_BitsFree;
    ULONG offset;
    LONG  remaining_bits;
    ULONG bit_field;

    while (bitpos < BitBuffPtr)
    {
        ULONG length = *bitpos++;
        offset = bit_index >> 4;
        remaining_bits = 32 - (bit_index & 15) - length;     // E [1;31]
        bit_field      = (output[offset] * 65536) | (*bitpos++ << remaining_bits);

        output[offset] = bit_field >> 16;
        bit_index += length;
        bits_free -= length;

        // Si le tampon est plein, l'écrire sur disque.

        if (bits_free <= 0)
        {
            bits_free = enc_sOutputStreamDatas.iOB_TotalBits;
            enc_sOutputStreamDatas.iOB_BitIndex = bits_free;

            WriteBuffer();

            output[0] = bit_field;

            bit_index  = 16 - remaining_bits;
            bits_free -= bit_index;
        }
        else
        {
            output[offset + 1] = bit_field;
        }
    }
    enc_sOutputStreamDatas.iOB_BitIndex = bit_index;
    enc_sOutputStreamDatas.iOB_BitsFree = bits_free;
#endif
#endif
}
///

//----------------------------------------------------------------------------------------------------
//========================================= Classe CoderL1C ==========================================
//----------------------------------------------------------------------------------------------------

// Constructeur et destructeur.

/// CoderL1C::CoderL1C()
CoderL1C::CoderL1C(FileInfosC &roFileInfos) : CoderC(roFileInfos, 576 + 64)
{
    if (enc_iEncoderStatus == ESTAT_READY)
    {
        int iInitError;

        // Initialiser l'encodeur.

        InitSlotsPerFrame(FRAME_SIZE, 5);

        iInitError = !(enc_poPsychoC = new Psycho_L1C(*this));

        iInitError |= !(enc_pdSB_Samples = (T_SubBandSmplStereo *) AllocAlignedBuffer(sizeof(T_SubBandSmplStereo)));
        iInitError |= !(enc_pdJ_Samples =  (T_SubBandSmplMono *) AllocAlignedBuffer(sizeof(T_SubBandSmplMono)));

        // A l'initialisation de l'encodeur, la variable "mode_ext" est soit
        // non définie, soit nulle. Voir les sources d'origine pour plus ample
        // information.

        enc_BSHeader.sBSHeader.bsh_Layer = 3;

        if (GetEncodingMode() == JOINTSTEREO) SetJSBound(0);
        else                                  enc_bJSBound = enc_bSBLimit;

        if (iInitError != FALSE || enc_poPsychoC->CheckPsychoStatus() != FALSE)
        {
            SetErrorCodes(ESTAT_NOMEMORY, ERR_INTERNAL_BUFFERS);
        }
        else if (!enc_poPsychoC->InitPsychoAnalyzer(SBLIMIT))
        {
            SetErrorCodes(ESTAT_NOTSUPPORTED, ERR_BAD_FREQUENCY);
        }
    }
}
///
/// CoderL1C::~CoderL1C()

CoderL1C::~CoderL1C()
{
    if (enc_pdSB_Samples)   FreeAlignedBuffer(enc_pdSB_Samples);
    if (enc_pdJ_Samples)    FreeAlignedBuffer(enc_pdJ_Samples);
}
///

// Fonctions virtuelles.

/// CoderL1C::GetAudio()
/****** Class CoderL1C/GetAudio *********************************************
*
*   NAME
*   CoderL1C::GetAudio -- Préparer l'analyse de la page suivante.
*
*   SYNOPSIS
*   Result = CoderL1C::GetAudio()
*
*   bool CoderL1C::GetAudio();
*
*   FUNCTION
*   Prépare l'analyse de la page suivante.
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Du fait que la fonction SubBandCoding() exploite les échantillons direc-
*   tement dans le tampon de lecture, le pointeur d'accès doit être adapté en
*   conséquence.
*   Même chose en ce qui concerne l'analyseur psycho-acoustique.
*
*   BUGS
*
*   SEE ALSO
*   CoderC::SubBandCoding_Stereo(), CoderC::SubBandCoding_Mono()
*
*****************************************************************************
*
*/


int CoderL1C::GetAudio()
{
    int success;
    ULONG iOffset1 = 576 + 64;
    ULONG iOffset2 = 64 + 480;

    if (enc_bStereo == 2)
    {
        iOffset1 += iOffset1;
        iOffset2 += iOffset2;
    }

    if ( (success = CoderC::GetAudio()) )
    {
        enc_pwSampleBuffer= enc_pSamples - iOffset1;
        enc_pSB_ActualPtr = enc_pSamples - iOffset2;
    }

    return success;
}
///
/// CoderL1C::BitsForNoNoise
/****** Class CoderL1C/BitsForNoNoise ***************************************
*
*   NAME
*   CoderL1C::BitsForNoNoise --
*
*   SYNOPSIS
*   NumBits = CoderL1C::BitsForNoNoise()
*
*   ULONG CoderL1C::BitsForNoNoise();
*
*   FUNCTION
*   Calcule le nombre de bits nécessaires à l'encodage des données avec les
*   réglages courants.
*
*   RESULT
*   NumBits - Nombre de bits nécessaires pour l'encodage.
*
*   NOTES
*   Cette fonction n'est appelée qu'en mode JOINT_STEREO. Par consequent,
*   il y a toujours 2 canaux à traiter.
*   La fonction a été modifiée en conséquence.
*
*   BUGS
*   A l'inverse des autres fonctions d'allocation, celle-ci n'alloue pas 16
*   bits supplémentaires lorsqu'on encode avec le CRC.
*   Ce n'est peut-être pas normal...
*
*****************************************************************************
*
*/


ULONG CoderL1C::BitsForNoNoise()
{
    const ULONG REQBITS = 32 + SBLIMIT * 4;

    ULONG i, j, k;
    ULONG jsbound  = enc_bJSBound;
    ULONG req_bits = REQBITS + (jsbound * 4);

    LONG LTMin;

    for (i = 0; i < jsbound; i++)
    {
        for (j = 0; j < 2; j++)
        {
            for (k = 0, LTMin = enc_LTMin[j][i]; snr[k] < LTMin && k < 14; k++) ;

            if (k) req_bits += 6 * (k + k + 3);             // NB : (k + 1) × SCALE_BLOCK + 6 = 6 × (2k + 3)
        }
    }

    LONG *p1 = &enc_LTMin[0][i];
    LONG *p2 = &enc_LTMin[1][i];

    for ( /*i = jsbound,*/ ; i < SBLIMIT; i++)
    {
        for (k = 0, LTMin = *p1; snr[k] < LTMin && k < 14; k++) ;
        for (       LTMin = *p2; snr[k] < LTMin && k < 14; k++) ;

        ++p1; ++p2;

        if (k) req_bits += 6 * (k + k + 4);                 // NB : (k + 1) × SCALE_BLOCK + 12 = 6 × (2k + 4)
    }

    return req_bits;
}
///
/// CoderL1C::OneBitAllocation()
/****** Class CoderL1C/OneBitAllocation *************************************
*
*   NAME
*   CoderL1C::OneBitAllocation --
*
*   SYNOPSIS
*   CoderL1C::OneBitAllocation()
*
*   void CoderL1C::OneBitAllocation();
*
*   FUNCTION
*   Adapte le nombre de bits nécessaire à l'encodage des données, bit à bit,
*   en augmentant progressivement la qualité jusqu'à ce que le nombre de bits
*   libres soit insuffisant.
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL1C::OneBitAllocation()
{
    ULONG numbits, offset;
    LONG  min_sb, min_ch;
    UBYTE *pBitAlloc;

    UBYTE used[2 * SBLIMIT];

    LONG  MinNR[2 * SBLIMIT];
    LONG *pLTMin;

    enc_iAvgDataBits -= enc_iMinNeededBits + 4 * (SBLIMIT + ( (enc_bStereo == 2) ? enc_bJSBound : 0) );

    // Initialiser les tables intermédiaires.

    {
        LONG *pMNR;
        UBYTE *pUsed;

        pMNR      = &MinNR[0];
        pLTMin    = &enc_LTMin[0][0];
        pUsed     = &used[0];
        pBitAlloc = &enc_bBitAlloc[0][0];

        for (min_ch = enc_bStereo; min_ch > 0; min_ch--)
        {
            for (min_sb = SBLIMIT; min_sb > 0; min_sb--)
            {
                *pMNR++      = - *pLTMin++;
                *pUsed++     = 0;
                *pBitAlloc++ = 0;
            }
        }
    }

    // Localiser la sous-bande ayant le SMR minimum.

    numbits = 0;

    do
    {
        min_sb = -1;

        {
            LONG i, k;
            LONG small  = MinNR[0] + 1;

            for (k = enc_bStereo - 1; k >= 0; --k)
            {
                for (i = SBLIMIT, offset = k * SBLIMIT; i > 0; --i, ++offset)
                {
                    if (small > MinNR[offset] && used[offset] != 2)
                    {
                        small  = MinNR[offset];
                        min_sb = SBLIMIT - i;
                        min_ch = k;
                    }
                }
            }
        }

        if (min_sb >= 0)
        {
            ULONG smpl_bits;

            offset = (min_ch * SBLIMIT) + min_sb;

            pLTMin    = &enc_LTMin[0][0];
            pBitAlloc = &enc_bBitAlloc[0][0];

            if (used[offset])
            {
                smpl_bits  = SCALE_BLOCK;
            }
            else
            {
                smpl_bits  = 24 + 6;

                if (min_sb >= enc_bJSBound && enc_bStereo == 2)
                {
                    smpl_bits = 24 + 12;
                }
            }

            smpl_bits += numbits;

            if (enc_iAvgDataBits >= smpl_bits)
            {
                numbits = smpl_bits;
                pBitAlloc[offset]++;
                used[offset] = 1;
                MinNR[offset] = snr[pBitAlloc[offset]] - pLTMin[offset];

                if (pBitAlloc[offset] == 14) used[offset] = 2;
            }
            else used[offset] = 2;

            if (min_sb >= enc_bJSBound && enc_bStereo == 2)
            {
                ULONG oth_ch = SBLIMIT - (min_ch * SBLIMIT) + min_sb;

                pBitAlloc[oth_ch] = pBitAlloc[offset];
                used[oth_ch]      = used[offset];
                MinNR[oth_ch]     = snr[pBitAlloc[offset]] - pLTMin[oth_ch];
            }
        }

    } while (min_sb >= 0);

    enc_iAvgDataBits -= numbits;
}
///
/// CoderL1C::Encode()
/****** Class CoderL1C/Encode ***********************************************
*
*   NAME
*   CoderL1C::Encode -- Fonction d'encodage principale.
*
*   SYNOPSIS
*   Success = CoderL1C::Encode()
*
*   bool CoderL1C::Encode();
*
*   FUNCTION
*   Cette fonction appelle toutes les autres fonctions nécessaires pour
*   encoder la portion actuelle du tampon d'entrée.
*
*   RESULT
*   Success - FALSE en cas d'erreur.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


int CoderL1C::Encode()
{
    enc_poPsychoC->PsychoAnal(enc_LTMin);
    EncodeSubBand();

    if (GetEncodingMode() == JOINTSTEREO)
    {
        MainBitAllocation();
    }

    OneBitAllocation();

#ifdef USE_QUICK_PUTBITS
    InitBitBuffer();
#endif

    PutBits(enc_BSHeader.iBSHeader >> 16, 16);
    PutBits(enc_BSHeader.iBSHeader & 0xFFFF, 16);

    if (IsCRC())
    {
        CRC_Calc();
        PutBits(enc_wCRC, 16);                      // encode_CRC()
    }

    EncodeScale();
    EncodeSamples();
    FillFrame();

    return (enc_iEncoderStatus == ESTAT_READY);
}
///

// Fonctions de gestion

/// CoderL1C::EncodeSubBand()
void CoderL1C::EncodeSubBand()
{
    if (enc_bStereo == 2)   SubBandCoding_Stereo(enc_pdSB_Samples);
    else                    SubBandCoding_Mono( (T_SubBandSmplMono *) enc_pdSB_Samples);

    ScaleFactor(&enc_bScalar, enc_pdSB_Samples);

    if (GetEncodingMode() == JOINTSTEREO)
    {
        MixChannels(enc_pdSB_Samples, enc_pdJ_Samples);
        ScaleFactorCalc(&enc_bJScale[0], enc_pdJ_Samples);
    }
}
///
/// CoderL1C::CRC_Calc()
/****** Class CoderL1C/CRC_Calc *********************************************
*
*   NAME
*   CoderL1C::CRC_Calc -- Ajuste la somme de contrôle.
*
*   SYNOPSIS
*   CoderL1C::CRC_Calc()
*
*   void CoderL1C::CRC_Calc();
*
*   FUNCTION
*   Initialise la somme de contrôle avec les données de l'entête et la table
*   d'allocation des bits.
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL1C::CRC_Calc()
{
    int   i;
    ULONG stereo  = enc_bStereo - 1;
    ULONG jsbound = enc_bJSBound;

    UBYTE *pBitAlloc = &enc_bBitAlloc[0][0];

    CoderC::CRC_Calc();

    for (i = 0; i < SBLIMIT; i++)
    {
        UpdateCRC(*pBitAlloc, 4);

        if (stereo && (i < jsbound))
            UpdateCRC(pBitAlloc[SBLIMIT], 4);

         ++pBitAlloc;
    }
}
///
/// CoderL1C::EncodeScale()
/****** Class CoderL1C/EncodeScale ******************************************
*
*   NAME
*   CoderL1C::EncodeScale -- Encoder les informations de gestion.
*
*   SYNOPSIS
*   CoderL1C::EncodeScale()
*
*   void CoderL1C::EncodeScale();
*
*   FUNCTION
*   Encode les informations de gestion et les envoie dans le tampon de sortie
*
*   NOTES
*   Cumule les fonctions "encode_bit_alloc()" et "encode_scale()"
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL1C::EncodeScale()
{
    int     i;
    UBYTE   *pBitAlloc = &enc_bBitAlloc[0][0];
    UBYTE   *pScalar   = &enc_bScalar[0][0];
    ULONG   stereo  = enc_bStereo - 1;
    ULONG   jsbound = enc_bJSBound;

    // encode_bit_alloc()

    for (i = 0; i < SBLIMIT; i++)
    {
        PutBits(*pBitAlloc, 4);

        if (stereo && (i < jsbound))
            PutBits(pBitAlloc[SBLIMIT], 4);

        ++pBitAlloc;
    }

    pBitAlloc = &enc_bBitAlloc[0][0];

    // encode_scale()

    for (i = 0; i < SBLIMIT; i++)
    {
        if (*pBitAlloc)                   PutBits(*pScalar, 6);
        if (stereo && pBitAlloc[SBLIMIT]) PutBits(pScalar[SBLIMIT], 6);

        ++pBitAlloc; ++pScalar;
    }
}
///
/// CoderL1C::EncodeSamples()
/****** Class CoderL1C/EncodeSamples ****************************************
*
*   NAME
*   CoderL1C::EncodeSamples -- Encode les échantillons.
*
*   SYNOPSIS
*   CoderL1C::EncodeSamples()
*
*   void CoderL1C::EncodeSamples();
*
*   FUNCTION
*   Encode les échantillons et les envoies dans le tampon de sortie.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL1C::EncodeSamples()
{
    int     i, j, k, n;
    ULONG   jsbound = enc_bJSBound;
    ULONG   sign;
    double  d;
    double  one = 1.0;

    UBYTE *pBitAlloc;
    UBYTE *pJScale;
    UBYTE *pSScale;

    REAL  *pSSamples = &(*enc_pdSB_Samples)[0][0][0];
    REAL  *pJSamples = &(*enc_pdJ_Samples)[0][0];

    for (j = 0; j < SCALE_BLOCK; j++)
    {
        pBitAlloc = &enc_bBitAlloc[0][0];
        pJScale   = &enc_bJScale[0];
        pSScale   = &enc_bScalar[0][0];

        for (i = 0; i < SBLIMIT; i++)
        {
            for (k = 0; k < ( (i < jsbound) ? (enc_bStereo * SBLIMIT) : SBLIMIT); k += SBLIMIT)
            {
                if ( (n = pBitAlloc[k]) )
                {
                    if (i >= jsbound && enc_bStereo == 2)
                    {
                        d = *pJSamples * enc_adMultiples[*pJScale];
                    }
                    else
                    {
                        d = pSSamples[k * SCALE_BLOCK] * enc_adMultiples[pSScale[k]];
                    }

                    d = (d + one) * quant_a[n - 1];
                    n = 1 << n;

                    if (d < one)
                    {
                        sign = 0;
                    }
                    else
                    {
                        sign = n;
                        d -= one;
                    }

                #ifndef USE_OLD_MATHLIB
                    n = ( (ULONG) (d * n) ) | sign;
                #else
                    n = ( (ULONG) floor(d * n) ) | sign;
                #endif

                    PutBits(n, pBitAlloc[k] + 1);
                }
            }
            ++pBitAlloc; ++pJSamples; ++pSSamples; ++pJScale; ++pSScale;
        }
    }
}
///


//----------------------------------------------------------------------------------------------------
//========================================= Classe CoderL2C ==========================================
//----------------------------------------------------------------------------------------------------

// Constructeur et destructeur.

/// CoderL2C::CoderL2C()
CoderL2C::CoderL2C(FileInfosC &roFileInfos) : CoderC(roFileInfos, 480)
{
    if (enc_iEncoderStatus == ESTAT_READY)
    {
        int iInitError;

        // Initialiser l'encodeur.

        InitSlotsPerFrame(BIGGEST_FRAME, 3);

        // Allouer les tampons.

        iInitError = !(enc_poPsychoC = new Psycho_L2C(*this));

        iInitError |= !(enc_pdSB_Samples = (T_SB_Samples *) AllocAlignedBuffer(sizeof(T_SB_Samples)));
        iInitError |= !(enc_pdJ_Samples =  (T_J_Samples *) AllocAlignedBuffer(sizeof(T_J_Samples)));
        iInitError |= !(enc_psAllocTable = (T_AllocTable *) AllocBuffer(sizeof(T_AllocTable)));

        // Terminer l'initialisation de l'encodeur.

        enc_BSHeader.sBSHeader.bsh_Layer = 2;

        if (!iInitError && !enc_poPsychoC->CheckPsychoStatus())
        {
            // Sélectionner le numéro de la table "Alloc_x" en fonction de la fréquence
            // d'échantillonage et du taux de sortie.

            UWORD   iSampleFreq = GetSampleFreq();
            UWORD   iBitRate    = GetBitRate();
            UWORD   iTable;

            if (enc_bStereo == 2) iBitRate >>= 1;   // Taux de sortie par canal.

            if ( iBitRate >= 56 && (iSampleFreq == 48000 || iBitRate <= 80) )   iTable = 0;
            else if (iSampleFreq != 48000 && iBitRate >= 96)                    iTable = 1;
            else if (iSampleFreq != 32000 && iBitRate <= 48)                    iTable = 2;
            else                                                                iTable = 3;

            enc_bSBLimit = InitAllocTables(enc_psAllocTable, iTable);

            // A l'initialisation de l'encodeur, la variable "mode_ext" est soit
            // non définie, soit nulle. Voir les sources d'origine pour plus ample
            // information.

            if (GetEncodingMode() == JOINTSTEREO) SetJSBound(0);
            else                                  enc_bJSBound = enc_bSBLimit;

            if (!enc_poPsychoC->InitPsychoAnalyzer(enc_bSBLimit))
                SetErrorCodes(ESTAT_NOTSUPPORTED, ERR_BAD_FREQUENCY);
        }
        else SetErrorCodes(ESTAT_NOMEMORY, ERR_INTERNAL_BUFFERS);
    }
}
///
/// CoderL2C::~CoderL2C()
CoderL2C::~CoderL2C()
{
    if (enc_pdSB_Samples)   FreeAlignedBuffer(enc_pdSB_Samples);
    if (enc_pdJ_Samples)    FreeAlignedBuffer(enc_pdJ_Samples);
    if (enc_psAllocTable)   FreeBuffer(enc_psAllocTable);
}
///

// Fonctions virtuelles.

/// CoderL2C::GetAudio()
/****** Class CoderL2C/GetAudio *********************************************
*
*   NAME
*   CoderL2C::GetAudio -- Préparer l'analyse de la page suivante.
*
*   SYNOPSIS
*   Result = CoderL2C::GetAudio()
*
*   bool CoderL2C::GetAudio();
*
*   FUNCTION
*   Prépare l'analyse de la page suivante.
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Du fait que la fonction SubBandCoding() exploite les échantillons direc-
*   tement dans le tampon de lecture, le pointeur d'accès doit être adapté en
*   conséquence.
*   Même chose en ce qui concerne l'analyseur psycho-acoustique.
*
*   BUGS
*
*   SEE ALSO
*   CoderC::SubBandCoding_Stereo(), CoderC::SubBandCoding_Mono()
*
*****************************************************************************
*
*/


int CoderL2C::GetAudio()
{
    int  success;
    ULONG iOffset;

    iOffset = (enc_bStereo == 2) ? 480 * 2 : 480;

    if ( (success = CoderC::GetAudio()) )
    {
        enc_pSB_ActualPtr = enc_pwSampleBuffer = enc_pSamples - iOffset;
    }

    return success;
}
///
/// CoderL2C::BitsForNoNoise()
/****** Class CoderL2C/BitsForNoNoise ***************************************
*
*   NAME
*   CoderL2C::BitsForNoNoise --
*
*   SYNOPSIS
*   NumBits = CoderL2C::BitsForNoNoise()
*
*   ULONG CoderL2C::BitsForNoNoise();
*
*   FUNCTION
*   Calcule le nombre de bits nécessaires pour encoder les données avec les
*   réglages courants.
*
*   RESULT
*   NumBits - Nombre de bits nécessaires.
*
*   NOTES
*   Cette fonction n'est appelée qu'en mode JOINT_STEREO. Par consequent,
*   il y a toujours 2 canaux à traiter.
*   La fonction a été modifiée en conséquence.
*
*   BUGS
*
*****************************************************************************
*
*/


ULONG CoderL2C::BitsForNoNoise()
{
    ULONG sb, ch, ba;
    ULONG jsbound = enc_bJSBound;
    ULONG req_bits;
    LONG  maxAlloc;
    s_SBAlloc  *pAlloc;
    LONG LTMin;

    req_bits = 0;

    for (sb = 0; sb < jsbound; sb++)
    {
        req_bits += (*enc_psAllocTable)[sb][0].bBits;
    }

    req_bits += req_bits;

    for ( ; sb < enc_bSBLimit; sb++)
    {
        req_bits += (*enc_psAllocTable)[sb][0].bBits;
    }

    req_bits += enc_iMinNeededBits;

    for (sb = 0; sb < jsbound; sb++)
    {
        for (ch = 0; ch < 2; ch++)
        {
            pAlloc = &(*enc_psAllocTable)[sb][0];
            maxAlloc = pAlloc->iBitMask - 2;

            for (ba = 0, LTMin = enc_LTMin[ch][sb]; snr[pAlloc->wQuant + (ba ? 1 : 0)] < LTMin && ba < maxAlloc; ba++, pAlloc++) ;

            if (ba)
            {
                req_bits += pAlloc->iTotalBits + sfsPerScfsi[enc_bScaleFactorInfo[ch][sb]];
            }
        }
    }

    LONG *p1 = &enc_LTMin[0][sb];      // sb = jsbound
    LONG *p2 = &enc_LTMin[1][sb];

    for ( ; sb < enc_bSBLimit; sb++)
    {
        pAlloc = &(*enc_psAllocTable)[sb][0];
        maxAlloc = pAlloc->iBitMask - 2;

        for (ba = 0, LTMin = *p1; snr[pAlloc->wQuant + (ba ? 1 : 0)] < LTMin && ba < maxAlloc; ba++, pAlloc++) ;
        for (        LTMin = *p2; snr[pAlloc->wQuant + (ba ? 1 : 0)] < LTMin && ba < maxAlloc; ba++, pAlloc++) ;

        ++p1; ++p2;

        if (ba)
        {
            req_bits += pAlloc->iTotalBits + sfsPerScfsi[enc_bScaleFactorInfo[0][sb]] + sfsPerScfsi[enc_bScaleFactorInfo[1][sb]];
        }
    }

    return req_bits;
}
///
/// CoderL2C::OneBitAllocation()
/****** Class CoderL2C/OneBitAllocation *************************************
*
*   NAME
*   CoderL2C::OneBitAllocation --
*
*   SYNOPSIS
*   CoderL2C::OneBitAllocation()
*
*   void CoderL2C::OneBitAllocation();
*
*   FUNCTION
*   Alloue un groupe de bits pour chacune des sous-bandes.
*   Le nombre de bit de chaque bande est incrémenté graduellement jusqu'à ce
*   qu'il n'y ait plus assez de bits libres.
*
*   NOTES
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL2C::OneBitAllocation()
{
    ULONG numbits, offset;
    LONG  min_sb, min_ch;

    UBYTE used[2 * SBLIMIT];
    UBYTE *pBitAlloc;

    LONG MinNR[2 * SBLIMIT];
    s_SBAlloc *pAlloc;

    // Initialiser les tables intermédiaires.

    {
        LONG *pLTMin;

        pLTMin    = &enc_LTMin[0][0];
        pBitAlloc = &enc_bBitAlloc[0][0];

        for (min_ch = enc_bStereo - 1; min_ch >= 0; --min_ch)
        {
            for (min_sb = enc_bSBLimit, offset = min_ch * SBLIMIT; min_sb > 0; --min_sb, ++offset)
            {
                MinNR[offset]     = - pLTMin[offset];
                used[offset]      = 0;
                pBitAlloc[offset] = 0;
            }
        }
    }

    // Calculer le nombre de bits nécessaire pour les tables de décodage.

    numbits = 0;
    pAlloc = &(*enc_psAllocTable)[0][0];

    for (min_sb = 0; min_sb < enc_bJSBound; ++min_sb)
    {
        // numbits += (*enc_psAllocTable)[min_sb][0].bBits;
        numbits += pAlloc->bBits;
        pAlloc  += MAX_ALLOC_COLS;
    }

    if (enc_bStereo == 2) numbits += numbits;

    for ( ; min_sb < enc_bSBLimit; ++min_sb)
    {
        // numbits += (*enc_psAllocTable)[min_sb][0].bBits;
        numbits += pAlloc->bBits;
        pAlloc  += MAX_ALLOC_COLS;
    }

    enc_iAvgDataBits -= numbits + enc_iMinNeededBits;

    // Localiser la sous-bande ayant le SMR minimum.

    numbits = 0;

    do
    {
        min_sb = -1;

        {
            LONG i, k;
            LONG small = 999999;

            for (k = enc_bStereo - 1; k >= 0; --k)
            {
                offset = k * SBLIMIT;

                for (i = enc_bSBLimit; i > 0; --i, ++offset)
                {
                    if (small > MinNR[offset] && used[offset] != 2)
                    {
                        small  = MinNR[offset];
                        min_sb = enc_bSBLimit - i;
                        min_ch = k;
                    }
                }
            }
        }

        if (min_sb >= 0)
        {
            LONG  oth_ch;
            ULONG ba, increment;

            LONG *pLTMin;

            offset = (min_ch * SBLIMIT);
            oth_ch = 32 - offset + min_sb;
            offset += min_sb;

            pBitAlloc = &enc_bBitAlloc[0][0];
            pAlloc    = &(*enc_psAllocTable)[min_sb][0];
            pLTMin    = &enc_LTMin[0][0];

            ba = pBitAlloc[offset];
            increment = pAlloc[ba + 1].iTotalBits;

            if (used[offset])
            {
                increment -= pAlloc[ba].iTotalBits;
            }
            else
            {
                UBYTE *pSCFSI = &enc_bScaleFactorInfo[0][0];

                increment += sfsPerScfsi[pSCFSI[offset]];

                if (min_sb >= enc_bJSBound && enc_bStereo == 2)
                {
                    increment += sfsPerScfsi[pSCFSI[oth_ch]];
                }
            }

            increment += numbits;

            if (enc_iAvgDataBits >= increment)
            {
                ba = ++pBitAlloc[offset];
                numbits = increment;

                used[offset] = 1;
                MinNR[offset] = snr[pAlloc[ba].wQuant + 1] - pLTMin[offset];

                if (ba >= pAlloc[0].iBitMask) used[offset] = 2;
            }
            else used[offset] = 2;

            if (min_sb >= enc_bJSBound && enc_bStereo == 2)
            {
                pBitAlloc[oth_ch] = ba;
                used[oth_ch]      = used[offset];
                MinNR[oth_ch]     = snr[pAlloc[ba].wQuant + 1] - pLTMin[oth_ch];
            }
        }

    } while (min_sb >= 0);

    enc_iAvgDataBits -= numbits;
}
///
/// CoderL2C::Encode()
/****** Class CoderL2C/Encode ***********************************************
*
*   NAME
*   CoderL2C::Encode -- Fonction principale.
*
*   SYNOPSIS
*   success = CoderL2C::Encode()
*
*   bool CoderL2C::Encode();
*
*   FUNCTION
*   Cette fonction appelle toutes les fonctions nécessaires pour l'encodage
*   de la portion courante du tampon d'entrée.
*
*   RESULT
*   success - FALSE en cas d'erreur
*
*   BUGS
*
*****************************************************************************
*
*/


int CoderL2C::Encode()
{
    enc_poPsychoC->PsychoAnal(enc_LTMin);
    EncodeSubBand();

    TransmissionPattern();

    if (GetEncodingMode() == JOINTSTEREO)
    {
        MainBitAllocation();
    }

    OneBitAllocation();

#ifdef USE_QUICK_PUTBITS
    InitBitBuffer();
#endif

    PutBits(enc_BSHeader.iBSHeader >> 16, 16);
    PutBits(enc_BSHeader.iBSHeader & 0xFFFF, 16);

    if (IsCRC())
    {
        CRC_Calc();
        PutBits(enc_wCRC, 16);                  // encode_CRC()
    }

    EncodeScale();
    EncodeSamples();
    FillFrame();

    return (enc_iEncoderStatus == ESTAT_READY);
}
///

// Fonctions de gestion.

/// CoderL2C::EncodeSubBand()
void CoderL2C::EncodeSubBand()
{
    int iFrame;

    for (iFrame = 0; iFrame < 3; iFrame++)
    {
        T_SubBandSmplStereo *pSB_Samples = (T_SubBandSmplStereo *) &(*enc_pdSB_Samples)[iFrame][0][0][0];
        T_SubBandSmplMono   *pJ_Samples  = (T_SubBandSmplMono *)   &(*enc_pdJ_Samples)[iFrame][0][0];

        if (enc_bStereo == 2)   SubBandCoding_Stereo(pSB_Samples);
        else                    SubBandCoding_Mono( (T_SubBandSmplMono *) pSB_Samples);

        ScaleFactor( (T_ScaleStereo *) &enc_bScalar[iFrame][0][0], pSB_Samples);

        if (GetEncodingMode() == JOINTSTEREO)
        {
            MixChannels(pSB_Samples, pJ_Samples);
            ScaleFactorCalc(&enc_bJScale[iFrame][0], pJ_Samples);
        }
    }
}
///
/// CoderL2C::InitAllocTables()
//----------------------------------------------------------------------------------------------------
//================================ Reconstruction des tables ALLOC_X =================================
//----------------------------------------------------------------------------------------------------
//
// TECHNIQUE :
//
//    Chacune des 4 tables contient 'L' lignes, 'L' étant donné par le tableau t_alloc_entries[].
//  Pour chacune de ces lignes, la table t_alloc_tables[][] donne l'index de la table de référence
//  à utiliser (t_ref_index[][]). Cette table de réference, associé au numéro de colonne, donne
//  l'index de la ligne d'information à entrer dans la table "alloc_x".
//
// => y = t_alloc_tables[x][line]
// => L = t_ref_index[y][col]
// => alloc_x[line][col] = t_ref_lines[L]

UBYTE CoderL2C::InitAllocTables(CoderL2C::T_AllocTable *paTable, UBYTE iTable)
{
    static const UBYTE a_bAllocEntries[] = {27, 30, 8, 12};

    // "Lignes" d'information utilisées.
                                                   // steps bits group quant
    static const struct s_InitTbl a_sRefLines[] = {  {    0,   2,    0,    0},    //  0
                                                     {    0,   3,    0,    0},    //  1
                                                     {    0,   4,    0,    0},    //  2
                                                     {    3,   5,    1,    0},    //  3
                                                     {    5,   7,    1,    1},    //  4
                                                     {    7,   3,    3,    2},    //  5
                                                     {    9,  10,    1,    3},    //  6
                                                     {   15,   4,    3,    4},    //  7
                                                     {   31,   5,    3,    5},    //  8
                                                     {   63,   6,    3,    6},    //  9
                                                     {  127,   7,    3,    7},    // 10
                                                     {  255,   8,    3,    8},    // 11
                                                     {  511,   9,    3,    9},    // 12
                                                     { 1023,  10,    3,   10},    // 13
                                                     { 2047,  11,    3,   11},    // 14
                                                     { 4095,  12,    3,   12},    // 15
                                                     { 8191,  13,    3,   13},    // 16
                                                     {16383,  14,    3,   14},    // 17
                                                     {32767,  15,    3,   15},    // 18
                                                     {65535,  16,    3,   16},    // 19
                                                  };


    // Index, pour chaque table de référence, des lignes à utiliser.

    static const UBYTE a_bRefIdx[][MAX_ALLOC_COLS] = {
                                              {2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},   // 0
                                              {2, 3, 4, 5, 6, 7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 19},   // 1
                                              {1, 3, 4, 5, 6, 7,  8, 19, },                                 // 2
                                              {0, 3, 4, 19, },                                              // 3
                                              {2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},    // 4
                                              {1, 3, 4, 6, 7, 8, 9, 10, },                                  // 5
                                            };


    // Index, pour chaque table, des tables de référence à utiliser.
    // Si les fonctions d'accès aux tables le permettent, il est possible de supprimer les tables
    // n° 0 et 2 en les remplaçant respectivement par les tables 1 et 3 (cela suppose que les fonctions
    // de gestion n'accèdent pas aux lignes non définies dans les tables d'origine).

    static const UBYTE a_bAllocTbl[][MAX_ALLOC_LINES] = {
                                                 {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, },
                                                 {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3},
                                                 {4, 4, 5, 5, 5, 5, 5, 5, },
                                                 {4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, },
                                               };


    UBYTE  bNumEntries = a_bAllocEntries[iTable];
    UBYTE  bLine, bSubTable, bCol, bEntry;
    struct s_SBAlloc *pTable;

    for (bLine = 0; bLine < bNumEntries; bLine++)
    {
        bSubTable = a_bAllocTbl[iTable][bLine];

        for (bCol = 0; bCol < MAX_ALLOC_COLS; bCol++)
        {
            bEntry = a_bRefIdx[bSubTable][bCol];
            pTable = & (*paTable)[bLine][bCol];

            // Initialiser la table.
            pTable->wSteps     = a_sRefLines[bEntry].wSteps;
            pTable->bBits      = a_sRefLines[bEntry].bBits;
            pTable->bGroup     = a_sRefLines[bEntry].bGroup;
            pTable->wQuant     = a_sRefLines[bEntry].bQuant;

            // Calculer des valeurs déduites des tables pour optimiser quelques fonctions.

            int n2 = 1;
            pTable->iTotalBits = SCALE_BLOCK * (pTable->bBits * pTable->bGroup);
            pTable->iBitMask   = (1 << pTable->bBits) - 1;

            while (n2 < pTable->wSteps && n2 < (1 << 16))
            {
                n2 *= 2;
            }

            pTable->wStepMask = n2 >> 1;

            // bGroup ne sert que de drapeau (EncodeSamples())

            pTable->bGroup = (pTable->bGroup == 3) ? 1 : 0;
        }
    }
    return (bNumEntries);
}
///
/// CoderL2C::TransmissionPattern()
/****** Class CoderL2C/TransmissionPattern **********************************
*
*   NAME
*   CoderL2C::TransmissionPattern -- Déterminer les sous-bandes utiles.
*
*   SYNOPSIS
*   CoderL2C::TransmissionPattern()
*
*   void CoderL2C::TransmissionPattern();
*
*   FUNCTION
*   Pour une sous-bande donnée, détermine s'il faut envoyer 1, 2 ou les 3
*   valeurs ScaleFactor[], puis initialise le tableau de sélection en accord
*   avec cette analyse.
*
*   BUGS
*
*****************************************************************************
*
*  13.09.98 : Modification des patterns [3][0], [4][0] et [4][4].
*/

void CoderL2C::TransmissionPattern()
{
    int i, k;
    int class0, class1;
    UBYTE *scalar0, *scalar1, *scalar2;
    UBYTE *scfsi;

    // bits 0-2 : Masque. Un bit à 1 indique qu'il faut utiliser le facteur d'echelle.
    // bits 3-4 : Code scsfi.

    const int CODE0 = 0;    // Utiliser les trois facteurs d'echelle.
    const int CODE1 = 1;    // Utiliser deux fois le premier, puis le dernier.
    const int CODE2 = 2;    // Utiliser trois fois le même facteur d'echelle.
    const int CODE3 = 3;    // Utiliser le premier, puis deux fois le dernier.

    const int SC_1 =  4;
    const int SC_2 =  8;
    const int SC_3 = 16;

    const int SC_122 = CODE3 | SC_1 | SC_2;
    const int SC_133 = CODE3 | SC_1 | SC_3;
    const int SC_113 = CODE1 | SC_1 | SC_3;
    const int SC_223 = CODE1 | SC_2 | SC_3;
    const int SC_123 = CODE0 | SC_1 | SC_2 | SC_3;
    const int SC_111 = CODE2 | SC_1;
    const int SC_222 = CODE2 | SC_2;
    const int SC_333 = CODE2 | SC_3;
    const int SC_xxx = CODE2;           // Code spécial.

                            //  2 >> 3   2 > 3  2 == 3   2 < 3  2 << 3
    static UBYTE pattern[25] = {SC_123, SC_122, SC_122, SC_133, SC_123,     // 1 >> 2
                                SC_113, SC_111, SC_111, SC_xxx, SC_113,     // 1 > 2
                                SC_113, SC_111, SC_111, SC_333, SC_113,     // 1 == 2
                                SC_223, SC_222, SC_222, SC_333, SC_223,     // 1 < 2
                                SC_123, SC_122, SC_122, SC_133, SC_123};    // 1 << 2

    for (k = 0; k < enc_bStereo; k++)
    {
        scalar0 = &enc_bScalar[0][k][0];
        scalar1 = scalar0 + SBLIMIT * 2;
        scalar2 = scalar0 + SBLIMIT * 4;

        scfsi = &enc_bScaleFactorInfo[k][0];

        for (i = 0; i < enc_bSBLimit; i++)
        {
            class0 = *scalar0 - *scalar1;
            class1 = *scalar1 - *scalar2;

            if      (class0 <= -3)  class0 = 0 * 5;
            else if (class0 < 0)    class0 = 1 * 5;
            else if (class0 == 0)   class0 = 2 * 5;
            else if (class0 < 3)    class0 = 3 * 5;
            else                    class0 = 4 * 5;

            if (class1 > -3)
            {
                if      (class1 < 0)    class0 += 1;
                else if (class1 == 0)   class0 += 2;
                else if (class1 < 3)    class0 += 3;
                else                    class0 += 4;
            }

            class1   = pattern[class0];
            *scfsi++ = class0 = class1 & 3;

            // Mettre à jour les facteurs d'echelle (utilisés pour l'encodage des échantillons).

            if      (class0 == 1)       // 2x le premier, puis le dernier.
            {
                if (class1 == SC_113)   *scalar1 = *scalar0;    // SC_113
                else                    *scalar0 = *scalar1;    // SC_223
            }
            else if (class0 == 2)       // 3x le premier.
            {
                if      (class1 == SC_111)      *scalar2 = *scalar1 = *scalar0;
                else if (class1 == SC_222)      *scalar0 = *scalar2 = *scalar1;
                else if (class1 == SC_333)      *scalar0 = *scalar1 = *scalar2;
                else if (*scalar0 > *scalar2)   *scalar1 = *scalar0 = *scalar2;
                else                            *scalar1 = *scalar2 = *scalar0;
            }
            else if (class0 == 3)       // 1x le premier, puis 2x le dernier.
            {
                if (class1 == SC_122)   *scalar2 = *scalar1;    // SC_122
                else                    *scalar1 = *scalar2;    // SC_133
            }

            ++scalar0; ++scalar1; ++scalar2;
        }
    }
}
///
/// CoderL2C::CRC_Calc()
/****** Class CoderL2C/CRC_Calc *********************************************
*
*   NAME
*   CoderL2C::CRC_Calc -- Initialise la somme de contrôle.
*
*   SYNOPSIS
*   CoderL2C::CRC_Calc()
*
*   void CoderL2C::CRC_Calc();
*
*   FUNCTION
*   Initialise la somme de contrôle avec le bloc d'entête et les données de
*   gestion.
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL2C::CRC_Calc()
{
    int   i;
    ULONG sblimit = enc_bSBLimit;
    ULONG jsbound = enc_bJSBound;
    ULONG stereo  = enc_bStereo - 1;

    UBYTE *pBitAlloc = &enc_bBitAlloc[0][0];
    UBYTE *pSCFSI    = &enc_bScaleFactorInfo[0][0];

    CoderC::CRC_Calc();

    for (i = 0; i < sblimit; i++)
    {
        ULONG bits = (*enc_psAllocTable)[i][0].bBits;

        UpdateCRC(*pBitAlloc, bits);

        if (stereo && (i < jsbound))
            UpdateCRC(pBitAlloc[SBLIMIT], bits);

        ++pBitAlloc;
    }

    pBitAlloc = &enc_bBitAlloc[0][0];

    for (i = 0; i < sblimit; i++)
    {
        if (*pBitAlloc)                     UpdateCRC(*pSCFSI, 2);
        if (stereo && pBitAlloc[SBLIMIT])   UpdateCRC(pSCFSI[SBLIMIT], 2);
        ++pSCFSI; ++pBitAlloc;
    }
}
///
/// CoderL2C::EncodeScale()
/****** Class CoderL2C/EncodeScale ******************************************
*
*   NAME
*   CoderL2C::EncodeScale -- Encoder les données de gestion.
*
*   SYNOPSIS
*   CoderL2C::EncodeScale()
*
*   void CoderL2C::EncodeScale();
*
*   FUNCTION
*   Encode les données de gestion et les envoies dans le tampon de sortie.
*   NOTES
*   Cumule les fonctions "encode_bit_alloc()" et "encode_scale()"
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL2C::EncodeScale()
{
    int     i, k;
    ULONG   stereo  = (enc_bStereo - 1) * SBLIMIT;
    ULONG   sblimit = enc_bSBLimit;
    ULONG   jsbound = enc_bJSBound;

    UBYTE *pBitAlloc = &enc_bBitAlloc[0][0];

    // encode_bit_alloc()

    {
        s_SBAlloc *pAlloc = &(*enc_psAllocTable)[0][0];

        for (i = 0; i < sblimit; i++)
        {
            k = pAlloc->bBits;

            PutBits(*pBitAlloc, k);

            if (stereo && (i < jsbound))
                PutBits(pBitAlloc[SBLIMIT], k);

            ++pBitAlloc; pAlloc += MAX_ALLOC_COLS;
        }
    }

    // encode_scale()

    {
        UBYTE *pSCFSI    = &enc_bScaleFactorInfo[0][0];
        UBYTE *pScalar   = &enc_bScalar[0][0][0];

        pBitAlloc = &enc_bBitAlloc[0][0];

        for (i = 0; i < sblimit; i++)
        {
            for (k = 0; k <= stereo; k += SBLIMIT)
            {
                if (pBitAlloc[k])  PutBits(pSCFSI[k], 2);
            }
            ++pBitAlloc; ++pSCFSI;
        }

        pSCFSI    = &enc_bScaleFactorInfo[0][0];
        pBitAlloc = &enc_bBitAlloc[0][0];

        for (i = 0; i < sblimit; i++)
        {
            for (k = 0; k <= stereo; k += SBLIMIT)
            {
                if (pBitAlloc[k])
                {
                    PutBits(pScalar[k], 6);

                    if (pSCFSI[k] == 0)
                    {
                        PutBits(pScalar[k + SBLIMIT * 2], 6);
                    }

                    if (pSCFSI[k] != 2)
                    {
                        PutBits(pScalar[k + SBLIMIT * 4], 6);
                    }
                }
            }
            ++pSCFSI; ++pBitAlloc; ++pScalar;
        }
    }
}
///
/// CoderL2C::EncodeSamples()
/****** Class CoderL2C/EncodeSamples ****************************************
*
*   NAME
*   CoderL2C::EncodeSamples -- Encoder les échantillons.
*
*   SYNOPSIS
*   CoderL2C::EncodeSamples()
*
*   void CoderL2C::EncodeSamples();
*
*   FUNCTION
*   Encode les échantillons et les envoies dans le tampon de sortie.
*
*   NOTES
*   Cumule les fonctions "subband_quantization()" et "sample_encoding()"
*
*   BUGS
*
*****************************************************************************
*
*/


void CoderL2C::EncodeSamples()
{
    int    f, i, j, k, n, n2, j2;
    ULONG  jsbound = enc_bJSBound;
    ULONG  sign;
    double d;
    const float one = 1.0;

    UWORD  sbband[3][2*32];

    UBYTE     *pBitAlloc;
    UBYTE     *pJScale;
    UBYTE     *pSScale;
    REAL      *pSSamples;
    REAL      *pJSamples;
    s_SBAlloc *pAlloc;

    for (f = 0; f < 3; f++)
    {
        for (j = 0, j2 = 0; j < SCALE_BLOCK; j++, j2++)
        {
            pSSamples = &(*enc_pdSB_Samples)[f][0][j][0];
            pJSamples = &(*enc_pdJ_Samples)[f][j][0];

            pBitAlloc = &enc_bBitAlloc[0][0];
            pJScale   = &enc_bJScale[f][0];
            pSScale   = &enc_bScalar[f][0][0];

            if (j2 == 3) j2 = 0;

            for (i = 0; i < enc_bSBLimit; i++)
            {
                //pAlloc = &(*enc_psAllocTable)[i][0];

                for (k = 0; k < ( (i < jsbound) ? (enc_bStereo * SBLIMIT) : SBLIMIT); k += SBLIMIT)
                {
                    if ( (n = pBitAlloc[k]) )
                    {
                        pAlloc = &(*enc_psAllocTable)[i][n];
                        if (i >= jsbound && enc_bStereo == 2) {
                            d = *pJSamples * enc_adMultiples[*pJScale];
                        }
                        else {
                            d = pSSamples[k * SCALE_BLOCK] * enc_adMultiples[pSScale[k]];
                        }

                        d  = (d + one) * quant_a[pAlloc->wQuant] - one;
                        n2 = pAlloc->wStepMask;

                        if (d < 0) {
                            sign = 0;
                            d += one;
                        }
                        else {
                            sign = n2;
                        }

                        {
                            UWORD *pSBBand = &sbband[0][k + i];

                    #ifndef USE_OLD_MATHLIB
                            pSBBand[j2*64] = ((ULONG) (d * n2)) | sign;
                    #else
                            pSBBand[j2*64] = ( (ULONG) floor(d * n2) ) | sign;
                    #endif
                            if (j2 == 2) {
                                if (pAlloc->bGroup) {         //  == 3)
                                    n = pAlloc->bBits;
                                    for (int x = 0; x < 3 * 64; x += 64)
                                        PutBits(pSBBand[x], n);
                                }
                                else {
                                    int y = pAlloc->wSteps;
                                    int x = pSBBand[0 * 64] + y * (pSBBand[1 * 64] + y * pSBBand[2 * 64]);

                                    PutBits(x, pAlloc->bBits);
                                }
                            }
                        }
                    }
                }
                ++pBitAlloc; ++pJSamples; ++pSSamples; ++pJScale; ++pSScale;
            }
        }
    }
}
///

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


