                          /**************************************************/
                          /*                                                */
                          /*                   Common.cpp                   */
                          /*                                                */
                          /*         Fonctions communes aux modules         */
                          /*                                                */
                          /**************************************************/

/*
** $VER: common.cpp 1.8 (15.8.2000)
*/

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

/// Includes

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

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

#ifndef  _INCLUDE_IOMANIP_H
#include <iomanip.h>
#endif

#ifndef  DOS_DOS_H
#include <dos/dos.h>
#endif

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

#ifndef  _PEGASE_HPP
#include "Pegase.hpp"
#endif

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

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

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

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

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

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

#ifndef  CLASS_DISPLAY_HPP
#include "Display.hpp"
#endif

///

#define  CDDA_MULTIPLE  2352            // Longueur des blocs des fichiers CDDA

#define  CATCOMP_NUMBERS
#ifndef  PEGASE_H
#include "Pegase.h"
#endif

#if defined DEBUG_SMR || defined DEBUG_FFT || defined DEBUG_SNR
#include "Debug_tools.hpp"
#endif


//----------------------------------------------------------------------------------------------------
//======================================== Prototypes privés =========================================
//----------------------------------------------------------------------------------------------------

e_AudioTypes     IdentifyInputFile(FileInfosC &roFileInfo);
e_IoErrors       InitFileInfos(FileInfosC &roFileInfo);
e_FileListErrors IdentifyFile(AnchorPath *pAnchor, char *cPath, PegaseConfigC &roCfg);

void InitTimer();
LONG GetElapsedTime();


//----------------------------------------------------------------------------------------------------
//============================================ Fonctions =============================================
//----------------------------------------------------------------------------------------------------

/// ReadFileList()
//----------------------------------------------------------------------------------------------------
//========================================== ReadFileList() ==========================================
//----------------------------------------------------------------------------------------------------
/****** Common/ReadFileList *************************************************
*
*   NAME
*   ReadFileList -- Initialiser la liste chaînée des fichier à traiter.
*
*   SYNOPSIS
*   error = ReadFileList(cFrom)
*
*   bool ReadFileList(const char *);
*
*   FUNCTION
*   Examine la source passée en argument, puis analyse les fichiers qu'elle
*   contient. Lorsqu'un fichier audio est détecté, il est ajouté à la liste
*   globale (sa position étant déterminée par son type).
*   Les fichiers non-audio, ou dont le format n'est pas supporté sont
*   ignorés.
*
*   La source peut être un nom du fichier, un nom de répertoire ou un motif
*   de recherche. Lorsqu'il s'agit d'un répertoire, tous les fichiers de ce
*   répertoire sont analysés, mais tous les sous-répertoires sont ignorés.
*
*   INPUTS
*   cFrom    - Nom du répertoire à examiner, nom du fichier à charger, ou
*              motif de recherche. S'il s'agit d'un nom de répertoire, tous
*              les fichiers contenus dans ce répertoire sont analysés, et
*              les éventuels sous-répertoires sont ignorés.
*              Un pointeur nul ou une chaîne nulle sont acceptés, la fonction
*              retournant alors "NO_NAME_GIVEN".
*
*   RESULT
*   error - Code de l'erreur rencontrée.
*           La présence d'une erreur ne signifie pas qu'aucun fichier n'a été
*           ajouté à la liste globale.
*
*   NOTES
*   Cette fonction utilise les fonctions DOS MatchFirst()/MatchNext() et
*   MatchEnd().
*   Le tampon alloué dynamiquement pour recevoir la copie du nom complet du
*   répertoire parent peut voir sa taille augmenter lorsque c'est nécessaire
*   (gestion dynamique).
*
*   Si la fonction vient à manquer de mémoire, l'examen des sources est
*   immédiatement stoppée. Par contre, une erreur I/O sur l'un des fichiers
*   n'interdit pas l'examen des autres fichiers audio.
*
*   BUGS
*   Voir les Autodocs concernant MatchFirst().
*   La fonction ignore les SoftLinks.
*
*   SEE ALSO
*   class FileInfosC, class PegaseConfigC
*
*****************************************************************************
*
*/

#define INITIAL_PATH_BUFF_SIZE   256            // Taille originale par défaut du tampon dynamique
                                                // utilisé pour la recopie du nom du répertoire.
#define INCREASE_BUFF_SIZE_VALUE  64            // Incrément pour l'augmentation de la taille du
                                                // tampon dynamique.

e_FileListErrors ReadFileList(PegaseConfigC &roGlobalCfg, ULONG iIndex)
{
    CMatch           match;
    LONG             iMatch;
    e_FileListErrors iError = NO_ERROR;         // Code d'erreur à retourner.
    int              EnableDoDir = TRUE;        // Drapeau permettant d'entrer dans un répertoire.
    AnchorPath      *pAnchor;

    // Le nom du répertoire parent retourné est le nom complet du chemin d'accès (après
    // résolution éventuelle de toute assignation).
    // La taille du tampon utilisé pour la recopie de ce nom de répertoire est dynamique.
    // Autrement dit, la fonction est capable d'allouer un tampon plus grand que celui
    // disponible lorsque c'est nécessaire.

    ULONG iPathBuffSize = INITIAL_PATH_BUFF_SIZE;
    CString cPath;

    // Avorter si le nom du fichier/répertoire/motif est incorrect ou s'il n'y a plus assez
    // de mémoire pour allouer le tampon dynamique.

    STRING cFrom = roGlobalCfg[iIndex];       // Rechercher la source (fichier ou répertoire).

    if (!cFrom || !*cFrom)
        return (NO_NAME_GIVEN);

    // pAnchor->ap_BreakBits = 0;
    // pAnchor->ap_Flags     = 0;
    // pAnchor->ap_Strlen    = 0;

    // Rechercher les fichiers audios (source inspiré de l'Amiga Guru Book p.456)
    // Si l'utilisateur a choisi un nom de répertoire, entrer dans ce répertoire.
    // Passée la première entrée, les répertoires sont ignorés.

    for (iMatch = match.First(cFrom); iMatch == 0; iMatch = match.Next() ) {
        pAnchor = match;

        // Si la source passée en argument correspond à un répertoire, y entrer. Dans les autres
        // cas, les répertoires sont ignorés.
        if (pAnchor->ap_Info.fib_DirEntryType >= 0 && pAnchor->ap_Info.fib_DirEntryType != ST_SOFTLINK && EnableDoDir)
            pAnchor->ap_Flags |= APF_DODIR;                     // Entrer dans le répertoire.
        else {
            pAnchor->ap_Flags &= ~APF_DIDDIR;

            // Examiner le fichier trouvé, à condition que ce ne soit pas un SoftLink.
            if (pAnchor->ap_Info.fib_DirEntryType < 0) {        // Ignore les SoftLinks
                if ( DOSLib::NameFromLock(pAnchor->ap_Current->an_Lock, cPath) )
                    IdentifyFile(pAnchor, cPath, roGlobalCfg);
            }
        }
        EnableDoDir = FALSE;                        // Interdire d'entrer dans les prochains répertoire.
        if (iError == NO_MEMORY)                    // Avorter immédiatement si la mémoire manque.
            break;
    }

    // Libérer les ressources locales.

    if (iMatch != ERROR_NO_MORE_ENTRIES && iError == NO_ERROR)
        iError = MATCHING_ERROR;                    // Cette erreur n'est pas prioritaire.

    return iError;
}
///
/// IdentifyFile()
//----------------------------------------------------------------------------------------------------
//========================================== IdentifyFile() ==========================================
//----------------------------------------------------------------------------------------------------
/****** Common/IdentifyFile *************************************************
*
*   NAME
*   IdentifyFile -- Identifie un fichier.
*
*   SYNOPSIS
*   Erreur = IdentifyFile(pAnchor, cPath)
*
*   e_FileListErrors IdentifyFile(AnchorPath *, char *);
*
*   FUNCTION
*   Identifie le fichier actuellement pointé par la structure AnchorPath.
*   La fonction initialise et chaîne un objet FileInfos décrivant ce fichier
*   s'il s'agit d'un fichier audio. Dans le cas contraire, le fichier est
*   simplement ignoré.
*
*   Si Pegase a été lancé depuis le Shell, la fonction rend compte de l'
*   analyse du fichier.
*
*   14.8.99 :
*   Si le fichier d'entrée arrive par un PIPE, le format est automatiquement
*   choisi comme étant CDDA. Le format des échantillons peut être forcé en
*   utilisant les drapeaux INTEL ou MOTOROLA sur la ligne de commande (le
*   format MOTOROLA étant le format par défaut).
*
*   INPUTS
*   pAnchor  - Structure AnchorPath.
*   cPath    - Chemin d'accès au fichier.
*
*   RESULT
*   Erreur - Code d'erreur (NO_ERROR ou NO_MEMORY)
*
*****************************************************************************
*
*/


e_FileListErrors IdentifyFile(AnchorPath *pAnchor, char *cPath, PegaseConfigC &roCfg)
{
    e_FileListErrors iError = NO_ERROR;
    e_AudioTypes     iAudioType = AFT_UNKNOWN;
    e_IoErrors       iIOErr = IOERR_NONE;
    FileInfosC      *poNode;

    if ( (poNode = new FileInfosC(pAnchor->ap_Info.fib_FileName, cPath, roCfg)) ) {
        poNode->SetFileLength(pAnchor->ap_Info.fib_Size);
        display->out() << GetString(MSG_ANALYSING_TXT) << " \"" << pAnchor->ap_Info.fib_FileName << "\" : ";

        if (!roCfg.GetCDDAFmt()) {
            // Examiner le fichier rencontré.
            iAudioType = IdentifyInputFile(*poNode);
            iIOErr     = poNode->GetIoError();

            // Si le fichier a été identifié comme un fichier audio, et s'il n'y a pas eu
            // d'erreurs, initialiser les informations le concernant.
            if (iAudioType < AFT_NOT_AUDIO && iIOErr == IOERR_NONE) {
                iIOErr = InitFileInfos(*poNode);
                iAudioType = poNode->GetFileType();
            }
        }
        else {  // MOTOROLA/INTEL ==> CDDA.
            if (roCfg.GetCDDAFmt() == 2)
                iAudioType = AFT_CDDA_INTEL;
            else {
                iAudioType = AFT_CDDA;
                roCfg.SetCDDAFmt(1);            // Format MOTOROLA par défaut.
            }

            poNode->SetFileType(iAudioType);
            iIOErr = InitFileInfos(*poNode);
        }

        // Traiter les erreurs, ou chaîner le fichier à la liste globale s'il n'y en a pas eu.

        if (iIOErr != IOERR_NONE && iIOErr != IOERR_WARN || iAudioType >= AFT_NOT_AUDIO) {
            if (iIOErr == IOERR_INTERNAL && iAudioType < AFT_NOT_AUDIO)
                display->out() << GetString(MSG_UNSUPPORTED_FREQUENCY_ERROR);
            else {
                long stringID = MSG_READ_ERROR;

                switch(iAudioType) {
                    case AFT_NOT_AUDIO:
                        stringID = MSG_NOT_AN_AUDIO_FILE_ERROR;
                        break;

                    case AFT_BAD_AIFF:
                        stringID = MSG_BAD_AIFF_ERROR;
                        break;

                    case AFT_BAD_MAUD:
                        stringID = MSG_BAD_MAUD_ERROR;
                        break;

                    case AFT_BAD_WAV:
                        stringID = MSG_BAD_WAVE_ERROR;
                        break;
                }
                display->out() << GetString(stringID) << endl;
            }

            if (iIOErr == IOERR_NO_MEMORY)
                iError = NO_MEMORY;

            delete poNode;              // Détruire l'objet associé au fichier courant en cas d'erreur.
        }
        else {
            roCfg.Enqueue(poNode);
            display->out() << poNode->GetAudioName() << endl;

            if (iIOErr == IOERR_WARN && poNode->GetError2() == ERROR_BAD_HUNK)
                display->out() << GetString(MSG_WARN_BADIFF_SIZE) << endl;

            poNode->SetIoError(IOERR_NONE);
        }
    }
    else
        iError = NO_MEMORY;

    return iError;
}
///
/// RequestFileList()
//----------------------------------------------------------------------------------------------------
//======================================== RequestFileList() =========================================
//----------------------------------------------------------------------------------------------------
/****** Common/RequestFileList **********************************************
*
*   NAME
*   RequestFileList -- Obtenir une liste de fichiers.
*
*   SYNOPSIS
*   RequestFileList(cDefPath)
*
*   void RequestFileList(STRING);
*
*   FUNCTION
*   Ouvre un requester de fichier permettant à l'utilisateur de choisir les
*   fichiers à encoder.
*   La fonction crée un tableau de pointeurs, chacun d'eux adressant un
*   chemin d'accès correspondant à un fichier. Cela permet d'émuler le
*   tableau retourné par ReadArgs() lors de l'examen de la ligne de commande.
*
*   INPUTS
*   cDefPath - Chemin d'accès par défaut ou NULL.
*
*   NOTES
*   · La position du requester, de même que ses dimensions, ne sont pas
*     modifiables (codées dans le programme lui-même). Voir le tableau
*     s_Tags[].
*
*   · Cette fonction pourrait éventuellement tirer parti des nouveautés des
*     versions 38+ de l'asl.library
*
*****************************************************************************
*
*/

extern CPegaseArgs goPegaseArgs;

void RequestFileList()
{
    // Tags initiaux pour l'ouverture du requester.

    static struct TagItem s_Tags[] = {
        {ASLFR_Flags1, FRF_DOMULTISELECT|FRF_DOPATTERNS},
        {ASLFR_Flags2, FRF_REJECTICONS},
        {TAG_DONE, NULL}
    };

    CFileRequest Req;
    FileRequester *fileReq;

    if (Req.Init(s_Tags)) {
        if ( (fileReq = Req.Request(ASLFR_TitleText, GetString(MSG_FILEREQUEST_TITLE),
                                    ASLFR_InitialPattern, GlobalConfig.GetPattern(),
                                    ASLFR_InitialLeftEdge, *goPegaseArgs.pa_FR_XPos,
                                    ASLFR_InitialTopEdge, *goPegaseArgs.pa_FR_YPos,
                                    ASLFR_InitialWidth, *goPegaseArgs.pa_FR_Width,
                                    ASLFR_InitialHeight, *goPegaseArgs.pa_FR_Height,
                                    (goPegaseArgs.pa_DefSource ? ASLFR_InitialDrawer : TAG_DONE), goPegaseArgs.pa_DefSource,
                                    TAG_DONE)) )
        {
            char **file;
            ULONG  path_length = 0;
            ULONG  num_entries = fileReq->fr_NumArgs;
            char  *path        = fileReq->fr_Drawer;
            CString buff;

            if (!path || !*path)
                path = NULL;   // Ignorer un nom de répertoire nul.

            // Prendre en compte la sélection multiple le cas échéant.

            if (num_entries)
                file = (char **) &fileReq->fr_ArgList->wa_Name;
            else {
                num_entries = 1;
                file = (char **) &fileReq->fr_File;
            }

            // Allouer le tableau de pointeurs, puis copier les chaînes obtenues.

            for (int entry_index = 0; entry_index < num_entries; ++entry_index, file += 2) {
                buff = path;
                DOSLib::AddPart(buff, *file);
                GlobalConfig.Add(buff);
            }
        }
    }
}
///

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

/// IdentifyInputFile()
/****** Common.cpp/IdentifyInputFile ****************************************
*
*   NAME
*   IdentifyInputFile -- Identifier un fichier audio.
*
*   SYNOPSIS
*   type = IdentifyInputFile(roFileInfo)
*
*   e_AudioTypes IdentifyInputFile(FileInfosC);
*
*   FUNCTION
*   Cette fonction se charge d'identifier le fichier dont le nom est passé
*   en paramètre. Cette reconnaîssance se fonde sur le contenu des tous
*   premiers octets du fichier, ou sur l'extension du nom de fichier lorsqu'
*   une reconnaîssance plus fine n'est pas possible.
*
*   Actuellement, les fichiers suivants sont traîtés :
*       IFF_AIFF : Fichier IFF
*       IFF_MAUD : Fichier IFF
*       RIFF     : Fichier WAVE
*       #?.raw   : Données audio brutes
*       #?.pcm   : Données audio brutes
*       #?.cdda  : Fichier CDDA
*       0xE310   : Fichier icône (rejeté)
*       0x03F3   : Fichier exécutable (rejeté)
*       'GIF8'   : Fichier GIF (rejeté)
*       0xFFD8.. : Fichier JPEG (rejeté)
*
*   Les fichiers dont le format n'est pas reconnu immédiatement, ou les
*   fichiers RAW sont promus au type CDDA si leur taille est un multiple
*   entier de 2352.
*
*   INPUTS
*   roFileInfo - Objet FileInfos décrivant le fichier.
*
*   RESULT
*   type - Type du fichier. AFT_UNKNOWN est retourné lorsque le type du
*       fichier n'a pu être déterminé, ou lorsqu'une erreur s'est produite.
*       Dans ce dernier cas, roFileInfos.GetIoError() permet d'obtenir un
*       supplément d'information sur cette erreur.
*       Le type AFT_DEFAULT est similaire au type AFT_PCM, à la différence
*       que le fichier n'a pas été identifié comme tel de manière certaine.
*
*   NOTES
*   Cette fonction crée un certain nombre de tableaux sur la pile. S'
*   assurer qu'elle est appelée avec une taille de pile suffisante.
*
*****************************************************************************
*
*/

static const char PCM_EXTENSION[]  = "#?.pcm";
static const char RAW_EXTENSION[]  = "#?.raw";
static const char CDDA_EXTENSION[] = "#?.cdda";


e_AudioTypes IdentifyInputFile(FileInfosC &roFileInfo)
{
    const ULONG BUFFSIZE = 32;

    CFile    FH;
    CLock    NewDir;
    BPTR     bpOldDir;
    ULONG    a_iIdentBuff[BUFFSIZE];

    e_AudioTypes iType;
    STRING cFileName = roFileInfo.NodeName();
    ULONG iFL = roFileInfo.GetFileLength();

    extern int CheckExtension(STRING cFileName, STRING cExtension, ULONG iExtLen);

    // Si le fichier a une taille impaire, il ne peut pas s'agir d'un fichier audio.

    if ( (iFL & 1) || iFL < 4)
        iType = AFT_NOT_AUDIO;
    else {
        // Changer le répertoire courant pour s'assurer qu'on adresse le bon fichier.
        iType = AFT_UNKNOWN;

        if ( (NewDir.Lock(roFileInfo.GetPathName(), CLock::READ)) ) {
            bpOldDir = DOSLib::CurrentDir(NewDir);

            // Ouvrir le fichier, et examiner son type.
            if ( (FH.Open(cFileName, CFile::OLDFILE)) ) {
                if (FH.Read(a_iIdentBuff, BUFFSIZE * 4) > 4) {
                    if (a_iIdentBuff[0] == 'FORM') {        // Fichier IFF. Examiner le type exact.
                        switch(a_iIdentBuff[2]) {
                            case 'AIFF':
                                iType = AFT_AIFF;           // AIFF.
                                break;

                            case 'MAUD':
                                iType = AFT_MAUD;           // MAUD.
                                break;

                            default:
                                iType = AFT_NOT_AUDIO;      // Non supporté (image par exemple).
                        }
                    }
                    if (a_iIdentBuff[0] == 'RIFF')
                        iType = AFT_WAV;

                    // Reconnaissance basée sur l'extension du nom de fichier (.pcm, .raw)

                    if (CheckExtension(cFileName, PCM_EXTENSION, sizeof(PCM_EXTENSION)-1)
                        || CheckExtension(cFileName, RAW_EXTENSION, sizeof(RAW_EXTENSION)-1))
                        iType = AFT_PCM;

                    // Les fichiers CDDA doivent avoir une longueur multiple de 2352.

                    if (CheckExtension(cFileName, CDDA_EXTENSION, sizeof(CDDA_EXTENSION)-1)
                        && roFileInfo.GetFileLength() % CDDA_MULTIPLE == 0)
                        iType = AFT_CDDA;

                    // Types spéciaux... (forcément rejetés).

                    ULONG hdr = a_iIdentBuff[0];

                    if (  (hdr >> 16) == 0xE310         // Fichier icône.
                        || hdr == 0x3F3                 // Fichier exécutable.
                        || hdr == 'GIF8'                // Fichier GIF.
                        || hdr == 0xFFD8FFE0)           // Fichier JPEG (?)
                        iType = AFT_NOT_AUDIO;
                }
                else
                    roFileInfo.SetIoError(IOERR_READ, DOSLib::IoErr());
            }
            else
                roFileInfo.SetIoError(IOERR_OPEN, DOSLib::IoErr());

            DOSLib::CurrentDir(bpOldDir);         // Restaurer le répertoire original
        }
        else
            roFileInfo.SetIoError(IOERR_DOS, DOSLib::IoErr());

        // Si le type du fichier est inconnu, on part du principe que c'est un fichier RAW...
        if (iType == AFT_UNKNOWN)
            iType = AFT_DEFAULT;

        // Promouvoir les types RAW/PCM en type CDDA si la longueur du fichier fait
        // penser à un fichier CDDA.

        if ( (iType == AFT_PCM || iType == AFT_DEFAULT) && (roFileInfo.GetFileLength() % CDDA_MULTIPLE) == 0)
            iType = AFT_CDDA;
    }

    roFileInfo.SetFileType(iType);          // Définir le type de fichier audio.
    return (iType);
}


// Fonction privée remplaçant avantageusement ParsePattern/MatchPattern
// Retourne TRUE si le fichier possède bien l'extension passée en argument.

int CheckExtension(STRING cFileName, STRING cExtension, ULONG iExtLen)
{
    return DOSLib::MatchPatternNoCase(cFileName, cExtension);
}
///
/// InitFileInfos()
/****** Common.cpp/InitFileInfos ********************************************
*
*   NAME
*   InitFileInfos -- Obtenir les infos du fichier.
*
*   SYNOPSIS
*   Error = InitFileInfos(roFileInfo)
*
*   e_IoErrors InitFileInfos(FileInfosC &);
*
*   FUNCTION
*   Examine le fichier afin d'obtenir les informations concernant le nombre
*   d'échantillons, la fréquence d'échantillonage, etc.
*
*   INPUTS
*   roFileInfo - Informations sur le fichier courant.
*
*   RESULT
*   Error - Code d'erreur.
*
*   NOTES
*   Cette fonction crée un objet InputStream afin d'analyser le fichier audio
*   (fréquence d'échantillonage, etc).
*
*   Le type du fichier audio peut être modifié par cette fonction (notament
*   lorsque le fichier n'est pas dans un format correct conformément au
*   type attendu).
*
*   SEE ALSO
*   Class InputStreamC
*
*****************************************************************************
*
*/


e_IoErrors InitFileInfos(FileInfosC &roFileInfo)
{
    InputStreamC *poInput = NULL;               // Handle
    poInput = CreateAudioC(roFileInfo);

    // Rechercher les informations sur le fichier.

    if (poInput)
        poInput->GetFileInfos();
    else
        roFileInfo.SetIoError(IOERR_NO_MEMORY);

    // Libérer l'objet InputStream étant donné qu'il n'a servi qu'à obtenir certaines informations
    // sur le fichier.
    delete poInput;

    return roFileInfo.GetIoError();
}
///
/// CreateAudioC()
/****** Common/CreateAudioC *************************************************
*
*   NAME
*   CreateAudioC -- Créer un objet AudioC
*
*   SYNOPSIS
*   AudioObject = CreateAudioC(roFileInfo)
*
*   InputStreamC * CreateAudioC(FileInfosC &);
*
*   FUNCTION
*   Crée un fichier AudioC correspondant au fichier à traiter.
*
*   INPUTS
*   roFileInfo - Informations concernant le fichier à traiter.
*
*   RESULT
*   AudioObject - Objet AudioC ou NULL en cas de problème.
*
*****************************************************************************
*
*/


InputStreamC *CreateAudioC(FileInfosC &roFileInfo)
{
    InputStreamC *poInput = NULL;

    switch(roFileInfo.GetFileType()) {
        // Fichiers RAW/PCM/CDDA/UNKNOWN
        case AFT_DEFAULT:
        case AFT_PCM:
            poInput = new RAW_AudioC(roFileInfo);
            break;

        case AFT_CDDA:
        case AFT_CDDA_INTEL:
            poInput = new CDDA_AudioC(roFileInfo);
            break;

        // Fichiers IFF AIFF/MAUD
        case AFT_AIFF:
            poInput = new AIFF_AudioC(roFileInfo);
            break;

        case AFT_MAUD:
            poInput = new MAUD_AudioC(roFileInfo);
            break;

        // Fichiers RIFF-WAV
        case AFT_WAV:
            poInput = new WAV_AudioC(roFileInfo);
            break;
    }

    return poInput;
}
///

//----------------------------------------------------------------------------------------------------
//=========================================== EncodeFile() ===========================================
//----------------------------------------------------------------------------------------------------

int EncodeFile(FileInfosC &roFileInfos)
{
    e_EncoderST iStatus = ESTAT_NOTREADY;

    STRING sResultString = NULL;
    int    done = TRUE;             // FALSE == Arrêt global
    long   resultID = 0;

    // Créer un objet Layer pour l'encodage du fichier courant.

    e_Layer iLayerType = roFileInfos.GetLayer();
    CoderC *poEncoder = NULL;

    if (iLayerType == LAYER_I)
        poEncoder = new CoderL1C(roFileInfos);
    else
        poEncoder = new CoderL2C(roFileInfos);

    if (poEncoder)
        iStatus = poEncoder->GetEncoderStatus();

    // Si l'encodeur est prêt, commencer l'encodage.
    if (iStatus == ESTAT_READY) {
        InitTimer();

        //-----------------------------
        //===== BOUCLE PRINCIPALE =====
        //-----------------------------

        int stop = 0;
        display->ResetAnimVal();

        while (poEncoder->GetAudio() && !stop) {
            if (!poEncoder->Encode())
                break;

            stop = display->UpdateProgression(poEncoder->GetProgression(), &roFileInfos);
            if (stop & SIGBREAKF_CTRL_D)
                done = FALSE;
        }

        #ifdef DEBUG_SMR
        debugSMR.DumpResults();
        #endif
        #ifdef DEBUG_FFT
        debugFFT.DumpResults();
        #endif
        #ifdef DEBUG_SNR
        debugSNR.DumpResults();
        #endif

        // Afficher les messages d'erreurs éventuels.

        poEncoder->CloseIStream();
        e_IoErrors IOErr = roFileInfos.GetIoError();
        ULONG      Err2  = roFileInfos.GetError2();

        if (IOErr == IOERR_INTERNAL && Err2 == ERROR_BREAK)
            resultID = MSG_BREAK_TXT;
        else if (IOErr == IOERR_WARN && Err2 == ERROR_NO_MORE_ENTRIES) {
            LONG iNumSecs = GetElapsedTime();

            // Afficher le temps écoulé pendant l'encodage.
            if (iNumSecs < 0)
                display->out() << "\n" << GetString(MSG_NO_TIMER_ERRORTXT) << "\n" << endl;
            else {
                div_t temp;
                int s;

                temp = div(iNumSecs, 60);
                s = temp.rem;
                temp = div(temp.quot, 60);

                display->out() << "\n" << GetString(MSG_ELAPSED_TIME_TXT) << " : ";
                display->out().fill('0');
                display->out().width(2);
                display->out() << temp.quot << ":";
                display->out().width(2);
                display->out() << temp.rem << ":";
                display->out().width(2);
                display->out() << s;

                u_Time t = roFileInfos.GetTime();
                if (t.iTime != 0x5858) {
                    int duration = t.sTime.bMinutes * 60 + t.sTime.bSecondes;
                    int elapsed  = (temp.quot * 60 + temp.rem) * 60 + s;
                    double r = (double) elapsed / duration;
                    int prec = display->out().precision(2);

                    display->out() << "  (ratio = " << r << ":1)";
                    display->out().precision(prec);
                }
                display->out() << endl;
            }
            display->out() << GetString(MSG_DONE_TXT) << endl;

            // 15.8.2000 : Option DELETE == Effacer le fichier source.
            if (GlobalConfig.DeleteSource()) {
                CString pathname = roFileInfos.GetPathName();
                DOSLib::AddPart(pathname, roFileInfos.NodeName());

                display->out() << GetString(MSG_DELETING_TXT) << '"' << pathname << "\"..." << endl;
                ::DeleteFile(pathname);
            }
        }
        else if (IOErr == IOERR_NO_MEMORY) {
            resultID = MSG_OUT_OF_MEMORY_ERROR;
            done = FALSE;
        }
        else if (IOErr != IOERR_NONE)
            resultID = MSG_STREAM_ERROR;
    }

    // Traiter les codes d'erreur de l'encodeur.

    if (poEncoder) {
        e_EncErrCodes iErrCode = poEncoder->GetSecondaryError();
        iStatus = poEncoder->GetEncoderStatus();

        if (iStatus == ESTAT_NOTSUPPORTED) {
            // Actuellement, la seule erreur possible est : ERR_BAD_FREQUENCY.
            resultID = MSG_UNSUPPORTED_FREQUENCY_ERROR;
        }
        else if (iStatus == ESTAT_NOTREADY) {
            switch (iErrCode) {
                case ERR_NOT_INITIALIZED:
                    resultID = MSG_ENCODER_NOT_READY_ERROR;
                    done = FALSE;
                    break;

                case ERR_ISTREAM_FAILURE:
                    resultID = MSG_ISTREAM_ERROR;
                    break;
            }
        }
        else if (iStatus == ESTAT_IOERROR) {
            switch (iErrCode) {
                case ERR_OPEN_OSTREAM:
                    resultID = MSG_OPEN_OSTREAM_ERROR;
                    done = FALSE;
                    break;

                case ERR_SEEK_OSTREAM:
                    resultID = MSG_SEEK_OSTREAM_ERROR;
                    break;

                case ERR_WRITE_OSTREAM:
                    resultID = MSG_WRITE_OSTREAM_ERROR;
                    break;

                case ERR_CLOSE_OSTREAM:
                    resultID = MSG_CLOSE_OSTREAM_ERROR;
                    break;

                #if 0
                case ERR_DISKFULL:
                    resultID = MSG_FILE_WONT_FIT_ERROR;
                    done = FALSE;   // Avorter la totalité de l'encodage.
                    break;
                #endif
            }
        }
        else if (iStatus == ESTAT_NOMEMORY) {
            // Actuellement, la seule erreur possible est : ERR_INTERNAL_BUFFERS
            resultID = MSG_OUT_OF_MEMORY_ERROR;
            done = FALSE;
        }
    }
    else {
        resultID = MSG_OUT_OF_MEMORY_ERROR;
        done = FALSE;
    }

    if (resultID)
        display->PrintErrorMsg(GetString(resultID));

    // Libérer les ressources allouées.

    delete poEncoder;
    return done;
}


//----------------------------------------------------------------------------------------------------
//============================================== Timer ===============================================
//----------------------------------------------------------------------------------------------------

/// Définitions pour l'utilisation du Timer

#ifndef  DEVICES_TIMER_H
#include <devices/timer.h>
#endif

#ifndef  CLIB_TIMER_PROTOS_H
#include <clib/timer_protos.h>
#endif

extern "C"{
struct Library *TimerBase = NULL;
};

timerequest TimerIO;
timeval     Time1;

///
/// InitTimer()
/****** Common/InitTimer ****************************************************
*
*   NAME
*   InitTimer -- Initialiser le Timer.
*
*   SYNOPSIS
*   InitTimer()
*
*   void InitTimer();
*
*   FUNCTION
*   Ouvre le timer.device, puis note l'heure courante afin de pouvoir cal-
*   culer ultérieurement le temps de calcul de l'encodeur.
*
*   SEE ALSO
*   GetElapsedTime(), EncodeFile()
*
*****************************************************************************
*
*/


void InitTimer()
{
    if (!::OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *) &TimerIO, 0)) {
        TimerBase = (Library *) TimerIO.tr_node.io_Device;
        ::GetSysTime(&Time1);
    }
}
///
/// GetElapsedTime()
/****** Common/GetElapsedTime ***********************************************
*
*   NAME
*   GetElapsedTime -- Calculer une durée.
*
*   SYNOPSIS
*   NumSecs = GetElapsedTime()
*
*   LONG GetElapsedTime();
*
*   FUNCTION
*   Calcule le temps écoulé depuis l'appel de la fonction InitTimer().
*   A l'issue de ce calcul, le timer.device est automatiquement libéré.
*
*   RESULT
*   NumSecs - Nombre de secondes écoulées, ou -1 en cas d'erreur.
*
*   SEE ALSO
*   InitTimer(), EncodeFile()
*
*****************************************************************************
*
*/


LONG GetElapsedTime()
{
    timeval Time2;
    LONG numsecs = -1;

    if (TimerBase) {
        ::GetSysTime(&Time2);
        ::SubTime(&Time2, &Time1);
        ::CloseDevice((struct IORequest *) &TimerIO);

        numsecs = Time2.tv_secs;
    }
    return (numsecs);
}
///

//----------------------------------------------------------------------------------------------------
//====================================== Gestion de la mémoire =======================================
//----------------------------------------------------------------------------------------------------

/// AllocAlignedBuffer()
//----------------------------------------------------------------------------------------------------
//======================================== AllocAlignedBuffer ========================================
//----------------------------------------------------------------------------------------------------
//
//  Allouer un tampon de la taille indiquée, aligné sur une adresse multiple de 16.
//
//  ALGO : Exec retourne toujours une adresse multiple de 8, donc il suffit d'ajouter 8 à l'adresse
//      retournée lorsqu'elle n'est pas multiple de 16 pour obtenir un pointeur sur un bloc aligné.
//      Une entête est insérée avant le bloc de façon à garder une trace du pointeur original (qui
//      devra être passé à FreeBuffer() tôt ou tard).

#ifdef __PPC__
#define ALIGN   32
#else
#define ALIGN   16
#endif


void *AllocAlignedBuffer(ULONG size)
{
    LONG *p;
    LONG *pa = NULL;

    if ( (p = (LONG *) AllocBuffer(size + (ALIGN+8)) ) ) {
        pa = (LONG *) ( ((LONG)p + (ALIGN+7)) & (~(ALIGN-1)) );
                                                            // pa = Pointeur aligné.
        *(pa - 1) = (LONG)pa - (LONG)p;                     // Pour contrôle (0 ou 8).
        *(pa - 2) = (LONG)p;                                // Adresse à passer à FreeBuffer()
    }

    return pa;
}
///
/// FreeAlignedBuffer()
//----------------------------------------------------------------------------------------------------
//======================================== FreeAlignedBuffer =========================================
//----------------------------------------------------------------------------------------------------
//
//  Libérer un bloc aligné sur une adresse multiple de 16.
//  L'assertion permet de vérifier la conformité du pointeur présent dans l'entête de bloc (ceci
//  assure que l'adresse transmise en argument correspond bien à un bloc aligné, et que le pointeur
//  à passer à FreeBuffer() n'a pas été écrasé par mégarde).

void FreeAlignedBuffer(void *buffaddr)
{
    FreeBuffer((void *) *((LONG *)buffaddr - 2));
}
///

