/*
//////////////////////////////////////////////////////////////////////////
//              Module SEQUENCE.C                                                                                                                               //
//////////////////////////////////////////////////////////////////////////
*/
 
#include "SNDinc.h"
 
#include "sndres.h"
#include "sndseq.h"
#include "sndplay.h"
#include "sndthrd.h"
 
#if !defined(DISABLE_SEQUENCE) && !defined(NO_ACP_SOUND)
 
/*static int iCurrentSequence;*/
 
typedef struct _tdstInfoPlayingSequence
{
  unsigned int iCurrentElement;
}
tdstInfoPlayingSequence;
 
 
/*-----------------------------------*/
/*- Partie concernant le Sequenceur -*/
/*-----------------------------------*/
 
#define NB_MAX_SEQUENCE                                         10
#define NO_ACTIVE_SEQUENCE										-1
#define OFFSET_SEQ_VOICE                                        0x2000
#define MASK_OFFSET_SEQ_VOICE									0x0FFF
#define NO_SEQUENCE_VOICE                                       0x1110
#define MSG_ERR_SEQ                                             "Sequence Device Error"
#define MSG_ERR_SEQ_FILE                                        "Sequence File Error"
 
 
/* instance de l'application, liste principale de ressources*/
/****************************************/
#undef CPA_EXPORT
#if defined(CPA_WANTS_IMPORT)
#define CPA_EXPORT __declspec(dllimport)
#elif defined(CPA_WANTS_EXPORT)
#define CPA_EXPORT __declspec(dllexport)
#else
#define CPA_EXPORT
#endif
/****************************************/
/*extern  CPA_EXPORT tdstSndPolyResource stMainResList;*/
 
/* Sauvegarde des elements interessant de la ressource active de la sequence*/
 
/*
typedef struct _tdstSequence {
        SndBool bNoAvailableTrack;
        tduRefRes uRefThisRes;
                //tdstInfoTrackMem stRefCurrentRes;
        tdstInfoTrackList * pstRefResList; 
        unsigned long ulNbLoops;
        unsigned long ulStartLoop;
        unsigned long ulEndLoop;
} tdstSequence;
*/
 
typedef struct _tdstActiveSequence  tdstActiveSequence;
struct _tdstActiveSequence  {
                SndBool_field_decl(bActive);        /*en cours de jeu*/
                SndBool_field_decl(bValid);         /*encore valide*/
                SndBool_field_decl(bPaused);
                SndBool_field_decl(bNoAvailableTrack);
                SndBool *pbValid;       /*ressource fille valide*/
                tduRefRes uRefThisRes;
                tdstInfoTrackMem * pstRefCurrentRes;
/*              tdstInfoTrackList * pstRefResList; */
                long lCurrentTrack;     /*id de la ressource courante (0=1°;...)*/
                long lCurrentTrackVoice; /*voice bas_niveau*/
                unsigned long ulNbLoops;
                unsigned long ulStartLoop;
                unsigned long ulEndLoop;
                SoundParam* pstSoundParam;        /*copie des parametres en cours*/
                long lPrio;
                SND_td_pfn_vSoundCallback pfnSoundCallback;
                long lParSoundCallback;
                SndBool bManagingSequence;      /*TRUE quand transition en cours*/
};
 
 
/* tableau des sequences */
 
/*tdstSequence stSequence[NB_MAX_SEQUENCE];*/
static tdstActiveSequence stActiveSequence[NB_MAX_SEQUENCE];
 
static SndBool bSequenceDriverOk;
static unsigned char ucSequenceCapacities;
static unsigned long iNbSequences;
 
 
/* -------------------------------------------*/
/* VARIABLES CONCERNANT LA GESTION DES THREADS*/
 
#ifndef U64
HANDLE hSequenceThreadCallBack;
DWORD dwSequenceThreadIdCallBack;
 
DWORD dw_a5_Params[5];
DWORD dw_a2_threadParams[2];
#endif
 
/* prototypes des fonctions du module*/
 
/*SndBool SND_fn_bLoadSequence(tdstBlockResourceMem * ptrMem);*/
void SND_CALL SND_fn_vNextElement(long par);
 
long get_SoundParam_member_size(SoundParam *par)
{
    return sizeof(SoundParam)+par->iNbLinks*sizeof(MicroLink);
}
 
void refresh_SoundParam_member(tdstActiveSequence *seq,SoundParam *new_par)
{
    seq->pstSoundParam=SND_fn_pvReAllocSndEx(E_ucSndBlockMain,seq->pstSoundParam,get_SoundParam_member_size(new_par));
    memcpy(seq->pstSoundParam,new_par,get_SoundParam_member_size(new_par));
}
 
void create_SoundParam_member(tdstActiveSequence *seq,SoundParam *new_par)
{
    seq->pstSoundParam=SND_fn_pvMallocSndEx(E_ucSndBlockMain,get_SoundParam_member_size(new_par));
    memcpy(seq->pstSoundParam,new_par,get_SoundParam_member_size(new_par));
}
 
void free_SoundParam_member(tdstActiveSequence *seq)
{
    SND_fn_vFreeSndEx(E_ucSndBlockMain,seq->pstSoundParam);
}
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_bConvertIDListToPtrList*/
/**/
/*      But                     : Met a jours la liste ACP de ressource de */
/*                                              la sequence */
/*      Entrees : */
/*              czFileName : nom du fichier contenant les IDs des res*/
/*              mem : BlockResourceMem lie a la sequence*/
/*      Sorties : TRUE,FALSE suivant la reussite*/
/*------------------------------------------------------------*/
/*  // Mise en commentaire pour cause de probleme avec stMainResList
SndBool SND_fn_bConvertIDListToPtrList( char * czFileName,tdstBlockResourceMem * mem)
{
        tdstBlockResourceMem * pstCurrentBlockMem;
        tdstSndPolyResource * pstMyMainRefResList;
        tdstInfoTrackDisk * pstTrackDisk;
        tdstInfoTrackMem * pstTrackMem;
  tdstInfoTrackList  * pstTrackList;
        tdhSndFile hResFile;
        unsigned long ulFileSize = 0,ulNbResources = 0,i;
        unsigned long ul_InfoTrackDiskSize = sizeof(tdstInfoTrackDisk);
        unsigned long ul_InfoTrackMemSize = sizeof(tdstInfoTrackMem);
 
        SND_fn_bGetDirectorySnd(StrIdDirectory);
  strcat((LPSTR)StrIdDirectory,(LPCSTR)czFileName);
        hResFile = SND_fn_hOpenFileReadSnd(StrIdDirectory,OPENFILESND_READ);
 
#ifdef FILE_MODE_C_WIN95
        if (hResFile != NULL)
#else
        if (hResFile != FILE_SND_ERROR)
#endif
        {
                ulFileSize = SND_fn_dwFileSndLength(hResFile);
                ulNbResources = ulFileSize / ul_InfoTrackDiskSize;
 
                pstTrackDisk = (tdstInfoTrackDisk *)SND_fn_pvMallocSnd(ul_InfoTrackDiskSize*ulNbResources);
                pstTrackMem = (tdstInfoTrackMem*)SND_fn_pvMallocSnd(ul_InfoTrackMemSize*ulNbResources);
        
                if (SND_fn_dwReadFileSnd(hResFile,ulFileSize,(void*)pstTrackDisk) != ulFileSize)
                {
                        SND_fn_vCloseFileSnd(hResFile);
                        SND_fn_vFreeSnd(pstTrackDisk);
                        SND_fn_vFreeSnd(pstTrackMem);
                        return FALSE;
                }
 
                // on recupere un pointeur sur la main liste
                pstMyMainRefResList = &stMainResList;
                // on initialise la liste sequence
                pstTrackList = &(mem->uRes.stSequence.stResList);
                M_LstInitAnchor(pstTrackList);  
 
                for (i=0;i<ulNbResources;i++)
                {
                        // on positionne le pointeur courant sur le 1er element de la main liste
                        M_LstGetElementNumber(pstMyMainRefResList,pstCurrentBlockMem,0);
                        do
                        {
                                // si on trouve l'Id de ressource correspondant, on rajoute le pointeur 
                                // sur la ressource dans la liste sequence
                                if (pstCurrentBlockMem->Id == pstTrackDisk->uRes.Id)
                                {
                                        M_LstAddTail(pstTrackList,pstTrackMem);
                                        pstTrackMem->uRes.pstPtr = pstCurrentBlockMem;
                                        pstTrackMem->ulFadeIn = pstTrackDisk->ulFadeIn;
                                        pstTrackMem->ulFadeOut = pstTrackDisk->ulFadeOut;
                                        pstTrackMem->ulCrossFade = pstTrackDisk->ulCrossFade;
                                        pstTrackMem++;
                                        pstTrackDisk++;
                                        break;
                                }               
                                else
                                        // La ressource n'est pas la bonne, on continue
                                        pstCurrentBlockMem = M_LstNextElement(pstCurrentBlockMem);
                        }
                        while (pstCurrentBlockMem != NULL);
                }
 
                SND_fn_vCloseFileSnd(hResFile);
                SND_fn_vFreeSnd(pstTrackDisk);
                return TRUE;
        }
        return FALSE;
}
*/
/*-----------------------------------------------------------*/
/*      SND_fn_vConvertRsvDiskToMemSeq*/
/**/
/*      But                     : Convertit un BlockResourceDisk en Mem*/
/*      Entrees : */
/*              disk : BlockResourceDisk lie a la sequence*/
/*              mem : BlockResourceMem lie a la sequence*/
/*              ptrBegin : pointeur void sur les donnees*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
 
 
void SND_fn_vConvertResDiskToMemSeq(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,tdstInfoTrackList *ptr)
{       
        /*fred's version
        pResMem->Id=pResDisk->Id;
        pResMem->eType=pResDisk->eType;
        pResMem->eStorage=pResDisk->eStorage;
        pResMem->ucVolume=pResDisk->ucVolume;
 
        // Set BRMem pointers:
        pMemSeq=&pResMem->uRes.stSequence;
        pDiskSeq=&pResDisk->uRes.stSequence;
 
        pMemSeq->bLoop = pDiskSeq->bLoop;
        pMemSeq->ulNbElements = pDiskSeq->ulNbElements;
        pMemSeq->ulStartLoop = pDiskSeq->ulStartLoop;
        pMemSeq->ulEndLoop = pDiskSeq->ulEndLoop;
        pMemSeq->ulNbLoops = pDiskSeq->ulNbLoops;
        */
        mem->Id=disk->Id;
        mem->eType=disk->eType;
        mem->eStorage=disk->eStorage;
        mem->ucVolume=disk->ucVolume;
        mem->bIsLoaded=TRUE;
 
 
        mem->uRes.stSequence.bLoop = disk->uRes.stSequence.bLoop;
        mem->uRes.stSequence.ulNbElements = disk->uRes.stSequence.ulNbElements;
 
        if (disk->uRes.stSequence.bLoop)
        {
            mem->uRes.stSequence.ulStartLoop = disk->uRes.stSequence.ulStartLoop;
            mem->uRes.stSequence.ulEndLoop = disk->uRes.stSequence.ulEndLoop;
            mem->uRes.stSequence.ulNbLoops = disk->uRes.stSequence.ulNbLoops;
        }
        else
        {
            mem->uRes.stSequence.ulStartLoop = 0;
            mem->uRes.stSequence.ulEndLoop = 0;
            mem->uRes.stSequence.ulNbLoops = 0;
        }
}
 
 
#ifndef NO_ACP_SCRIPT
SndBool SND_fn_bLoadResScriptSeq(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
{
	SND_fn_vConvertResDiskToMemSeq(disk,mem,NULL);
	return TRUE;
}
#endif
 
SndBool SND_fn_bLoadResBinarySeq(tdstBlockResourceDisk *_pBRDisk,tdstBlockResourceMem *_pBRMem,char *pDataBloc)
{
#ifndef NO_ACP_LDBIN
    tdstInfoTrackMem *pTrackArray,*pTrackElement;
    
    int iPos;
    /* Convert the ulDataOffset member to a pointer:*/
    pTrackArray=(tdstInfoTrackMem *)(pDataBloc + _pBRDisk->ulDataOffset);
 
	SNDLST2_M_StaticSetFirstElement(&_pBRMem->uRes.stSequence.stResList,pTrackArray);
	SNDLST2_M_StaticSetNumberOfElements(&_pBRMem->uRes.stSequence.stResList,
				_pBRDisk->uRes.stSequence.ulNbElements);  
 
    /* Set pointers to range list:*/
/*    SNDLST2_M_StaticSetMember(&_pBRMem->uRes.stSwitch,hFirstElement,pSwitchArray);*/
/*    SNDLST2_M_StaticSetMember(&_pBRMem->uRes.stSwitch, lNumberOfElements, _pBRDisk->uRes.stSwitch.ulNbElements);*/
 
    /* Reset pointers in track:*/
    SNDLST2_M_StaticForEachElementOf( &_pBRMem->uRes.stSequence.stResList, pTrackElement, iPos)
    {
		pTrackElement->uRes.pstPtr=SND_fn_pGetBinRes(pTrackElement->uRes.Id);
    }
 
	SND_fn_vConvertResDiskToMemSeq(_pBRDisk,_pBRMem,NULL);
	return TRUE;
#else
	return FALSE;
#endif
}
 
 
 
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_iInitSeq*/
/**/
/*      But                     : Realise les initialisations sequences*/
/*      Entrees :       neant                                   */
/*      Sorties : C_INIT_OK ou C_INIT_FAILED*/
/*------------------------------------------------------------*/
int SND_fn_iInitSeq(void)
{
        int i;
 
        for (i=0;i<NB_MAX_SEQUENCE;i++)
        {
                stActiveSequence[i].bActive = FALSE;  
                stActiveSequence[i].pstSoundParam=NULL;  
                stActiveSequence[i].bPaused = FALSE;  
                stActiveSequence[i].lCurrentTrack = -1;
                stActiveSequence[i].lCurrentTrackVoice = 0;
                stActiveSequence[i].ulNbLoops = 0;
                stActiveSequence[i].ulStartLoop = 0;
                stActiveSequence[i].ulEndLoop = 0;
                stActiveSequence[i].lPrio = 0;
                stActiveSequence[i].pfnSoundCallback= NULL;
                stActiveSequence[i].lParSoundCallback=0;
                stActiveSequence[i].bNoAvailableTrack = TRUE;
        }
 
    bSequenceDriverOk=TRUE;
    ucSequenceCapacities = 0;
    iNbSequences = 0;
 
    return C_INIT_OK;
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_bTestInitSeq*/
/**/
/*      But                     : init asynchrone*/
/*      Entrees : */
/*      Sorties : TRUE si l'init des sequences a eu lieu*/
/*------------------------------------------------------------*/
SndBool SND_fn_bTestInitSeq(void)
{
        return FALSE;
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_vDesInitSeq*/
/**/
/*      But                     : libere les sequences*/
/*      Entrees : neant*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_fn_vDesInitSeq(void)
{
        int i;
 
        for (i=0;i<NB_MAX_SEQUENCE;i++)
        {
            if (stActiveSequence[i].bActive)
                free_SoundParam_member(&stActiveSequence[i]);
            stActiveSequence[i].bActive = FALSE;  
            stActiveSequence[i].bPaused = FALSE;  
            stActiveSequence[i].lCurrentTrack = -1;
            stActiveSequence[i].lCurrentTrackVoice = 0;
            stActiveSequence[i].ulNbLoops = 0;
            stActiveSequence[i].ulStartLoop = 0;
            stActiveSequence[i].ulEndLoop = 0;
            stActiveSequence[i].lPrio = 0;
            stActiveSequence[i].pfnSoundCallback= NULL;
            stActiveSequence[i].lParSoundCallback=0;
            stActiveSequence[i].bNoAvailableTrack = TRUE;
        }
}
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_iInitBnkSeq*/
/**/
/*      But                     : Charge une bank sequence*/
/*      Entrees : */
/*              num : numero de la banque*/
/*      Sorties : C_INIT_OK ou C_INIT_FAILED*/
/*------------------------------------------------------------*/
int SND_fn_iInitBnkSeq(long num)
{
        return C_INIT_FAILED;
}
 
SndBool SND_fn_bTestInitBnkSeq(long num)
{
        return FALSE;
}
 
void SND_fn_vDesInitBnkSeq(long num)
{
}
 
void SND_fn_vDesInitAllBnkSeq(void)
{
}
 
void SND_fn_vSetEffectSeq(long num)
{
}
 
/*-----------------------------------------------------------*/
/*	SND_fn_iCdThreadCallBack*/
/**/
/*	But			: Procedure de la thread qui contient la fenetre qui traite les messages MCI*/
/*	Entrees : void * : parametre inutilise*/
/*	Sorties : void*/
/*------------------------------------------------------------*/
#ifndef U64
int WINAPI SND_fn_iSequenceThreadCallBack(long arglist)
{
    long * pArg = (void *)arglist;
    SND_td_pfn_vSoundCallback 	pfnCallback = (SND_td_pfn_vSoundCallback)pArg[0];
    long lParCallback = pArg[1];
 
	SND_fn_vEnterCriticalSectionThreadSnd();
 
    if (pfnCallback != NULL)
	    (*pfnCallback)(lParCallback);
    
	SND_fn_vQuitCriticalSectionThreadSnd();
 
    return (0);
}
#endif
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_ulGetActiveSequence*/
/**/
/*      But                     : Renvoie la voie physique de la sequence passee*/
/*                                              en parametres*/
/*      Entrees : */
/*              uRes : Ressource dont on cherche la voie*/
/*      Sorties : Voie physique (indice dans le tableau)*/
/*------------------------------------------------------------*/
/*
unsigned long fn_ulGetActiveSequence(tduRefRes uRes)
{
        unsigned long i = 0;
        while ((stSequence[i].uRefThisRes.pstPtr != uRes.pstPtr) && (++i < iNbSequences));
        if (i == iNbSequences)
                return ((unsigned long)NO_ACTIVE_SEQUENCE);
        else
                return (i);
}
*/
 
/*-----------------------------------------------------------*/
/*      SND_fn_lPlaySeq*/
/**/
/*      But                     : Joue la sequence specifiee par res*/
/*      Entrees : */
/*              res : ressource a jouer*/
/*              par : pointeur sur les parametres sons*/
/*              prio : priorite de la requete*/
/*              fct : callback a appeler en fin de jeu*/
/*      Sorties : voie theorique de play*/
/*------------------------------------------------------------*/
long SND_fn_lPlaySeq(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback,SndBool* valid)
{
        long voice;
        tdstInfoTrackMem * pstFirstRes;
        long result;/* = fn_ulGetActiveSequence(res);*/
        SND_td_pfn_vSoundCallback temp_pfnSoundCallback;
        long temp_lParSoundCallback;
 
        if (valid!=NULL)
                *valid=FALSE;
 
        /*recherche d'une voie de sequence dispo*/
        for (voice=0;voice<NB_MAX_SEQUENCE;voice++)
                if (!stActiveSequence[voice].bActive) break;
        if (voice==NB_MAX_SEQUENCE)
        {
                SND_M_DisplayErrorEx(E_uwSndTooManySequences,"",SNDERR_DISPLAY_IF_WANTED);
                return C_PLAY_FAILED;
        }
 
        /*la voie à utiliser est "voice"*/
 
 
        if (res.pstPtr->uRes.stSequence.ulNbElements != 0)
        {/* la sequence est jouable (enfin presque)*/
        if ((res.pstPtr->uRes.stSequence.ulStartLoop < res.pstPtr->uRes.stSequence.ulNbElements)
                        && (res.pstPtr->uRes.stSequence.ulEndLoop < res.pstPtr->uRes.stSequence.ulNbElements))
        {/* La boucle de sequence est valide*/
                /* Si le nombre d'elements est non nul, on recupere le pointeur sur la premiere*/
                /* ressource et on lance la ressource */
                pstFirstRes=SNDLST2_M_StaticGetFirstElement(&(res.pstPtr->uRes.stSequence.stResList));
                /*SNDLST2_M_GetElementNumber(&(res.pstPtr->uRes.stSequence.stResList),pstFirstRes,(long)res.pstPtr->uRes.stSequence.ulStartLoop);*/
                stActiveSequence[voice].pstRefCurrentRes = pstFirstRes;
 
 
                temp_pfnSoundCallback=SND_fn_vNextElement;
                temp_lParSoundCallback=voice;
                /*fade_in=pstFirstRes->ulFadeIn;fade_out=pstFirstRes->ulFadeOut*/
                result = SND_fn_lPlaySnd(pstFirstRes->uRes,par,prio,temp_pfnSoundCallback,temp_lParSoundCallback,&(stActiveSequence[voice].bValid));
 
                if (result != C_PLAY_FAILED)
                {
                    /* on sauvegarde une reference a la ressource ainsi que la voie attribuee*/
                    /* qui est ???*/
                    stActiveSequence[voice].uRefThisRes = res;
                    stActiveSequence[voice].pstRefCurrentRes = pstFirstRes;
                    stActiveSequence[voice].ulNbLoops = res.pstPtr->uRes.stSequence.ulNbLoops;
                    stActiveSequence[voice].ulStartLoop = res.pstPtr->uRes.stSequence.ulStartLoop;
                    stActiveSequence[voice].ulEndLoop = res.pstPtr->uRes.stSequence.ulEndLoop;
                    stActiveSequence[voice].bNoAvailableTrack = FALSE;
                    stActiveSequence[voice].pbValid=valid;
                    stActiveSequence[voice].lPrio = prio;
                    create_SoundParam_member(&stActiveSequence[voice],par);
                    stActiveSequence[voice].pfnSoundCallback= fn_callback;
                    stActiveSequence[voice].lParSoundCallback= par_callback;
                    stActiveSequence[voice].bActive = TRUE;
                    stActiveSequence[voice].bPaused = FALSE;
                    stActiveSequence[voice].lCurrentTrack = 0;
                    stActiveSequence[voice].lCurrentTrackVoice = result;
                    stActiveSequence[voice].bManagingSequence= FALSE;
                    if (stActiveSequence[voice].pbValid!=NULL) 
                        *(stActiveSequence[voice].pbValid)=TRUE;
                    return (voice);
                }
                else
                {
                    stActiveSequence[voice].bActive = FALSE;
                    return (C_PLAY_FAILED);
                }
        }
        else
        {
                        SND_M_DisplayError(E_uwSndInvalidStaticParamForResource,"");
                return (C_PLAY_FAILED);
        }
        }
    return (C_PLAY_FAILED);
}
 
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_vNextElement*/
/**/
/*      But     : Passe a l'element suivant de la sequence*/
/*      Entrees :neant */
/*      lEndedTrack : NO_ACTIVE_SEQUENCE si la piste de la sequence n'est pas valide*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vNextElement(long voice)
{
        tdstInfoTrackMem * pstNextRes=NULL;
        long result;
        unsigned long ulEndingTrack;/*dernier id de track valide*/
        SND_td_pfn_vSoundCallback temp_pfnSoundCallback;
        long temp_lParSoundCallback;
        SndBool sequence_is_finished;
#ifndef U64	
		SECURITY_ATTRIBUTES sa;
#endif
		int i; 
 
        stActiveSequence[voice].bManagingSequence=TRUE;/*blindage à l'entree*/
    
        result=C_PLAY_FAILED;
        sequence_is_finished=FALSE;
 
        if (stActiveSequence[voice].bActive)/* || lEndedTrack == NO_ACTIVE_SEQUENCE)*/
        {
                do /*while (result == C_PLAY_FAILED)*/
                {
                        /*l'ancien element n'est plus valide*/
                        stActiveSequence[voice].lCurrentTrackVoice = C_PLAY_FAILED;
 
                        /*trouver le nb d'elements de la sequence*/
                        if (stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.bLoop)
                                ulEndingTrack = stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.ulEndLoop;
                        else
                                ulEndingTrack = stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.ulNbElements-1;
 
                        /*la sequence est-elle finie*/
                        if (ulEndingTrack > (unsigned short)stActiveSequence[voice].lCurrentTrack)
                        {/*passage à l'element suivant de la sequence*/
                                pstNextRes = SNDLST2_M_StaticGetNextElement(stActiveSequence[voice].pstRefCurrentRes);
                                stActiveSequence[voice].lCurrentTrack++;
 
                        }   
                        else
                        {/* reboulage ?*/
                                if ((stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.bLoop)
                                        && (stActiveSequence[voice].ulNbLoops--))
                                {
                                    SNDLST2_M_StaticGetElementNumber(&(stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.stResList)
										,pstNextRes
										,(long)stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.ulStartLoop
										,i);
                                    stActiveSequence[voice].lCurrentTrack=(short)stActiveSequence[voice].uRefThisRes.pstPtr->uRes.stSequence.ulStartLoop;
                                }
                                else
                                {
									/* la sequence est terminee, on appelle la callback, si elle n'a pas ete annulee*/
									sequence_is_finished=TRUE;
									stActiveSequence[voice].bActive=FALSE;
#ifndef U64
      								if (stActiveSequence[voice].pfnSoundCallback)
									{
										sa.nLength = sizeof(SECURITY_ATTRIBUTES);
										sa.bInheritHandle = TRUE;
										sa.lpSecurityDescriptor = NULL;
										dw_a2_threadParams[0] = (DWORD)stActiveSequence[voice].pfnSoundCallback;
										dw_a2_threadParams[1] = (DWORD)stActiveSequence[voice].lParSoundCallback;
										if (!(hSequenceThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iSequenceThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwSequenceThreadIdCallBack)))
											SND_M_DisplayError(E_uwSndCDCreateThread,"");
										stActiveSequence[voice].pfnSoundCallback = NULL;
										stActiveSequence[voice].lParSoundCallback = 0;
									}
#else
  								if (stActiveSequence[voice].pfnSoundCallback)
									{
										(*stActiveSequence[voice].pfnSoundCallback)(stActiveSequence[voice].lParSoundCallback);
										stActiveSequence[voice].pfnSoundCallback = NULL;
										stActiveSequence[voice].lParSoundCallback = 0;
									}
#endif
                                }
                        }
 
                        if (!sequence_is_finished)
                        {
                                temp_pfnSoundCallback=SND_fn_vNextElement;
                                temp_lParSoundCallback=voice;
                                /*fade_in=pstNextRes->ulFadeIn;fade_out=pstNextRes->ulFadeOut*/
                                result=SND_fn_lPlaySnd(pstNextRes->uRes,stActiveSequence[voice].pstSoundParam,stActiveSequence[voice].lPrio,temp_pfnSoundCallback,temp_lParSoundCallback,&(stActiveSequence[voice].bValid));
 
                                if (result != C_PLAY_FAILED)
                                {/* on sauvegarde une reference a la ressource ainsi que la voie attribuee*/
                                                stActiveSequence[voice].bActive = TRUE;  
                                                stActiveSequence[voice].pstRefCurrentRes = pstNextRes;
                                                stActiveSequence[voice].lCurrentTrackVoice = result;
                                                stActiveSequence[voice].bNoAvailableTrack = FALSE;
                                }
                        }
                } while (!sequence_is_finished && (result==C_PLAY_FAILED));
        }
 
        if (sequence_is_finished || (result==C_PLAY_FAILED))
        {
                *(stActiveSequence[voice].pbValid)=FALSE;
        }
 
        stActiveSequence[voice].bManagingSequence=FALSE;/*blindage à la sortie*/
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_vStopSeq*/
/**/
/*      But                     : Stoppe une sequence*/
/*      Entrees : */
/*              voice : voie theorique a arreter*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vStopSeq(long voice)
{
#ifndef U64
		SECURITY_ATTRIBUTES sa;
       /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
#endif
        if (stActiveSequence[voice].bActive)
        {
                stActiveSequence[voice].bActive=FALSE;
 
                SND_fn_vStopSnd(stActiveSequence[voice].lCurrentTrackVoice);
 
#ifndef U64
				if (stActiveSequence[voice].pfnSoundCallback)
				{
					sa.nLength = sizeof(SECURITY_ATTRIBUTES);
					sa.bInheritHandle = TRUE;
					sa.lpSecurityDescriptor = NULL;
					dw_a2_threadParams[0] = (DWORD)stActiveSequence[voice].pfnSoundCallback;
					dw_a2_threadParams[1] = (DWORD)stActiveSequence[voice].lParSoundCallback;
					if (!(hSequenceThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iSequenceThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwSequenceThreadIdCallBack)))
						SND_M_DisplayError(E_uwSndCDCreateThread,"");
					stActiveSequence[voice].pfnSoundCallback = NULL;
					stActiveSequence[voice].lParSoundCallback = 0;
				}
#else
  			if (stActiveSequence[voice].pfnSoundCallback)
				{
					(*stActiveSequence[voice].pfnSoundCallback)(stActiveSequence[voice].lParSoundCallback);
					stActiveSequence[voice].pfnSoundCallback = NULL;
					stActiveSequence[voice].lParSoundCallback = 0;
				}
#endif
 
                stActiveSequence[voice].bActive = FALSE;
                *(stActiveSequence[voice].pbValid)= FALSE;
                stActiveSequence[voice].lCurrentTrack = -1;
                stActiveSequence[voice].lCurrentTrackVoice = 0;
        }
}
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_vPauseSeq*/
/**/
/*      But                     : Pause la sequence de voie voice si active */
/*      Entrees : */
/*              voice : voie theorique a arreter*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vPauseSeq(long voice)
{
  /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
 
        if (stActiveSequence[voice].bActive)
        {
                /* Erreur, si la demande invoquee ne concerne pas la sequence active*/
                if ((stActiveSequence[voice].bActive) && (!stActiveSequence[voice].bPaused))
                {
					SND_fn_vPauseSnd(stActiveSequence[voice].lCurrentTrackVoice);
                    stActiveSequence[voice].bPaused = TRUE;
                }
        }
 
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_vResumeSeq*/
/**/
/*      But                     : Resume la sequence en voie voice*/
/*      Entrees : */
/*              voice : voie theorique a resumer*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vResumeSeq(long voice)
{
  /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
 
        if (stActiveSequence[voice].bActive)
        {
                /* Erreur, si la demande invoquee ne concerne pas la sequence active*/
                /* Erreur, si la sequence active n'etait pas en pause*/
                if (stActiveSequence[voice].bPaused)
                {
                                        SND_fn_vResumeSnd(stActiveSequence[voice].lCurrentTrackVoice);
                    stActiveSequence[voice].bPaused = FALSE;
                }
        }
}
 
 
/*-----------------------------------------------------------*/
/*      SND_fn_vRemoveCallbackSeq*/
/**/
/*      But                     : Enleve la callback precedement passee par play*/
/*      Entrees : */
/*              voice : voie theorique a virer*/
/*      Sorties : neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vRemoveCallbackSeq(long voice)
{
        /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
 
        if (stActiveSequence[voice].bActive)
        {
                stActiveSequence[voice].pfnSoundCallback= NULL;
                stActiveSequence[voice].lParSoundCallback=0;
        }
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_bSetParamSeq*/
/**/
/*      But                     : */
/*      Entrees : */
/*              voice : voie theorique a resumer*/
/*              par : nouveaux parametres son*/
/*      Sorties : TRUE,FALSE*/
/*------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bSetParamSeq(long voice,SoundParam *par)
{
        /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
 
        if (stActiveSequence[voice].bActive)
        {
            refresh_SoundParam_member(&stActiveSequence[voice],par);
            return stActiveSequence[voice].bActive;
        }
        else
                return FALSE;
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_bTestIsPlayingSeq*/
/**/
/*      But                     : Teste si une sequence est active*/
/*      Entrees : */
/*              voice : voie theorique a resumer*/
/*      Sorties : TRUE,FALSE*/
/*------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bTestIsPlayingSeq(long voice)
{
    /*SND_fn_vWaitForValueThreadSnd((long*)&stActiveSequence[voice].bManagingSequence,FALSE);*/
 
    return (stActiveSequence[voice].bActive);
}
 
/*-----------------------------------------------------------*/
/*      SND_fn_bLoadSequence*/
/**/
/*      But                     : Initialise les parametres d'une sequence*/
/*                                              en particulier*/
/*      Entrees : */
/*              ptrMem : BlockResourceMem lie a la sequence*/
/*      Sorties : TRUE,FALSE*/
/*------------------------------------------------------------*/
/*
SndBool SND_fn_bLoadSequence(tdstBlockResourceMem * ptrMem)
{
        stSequence[iNbSequences].bNoAvailableTrack = TRUE;
        stSequence[iNbSequences].uRefThisRes.pstPtr = ptrMem;
        stSequence[iNbSequences].pstRefResList = &(ptrMem->uRes.stSequence.stResList); 
                stSequence[iNbSequences].ulNbLoops = ptrMem->uRes.stSequence.ulNbLoops;
        stSequence[iNbSequences].ulStartLoop = ptrMem->uRes.stSequence.ulStartLoop;
        stSequence[iNbSequences].ulEndLoop = ptrMem->uRes.stSequence.ulEndLoop;
        iNbSequences++;
        return TRUE;
}
*/
 
/*---------------------------------------------------------*/
/*	GetPos: retourne la position "theorique" de la ressource*/
/*			exprime en seconde depuis le debut*/
/*			different du temps absolu (car pitch, derive..)*/
/*	Entree: voie concernee*/
/*	Sortie: temps en S (SndReal)*/
/*----------------------------------------------------------*/
SndReal SND_CALL SND_fn_rGetPosSeq(long voie)
{
	return SND_C_POS_UNKNOWN;
}
 
/*---------------------------------------------------------*/
/*	GetLength: retourne la duree "theorique" de la ressource*/
/*			exprime en seconde (constant pour une ressource donnee)*/
/*	Entree: voie concernee*/
/*	Sortie: duree en S (SndReal)*/
/*----------------------------------------------------------*/
SndReal SND_CALL SND_fn_rGetLengthSeq(long voie)
{
	return SND_C_POS_UNKNOWN;
}
 
SndBool  SND_fn_bSetResourceStaticVolumeSeq(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	pstRes->ucVolume=ucVolume;

	return TRUE;
}

 
#endif /*NO_ACP_SOUND*/
        
