/*
**
** InputStreamC.cpp
**
** $VER: InputStreamC.cpp 1.4 (17.06.99)
**
** $Revision: 1.3 $
** $State: Exp $
** $Date: 1998/08/16 19:03:37 $
**
*/


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

/// Includes

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

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

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

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

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

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

///


// Fonctions permettant de convertir des données au format Intel vers le format Motorola (et inversement)

inline WORD _getword(WORD w)
{
    return ( (w >> 8) + (w * 256) );
}

inline LONG _getlong(LONG l)
{
    return ( ((l & 0xFF000000) >> 24) + ((l & 0x00FF0000) >> 8) + ((l & 0x0000FF00) * 256) + ((l & 0x000000FF) << 24) );
}


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

// 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)
{
    LONG p = ::Seek(file, position, offset);
    if (IoErr()) p = -1;
    return p;
}


//----------------------------------------------------------------------------------------------------
//======================================= Classe InputStreamC ========================================
//----------------------------------------------------------------------------------------------------

/// InputStreamC::AllocStreamBuffer()
/****** Class InputStreamC/AllocStreamBuffer ********************************
*
*   NAME
*   InputStreamC::AllocStreamBuffer -- Allouer le tampon de lecture.
*
*   SYNOPSIS
*   ErrorCode = InputStreamC::AllocStreamBuffer()
*
*   e_IoErrors InputStreamC::AllocStreamBuffer();
*
*   FUNCTION
*   Alloue le tampon de lecture pour le flux d'entrée. Ce tampon est aligné
*   sur une frontière de page particulière, permettant ainsi certaines opti-
*   misations suivant le CPU utilisé.
*
*   La taille du tampon est fixe, déterminée à la compilation. C'est un mul-
*   tiple entier de la taille de chaque page attendue par l'encodeur.
*
*   RESULT
*   ErrorCode - IOERR_NONE ou IOERR_NO_MEMORY;
*
*   NOTES
*   Plusieurs fonctions assument que la taille du tampon de lecture est un
*   multiple de la taille de chaque page attendue par l'encodeur.
*
*   SEE ALSO
*   InputStreamC::FreeStreamBuffer()
*
*****************************************************************************
*
*/


e_IoErrors InputStreamC::AllocStreamBuffer()
{
    const ULONG iBufferSize = INPUT_BUFF_SIZE + (istr_iRollGap * 2);

    if ( (istr_pBufferAddr = (WORD *) AllocAlignedBuffer(iBufferSize)) )
    {
        istr_iBufferSize = INPUT_BUFF_SIZE;
        istr_pBufferAddr += istr_iRollGap;
        istr_pCurrentPos = istr_pUpperLimit = istr_pBufferAddr + (istr_iBufferSize >> 1);
        return IOERR_NONE;
    }

    SetIoError(IOERR_NO_MEMORY);
    return IOERR_NO_MEMORY;
}
///
/// InputStreamC::FreeStreamBuffer()
/****** Class InputStreamC/FreeStreamBuffer *********************************
*
*   NAME
*   InputStreamC::FreeStreamBuffer -- Libérer le tampon de lecture.
*
*   SYNOPSIS
*   InputStreamC::FreeStreamBuffer()
*
*   void InputStreamC::FreeStreamBuffer();
*
*   FUNCTION
*   Libère le tampon de lecture utilisé par le flux d'entrée (à condition
*   qu'il ait été effectivement alloué).
*
*   SEE ALSO
*   InputStreamC::AllocStreamBuffer()
*
*****************************************************************************
*
*/


void InputStreamC::FreeStreamBuffer()
{
    if (istr_pBufferAddr)
    {
        FreeAlignedBuffer(istr_pBufferAddr - istr_iRollGap);
        istr_pBufferAddr = NULL;
    }
}
///
/// InputStreamC::RollBuffer()
/****** Class InputStreamC/RollBuffer ***************************************
*
*   NAME
*   InputStreamC::RollBuffer -- "Enrouler le tampon de lecture"
*
*   SYNOPSIS
*   InputStreamC::RollBuffer()
*
*   void InputStreamC::RollBuffer();
*
*   FUNCTION
*   Recopie les dernières données du tampon dans la partie réservée en tête
*   de celui-ci. La taille du bloc transferé correspond à la taille du bloc
*   d'entête.
*
*****************************************************************************
*
*/


void InputStreamC::RollBuffer()
{
    if (istr_iRollGap)
    {
        ULONG  gap = istr_iRollGap;
        ULONG *dst = (ULONG *) (istr_pBufferAddr - gap);
        ULONG *src = dst + (istr_iBufferSize >> 2);

        for (; gap > 0; gap--)
            *dst++ = *src++;
    }
}
///
/// InputStreamC::OpenStream()
/****** Class InputStreamC/OpenStream ***************************************
*
*   NAME
*   InputStreamC::OpenStream -- Ouvre le fichier à encoder.
*
*   SYNOPSIS
*   ErrorCode = InputStreamC::OpenStream()
*
*   e_IoErrors InputStreamC::OpenStream();
*
*   FUNCTION
*   Ouvre le fichier à encoder en lecture.
*
*   RESULT
*   ErrorCode - Code d'erreur (IOERR_#?)
*
*   NOTES
*   Fonction virtuelle.
*
*   SEE ALSO
*   IFF_AudioC::OpenStream()
*   InputStreamC::CloseStream()
*
*****************************************************************************
*
*/


e_IoErrors InputStreamC::OpenStream()
{
    e_IoErrors IOErr = IOERR_NONE;
    istr_wEOF = istr_wContinue = FALSE;

    // Ouvrir le fichier audio. On change de répertoire pour s'assurer qu'on adresse
    // bien le bon fichier.
    // istr_iIoError reçoit une copie du code d'erreur du DOS après chaque opération de
    // façon à pouvoir contrôler que l'objet a été crée correctement.
    // Par la même occasion, istr_bpFileHandle reste NULL si le fichier n'a pas pu être
    // ouvert.

    if ( (istr_bpNewDirLock = ::Lock((STRPTR) GetPathName(), ACCESS_READ)) )
    {
        istr_bpOldDirLock = ::CurrentDir(istr_bpNewDirLock);

        if ( (istr_bpFileHandle = ::Open((STRPTR) GetFileName(), MODE_OLDFILE)) == 0)
            IOErr = IOERR_OPEN;
    }
    else IOErr = IOERR_DOS;

    SetIoError(IOErr, IoErr());

    return IOErr;
}
///
/// InputStreamC::CloseStream()
/****** Class InputStreamC/CloseStream **************************************
*
*   NAME
*   InputStreamC::CloseStream -- Ferme le fichier à encoder.
*
*   SYNOPSIS
*   InputStreamC::CloseStream()
*
*   void InputStreamC::CloseStream();
*
*   FUNCTION
*   Ferme le fichier à encoder. Cette fonction peut être appelée même si le
*   fichier a déjà été fermé, ou lorsqu'il n'a pas été ouvert.
*
*   NOTES
*   Fonction virtuelle
*
*   SEE ALSO
*   IFF_AudioC::CloseStream()
*   InputStreamC::OpenStream()
*
*****************************************************************************
*
*/


void InputStreamC::CloseStream()
{
    if (istr_bpFileHandle)
    {
        ::Close(istr_bpFileHandle);
        istr_bpFileHandle = NULL;
    }

    if (istr_bpNewDirLock)
    {
        ::CurrentDir(istr_bpOldDirLock);
        ::UnLock(istr_bpNewDirLock);
        istr_bpOldDirLock = istr_bpNewDirLock = 0;
    }
}
///
/// InputStreamC::SeekToAudio()
/****** Class InputStreamC/SeekToAudio **************************************
*
*   NAME
*   InputStreamC::SeekToAudio -- Préparer la lecture du bloc audio.
*
*   SYNOPSIS
*   Result = InputStreamC::SeekToAudio()
*
*   bool InputStreamC::SeekToAudio();
*
*   FUNCTION
*   Positionne le pointeur de lecture au début du bloc de données audio.
*   Le fichier doit avoir été ouvert au préalable !
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Fonction virtuelle.
*
*   SEE ALSO
*   IFF_AudioC::SeekToAudio()
*   WAV_AudioC::SeekToAudio()
*
*****************************************************************************
*
*/


int InputStreamC::SeekToAudio()
{
    /*
    if (NewSeek(istr_bpFileHandle, 0L, OFFSET_BEGINING) >= 0) return TRUE;

    return FALSE;
    */
    return TRUE;
}
///
/// InputStreamC::FillBuffer()
/****** Class InputStreamC/FillBuffer ***************************************
*
*   NAME
*   InputStreamC::FillBuffer -- Remplir le tampon de lecture.
*
*   SYNOPSIS
*   BytesRead = InputStreamC::FillBuffer()
*
*   LONG InputStreamC::FillBuffer();
*
*   FUNCTION
*   Remplis le tampon de lecture avec de nouveaux échantillons.
*
*   RESULT
*   BytesRead - Nombre d'octets lus, ZERO si la fin du fichier a été rencon-
*   trée, ou -1 en cas d'erreur.
*
*   Le fichier doit avoir été ouvert au préalable, et le tampon de lecture
*   doit avoir été alloué. Aucune vérification n'est effectuée sur ces deux
*   conditions !
*
*   NOTES
*   Fonction virtuelle
*
*   SEE ALSO
*   IFF_AudioC::FillBuffer()
*
*****************************************************************************
*
*/


LONG InputStreamC::FillBuffer()
{
    RollBuffer();
    return Read(istr_bpFileHandle, istr_pBufferAddr, istr_iBufferSize);
}
///
/// InputStreamC::ReadSamples()
/****** Class InputStreamC/ReadSamples **************************************
*
*   NAME
*   InputStreamC::ReadSamples -- Obtenir le bloc suivant.
*
*   SYNOPSIS
*   BlockAddr = InputStreamC::ReadSamples(iNumSamples)
*
*   WORD * InputStreamC::ReadSamples(UWORD);
*
*   FUNCTION
*   Retourne un pointeur vers le bloc d'échantillons suivant, ou NULL en cas
*   d'erreur (problème de lecture, ou fin de fichier atteinte).
*   La fonction s'occupe du remplissage du tampon lorsque c'est nécessaire,
*   à moins que la fin du fichier n'ait été atteinte.
*
*   Le flux d'entrée doit avoir été ouvert au préalable, et la tampon doit
*   avoir été alloué. Aucun contrôle n'est fait concernant ces conditions !
*
*   INPUTS
*   iNumSamples - Nombre d'échantillons à lire.
*
*   RESULT
*   BlockAddr - Adresse du prochain bloc, ou NULL. GetIoError() renseigne sur
*   le code d'erreur lorsque le pointeur retourné vaut NULL.
*   Le couple IOERR_WARN + ERROR_NO_MORE_ENTRIES indique que l'on a rencontré
*   la fin du fichier.
*
*   NOTES
*   Cette fonction repose sur le fait que la taille du tampon est un multiple
*   entier de toutes les tailles de bloc possibles. Voir AllocStreamBuffer().
*
*****************************************************************************
*
*/


WORD *InputStreamC::ReadSamples(UWORD iNumSamples)
{
    if (!istr_wEOF)
    {
        if (istr_iSamplesRead == 0)
        {
            // Le tampon est vide...

            if (!istr_wContinue)
            {
                // Procéder au premier remplissage du tampon.

                if (!SeekToAudio()) goto Abort;

                istr_wContinue = TRUE;
            }

            // Remplir le tampon avec les échantillons suivant.

            istr_iSamplesRead = FillBuffer();

            if (istr_iSamplesRead < 0)  goto Abort;
            if (istr_iSamplesRead == 0)
            {
                SetIoError(IOERR_WARN, ERROR_NO_MORE_ENTRIES);
                istr_wEOF = TRUE;                   // Fin de fichier atteinte.
                goto Exit_;
            }
            istr_iSamplesRead >>= 1;                // 16 bits par échantillon.
        }
        return GetNextBlockAddr(iNumSamples);
    }

Abort:
    SetIoError(IOERR_DOS, ::IoErr());

Exit_:
    return NULL;
}
///
/// InputStreamC::GetNextBlockAddr()
/****** InputStreamC/GetNextBlockAddr ***************************************
*
*   NAME
*   GetNextBlockAddr -- Obtenir l'adresse du prochain bloc (Privée).
*
*   SYNOPSIS
*   NextBlock = GetNextBlockAddr(iNumSamples)
*
*   WORD *GetNextBlockAddr(UWORD);
*
*   FUNCTION
*   Mets à jour les membres privés en rapport avec la gestion du tampon d'
*   entrée.
*   Lorsque le tampon vient d'être rempli pour la première fois, ou lorque l'
*   adresse du prochain bloc serait hors du tampon, le prochain bloc se situe
*   alors en début de tampon.
*   Si le bloc courant ne contient pas assez d'échantillons, il est completé
*   avec des 0.
*
*   Le nombre d'échantillons traités est mis à jour par cette fonction.
*
*   INPUTS
*   iNumSamples - Nombre d'échantillons.
*
*   RESULT
*   NextBlock - Pointeur vers le bloc de données à traiter.
*
*   NOTES
*   Cette fonction repose sur le fait que la taille du tampon d'entrée est un
*   multiple entier de toutes les tailles de bloc possibles.
*   void AllocStreamBuffer()
*
*****************************************************************************
*
*/


WORD *InputStreamC::GetNextBlockAddr(UWORD iNumSamples)
{
    istr_pCurrentPos += iNumSamples;

    if (istr_pCurrentPos >= istr_pUpperLimit)
    {
        istr_pCurrentPos = istr_pBufferAddr;    // Réinitialiser le pointeur courant en début de bloc
    }                                           // si le dernier bloc du tampon a été traité.

    istr_iSmplProcessed += (iNumSamples > istr_iSamplesRead) ? istr_iSamplesRead : iNumSamples;

    // Recalculer le facteur de progression si nécessaire.

    if (istr_roFileInfo.GetNumSamples() && istr_iSmplProcessed >= istr_iNextLimit)
    {
        istr_iProgressValue = istr_iSmplProcessed * 100 / istr_roFileInfo.GetNumSamples();
        istr_iNextLimit += istr_iStepMin;
    }

    // Compléter le bloc s'il est trop court.

    WORD *p = istr_pBufferAddr + istr_iSamplesRead;

    for (; istr_iSamplesRead < iNumSamples; istr_iSamplesRead++)
        *p++ = 0;

    istr_iSamplesRead -= iNumSamples;

    return istr_pCurrentPos;
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe RAW_AudioC =========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions spécialisées dans la gestion des fichiers RAW/PCM

/// RAW_AudioC/GetFileInfos()
/****** Class RAW_AudioC/GetFileInfos ***************************************
*
*   NAME
*   RAW_AudioC::GetFileInfos -- Analyse un fichier RAW/PCM
*
*   SYNOPSIS
*   Result = RAW_AudioC::GetFileInfos()
*
*   bool RAW_AudioC::GetFileInfos();
*
*   FUNCTION
*   Analyse le fichier afin de connaître le nombre d'échantillons.
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   - Le nombre d'échantillons est déterminé par la taille du fichier.
*   - Les échantillons sont considérés comme étant codés sur 16 bits et au
*     format Motorola.
*   - Le fichier audio est considéré comme étant en mono.
*   - La fréquence d'échantillonage est indéterminée.
*
*   SEE ALSO
*   AIFF_AudioC::GetFileInfos()
*   MAUD_AudioC::GetFileInfos()
*   CDDA_AudioC::GetFileInfos()
*   WAV_AudioC::GetFileInfos()
*
*****************************************************************************
*
*/


int RAW_AudioC::GetFileInfos()
{
    int  success;
    ULONG NumFrames = istr_roFileInfo.GetFileLength() >> 1;

    SetNumFrames(NumFrames);
    if ( !(success = istr_roFileInfo.OverrideSettings(0, 1) ))  // Mode mono.
        SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);

    istr_roFileInfo.SetEncodingMode(MONO);
    SetAudioFormat(FORMAT_MOTOROLA_MONO);

    return success;
}
///
/// RAW_AudioC/OverrideSettings()
/****** Class RAW_AudioC/OverrideSettings ***********************************
*
*   NAME
*   RAW_AudioC::OverrideSettings -- Modifier les réglages du fichier.
*
*   SYNOPSIS
*   Result = RAW_AudioC::OverrideSettings(iSampleFreq, bChannels)
*
*   bool RAW_AudioC::OverrideSettings(ULONG, UBYTE);
*
*   FUNCTION
*   Modifie la fréquence d'échantillonage du fichier audio.
*   Puisque les fichiers RAW/PCM sont censés être en mono, le nombre de
*   canaux spécifié est ignoré (toujours 1).
*
*   INPUTS
*   iSampleFreq - Fréquence d'échantillonage (Hz).
*   bChannels   - IGNORE
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle.
*
*   Un fichier RAW/PCM est supposé toujours être en mono.
*
*   SEE ALSO
*   CDDA_AudioC::GetFileInfos()
*   IFF_AudioC::GetFileInfos()
*   WAV_AudioC::GetFileInfos()
*
*****************************************************************************
*
*/


int RAW_AudioC::OverrideSettings(ULONG iSampleFreq, UBYTE bChannels)
{
    return istr_roFileInfo.OverrideSettings(iSampleFreq, 1);
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe IFF_AudioC =========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions de base pour la gestion des fichiers IFF.

/// IFF_AudioC::OpenStream()
/****** Class IFF_AudioC/OpenStream *****************************************
*
*   NAME
*   IFF_AudioC::OpenStream -- Ouvrir le flux IFF en lecture.
*
*   SYNOPSIS
*   Error = IFF_AudioC::OpenStream()
*
*   e_IoErrors IFF_AudioC::OpenStream();
*
*   FUNCTION
*   Initialise et ouvre en lecture un flux IFF.
*
*   RESULT
*   Error - Code d'erreur (IOERR_#?).
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   SEE ALSO
*   InputStreamC::OpenStream()
*   IFF_AudioC::CloseStream()
*
*****************************************************************************
*
*/


e_IoErrors IFF_AudioC::OpenStream()
{
    e_IoErrors IOErr = IOERR_NONE;
    LONG i2ndError = 0;

    // Ouvrir le fichier audio.

    if ( (IOErr = InputStreamC::OpenStream()) != IOERR_NONE) return IOErr;

    // Ouvrir le flux IFF.

    if ( (istr_pIFF = AllocIFF()) )
    {
        istr_pIFF->iff_Stream = istr_bpFileHandle;
        ::InitIFFasDOS(istr_pIFF);
        if ( (i2ndError = ::OpenIFF(istr_pIFF, IFFF_READ)) == 0) return IOERR_NONE;

        // Erreur pendant l'ouverture : Libérer les ressources allouées partiellement.

        ::FreeIFF(istr_pIFF);
        istr_pIFF = NULL;
    }
    else i2ndError = IFFERR_NOMEM;

    SetIoError(IOERR_IFF, i2ndError);
    return IOERR_IFF;
}
///
/// IFF_AudioC::CloseStream()
/****** Class IFF_AudioC/CloseStream ****************************************
*
*   NAME
*   IFF_AudioC::CloseStream -- Ferme un flux IFF
*
*   SYNOPSIS
*   IFF_AudioC::CloseStream()
*
*   void IFF_AudioC::CloseStream();
*
*   FUNCTION
*   Ferme et libère un flux IFF. Cette fonction peut être appelée même si le
*   flux IFF est déjà fermé ou lorsqu'il n'a pas été ouvert.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   SEE ALSO
*   InputStreamC::CloseStream()
*   IFF_AudioC::OpenStream()
*
*****************************************************************************
*
*/


void IFF_AudioC::CloseStream()
{
    // Fermer le flux IFF

    if (istr_pIFF)
    {
        ::CloseIFF(istr_pIFF);
        ::FreeIFF(istr_pIFF);
        istr_pIFF = 0;
    }

    // Fermer le fichier audio.

    InputStreamC::CloseStream();
}
///
/// IFF_AudioC::SeekToAudio()
/****** Class IFF_AudioC/SeekToAudio ****************************************
*
*   NAME
*   IFF_AudioC::SeekToAudio -- Préparer la lecture du bloc audio.
*
*   SYNOPSIS
*   Result = IFF_AudioC::SeekToAudio()
*
*   bool IFF_AudioC::SeekToAudio();
*
*   FUNCTION
*   Positionne le pointeur de lecture au début du bloc de données audio.
*   Le flux IFF doit avoir été ouvert et initialisé avant d'appeler cette
*   fonction.
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle.
*
*   SEE ALSO
*   InputStreamC::SeekToAudio()
*   WAV_AudioC::SeekToAudio()
*
*****************************************************************************
*
*   Le format du chunk audio varie d'un type de fichier à l'autre. De fait,
*   la fonction est capable de sauter les informations situées en début de
*   chunk, et précédant les données audio attendues.
*   A l'heure actuelle, les fichier MAUD ne possèdent aucune données en
*   entête, alors que les fichiers AIFF contiennent une structure de 8 octets
*   juste avant les données audio.
*
*/


int IFF_AudioC::SeekToAudio()
{
    const ULONG iBufferSize = sizeof(struct AIFF_SSND);     // Pour l'instant, cela correspond au maximum possible,
                                                            // à savoir la taille de la structure AIFF_SSND.

    ContextNode *pCNode;
    LONG i2ndError = 0;
    char a_TempBuff[iBufferSize];

    if (    (i2ndError = ::StopChunk(istr_pIFF, istr_iTypeChunkID, istr_iDataChunkID)) == 0
         && (i2ndError = ::ParseIFF(istr_pIFF, IFFPARSE_SCAN)) == 0)
    {
        pCNode = ::CurrentChunk(istr_pIFF);

        // S'il faut sauter des données au début du chunk, c'est le moment de le faire...

        for (LONG i = istr_iAudioDataOffset; i > 0 && i2ndError > 0; i -= iBufferSize)
            i2ndError = ::ReadChunkBytes(istr_pIFF, a_TempBuff, iBufferSize);

        if (i2ndError == 0) return TRUE;
    }

    // Placer les codes d'erreur.

    SetIoError(IOERR_IFF, i2ndError);
    return FALSE;
}
///
/// IFF_AudioC::FillBuffer()
/****** Class IFF_AudioC/FillBuffer *****************************************
*
*   NAME
*   IFF_AudioC::FillBuffer -- Remplir le tampon de lecture.
*
*   SYNOPSIS
*   BytesRead = IFF_AudioC::FillBuffer()
*
*   LONG IFF_AudioC::FillBuffer();
*
*   FUNCTION
*   Remplis le tampon de lecture avec de nouveaux échantillons.
*
*   RESULT
*   BytesRead - Nombre d'octets lus, ZERO si la fin du fichier a été rencon-
*   trée, ou -1 en cas d'erreur.
*
*   Le fichier doit avoir été ouvert au préalable, et le tampon de lecture
*   doit avoir été alloué. Aucune vérification n'est effectuée sur ces deux
*   conditions !
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   SEE ALSO
*   InputStreamC::FillBuffer()
*
*****************************************************************************
*
*/


LONG IFF_AudioC::FillBuffer()
{
    RollBuffer();
    return ::ReadChunkBytes(istr_pIFF, istr_pBufferAddr, istr_iBufferSize);
}
///
/// IFF_AudioC::ReadChunks()
/****** Class IFF_AudioC/ReadChunks *****************************************
*
*   NAME
*   IFF_AudioC::ReadChunks -- Lire le contenu d'un chunk précis.
*
*   SYNOPSIS
*   Datas = IFF_AudioC::ReadChunks()
*
*   StoredProperty * IFF_AudioC::ReadChunks();
*
*   FUNCTION
*   Recherche et charge les chunks du fichier IFF.
*   Actuellement, la fonction effectue une copie du bloc d'entête du fichier
*   IFF, et positionne le contexte sur le bloc de données audio.
*
*   RESULT
*   Datas - Pointeur vers la structure StoredProperty décrivant le bloc d'
*   entête, ou NULL en cas d'erreur.
*
*   NOTES
*   Le fichier IFF doit avoir été ouvert au préalable.
*
*****************************************************************************
*
*/


StoredProperty *IFF_AudioC::ReadChunks()
{
    LONG i2ndError = 0;

    if (    (i2ndError = ::PropChunk(istr_pIFF, istr_iTypeChunkID, istr_iHeaderChunkID)) == 0
         && (i2ndError = ::StopChunk(istr_pIFF,istr_iTypeChunkID, istr_iDataChunkID)) == 0
         && (i2ndError = ::ParseIFF(istr_pIFF, IFFPARSE_SCAN) == 0))
    {
        return ::FindProp(istr_pIFF, istr_iTypeChunkID, istr_iHeaderChunkID);
    }

    // Mémoriser les codes d'erreur.

    SetIoError(IOERR_IFF, i2ndError);
    return NULL;
}
///
/// IFF_AudioC::CheckAudioChunk()
/****** Class IFF_AudioC/CheckAudioChunk ************************************
*
*   NAME
*   IFF_AudioC::CheckAudioChunk -- Contrôler la taille du chunk audio.
*
*   SYNOPSIS
*   IFF_AudioC::CheckAudioChunk(iOffset)
*
*   void IFF_AudioC::CheckAudioChunk(ULONG);
*
*   FUNCTION
*   Recherche le chunk audio associé au fichier IFF courant afin de contrôler
*   sa taille par rapport au nombre d'échantillons spécifiés dans l'entête.
*
*   Si la taille ne correspond pas, le code d'erreur IOERR_WARN associé à
*   ERROR_BAD_HUNK est placé.
*
*   INPUT
*   iOffset - Nombre d'octets à sauter en début de chunk pour adresser les
*   échantillons.
*
*   NOTES
*   Le fichier IFF doit avoir été ouvert au préalable.
*
*****************************************************************************
*
*/


void IFF_AudioC::CheckAudioChunk(ULONG iOffset)
{
    ContextNode *pCNode;
    ULONG        iSize;

    pCNode = ::CurrentChunk(istr_pIFF);
    iSize = (pCNode->cn_Size - iOffset) >> 1;       // 16 bits par échantillons.

    if ( iSize != GetNumSamples() )
    {
        SetNumFrames( (GetNumChannels() == 1) ? iSize : iSize >> 1);
        SetIoError(IOERR_WARN, ERROR_BAD_HUNK);
    }
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe AIFF_AudioC ========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions spécialisées dans la gestion des fichiers AIFF.

/// AIFF_AudioC::GetFileInfos()
/****** Class AIFF_AudioC/GetFileInfos **************************************
*
*   NAME
*   AIFF_AudioC::GetFileInfos -- Analyse un fichier AIFF.
*
*   SYNOPSIS
*   Result = AIFF_AudioC::GetFileInfos()
*
*   bool AIFF_AudioC::GetFileInfos();
*
*   FUNCTION
*   Analyse le fichier AIFF afin de connaître ses réglages particuliers, tels
*   que le nombre de canaux, le nombre d'échantillons, la fréquence d'échan-
*   tillonage, etc.
*
*   La fonction vérifie aussi que la taille du chunk audio est en accord avec
*   le nombre d'échantillons annoncé dans l'entête du fichier.
*
*   RESULT
*   Result - TRUE si les paramètres du fichier ont été lus, FALSE en cas d'
*   erreur. GetIoError() permet de récupérer, dans ce dernier cas, un code
*   d'erreur plus précis.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle.
*
*   La fréquence d'échantillonage est obtenue avec une fonction simplifiée
*   basée sur le format d'un nombre flottant (IEEE-754).
*   Si le fichier AIFF n'est pas un fichier audio, ou si les échantillons ne
*   sont pas codés sur 16 bits, ou si le nombre de canaux est supérieur à 2,
*   alors le fichier AIFF est rejeté.
*
*   SEE ALSO
*   MAUD_AudioC::GetFileInfos()
*   CDDA_AudioC::GetFileInfos()
*   RAW_AudioC::GetFileInfos()
*   WAV_AudioC::GetFileInfos()
*
*****************************************************************************
*
*   AIFF : COMM = Chunk de description (obligatoire et unique).
*          SSND = Chunk des données audio (unique).
*
*/


int AIFF_AudioC::GetFileInfos()
{

// Structure des chunks AIFF.

struct IEEE_754 {short exponent;
                 long  mantissa_hi;
                 ULONG mantissa_lo;
                };

struct AIFF_COMM {short     NumChannels;        // Nombre de canaux (1, 2, 4, etc)
                  ULONG     NumSampleFrames;    // Nombre d'échantillons par canal.
                  short     SampleSize;         // Nombre de bits par échantillons.
                  IEEE_754  SampleRate;         // Fréquence d'échantillonage.
                 };

    int success = FALSE;

    if (OpenStream() == IOERR_NONE)
    {
        StoredProperty *pSP;

        // Décoder le chunk principal.

        if ( (pSP = ReadChunks()) )
        {
            AIFF_COMM *pCOMM = (AIFF_COMM *) pSP->sp_Data;

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

            if (pCOMM->SampleSize == 16 && pCOMM->NumSampleFrames != 0L)
            {
                // Décoder le format IEEE_754;
                // On utilise les hypothèses suivantes :
                // - La fréquence d'échantillonage est positive.
                // - Elle est définie par un nombre entier.

                WORD  e = (pCOMM->SampleRate.exponent & 0x7FFF) - 16383;
                ULONG smplfreq = pCOMM->SampleRate.mantissa_hi;

                if (e > 0 && e < 30 && pCOMM->NumChannels < 3)
                {
                    smplfreq = (((smplfreq >> (30-e)) + 1 ) >> 1);
                    SetNumFrames(pCOMM->NumSampleFrames);

                    if ( !(success = istr_roFileInfo.OverrideSettings(smplfreq, pCOMM->NumChannels)) )
                        SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);

                    SetAudioFormat( (pCOMM->NumChannels == 1) ? FORMAT_MOTOROLA_MONO : FORMAT_MOTOROLA_STEREO );

                    CheckAudioChunk(sizeof(struct AIFF_SSND));
                    CloseStream();
                    return success;
                }
            }
        }
        // Fichier AIFF non supporté.

        SetFileType(AFT_BAD_AIFF);
        SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);
        CloseStream();
    }

    return success;
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe MAUD_AudioC ========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions spécialisées dans la gestion des fichiers MAUD.

/// Définitions
#define ID_MAUD 0x4D415544  /* 'MAUD' */    /* the FORM-ID */
#define ID_MHDR 0x4D484452  /* 'MHDR' */    /* file header chunk */
#define ID_MDAT 0x4D444154  /* 'MDAT' */    /* sample data chunk */
#define ID_MINF 0x4D494E46  /* 'MINF' */    /* optional channel info chunk (future) */

/* ---- the file header 'MHDR' */

struct MaudHeader {
    ULONG   mhdr_Samples;       /* number of samples stored in MDAT */
    UWORD   mhdr_SampleSizeC;   /* number of bits per sample as stored in MDAT */
    UWORD   mhdr_SampleSizeU;   /* number of bits per sample after decompression */
    ULONG   mhdr_RateSource;    /* clock source frequency (see maud.doc) */
    UWORD   mhdr_RateDevide;    /* clock devide           (see maud.doc) */
    UWORD   mhdr_ChannelInfo;   /* channel information (see below) */
    UWORD   mhdr_Channels;      /* number of channels (mono: 1, stereo: 2, ...) */
    UWORD   mhdr_Compression;   /* compression type (see below) */
    ULONG   mhdr_Reserved1;     /* MUST be set to 0 when saving */
    ULONG   mhdr_Reserved2;     /* MUST be set to 0 when saving */
    ULONG   mhdr_Reserved3;     /* MUST be set to 0 when saving */
};

/* ---- possible values for mhdr_ChannelInfo */

#define MCI_MONO            0   /* mono */
#define MCI_STEREO          1   /* stereo */
#define MCI_MULTIMONO       2   /* mono multichannel (channels can be 2, 3, 4, ...) */
#define MCI_MULTISTEREO     3   /* stereo multichannel (channels must be 4, 6, 8, ...) */
#define MCI_MULTICHANNEL    4   /* multichannel (requires additional MINF-chunks) (future) */

/* ---- possible values for mhdr_Compression */

#define MCOMP_NONE      0       /* no compression */
#define MCOMP_FIBDELTA  1       /* 'Fibonacci Delta Compression' as used in 8SVX */
#define MCOMP_ALAW      2       /* 16->8 bit, European PCM standard A-Law */
#define MCOMP_ULAW      3       /* 16->8 bit, American PCM standard µ-Law */
#define MCOMP_ADPCM2    4       /* 16->2 bit, ADPCM compression */
#define MCOMP_ADPCM3    5       /* 16->3 bit, ADPCM compression */
#define MCOMP_ADPCM4    6       /* 16->4 bit, ADPCM compression */
#define MCOMP_ADPCM5    7       /* 16->5 bit, ADPCM compression */
#define MCOMP_LONGDAT   8       /* 16->12 bit, used for DAT-longplay */
///

/// MAUD_AudioC::GetFileInfos()
/****** Class MAUD_AudioC/GetFileInfos **************************************
*
*   NAME
*   MAUD_AudioC::GetFileInfos -- Analyse un fichier MAUD.
*
*   SYNOPSIS
*   Result = MAUD_AudioC::GetFileInfos()
*
*   bool MAUD_AudioC::GetFileInfos();
*
*   FUNCTION
*   Analyse le fichier MAUD afin de connaître ses réglages particuliers, tels
*   que le nombre de canaux, le nombre d'échantillons, la fréquence d'échan-
*   tillonage, etc.
*
*   RESULT
*   Result - TRUE si les paramètres du fichier ont été lus, FALSE en cas d'
*   erreur. GetIoError() permet de récupérer, dans ce dernier cas, un code
*   d'erreur plus précis.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   Pour que le fichier MAUD soit accepté, il faut que les échantillons
*   soient codés sur 16 bits, qu'il y ait 1 ou 2 canaux audio, et que les
*   échantillons ne soient pas compressés.
*
*   SEE ALSO
*   AIFF_AudioC::GetFileInfos()
*   CDDA_AudioC::GetFileInfos()
*   RAW_AudioC::GetFileInfos()
*   WAV_AudioC::GetFileInfos()
*
*****************************************************************************
*
*   MAUD : MHDR = Chunk de description (obligatoire et unique).
*          MDAT = Chunk des données audio (unique).
*
*/


int MAUD_AudioC::GetFileInfos()
{
    int success = FALSE;

    if (OpenStream() == IOERR_NONE)
    {
        StoredProperty *pSP;

        // Décoder le chunk principal.

        if ( (pSP = ReadChunks()) )
        {
            MaudHeader *pHDR = (MaudHeader *) pSP->sp_Data;

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

            // On ne supporte que les fichiers MAUD 16 bits (sans compression).

            if (pHDR->mhdr_SampleSizeC == 16 && pHDR->mhdr_Compression == MCOMP_NONE)
            {
                ULONG smplfreq = pHDR->mhdr_RateSource / pHDR->mhdr_RateDevide;

                if (pHDR->mhdr_ChannelInfo == MCI_MONO || pHDR->mhdr_ChannelInfo == MCI_STEREO)
                {
                    SetNumFrames(pHDR->mhdr_Samples);
                    success = istr_roFileInfo.OverrideSettings(smplfreq, pHDR->mhdr_Channels);
                    SetAudioFormat( (pHDR->mhdr_Channels == 1) ? FORMAT_MOTOROLA_MONO : FORMAT_MOTOROLA_STEREO );

                    if (!success) SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);

                    CheckAudioChunk(0L);
                    CloseStream();
                    return success;
                }
            }
        }
        // Fichier MAUD non supporté.

        SetFileType(AFT_BAD_MAUD);
        SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);
        CloseStream();
    }

    return success;
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe CDDA_AudioC ========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions spécialisées dans la gestion des fichiers CDDA

/// CDDA_AudioC/GetFileInfos()
/****** Class CDDA_AudioC/GetFileInfos **************************************
*
*   NAME
*   CDDA_AudioC::GetFileInfos -- Analyse un fichier CDDA
*
*   SYNOPSIS
*   Result = CDDA_AudioC::GetFileInfos()
*
*   bool CDDA_AudioC::GetFileInfos();
*
*   FUNCTION
*   Analyse le fichier afin de connaître le nombre d'échantillons. Ce nombre
*   est basé sur la taille du fichier.
*   La fonction analyse en outre un bloc pris dans le milieu du fichier afin
*   d'essayer de déterminer le format de ce fichier (MOTOROLA/INTEL).
*
*   RESULT
*   Result - FALSE en cas d'erreur (Erreur de lecture).
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   Les fichiers CDDA sont considérés comme étant en stéréo, avec des échan-
*   tillons codés sur 16 bits, et échantillonnés à 44100 Hz.
*   La discrimination entre les formats Motorola et Intel se base sur un test
*   empirique. Elle repose sur la moyenne des différences entre les échantil-
*   lons d'un bloc pris dans le milieu du fichier. Cette reconnaissance n'est
*   donc pas absolument fiable.
*
*   SEE ALSO
*   MAUD_AudioC::GetFileInfos()
*   AIFF_AudioC::GetFileInfos()
*   RAW_AudioC::GetFileInfos()
*   WAV_AudioC::GetFileInfos()
*
*****************************************************************************
*
*/


int CDDA_AudioC::GetFileInfos()
{
    const int iNumSmpl   = 256*16;      // Nombre d'échantillons à examiner.
    const int iThreshold = 10000;       // Limite acceptée pour la moyenne. Au delà de cette limite,
                                        // on formule l'hypothèse que le fichier est au format INTEL.
    ULONG NumFrames = istr_roFileInfo.GetFileLength() >> 2;

    SetNumFrames(NumFrames);

    istr_roFileInfo.SetEncodingMode(STEREO);
    istr_roFileInfo.OverrideSettings(44100, 2);

    int       i, sum;
    WORD     *pSamples = NULL;
    LONG      iRead    = 0;
    int       iResult  = FALSE;

    SetAudioFormat(FORMAT_MOTOROLA_STEREO);         // Présumer que le fichier est au format Motorola.

    if (istr_roFileInfo.GetCDDAFmt() == 1)
    {
        SetFileType(AFT_CDDA);
        iResult = TRUE;
    }
    else if (istr_roFileInfo.GetCDDAFmt() == 2)
    {
        SetAudioFormat(FORMAT_INTEL_STEREO);
        SetFileType(AFT_CDDA_INTEL);
        iResult = TRUE;
    }
    else
    {
        // Ouvrir le fichier, puis charger un petit bloc d'échantillons dans un tampon pour analyse.

        if (OpenStream() == IOERR_NONE && (pSamples = new WORD[iNumSmpl * 2]) )
        {
            if (NewSeek(istr_bpFileHandle, NumFrames >> 1, OFFSET_BEGINING) >= 0
                && (iRead = ::Read(istr_bpFileHandle, pSamples, iNumSmpl * 4) >> 1) )
            {
                // Analyser le tampon.
                // On calcule ici la somme des différences entre chaque echantillon.
                // Si la moyenne de ces sommes est inférieure à une certaine limite,
                // on fait l'hypothèse que le fichier est au format Motorola.

                for (i = 2, sum = 0; i < iRead; i += 2)
                {
                    int t = pSamples[i] - pSamples[i-2];
                    sum += (t < 0) ? -t : t;
                }

                int analyze = (sum * 2) / iRead;

                if (analyze > iThreshold)
                {
                    SetFileType(AFT_CDDA_INTEL);
                    SetAudioFormat(FORMAT_INTEL_STEREO);
                }
                iResult = TRUE;
            }
            CloseStream();
        }
        else
        {
            SetIoError(IOERR_DOS, ::IoErr());        // Erreur pendant la lecture du fichier.
        }
        delete [] pSamples;
    }

    return iResult;
}
///

//----------------------------------------------------------------------------------------------------
//======================================== Classe WAV_AudioC =========================================
//----------------------------------------------------------------------------------------------------
//
//  Fonctions spécialisées dans la gestion des fichiers WAVE

/// Définitions

#define fmt_ID  MAKE_ID('f','m','t',' ')
#define data_ID MAKE_ID('d','a','t','a')

/* RIFF header */
struct RIFFHeader
{
    ULONG   rh_RIFF;            // RIFF
    ULONG   rh_Size;
    ULONG   rh_Format;          // WAVE
};


/* RIFF chunk */
struct RIFFChunk
{
    ULONG   rc_ID;
    ULONG   rc_Size;
};


struct WaveFormat
{
    WORD     wf_Format;             // WAVE_FORMAT_PCM
    WORD     wf_Channels;           // 1 = Mono, 2 = Stereo
    ULONG    wf_SamplesPerSec;      // Fréquence d'échantillonage.
    ULONG    wf_AvgBytesPerSec;     // Taux de transfert (nombre d'octets par seconde)
    WORD     wf_BlockAlign;         // Nombre d'octets à lire par sample.
};

/* wf_Format values */
#define WAVE_FORMAT_PCM     1

struct PCMData
{
    UWORD    pd_BitsPerSample;      // Nombre de bits par échantillons.
};

///

/// WAV_AudioC::GetFileInfos()
/****** Class WAV_AudioC/GetFileInfos ***************************************
*
*   NAME
*   WAV_AudioC::GetFileInfos -- Analyse un fichier WAV.
*
*   SYNOPSIS
*   result = WAV_AudioC::GetFileInfos()
*
*   bool WAV_AudioC::GetFileInfos();
*
*   FUNCTION
*   Analyse un fichier WAV afin d'obtenir la fréquence d'échantillonage, le
*   nombre d'échantillons du fichier et le nombre de canaux.
*   Si le fichier possède plus de 2 canaux, ou si les échantillons ne sont
*   pas codés sur 16 bits, le fichier est rejeté.
*   De plus, ce fichier doit être au format MicroSoft PCM à l'exclusion de
*   tout autre.
*
*   RESULT
*   result - FALSE en cas d'erreur.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle
*
*   SEE ALSO
*   MAUD_AudioC::GetFileInfos()
*   AIFF_AudioC::GetFileInfos()
*   CDDA_AudioC::GetFileInfos()
*   RAW_AudioC::GetFileInfos()
*
*****************************************************************************
*
*/


int WAV_AudioC::GetFileInfos()
{
    struct WAV_fmt {struct WaveFormat sWaveFormat;      // Format du bloc d'entête.
                    struct PCMData    sPCMData;
                   };

    int     iGotIt     = FALSE;
    ULONG   iChunkSize = 0;
    ULONG   iRead;
    WAV_fmt sWavFmt;

    if (OpenStream() == IOERR_NONE)
    {
        // Rechercher le chunk 'fmt '.

        if ( (iChunkSize = LocateChunk('fmt ')) == sizeof(WAV_fmt) )
        {
            // Lire le bloc de format.

            if ( (iRead = Read(istr_bpFileHandle, &sWavFmt, iChunkSize)) == iChunkSize)
            {
                WORD  wav_Format      = _getword(sWavFmt.sWaveFormat.wf_Format);
                WORD  wav_Channels    = _getword(sWavFmt.sWaveFormat.wf_Channels);
                LONG  wav_SamplFreq   = _getlong(sWavFmt.sWaveFormat.wf_SamplesPerSec);
                WORD  wav_BitsPerSmpl = _getword(sWavFmt.sPCMData.pd_BitsPerSample);

                // Contrôler les données de l'entête.

                if (   wav_Format == WAVE_FORMAT_PCM
                    && wav_Channels > 0
                    && wav_Channels < 3
                    && wav_BitsPerSmpl == 16)
                {
                    // Rechercher le nombre d'échantillons.

                    istr_iChannelSize = LocateChunk('data') >> 1;     // 16 bits par échantillon.
                    if (wav_Channels == 2) istr_iChannelSize >>= 1;   // Mode stéréo.

                    if (istr_iChannelSize > 0)
                    {
                        SetNumFrames(istr_iChannelSize);
                        iGotIt = istr_roFileInfo.OverrideSettings(wav_SamplFreq, wav_Channels);
                        SetAudioFormat( (wav_Channels == 1) ? FORMAT_INTEL_MONO : FORMAT_INTEL_STEREO );
                        if (!iGotIt) SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);
                    }
                }
            }
        }
        CloseStream();
    }

    // Si le format du fichier n'est pas supporté (ou si le fichier ne peut être ouvert),
    // placer les codes d'erreur et modifier le code de type de fichier.

    if (!iGotIt)
    {
        SetFileType(AFT_BAD_WAV);
        SetIoError(IOERR_INTERNAL, ERROR_OBJECT_WRONG_TYPE);
    }

    return iGotIt;
}
///
/// WAV_AudioC::LocateChunk()
/****** Class WAV_AudioC/LocateChunk ****************************************
*
*   NAME
*   WAV_AudioC::LocateChunk -- Rechercher un chunk RIFF.
*
*   SYNOPSIS
*   Size = WAV_AudioC::LocateChunk(ChunkID)
*
*   ULONG WAV_AudioC::LocateChunk(ULONG);
*
*   FUNCTION
*   Recherche un chunk RIFF dans le fichier. Le fichier doit avoir été ouvert
*   au préalable.
*
*   INPUTS
*   ChunkID - Nom du chunk à trouver.
*
*   RESULT
*   Size - Taille du chunk, ou 0 en cas d'erreur.
*
*   NOTES
*   La fonction repositionne le pointeur de lecture juste après le chunk d'
*   entête (RIFFxxxxWAVE), puis saute tous les chunks rencontrés jusqu'à
*   trouver le chunk voulu ou la fin du fichier.
*
*   BUGS
*   La fonction utilise la longueur de bloc mentionnée dans les chunks pour
*   repositionner le pointeur de lecture sur le chunk suivant. Il n'est pas
*   évident que cette longueur corresponde toujours à la taille réelle du
*   chunk, ce qui peut causer des problèmes de gestion (puisque dès lors, la
*   fonction sera incapable de retrouver le chunk demandé).
*
*****************************************************************************
*
*/


ULONG WAV_AudioC::LocateChunk(ULONG ChunkID)
{
    RIFFChunk sChunk;
    ULONG     iSize = 0;
    ULONG     iRead;

    // Sauter le bloc d'entête (déjà connu puisque le fichier a été identifié
    // comme étant un fichier RIFF-WAVE).

    if (NewSeek(istr_bpFileHandle, sizeof(RIFFHeader), OFFSET_BEGINING) >= 0)
    {
        while ( (iRead = ::Read(istr_bpFileHandle, &sChunk, sizeof(RIFFChunk))) > 0)
        {
            iSize = _getlong(sChunk.rc_Size);

            if (sChunk.rc_ID == ChunkID) return iSize;
            if (NewSeek(istr_bpFileHandle, iSize, OFFSET_CURRENT) < 0) break;
        }
    }

    return (0L);
}
///
/// WAV_AudioC::SeekToAudio()
/****** Class WAV_AudioC/SeekToAudio ****************************************
*
*   NAME
*   WAV_AudioC::SeekToAudio -- Préparer la lecture du bloc audio.
*
*   SYNOPSIS
*   Result = WAV_AudioC::SeekToAudio()
*
*   bool WAV_AudioC::SeekToAudio();
*
*   FUNCTION
*   Positionne le pointeur de lecture au début du bloc de données audio.
*   Le fichier WAV doit avoir été ouvert et initialisé avant d'appeler cette
*   fonction.
*
*   RESULT
*   Result - FALSE en cas d'erreur.
*
*   NOTES
*   Surdéfinition d'une fonction virtuelle.
*
*   SEE ALSO
*   InputStreamC::SeekToAudio()
*   IFF_AudioC::SeekToAudio()
*
*****************************************************************************
*
*/

int WAV_AudioC::SeekToAudio()
{
    return (LocateChunk('data') != 0);
}
///

