/*
//////////////////////////////////////////////////////////////
//      SNDWAVE.CXX                                                                                             //
//////////////////////////////////////////////////////////////
//      Module d'exploitation des samples simples                               //
//      Version Win95 Pentium & MMX                                                             //
//////////////////////////////////////////////////////////////
*/
//---------------- FONCTIONS PRIVEES ---------------------------

//---mixer
//#include "sndwavmx.cxx"

//#define SND_LOG

void add_logfile(char* format,int par)
{
#ifdef SND_LOG
	HANDLE hBinFile;
	DWORD nb_written;
	SYSTEMTIME time;
	char texte[128];
	char date[32];

	GetSystemTime(&time);
	sprintf(texte,format,par);
	sprintf(date," at %d:%d:%d\n",time.wMinute,time.wSecond,time.wMilliseconds);
	hBinFile=CreateFile("c:\\snd.log",GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_ALWAYS,0,NULL);
	SetFilePointer(hBinFile,0,NULL,FILE_END);
	WriteFile(hBinFile,texte,strlen(texte),&nb_written,NULL);
	WriteFile(hBinFile,date,strlen(date),&nb_written,NULL);
	CloseHandle(hBinFile);
#endif
}

#ifndef DISABLE_WAVE
DWORD WaitForSingleObjectL(HANDLE h,DWORD inf)
{
        DWORD retWait;

        if ((retWait=WaitForSingleObject(h,50000))==WAIT_TIMEOUT) 
            //DEADLOCK !!!!!!!!!!!!!!!!!!!!!!!
            DebugBreak();
            //DEADLOCK !!!!!!!!!!!!!!!!!!!!!!!
        return retWait;
}

//--bufferclient asynchrone
void refresh_buffer_asynchrone(p_tdstBufferClient buffer)
{
        long id_sample_courant,nb_sample_calcul;
		tdstBufferClientAsynchrone *buffer_client=&buffer->stAsynchrone;

		id_sample_courant=(min(buffer_dispo[buffer->lVoice].pt_sign,buffer_dispo[buffer->lVoice].pt_max))>>OFFSET_BIT_INC;
		nb_sample_calcul=id_sample_courant-buffer_client->ulNextToCompute;//id_sample_courant-(size_sound_block>>FormatOutput.nBlockAlign);
		if (nb_sample_calcul>=0)
		{
			(*(buffer_client->pfnCallback))(buffer_client->lUserId
				,buffer_client->ulNextToCompute
				,nb_sample_calcul
				,(void*)((int)buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst+(buffer_client->ulNextToCompute<<buffer_client->ulDecalBytes)));
		}
		else
		{
			//calcul du 1° sample à la fin du buffer client
			(*(buffer_client->pfnCallback))(buffer_client->lUserId    //user id du buffer client
				,buffer_client->ulNextToCompute                //id du 1° sample à calculer
				,buffer_client->ulSamples-buffer_client->ulNextToCompute//nb de sample à calculer
				,(void*)((int)buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst+(buffer_client->ulNextToCompute<<buffer_client->ulDecalBytes)));
			//calcul du début du buffer au dernier sample
			(*(buffer_client->pfnCallback))(buffer_client->lUserId    //user id du buffer client
				,0//id du 1° cample à calculer
				,id_sample_courant//nb de sample à calculer
				,buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst);//adresse du 1° sample
		}
		if (buffer_client->ulSamples==(unsigned long)id_sample_courant)
			buffer_client->ulNextToCompute=0;
		else
			buffer_client->ulNextToCompute=id_sample_courant;
}

//--bufferclient synchrone
// on rafraichi toujours exactement la totalité du buffer, en veillant à ce qu'il se synchronise
//	avec resend buffer
void refresh_buffer_synchrone(p_tdstBufferClient buffer)
{
        long id_sample_courant,nb_sample_calcul;
		tdstBufferClientSynchrone *buffer_client=&buffer->stSynchrone;

		id_sample_courant=0;
		nb_sample_calcul=buffer_client->ulSamples;

		(*(buffer_client->pfnCallback))(buffer_client->lUserId
				,id_sample_courant
				,nb_sample_calcul
				,buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst);

		//resynchronisation
		buffer_dispo[buffer->lVoice].pt_sign=0;
}

//pour générer en continu en fichier WAV image du son envoyé au Speaker
//#define WAVE_TO_BINARYFILE


//--envoi du bloc suivant
void resend_sound_block(int id)
{
    int i,j;
    MMRESULT res;
    char texte[256];
    p_tdstBufferClient buffer,next_buffer;
#ifdef WAVE_TO_BINARYFILE
	HANDLE hBinFile;
	DWORD nb_written;
#endif

    i=id;

    WaitForSingleObjectL(hMutexBuffer,INFINITE);
    mouchard=egenere_buffer;

    WaveHdr[i].dwUser=id;//6ans,9mois,25jours,20heures,49minutes,40secondes,et 9dizièmes d'autonomie; après, le son s'arrête
    WaveHdr[i].dwFlags|=WHDR_DONE;

#ifdef PROFILE_WAVE
	QueryPerformanceCounter(&timeBegin);
#endif

    genere_buffer(DataBlock[i],size_sound_block);

#ifdef WAVE_TO_BINARYFILE
	hBinFile=CreateFile("c:\\binwav.raw",GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_ALWAYS,0,NULL);
	SetFilePointer(hBinFile,0,NULL,FILE_END);
	WriteFile(hBinFile,DataBlock[i],size_sound_block,&nb_written,NULL);
	CloseHandle(hBinFile);
#endif

    //raj des buffers clients
    SNDLST2_M_DynamicForEachMovingElementOf(&buffers_clients.pList,buffer,next_buffer,j)
    {
 		if (!buffer->bPaused && (buffer->lVoice==C_PLAY_FAILED || !buffer_dispo[buffer->lVoice].paused))
		{
			switch (buffer->eType)
			{
			case BUFFER_ASYNCHRONE:
				refresh_buffer_asynchrone(buffer);
				break;
			case BUFFER_SYNCHRONE:
				refresh_buffer_synchrone(buffer);
				break;
			}
		}
    }

#ifdef PROFILE_WAVE
	QueryPerformanceCounter(&timeEnd);
	timeDelta=(double)(timeEnd.QuadPart-timeBegin.QuadPart)/(double)(timeFreq.QuadPart);
#ifdef PROFILE_WAVE_IN_FILE
	sprintf(timeBuffer,"Block #%d:%f microsec\n",id,timeDelta*1000000.0);
	if (timeFile!=INVALID_HANDLE_VALUE)
		WriteFile(timeFile,timeBuffer,strlen(timeBuffer),&timeNbWriten,NULL);
#endif
	timeNbHisto++;
	timeCumul+=timeDelta;
	if (timeNbHisto>=100)
	{
		timeCumul/=(double)(timeEnd.QuadPart-timeHisto.QuadPart)/(double)(timeFreq.QuadPart);
		#ifdef SUPERVISATER
		dbgSND_fn_vSetProcessTimeSample((float)timeCumul);
		#endif
#ifdef PROFILE_WAVE_IN_FILE
		sprintf(timeBuffer,"Moyenne: %d/1000000 de temps machine\n",(int)(timeCumul*1000000.0));
		if (timeFile!=INVALID_HANDLE_VALUE)
			WriteFile(timeFile,timeBuffer,strlen(timeBuffer),&timeNbWriten,NULL);
#endif
		timeHisto.QuadPart=timeEnd.QuadPart;
		timeNbHisto=0;
		timeCumul=0.0;
	}
#endif

    ReleaseMutex(hMutexBuffer);

    if (hMutexAPI) WaitForSingleObjectL(hMutexAPI,INFINITE);

        if ((res=waveOutWrite(WaveOut,&WaveHdr[i],sizeof(WaveHdr[i])))!=MMSYSERR_NOERROR)
        {
                waveOutGetErrorText(res,texte,sizeof(texte));
                MessageBox(HWND_DESKTOP,texte,"erreur avec waveWrite",MB_OK);
        }

        if (hMutexAPI) ReleaseMutex(hMutexAPI);
}

//--alloc nécessaires au module
void alloc_buffers(void)
{
        int i;

        taille_buffer=nb_sound_block*size_sound_block;
        DataBuffer=(void*)SND_fn_pvMallocSnd(taille_buffer+8);
        memset(DataBuffer,0,taille_buffer);
        for (i=0;i<nb_sound_block;i++)
                DataBlock[i]=(void*)((int)DataBuffer+size_sound_block*i);
        //DataBufferInt=(void*)SND_fn_pvMallocSnd(2*taille_buffer+8);
        //memset(DataBufferInt,0,2*taille_buffer);
#ifdef REVERB
        InitReverb();
#endif
#ifdef SURROUND
        InitSurround();
#endif //SURROUND
}

//--desalloc nécessaires au module
void free_buffers(void)
{
    SND_fn_vFreeSnd(DataBuffer);
	DataBuffer=NULL;
    //SND_fn_vFreeSnd(DataBufferInt);
	//DataBufferInt=NULL;
#ifdef REVERB
    DesInitReverb();
#endif
#ifdef SURROUND
    DesInitSurround();
#endif //SURROUND
}

//---tente de prendre le driver (monothread)
void TryPrendDriverLocal(void)
{
    int i;
    char texte[256];

    MMRESULT res;

    if (hMutexAPI) WaitForSingleObjectL(hMutexAPI,INFINITE);

    g_stFormatOutput.wFormatTag=WAVE_FORMAT_PCM;
    g_stFormatOutput.nChannels=NB_CHANNEL_OUTPUT;
    g_stFormatOutput.nSamplesPerSec=FREQ_OUTPUT;
    g_stFormatOutput.wBitsPerSample=RES_OUTPUT;
    g_stFormatOutput.nAvgBytesPerSec=g_stFormatOutput.nSamplesPerSec*g_stFormatOutput.nChannels*g_stFormatOutput.wBitsPerSample/8;
    g_stFormatOutput.nBlockAlign=(short)(g_stFormatOutput.nChannels*(g_stFormatOutput.wBitsPerSample>>3)/*/8*/);
    g_stFormatOutput.cbSize=0;

    if (wanna_driver_SND)
    {//on tente l'init du driver Son
                while (1)
                {
#if __WATCOM_CPLUSPLUS__==1050
                        if ((res=waveOutOpen(&WaveOut,WAVE_MAPPER,(LPWAVEFORMAT)&g_stFormatOutput,GetCurrentThreadId(),0,CALLBACK_THREAD))!=MMSYSERR_NOERROR)
#else
                        if ((res=waveOutOpen(&WaveOut,WAVE_MAPPER,&g_stFormatOutput,(DWORD)GetCurrentThreadId(),0,CALLBACK_THREAD))!=MMSYSERR_NOERROR)
#endif
                        {
                                waveOutGetErrorText(res,texte,sizeof(texte));
                                //MessageBox(HWND_DESKTOP,texte,"erreur avec waveOutOpen",MB_OK);
                                driver_SND_OK=FALSE;
                        }
                        else
                                driver_SND_OK=TRUE;

                        if (driver_SND_OK)
                        {
                                for (i=0;i<nb_sound_block;i++)
                                {
                                        memset(&WaveHdr[i],0,sizeof(WaveHdr[i]));
#if __WATCOM_CPLUSPLUS__==1050
                                        WaveHdr[i].lpData=(unsigned char*)DataBlock[i];                   // address of the waveform buffer 
#else
                                        WaveHdr[i].lpData=(LPSTR)DataBlock[i];                   // address of the waveform buffer 
#endif
                                        WaveHdr[i].dwBufferLength=size_sound_block;           // length, in bytes, of the buffer 
                                        WaveHdr[i].dwFlags=0;//WHDR_BEGINLOOP | WHDR_ENDLOOP;
                                        WaveHdr[i].dwLoops=0;//6ans,9mois,25jours,20heures,49minutes,40secondes,et 9dizièmes d'autonomie; après, le son s'arrête
                                        WaveHdr[i].dwUser=i;//6ans,9mois,25jours,20heures,49minutes,40secondes,et 9dizièmes d'autonomie; après, le son s'arrête

                                        if (driver_SND_OK)
                                        if ((res=waveOutPrepareHeader(WaveOut,&WaveHdr[i],sizeof(WaveHdr[i])))!=MMSYSERR_NOERROR)
                                        {
                                                        waveOutGetErrorText(res,texte,sizeof(texte));
                                                        //MessageBox(HWND_DESKTOP,texte,"erreur avec waveWrite",MB_OK);
                                                        waveOutClose(WaveOut);
                                                        driver_SND_OK=FALSE;
                                        }

                                        if (driver_SND_OK)
                                        if ((res=waveOutWrite(WaveOut,&WaveHdr[i],sizeof(WaveHdr[i])))!=MMSYSERR_NOERROR)
                                        {
                                                        waveOutGetErrorText(res,texte,sizeof(texte));
                                                        MessageBox(HWND_DESKTOP,texte,"erreur avec waveWrite",MB_OK);
                                                        waveOutClose(WaveOut);
                                                        driver_SND_OK=FALSE;
                                        }

                                        CallbackRecu[i]=FALSE;
                                        next_sound_block_to_sent=(i+1)%nb_sound_block;
                                }
                        }
                        break;//sortie de la boucle infinie
        }
    }//if wanna_driver_SND
            
    //init du mixer
    if (driver_SND_OK)
        SND_fn_iMixerInitSample(WaveOut);
    else
        SND_fn_iMixerInitSample(NULL);

    if (hMutexAPI) ReleaseMutex(hMutexAPI);
}

//fonction privée (en thread unique) de prise du driver; appelée par WinMain et InitSndLocal
void PrendDriverLocal(void)
{
		//1 seule tentative: si echec, pas d'info à l'utilisateu; c'est SND_fn_iInitSxd qui affichera l'erreur
		TryPrendDriverLocal();

		/*
        while (1)
        {
            TryPrendDriverLocal();

            if (!driver_SND_OK)
            {
                ret=snd_erreur_init(SND_ERREUR_INIT_SNDDRIVERBUSY);
                switch (ret)
                {
                case INIT_FAILED:
                    wanna_driver_SND=FALSE;
                    exit(0);
                    break;
                case INIT_RETRY:
                    break;
                case INIT_IGNORE:
                    wanna_driver_SND=FALSE;
                    break;
                default:
                    SND_fn_vDisplayError(E_uwSndResourceMissing,"");
                    wanna_driver_SND=FALSE;
                    break;
                }
            }
            if (driver_SND_OK || !wanna_driver_SND)
            break;//sortie de la boucle infinie
        }
		*/
} 

//fonction privée de libération du driver
void LibereDriverLocal(void)
{
	if (init_SND_OK && driver_SND_OK)
    {
        if (hMutexAPI) WaitForSingleObjectL(hMutexAPI,INFINITE);

        SND_fn_iMixerDesInitSample();

        driver_SND_OK=FALSE;

        waveOutReset(WaveOut);
        waveOutClose(WaveOut);

        if (hMutexAPI) ReleaseMutex(hMutexAPI);
    }
}

/****************************************************************
DESINITSND(,,,)=désinitialisation des capacités sonores 
        Entrées:néant
        Retour: néant
                Appelé par:SOUND3D quand il n'y a plus besoin des ressources sonores
*****************************************************************/
void DesInitSampleLocal(void)
{
        int i;

        if (init_SND_OK)
        {
            wanna_driver_SND=FALSE;

            if (init_SND_OK && driver_SND_OK)
            {
              SND_fn_iMixerDesInitSample();

              driver_SND_OK=FALSE;

              waveOutReset(WaveOut);
              waveOutClose(WaveOut);
            }

            init_SND_OK=FALSE;

            WaitForSingleObjectL(hMutexBuffer,INFINITE);
            mouchard=eDesInitSampleLocal;

            for (i=0;i<NB_VOICE;i++) SND_fn_vStopSample(i);

#ifdef OBSOLETE_STREAMING
            CloseHandle(hEvtStreaming);
#endif

            free_buffers();

            ReleaseMutex(hMutexBuffer);
			CloseHandle(hMutexBuffer);
        }
}

void DesInitAtExit(void)
{
	SND_fn_vDesInitSample();
}

//-----------------------------------------------------------
//      InitSample: initialisation du module (appel en monothread)
//      Entrées:néant
//      Sortie:code de retour (C_INIT_FAILE,C_INIT_RUNNING,C_INIT_OK)
//-----------------------------------------------------------   
int InitSampleLocal(SND_tdstInitStruct **pInitStruct)
{
    char texte[80];

    //test du processeur
	/*
        if (!SND_fn_bTestSnd_Processor())
        {
            SND_fn_vDisplayError(E_uwSndBadHostConfiguration,C_PROCESSOR_WANTED);
            return C_INIT_FAILED;
        }
		*/

        //on test la présence des 2 drivers
		g_stFormatOutput.wFormatTag=WAVE_FORMAT_PCM;
		g_stFormatOutput.nChannels=NB_CHANNEL_OUTPUT;
		g_stFormatOutput.nSamplesPerSec=FREQ_OUTPUT;
		g_stFormatOutput.wBitsPerSample=RES_OUTPUT;
		g_stFormatOutput.nAvgBytesPerSec=g_stFormatOutput.nSamplesPerSec*g_stFormatOutput.nChannels*g_stFormatOutput.wBitsPerSample/8;
		g_stFormatOutput.nBlockAlign=(short)(g_stFormatOutput.nChannels*(g_stFormatOutput.wBitsPerSample>>3)/*/8*/);
		g_stFormatOutput.cbSize=0;

        if (!SND_fn_bTestSnd_WinMM(&g_stFormatOutput))
        {
            sprintf(texte,"%d bits,%d Hz,%s SoundDriver"
                    ,g_stFormatOutput.wBitsPerSample
                    ,g_stFormatOutput.nSamplesPerSec
                    ,(g_stFormatOutput.wBitsPerSample==2) ? "stereo" : "mono");
            SND_fn_vDisplayError(E_uwSndBadHostConfiguration,texte);
			driver_SND_OK=FALSE;
			wanna_driver_SND=FALSE;
            return C_INIT_FAILED;
        }

        //init de hwndSnd et hinst ,et SND_hResourceHandle:
        hwndMainSnd=(*pInitStruct)->hMainWindow;
        SND_hGlobalInst=(*pInitStruct)->hProcessInstance;
        if (SND_g_hResourceHandle==NULL)        SND_g_hResourceHandle=SND_hGlobalInst;
        hMutexAPI=(*pInitStruct)->hMutexDX;

        driver_SND_OK=TRUE;
        wanna_driver_SND=TRUE;

        //Création et prise du sémaphore du buffer
        hMutexBuffer=CreateMutex(NULL,TRUE,NULL);

        alloc_buffers();

        PrendDriverLocal();

        resol8=(g_stFormatOutput.wBitsPerSample==8) ? TRUE : FALSE;

        //init de buffer_dispo
        nb_voice_mixed=0;

        //réglage de la stereo
        SND_fn_vSetStereoSample(TRUE);

        //inc_timer=delay_timer*g_stFormatOutput.nAvgBytesPerSec/1000;

        //Release du mutex
        ReleaseMutex(hMutexBuffer);

                //pour être sur de désinitialiser ne sortant
        atexit(DesInitAtExit);

#ifdef OBSOLETE_STREAMING
                hDataStreaming[0]=SND_fn_pvMallocSnd(SIZE_BUFFER_STREAMING*2);
                hDataStreaming[1]=(void*)((int)hDataStreaming[0]+SIZE_BUFFER_STREAMING);

                hEvtStreaming=CreateEvent(NULL,TRUE,FALSE,NULL);

                buffer_dispo_streaming.bIsActive=FALSE;
#endif //OBSOLETE_STREAMING

        if (driver_SND_OK)
            return C_INIT_OK;
		else
			return C_INIT_FAILED;
}

//Thread permanente du moteur sonore; envoie les buffers à MMSYSTEM, prend et libère le driver
DWORD WINAPI ThreadSnd(HANDLE h_event)
{
    MSG msg;
    HANDLE hEvt;
    long *params;
    int new_id,id_received;
    int nb_done=0,nb_inqueue=0;
    int i;
    char texte[128];

    int c=0;

#ifndef DISABLE_RECORD
	//first call to init SNDREC.C
	memset(&msg,0,sizeof(msg));
	SND_fn_bManageMsgForRecord((long)&msg);
#endif

	PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);
    SetEvent(h_event);
	
    SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);

    while (GetMessage(&msg,NULL,0,0))
    {
        hEvt=(HANDLE)msg.wParam;
        params=(long*)msg.lParam;

#ifndef DISABLE_RECORD
	  if (!SND_fn_bManageMsgForRecord((long)&msg))
#endif
	  {
		if (msg.message==MM_WOM_DONE)
		{
            id_received=((WAVEHDR*)params)->dwUser;
            c++;

            CallbackRecu[id_received%nb_sound_block]=TRUE;

            if (mesurement)
            {
                nb_done=0;
                nb_inqueue=0;
                for (i=0;i<nb_sound_block;i++)
                {
                    if (WaveHdr[i].dwFlags & WHDR_DONE) nb_done++;
                    if ((WaveHdr[i].dwFlags & WHDR_INQUEUE)&&!(WaveHdr[i].dwFlags & WHDR_DONE)) nb_inqueue++;
                    if (nb_mesurement==4)
                    {
                        if (i==id_received%nb_sound_block) strcat(texte_info,"recu:");
                        sprintf(texte,"%d:%x\n",i,WaveHdr[i].dwFlags);
                        strcat(texte_info,texte);
                    }
                }
                nb_mesurement++;
                for (i=id_received+1;i<id_received+nb_sound_block;i++)
                        if (!(WaveHdr[i%nb_sound_block].dwFlags & WHDR_INQUEUE) || (WaveHdr[i].dwFlags & WHDR_DONE)) break;
                moy_avance=(moy_avance*(nb_mesurement-1)+(i-id_received-1))/nb_mesurement;
                moy_done=(moy_done*(nb_mesurement-1)+nb_done)/nb_mesurement;
                moy_inqueue=(moy_inqueue*(nb_mesurement-1)+nb_inqueue)/nb_mesurement;
                if (i-id_received-1==0) nb_miss++;
            }

            if (driver_SND_OK)
            {
                new_id=next_sound_block_to_sent%nb_sound_block;

                trie_les_voies_a_mixer();
                                 
                while ((new_id!=id_received)
					&& (((new_id+nb_sound_block-id_received)%nb_sound_block)<nb_sound_block_delay)
					&& CallbackRecu[(new_id+nb_sound_block-nb_sound_block_delay+1)%nb_sound_block])
                {
                    resend_sound_block(new_id);

                    CallbackRecu[new_id]=FALSE;
                    new_id++;
					new_id=new_id%nb_sound_block;
                }
                next_sound_block_to_sent=new_id%nb_sound_block;
            }

        }
                        /*
        else
        if (msg.message==snd_msg_PlaySnd)
        {
                params[4]=PlaySndLocal((short)params[0],(SOUNDPARAM*)params[1],params[2],(SOUNDCALLBACK)params[3]);
                SetEvent(hEvt);
        }
        else
        if (msg.message==snd_msg_StopSnd)
        {
                StopSndLocal(params[0]);
                SetEvent(hEvt);
        }
        else
        if (msg.message==snd_msg_SetParamSnd)
        {
                params[2]=SetParamSndLocal(params[0],(SOUNDPARAM*)params[1]);
                SetEvent(hEvt);
        }
                        */
        else
        if (msg.message==snd_msg_InitSample)
        {
                InitSampleLocal((SND_tdstInitStruct**)params);
                params[1]=C_INIT_OK;
                SetEvent(hEvt);
        }
        else
        if (msg.message==snd_msg_LibereDriver)
        {
                WaitForSingleObjectL(hMutexBuffer,INFINITE);
                mouchard=eLibereDriverLocal;
                LibereDriverLocal();
                ReleaseMutex(hMutexBuffer);

                SetEvent(hEvt);
        }
        else
        if (msg.message==snd_msg_TryPrendDriver)
        {
                WaitForSingleObjectL(hMutexBuffer,INFINITE);
                mouchard=eTryPrendDriverLocal;
                TryPrendDriverLocal();
                ReleaseMutex(hMutexBuffer);

                SetEvent(hEvt);
        }
        else
                if (msg.message==snd_msg_DesInitSample)
                    break;
	  }
    }

    DesInitSampleLocal();

    SetEvent(hEvt);

    ExitThread(0);

	return 0;
}

/*---------------------------------------------------------------------
    Evalue les voies à mixer en priorité et modifie les flags mixing
        en conséquence
    Critères: si prio augmente, le sample a plus de chance d'être mixé
        pour une prio donnée, si volume augmente, le sample a plus de chance d'être mixé
----------------------------------------------------------------------*/
void trie_les_voies_a_mixer(void)
{
    BUFFER_DISPO* to_be_mixed[NB_VOICE_MIXED];
    int nb_to_be_mixed=0;
    BUFFER_DISPO* buffer=buffer_dispo_min;
    int i;

    if (driver_SND_OK)
    {
        for (i=0;i<NB_VOICE_MIXED;i++)
            to_be_mixed[i]=NULL;

        do {
            buffer->is_mixing=FALSE;

            if ((buffer->vol>SEUIL_VOLUME_MINI) && buffer->is_playing && !buffer->paused)
            {
                if (nb_to_be_mixed<NB_VOICE_MIXED)
                {//il y a encore des voies mixées libres
                    to_be_mixed[nb_to_be_mixed++]=buffer;
                }
                else
                {//il faut trier
                    for (i=0;i<NB_VOICE_MIXED;i++)
                    {
                        if ((to_be_mixed[i]->prio<buffer->prio)
                            || ((to_be_mixed[i]->prio==buffer->prio)
                                && (to_be_mixed[i]->vol<buffer->vol)))
                                break;//celui-là va virer
                    }
                    if (i<NB_VOICE_MIXED)
                    {//substitution
                        to_be_mixed[i]=buffer;
                    }
                }
            }
        } while ((++buffer)<buffer_dispo_max);

        //raj des flags MIXING
        for (i=0;i<nb_to_be_mixed;i++)
            to_be_mixed[i]->is_mixing=TRUE;
    }
    else
    {
        for (i=0;i<NB_VOICE_MIXED;i++)
            buffer_dispo[i].is_mixing=FALSE;
    }
}

/*--------------------------------------------------------------------
        Calcule la fréquence exacte à appliquer à une voie pour un sample et un SoundPARAM donnés
                                Entrées:num=num du sample concerné (pour avoir sa frequence par défaut)
                freq=fréquence relative à prendre en paramètre
                                Sortie:inc exacte de la voie
        Appelé par SetParamSnd
---------------------------------------------------------------------*/
long get_inc_for_voice(tduRefRes sample,long freq)
{
        if (freq==0)
                return 1;
        else
            return (long)SND_fn_rMulRealRealSnd(sample.pstPtr->uRes.stSample.ulInc0,freq);
}

/*----------------------------------------------------------------
    remise à jour de streaming
    Entrée:par=paramètres
    Sortie:FALSE si sample fini
    Appelé par SetPAramSnd si streaming
----------------------------------------------------------------*/
SndBool SetParamStreaming(SampleParam* par)
{
#ifdef OBSOLETE_STREAMING

#endif //defined(OBSOLETE_STREAMING)
    return TRUE;
}

/*----------------------------------------------------------------
    remise à jour d'un sample
    Entrée:voie=voie du sample
        par=paramètres
    Sortie:FALSE si sample fini
    Appelé par SetPAramSnd si sample
----------------------------------------------------------------*/
SndBool SetParamSample(long voice,SampleParam* par)
{
    tduRefRes num_sample;
    long new_inc,deltaPitch;
    BUFFER_DISPO* buffer;

    buffer=&(buffer_dispo[voice]);
    num_sample=buffer->sample;
    if (buffer->is_playing)
    {
		//buffer->vol MUST be half of dynamic volume for stereo PCM resources
		//	but NOT for mono PCM resources of any Client Buffer
		//This is due to Client Buffre to be as loud as PCM resources
		//(ie a mono ADPCM resources shouls be as loud as any PCM resources)
        buffer->vol=(par->ucVol*buffer->sample.pstPtr->ucVolume)>>7;
		if (buffer->stereo && !buffer->bufferclient)
			buffer->vol>>=1;

        new_inc = get_inc_for_voice(num_sample,par->Freq);

        buffer->DeltaPitch=0;
        if (buffer->DeltaTemps)
        {
            deltaPitch=(labs(new_inc-buffer->inc_sign)+buffer->DeltaTemps-1)/buffer->DeltaTemps;
            if (deltaPitch)
            {
                buffer->DeltaReste=labs(new_inc-buffer->inc_sign)/deltaPitch;
                if (new_inc>(long)buffer->inc_sign)
                    buffer->DeltaPitch=deltaPitch;
                else
                    buffer->DeltaPitch=-deltaPitch;
            }
        }
        else
        {
            buffer->DeltaPitch=0;
            buffer->inc_sign=new_inc;
        }
        buffer->DeltaTemps = 0;

        if (stereo_active) 
        {
            if (stereo_reverse)
                buffer->pan=C_SOUNDPAN_RIGHT-par->ucPan;
            else
                buffer->pan=par->ucPan;

			buffer->pan=max(min(buffer->pan,C_SOUNDPAN_RIGHT),0);
        }
        else
            buffer->pan=64;

        buffer->space=par->ucSpace;

        RajParamHard(buffer);

        return TRUE;
    }//is_playing
    else
    {
		#ifdef SUPERVISATER
		dbgSND_fn_vDelVoiceSample(voice);
		#endif

        return FALSE;
    }
}

/*---------------------------------------------------------------
    Arrêt d'un sample
    Entrée:voie=id de la voie du sample
    Sortie:néant
    Appelé par StopSnd si type=sample
----------------------------------------------------------------*/
void StopSample(long voice)
{
    long indice;

                indice=voice;
    buffer_dispo[indice].is_playing=buffer_dispo[indice].is_looping=FALSE;

	#ifdef SUPERVISATER
	dbgSND_fn_vDelVoiceSample(voice);
	#endif

    if (buffer_dispo[indice].waiting_callback)
    {//raj du callback
    buffer_dispo[indice].waiting_callback=FALSE;
    (*buffer_dispo[indice].fct_callback)(buffer_dispo[indice].param_callback);
    }
}

/*---------------------------------------------------------------
    Arrêt du streaming
    Entrée:néant
    Sortie:néant
    Appelé par StopSnd si type=streaming
----------------------------------------------------------------*/
void StopStreaming(void)
{
#ifdef OBSOLETE_STREAMING
        if (buffer_dispo_streaming.bIsActive)
        {
                buffer_dispo_streaming.bIsActive=FALSE;

                StopSample(buffer_dispo_streaming.lVoiceUsed);
                SND_fn_vCloseFileSnd(buffer_dispo_streaming.hFile);
        }
#endif //OBSOLETE_STREAMING
}

/*---------------------------------------------------------------
    Lancement d'un sample simple (non streaming)
    Entrées:num_sample=indice du sample
        par=paramètre de lancement
        prio=niveau de priorité
        fn_callback=fonction de callback
		par_callback=paramètre à retourner à la callback
		bufferlcient=TRUE si voie associée à un buffer client
    Retour:indice de voie
    Appelé par PlaySnd pout type=sample
----------------------------------------------------------------*/
long PlaySample(tduRefRes num_sample,SampleParam* par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback,SndBool bufferclient)
{
    int indice;
    BUFFER_DISPO *buffer;
//    MMRESULT err;

    //recherche d'un buffer_dispo dispo
    indice=0;
    while ((indice<NB_VOICE) && (buffer_dispo[indice].is_playing)) indice++;

    if (indice==NB_VOICE)
    {//saturation
		SND_fn_vDisplayErrorEx(E_uwSndTooManySamples,"",SNDERR_DISPLAY_IF_WANTED);
		return C_PLAY_FAILED;
    }       

    //le buffer dispo est buffer_dispo[indice]
    buffer=&(buffer_dispo[indice]);
    
	buffer->bufferclient=bufferclient;

    buffer->prio=prio;

    buffer->pt_zero=(unsigned long)num_sample.pstPtr->uRes.stSample.uData.stMem.pvPtrFirst;
    buffer->pt_sign=0;

    buffer->pt_max=num_sample.pstPtr->uRes.stSample.uData.stMem.ulNbEch << OFFSET_BIT_INC;

    buffer->pt_loop=num_sample.pstPtr->uRes.stSample.uData.stMem.ulNbEchLoop << OFFSET_BIT_INC;
    buffer->sample=num_sample;
    buffer->must_looping=num_sample.pstPtr->uRes.stSample.bLoop;
    buffer->is_looping=FALSE;
    buffer->waiting_callback=FALSE;

    // init des valeurs changement de pitch inter-trames
    buffer->DeltaTemps = 0;
    buffer->DeltaPitch = 0;

    buffer->dynamic_effects = 0;
    buffer->pitchable = num_sample.pstPtr->uRes.stSample.bPitchable;

    //le buffer à jouer est buffer_dispo[indice]
    if (num_sample.pstPtr->uRes.stSample.uwNbChannels==2)
        buffer->stereo=TRUE;
    else
        buffer->stereo=FALSE;

	//buffer->vol MUST be half of dynamic volume for stereo PCM resources
	//	but NOT for mono PCM resources of any Client Buffer
	//This is due to Client Buffre to be as loud as PCM resources
	//(ie a mono ADPCM resources shouls be as loud as any PCM resources)
    buffer->vol=(par->ucVol*num_sample.pstPtr->ucVolume)>>7;
	if (buffer->stereo && !buffer->bufferclient)
		buffer->vol>>=1;

    // si un quart des voies sont prises, on commence a filtrer les sons faibles
    //              if (nb_samples_actifs > NB_VOICE_MIXED_MAX / 4)

    if (buffer->vol < SEUIL_VOLUME_MINI)
        buffer->is_mixing = FALSE;
    else
        buffer->is_mixing = TRUE;

    buffer->inc_sign=get_inc_for_voice(num_sample,par->Freq);

    //init du buffer_dispo
    if (num_sample.pstPtr->uRes.stSample.uwResolution==16)
    {//le pas est *2 (pas en ech->pas en bytes)
        buffer->resol8=FALSE;
//        buffer->pt_max>>=1;
//        buffer->pt_loop>>=1;
    }
    else
        buffer->resol8=TRUE;

    if (stereo_active)
    {
        if (stereo_reverse)
            buffer->pan=C_SOUNDPAN_RIGHT-par->ucPan;
        else
            buffer->pan=par->ucPan;

		buffer->pan=max(min(buffer->pan,C_SOUNDPAN_RIGHT),0);
    }
    else
        buffer->pan=64;

    buffer->space=par->ucSpace;

    //buffer->is_playing=TRUE;
    buffer->paused=FALSE;

    if (fn_callback!=NULL)
    {//il y a un Callback
        buffer->waiting_callback=TRUE;
        buffer->fct_callback=fn_callback;
        buffer->param_callback=par_callback;
    }
    else
    {//pas de Callback; peut-être looping ?
    if (num_sample.pstPtr->uRes.stSample.bLoop)
        {//loopant
            buffer->is_looping=TRUE;
        }
    }//fct!=NULL

    buffer->sample=num_sample;

    RajParamHard(buffer);

    buffer->is_playing=TRUE;

	#ifdef SUPERVISATER
	dbgSND_fn_vAddVoiceSample(indice,num_sample,par);
	#endif
	
    return (indice);
}


#ifdef OBSOLETE_STREAMING
//fonction timer pour le streaming
void CALLBACK TimerStreaming(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)
{
        static BOOL deja_dedans=FALSE;
        DWORD nb_lus;

        if (!deja_dedans)
                deja_dedans=TRUE;
        else
                return;

        if (buffer_dispo_streaming.bIsActive)
        {
                if (
                        (
                         (buffer_dispo_streaming.iNextBuffer==0)
                         && (buffer_dispo[buffer_dispo_streaming.lVoiceUsed].pt_sign>=buffer_dispo_streaming.pt_sign_middle)
                    )
                    ||
                    (
                         (buffer_dispo_streaming.iNextBuffer==1)
                         && (buffer_dispo[buffer_dispo_streaming.lVoiceUsed].pt_sign<buffer_dispo_streaming.pt_sign_middle)
                    )
                   )
                {//changement de demi-buffer
                        if (buffer_dispo_streaming.bIsEnding)
                        {//on a fini
                                buffer_dispo_streaming.bIsActive=FALSE;
                                SND_fn_vCloseFileSnd(buffer_dispo_streaming.hFile);
								StopSample(buffer_dispo_streaming.lVoiceUsed);
                        }
                        else
                        {
                                if ((nb_lus=SND_fn_dwReadFileSnd(buffer_dispo_streaming.hFile
                                        ,min(SIZE_BUFFER_STREAMING,buffer_dispo_streaming.pt_max-buffer_dispo_streaming.lNextPtr)
                                        ,hDataStreaming[buffer_dispo_streaming.iNextBuffer]))
                                        !=SIZE_BUFFER_STREAMING)
                                {//fin du sample
                                        if (buffer_dispo_streaming.bMustLoop)
                                        {//on reboucle
                                                nb_lus=SIZE_BUFFER_STREAMING-nb_lus;
                                                SND_fn_dwSeekFileSnd(buffer_dispo_streaming.hFile,buffer_dispo_streaming.pt_loop,SEEKFILESND_BEGIN);
                                                SND_fn_dwReadFileSnd(buffer_dispo_streaming.hFile,SIZE_BUFFER_STREAMING-nb_lus,hDataStreaming[buffer_dispo_streaming.iNextBuffer]);
                                                buffer_dispo_streaming.bIsLooping=TRUE;
                                                buffer_dispo_streaming.lNextPtr=buffer_dispo_streaming.pt_loop+SIZE_BUFFER_STREAMING-nb_lus;
                                        }
                                        else
                                        {//début de la fin
                                               memset(hDataStreaming[buffer_dispo_streaming.iNextBuffer],0,SIZE_BUFFER_STREAMING-nb_lus);
                                                buffer_dispo_streaming.bIsEnding=TRUE;
                                        }
                                }
                                else
                                {
                                        buffer_dispo_streaming.lNextPtr+=SIZE_BUFFER_STREAMING;
                                }
                                buffer_dispo_streaming.iNextBuffer=1-buffer_dispo_streaming.iNextBuffer;
                        }
                }
        }

        SND_g_mmIdTimerStreaming=timeSetEvent(DELAY_STREAMING,DELAY_STREAMING/4,TimerStreaming,0,TIME_ONESHOT);

        deja_dedans=FALSE;
}
#endif
  
/*---------------------------------------------------------------
    Lancement d'un sample en streaming
    Entrées:num_sample=indice du sample
        par=paramètre de lancement
        prio=niveau de priorité
                                fct=fonction de callback
    Retour:indice de voie
    Appelé par PlaySnd pout type=sample
----------------------------------------------------------------*/
long PlayStreaming(tduRefRes num_sample,SampleParam* par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
#if defined(OBSOLETE_STREAMING)
        long voice;
        char filename[256];

        SND_fn_vResolveFileName(num_sample.pstPtr->uRes.stSample.uData.stStream.szFileName,filename);

        buffer_dispo_streaming.hFile=SND_fn_hOpenFileReadSnd(filename);
        if (buffer_dispo_streaming.hFile==OPENFILESND_FAILED)
		{
			SND_fn_vDisplayError(E_uwSndCannotOpenFile,filename);
                return C_PLAY_FAILED;
		}
        SND_fn_dwSeekFileSnd(buffer_dispo_streaming.hFile,num_sample.pstPtr->uRes.stSample.uData.stStream.ulOffsetFirst,SEEKFILESND_BEGIN);
        SND_fn_dwReadFileSnd(buffer_dispo_streaming.hFile,2*SIZE_BUFFER_STREAMING,hDataStreaming[0]);

        //génération d'une ressource factice qui sera remise à jour par le streaming
        memcpy(&tdBlockResStreaming,num_sample.pstPtr,sizeof(tdBlockResStreaming));
    tdBlockResStreaming.bIsLoaded=TRUE;
    tdBlockResStreaming.uRes.stSample.bLoop=TRUE;
    tdBlockResStreaming.uRes.stSample.uData.stMem.pvPtrFirst=hDataStreaming[0];
    tdBlockResStreaming.uRes.stSample.uData.stMem.ulNbEch=2*SIZE_BUFFER_STREAMING/(2*(tdBlockResStreaming.uRes.stSample.uwNbChannels));                //indice du dernier ech
    tdBlockResStreaming.uRes.stSample.uData.stMem.ulNbEchLoop=0;
        tdResStreaming.pstPtr=&tdBlockResStreaming;

        voice=PlaySample(tdResStreaming,par,prio,fn_callback,par_callback);

        if (voice==C_PLAY_FAILED)
        {
                SND_fn_vCloseFileSnd(buffer_dispo_streaming.hFile);
                buffer_dispo_streaming.bIsActive=FALSE;
        }
        else
        {
                buffer_dispo_streaming.bMustLoop=num_sample.pstPtr->uRes.stSample.bLoop;
                buffer_dispo_streaming.pt_loop=num_sample.pstPtr->uRes.stSample.uData.stStream.ulOffsetLoop;
                buffer_dispo_streaming.pt_max=num_sample.pstPtr->uRes.stSample.uData.stStream.ulOffsetLast;
                buffer_dispo_streaming.lVoiceUsed=voice;
                buffer_dispo_streaming.bIsActive=TRUE;
                buffer_dispo_streaming.bIsEnding=FALSE;
                buffer_dispo_streaming.bIsLooping=FALSE;
                buffer_dispo_streaming.lNextPtr=2*SIZE_BUFFER_STREAMING;
                buffer_dispo_streaming.iNextBuffer=0;
                buffer_dispo_streaming.pt_sign_middle=buffer_dispo[voice].pt_max/2;

                if (!SND_g_mmIdTimerStreaming)
                    SND_g_mmIdTimerStreaming=timeSetEvent(DELAY_STREAMING,DELAY_STREAMING/4,TimerStreaming,0,TIME_ONESHOT);
        }

        return voice;
#else
    return C_PLAY_FAILED;
#endif //OBSOLETE_STREAMING
}



#endif //DISABLE_WAVE

//*************************************************************
//************** FONCTIONS PUBLIQUES NON STANDARDS ************
//*************************************************************
//prise inconditionnelle du driver: echec->pas de nouvelle tentative; pas d'info
void PrendDriver(void)
{
#ifndef DISABLE_WAVE
        HANDLE hEvt;

		if (GetCurrentThreadId()!=id_snd_thread)
        {
			if (!PostThreadMessage(id_snd_thread,snd_msg_TryPrendDriver,(WPARAM)(hEvt=CreateEvent(NULL,FALSE,FALSE,NULL))
					,0))
				SND_fn_vDisplayError(E_uwSndSystemError,"Error on PostThreadMessage");

			WaitForSingleObjectL(hEvt,INFINITE);

			CloseHandle(hEvt);
		}
		else
		{
            WaitForSingleObjectL(hMutexBuffer,INFINITE);
		    mouchard=ePrendDriver;
            TryPrendDriverLocal();
            ReleaseMutex(hMutexBuffer);
		}
#endif//HARSND
}

void LibereDriver(void)
{
#ifndef DISABLE_WAVE
        HANDLE hEvt;

    if (init_SND_OK && driver_SND_OK)
    {
		if (GetCurrentThreadId()!=id_snd_thread)
		{
			if (!PostThreadMessage(id_snd_thread,snd_msg_LibereDriver,(WPARAM)(hEvt=CreateEvent(NULL,FALSE,FALSE,NULL))
                ,0))
				SND_fn_vDisplayError(E_uwSndSystemError,"Error on PostThreadMessage");

			WaitForSingleObjectL(hEvt,INFINITE);

			CloseHandle(hEvt);

			driver_SND_OK=FALSE;
		}
		else
        {
                WaitForSingleObjectL(hMutexBuffer,INFINITE);
				mouchard=eLibereDriver;
                LibereDriverLocal();
                ReleaseMutex(hMutexBuffer);
        }
	}

#endif//HARSND
}

/*------------------------------------------------------------------
        Inverse (ou non) la stereo pour les bruitages
        Entrée: TRUE pour inverser la stereo, FALSE pour la mettre en normale
        Sortie: néant
-------------------------------------------------------------------*/
void SND_fn_vSetReverseSample(SndBool reverse)
{
#ifndef DISABLE_WAVE
    int i;
    
    WaitForSingleObjectL(hMutexBuffer,INFINITE);
    mouchard=eSetReverse;

    if ((reverse && !stereo_reverse) || (!reverse && stereo_reverse))
    {//on inverse la stereo ou on met la stereo à l'endroit
        for (i=0;i<NB_VOICE;i++)
            buffer_dispo[i].pan=C_SOUNDPAN_RIGHT-buffer_dispo[i].pan;
    }

    stereo_reverse=reverse;

    ReleaseMutex(hMutexBuffer);
#endif
}

/*------------------------------------------------------------------
        Indique si la stereo est inversée
        Entrée: néant
        Sortie: TRUE si la stereo est inversé, FALSE sinon
-------------------------------------------------------------------*/
SndBool SND_fn_bGetReverseSample(void)
{
#ifndef DISABLE_WAVE
    return stereo_reverse;
#else
    return FALSE;
#endif
}

__declspec(dllexport) void SND_CALL SND_fn_vSetDolbySample(SndBool active);
__declspec(dllexport) SndBool SND_CALL SND_fn_bGetDolbySample(void);

/*------------------------------------------------------------------
        Active ou non le dolby
        Entrée: TRUE pour activer le DOLBY, FALSE pour le déacriver
        Sortie: néant
-------------------------------------------------------------------*/
void SND_CALL SND_fn_vSetDolbySample(SndBool dolby)
{
#if !defined(DISABLE_WAVE) && defined(SURROUND)
	//dolby encoder doesn't work anymore
	//crashs may occur when dolby is set to ON
    //dolby_actif=dolby;
#endif
}

/*------------------------------------------------------------------
        Indique si le dolby est activé
        Entrée: néant
        Sortie: TRUE si dolby activer, FALSE sinon
        Appelé depuis le menu principal
-------------------------------------------------------------------*/
SndBool SND_CALL SND_fn_bGetDolbySample(void)
{
#if !defined(DISABLE_WAVE) && defined(SURROUND)
    return dolby_actif;
#else
    return FALSE;
#endif
}

//***************************************************************
//********************* FONCTIONS PUBLIQUES *********************
//***************************************************************
//-----------------------------------------------------------
//      PlaySample: jouer une son
//      Entrées:res=id de la ressource
//              par=paramètres au lancement
//              prio=priorité
//              fct=fonction de callback
//      Sortie:voie associée au son
//-----------------------------------------------------------   
long SND_fn_lPlaySample(tduRefRes res,SampleParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
#ifndef DISABLE_WAVE
        snd_assert((res.pstPtr->eType)==TYPE_SAMPLE);
        snd_assert(!res.pstPtr->uRes.stSample.bStream);

#ifdef OBSOLETE_STREAMING
        if ((res.pstPtr->uRes.stSample.bStream)
			&& (driver_SND_OK || res.pstPtr->uRes.stSample.bLoop))
                return PlayStreaming(res,par,prio,fn_callback,par_callback);
#endif //OBSOLETE_STREAMING
		//on ne joue pas la ressource si:
		//	-elle n'est pas chargée
		//ou
		//	-le driver n'est pas OK ET la ressource ne boucle pas
        if ((res.pstPtr->bIsLoaded) 
			&& (driver_SND_OK || res.pstPtr->uRes.stSample.bLoop))
                return PlaySample(res,par,prio,fn_callback,par_callback,FALSE);

#endif  //#ifndef DISABLE_WAVE
    return C_PLAY_FAILED;
}

//-----------------------------------------------------------
//      RemoveCallbackSample: annuler une focntion de Cllback en attente
//      Entrées:voice=voie concernée
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vRemoveCallbackSample(long voice)
{
}

//-----------------------------------------------------------
//      SetParamSample: changer les paramètres d'un son
//      Entrées:voice=voie concernée
//              par=nouveaux paramètres du son
//      Sortie:FALSE si le son est fini, TRUE sinon
//-----------------------------------------------------------   
SndBool SND_fn_bSetParamSample(long voice,SampleParam *par)
{
#ifndef DISABLE_WAVE
/*
#ifdef OBSOLETE_STREAMING
    if ((voice==buffer_dispo_streaming.lVoiceUsed) && (buffer_dispo_streaming.bIsActive))
        return SetParamStreaming(par);
    else
#endif //OBSOLETE_STREAMING
*/
	#ifdef SUPERVISATER
	dbgSND_fn_vSetParVoiceSample(voice,par);
	#endif
                return SetParamSample(voice,par);
#else    
        return TRUE;
#endif //#ifndef DISABLE_WAVE
}

//-----------------------------------------------------------
//      TestIsPlayingSample: teste si un son est en cours de jeu
//      Entrées:voice=voie concernée
//      Sortie:FALSE si le son est fini, TRUE sinon
//-----------------------------------------------------------   
SndBool SND_fn_bTestIsPlayingSample(long voice)
{
#ifndef DISABLE_WAVE
#ifdef OBSOLETE_STREAMING
    if ((voice==buffer_dispo_streaming.lVoiceUsed) && (buffer_dispo_streaming.bIsActive))
        return buffer_dispo_streaming.bIsActive;
    else
#endif
	#ifdef SUPERVISATER
	if (!buffer_dispo[voice].is_playing)
		dbgSND_fn_vDelVoiceSample(voice);
	#endif

    return buffer_dispo[voice].is_playing;
#else
    return FALSE;
#endif //#ifndef DISABLE_WAVE
}

//-----------------------------------------------------------
//      StopSample: stoper un son
//      Entrées:voice=voie concernée
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vStopSample(long voice)
{
#ifndef DISABLE_WAVE
        WaitForSingleObjectL(hMutexBuffer,INFINITE);
	    mouchard=eStopSample;

#ifdef OBSOLETE_STREAMING
    if ((voice==buffer_dispo_streaming.lVoiceUsed) && (buffer_dispo_streaming.bIsActive))
        StopStreaming();
        else
#endif //OBSOLETE_STREAMING
        StopSample(voice);

        ReleaseMutex(hMutexBuffer);
#endif //#ifndef DISABLE_WAVE
}

//-----------------------------------------------------------
//      PauseSample: mettre une voie en pause
//      Entrées:voice=voie concernée
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vPauseSample(long voice)
{
#ifndef DISABLE_WAVE
    buffer_dispo[voice].paused=TRUE;
    buffer_dispo[voice].DeltaPitch=0;
    buffer_dispo[voice].DeltaTemps=0;

	#ifdef SUPERVISATER
	dbgSND_fn_vPauseVoiceSample(voice);
	#endif
#endif
}

//-----------------------------------------------------------
//      ResumeSample: reprendre une voie
//      Entrées:voice=voie concernée
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vResumeSample(long voice)
{
#ifndef DISABLE_WAVE
    buffer_dispo[voice].paused=FALSE;
    buffer_dispo[voice].DeltaPitch=0;
    buffer_dispo[voice].DeltaTemps=0;

	#ifdef SUPERVISATER
	dbgSND_fn_vResumeVoiceSample(voice);
	#endif
#endif
}



//-----------------------------------------------------------
//      SetStereoSample: modifier l'état de la stereo
//      Entrées:active=TRUE pour activer la stéreo, FALSE pour le mono
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vSetStereoSample(SndBool active)
{
#ifndef DISABLE_WAVE
        stereo_active=active;
#endif
}

//-----------------------------------------------------------
//      GetStereoSample: obtenir l'état de la stéreo
//      Entrées:néant
//      Sortie:TRUE si stéreo active, FALSE sinon
//-----------------------------------------------------------   
SndBool SND_fn_bGetStereoSample(void)
{
#ifndef DISABLE_WAVE
        return stereo_active;
#else
        return 0;
#endif
}


//-----------------------------------------------------------
//      InitSample: initialisation du module
//      Entrées:néant
//      Sortie:code de retour (C_INIT_FAILE,C_INIT_RUNNING,C_INIT_OK)
//-----------------------------------------------------------   
int SND_fn_iInitSample(SND_tdstInitStruct *pInitStruct)
{
#ifndef DISABLE_WAVE
        HANDLE hEvt;
        int ret;
        long *params;

        snd_msg_InitSample=RegisterWindowMessage("MSG_InitSnd");
        snd_msg_DesInitSample=RegisterWindowMessage("MSG_DesInitSnd");
        /*
                snd_msg_PlaySnd=RegisterWindowMessage("MSG_PlaySnd");
        snd_msg_SetParamSnd=RegisterWindowMessage("MSG_SetParamSnd");
        snd_msg_StopSnd=RegisterWindowMessage("MSG_StopSnd");
        */
        snd_msg_LibereDriver=RegisterWindowMessage("MSG_LibereDriver");
        snd_msg_TryPrendDriver=RegisterWindowMessage("MSG_PrendDriver");

        hEvt=CreateEvent(NULL,FALSE,FALSE,NULL);
        if (!(handle_snd_thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadSnd,hEvt,0,&id_snd_thread)))
        {
            SND_fn_vDisplayError(E_uwSndWaveCreateThread,"");
            return C_INIT_FAILED;
        }

        WaitForSingleObjectL(hEvt,INFINITE);
        CloseHandle(hEvt);

        params=SND_fn_pvMallocSndEx(E_ucSndBlockMain,2*sizeof(long));
        params[0]=(long)pInitStruct;
        params[1]=C_INIT_FAILED;

        if (!PostThreadMessage(id_snd_thread,snd_msg_InitSample,(WPARAM)(hEvt=CreateEvent(NULL,FALSE,FALSE,NULL))
                ,(LPARAM)params))
				SND_fn_vDisplayError(E_uwSndSystemError,"Error on PostThreadMessage");

        WaitForSingleObjectL(hEvt,INFINITE);

#ifdef PROFILE_WAVE
#ifdef PROFILE_WAVE_IN_FILE
		strncpy(timeFileName,SND_fn_szGetSoftDirectory(),sizeof(timeFileName));
		strncat(timeFileName,"\\wave_profile.log",sizeof(timeFileName)-strlen(timeFileName)-1);
		timeFile=CreateFile(timeFileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
#endif
		QueryPerformanceFrequency(&timeFreq);
		QueryPerformanceCounter(&timeHisto);
#endif

		while (1)
		{
            if (!driver_SND_OK)
            {
                ret=snd_erreur_init(SND_ERREUR_INIT_SNDDRIVERBUSY);

                switch (ret)
                {
                case INIT_FAILED:
                    wanna_driver_SND=FALSE;
                    exit(0);
                    break;
                case INIT_RETRY:
                    break;
                case INIT_IGNORE:
                    wanna_driver_SND=FALSE;
                    break;
                default:
                    SND_fn_vDisplayError(E_uwSndResourceMissing,"");
                    wanna_driver_SND=FALSE;
                    break;
                }
            }
            if (driver_SND_OK || !wanna_driver_SND)
				break;//sortie de la boucle infinie
			else
				PrendDriver();
        }

        CloseHandle(hEvt);
        ret=params[1];
        SND_fn_vFreeSndEx(E_ucSndBlockMain,params);
        
                init_SND_OK=TRUE;

                return ret;
#else
                return C_INIT_FAILED;
#endif //DISABLE_WAVE
}

//-----------------------------------------------------------
//      TestInitSample: Teste la fin de l'init
//      Entrées:néant
//      Sortie:TRUE si init OK
//-----------------------------------------------------------   
SndBool SND_fn_bTestInitSample(void)
{
#ifndef DISABLE_WAVE
        if (init_SND_OK)
                return TRUE;
        else
#endif //DISABLE_WAVE
                return FALSE;
}

//-----------------------------------------------------------
//      DesInitSample; désinitialisation du module
//      Entrées:néant
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vDesInitSample(void)
{
#ifndef DISABLE_WAVE
    HANDLE hEvt;
	DWORD ret,err;

    if (init_SND_OK)
    {
		ret=WaitForSingleObject(handle_snd_thread,0);
		if (ret==WAIT_OBJECT_0)
		{//cas du DoExit -> la thread a été tuée par le système
		    DesInitSampleLocal();
		}
		else
		{
			if (!PostThreadMessage(id_snd_thread
					,snd_msg_DesInitSample
					,(WPARAM)(hEvt=CreateEvent(NULL,FALSE,FALSE,NULL))
					,0))
			{//thread may have been killed by system->handle_snd_thread
				err=GetLastError();

				DesInitSampleLocal();
				//SND_fn_vDisplayError(E_uwSndSystemError,"Error on PostThreadMessage in SND_fn_vDesInitSample");
			}
			else
			{
				WaitForSingleObjectL(hEvt,INFINITE);
	
				CloseHandle(hEvt);

				WaitForSingleObjectL(handle_snd_thread,INFINITE);
				CloseHandle(handle_snd_thread);
			}
		}

#ifdef PROFILE_WAVE
#ifdef PROFILE_WAVE_IN_FILE
		CloseHandle(timeFile);
#endif
#endif
    }
#endif
}


//-----------------------------------------------------------
//      InitBnkSample: initialisation d'une banque
//      Entrées:num=id de la banque
//      Sortie:code de retour (C_INIT_OK,C_INIT_FAILED ou C_INIT_RUNNING)
//-----------------------------------------------------------   
int SND_fn_iInitBnkSample(long num)
{
        return C_INIT_FAILED;
}

//-----------------------------------------------------------
//      TestInitBnkSample: teste l'initialisation d'une banque
//      Entrées:num=id de la banque
//      Sortie:TRUE si banque OK
//-----------------------------------------------------------   
SndBool SND_fn_bTestInitBnkSample(long num)
{
        return TRUE;
}

//-----------------------------------------------------------
//      DesInitBnkSample: désinitialisation d'une banque
//      Entrées:num=id de la banque
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vDesInitBnkSample(long num)
{
#ifndef DISABLE_WAVE
#endif //#ifndef DISABLE_WAVE
}

//-----------------------------------------------------------
//      DesInitAllBnkSample: désinitialisation de toutes les banques
//      Entrées:néant
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vDesInitAllBnkSample(void)
{
}

//-----------------------------------------------------------
//      SetEffectSample: établir un effet sonore
//      Entrées:num=id de l'effet
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vSetEffectSample(long num)
{
#ifndef DISABLE_WAVE
        Sound_Local_Effect = NO_EFFECT;
        switch (num)
        {
                case 0 :      Sound_Global_Effect = NO_EFFECT;break;
					//arrêt de tout autre effet

                case 1 :      Sound_Global_Effect = GREVERB1;break;
					//Reverb FORTE: coef=0.75, delta=8000
					//	commune à P5 et MMX

                case 2 :      Sound_Global_Effect = GREVERB2;break;
					//Reverb MOYENNE: coef=0.5, delta=8000
					//	commune à P5 et MMX

                case 3 :      Sound_Global_Effect = GREVERB3;break;
					//spécifique à MMX
                case 4 :      Sound_Global_Effect = GREVERB4;break;
					//spécifique à MMX
                case 5 :      Sound_Global_Effect = GFLANGE;break;
					//spécifique à MMX
                case 6 :      Sound_Global_Effect = LOCAL;break;
					//spécifique à MMX
        }
#endif
}

//---------------------------------------------------
//	  LoadResFromDiskSample:rendre une ressource opérationnelle
//	Entrées:blockdisk à exploiter
//		blockmem à remplir
//--------------------------------------------------------------
/*
void SND_fn_vLoadResFromDiskSample(tdstBlockResourceDisk* disk,tdstBlockResourceMem* mem)
{
#ifndef DISABLE_WAVE
	void* data;
    char file_name[256];

    if (!disk->uRes.stSample.bStream)
	{
		data=SND_fn_pvMallocSnd(disk->ulDataSize);
        SND_fn_vResolveFileName(disk->uRes.stSample.czFileName,file_name);
		if (!SND_fn_bLoadDataInMem(data,disk->ulDataSize,file_name,disk->ulDataOffset))
		{
			SND_fn_vDisplayError(E_uwSndErrorReadingFile,file_name);
			SND_fn_vFreeSnd(data);
			data=NULL;
		}
	}
	else
		data=NULL;

	SND_fn_vConvertResDiskToMemSample(disk,mem,data);
#endif
}
*/

//----------------------------------------------
//	  UnLoadResSample:libérer une ressource opérationnelle
//	Entrées:blockmem correspondant
//--------------------------------------------------------------
void SND_fn_vUnLoadResSample(tdstBlockResourceMem* mem)
{
#ifndef DISABLE_WAVE
	snd_assert(!mem->uRes.stSample.bStream);

    if (mem->eStorage==TYPE_EXTERNAL)
	{
		if (mem->uRes.stSample.uData.stMem.pvPtrFirst!=NULL)
			SND_fn_vFreeSnd(mem->uRes.stSample.uData.stMem.pvPtrFirst);
	}

	mem->uRes.stSample.uData.stMem.pvPtrFirst=NULL;

	mem->eType=TYPE_INVALID;
#endif
}

//-----------------------------------------------------------
//      ConvertRsvDiskToMem: convertir in BlockResourceDisk en Mem
//      Entrées:ptrBegin=pointeur sur les données UTILES (jamias d'offset)
//------------------------------------------------------------
void SND_fn_vConvertResDiskToMemSample(tdstBlockResourceDisk *disk
                                                 ,tdstBlockResourceMem *mem
                                                 ,void* ptrBegin)
{       
#ifndef DISABLE_WAVE
        snd_assert(disk->eType==TYPE_SAMPLE);
        snd_assert((disk->uRes.stSample.eZip==SAMPLE_PCM) || (disk->uRes.stSample.eZip==SAMPLE_AIFF));
        snd_assert(!disk->uRes.stSample.bStream);

        mem->Id=disk->Id;
        mem->eType=disk->eType;
        mem->eStorage=disk->eStorage;
        mem->ucVolume=disk->ucVolume;
        mem->bIsLoaded=TRUE;

		//partie spécifique au type de ressource
       
        //
        mem->uRes.stSample.eZip=SAMPLE_PCM;
        mem->uRes.stSample.bPitchable=disk->uRes.stSample.bPitchable;
        mem->uRes.stSample.bVolable=disk->uRes.stSample.bVolable;
        mem->uRes.stSample.bPanable=disk->uRes.stSample.bPanable;
        mem->uRes.stSample.bSpacable=disk->uRes.stSample.bSpacable;
        mem->uRes.stSample.bReverbable=disk->uRes.stSample.bReverbable;
        mem->uRes.stSample.bStream=disk->uRes.stSample.bStream;
        mem->uRes.stSample.bLoop=disk->uRes.stSample.bLoop;
        mem->uRes.stSample.ulInc0=(disk->uRes.stSample.ulFreq<<OFFSET_BIT_INC)/g_stFormatOutput.nSamplesPerSec;
        mem->uRes.stSample.ulFreq=disk->uRes.stSample.ulFreq;
        mem->uRes.stSample.uwResolution=disk->uRes.stSample.uwResolution;
        mem->uRes.stSample.uwNbChannels=disk->uRes.stSample.uwNbChannels;

        mem->uRes.stSample.uData.stMem.ulNbEchLoop=disk->uRes.stSample.ulStartLoop;
        if (disk->uRes.stSample.bLoop)
            mem->uRes.stSample.uData.stMem.ulNbEch=disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength;
        else
            mem->uRes.stSample.uData.stMem.ulNbEch=disk->ulDataSize;
		
		snd_assert(mem->uRes.stSample.uData.stMem.ulNbEch<=disk->ulDataSize);
		if (mem->uRes.stSample.uData.stMem.ulNbEch>disk->ulDataSize)
			mem->uRes.stSample.uData.stMem.ulNbEch=disk->ulDataSize;

        if (mem->uRes.stSample.uwResolution==16)
        {
            (mem->uRes.stSample.uData.stMem.ulNbEchLoop)>>=1;
            (mem->uRes.stSample.uData.stMem.ulNbEch)>>=1;
        }
		else
            SND_fn_vDisplayError(E_uwSndInvalidTypeOfResource,"8 bits samples are not allowed");

        if (mem->uRes.stSample.uwNbChannels==2)
        {
            (mem->uRes.stSample.uData.stMem.ulNbEchLoop)>>=1;
            (mem->uRes.stSample.uData.stMem.ulNbEch)>>=1;
        }
		
		if (ptrBegin!=NULL)
            mem->uRes.stSample.uData.stMem.pvPtrFirst=(void*)((unsigned long)ptrBegin/*+disk->ulDataOffset*/);
		else
			mem->uRes.stSample.uData.stMem.pvPtrFirst=NULL;

		//#####ANTIBUG pour MMX: arrondi à un nombre pair inférieur
		mem->uRes.stSample.uData.stMem.ulNbEchLoop&=0xFFFFFFFE;
		mem->uRes.stSample.uData.stMem.ulNbEch&=0xFFFFFFFE;
		//#####Fin de l'ANTIBUG pour MMX

        //le sample est prêt à l'emplois
#endif
}


SndBool SND_fn_bLoadResScriptSample(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
{
#ifndef DISABLE_WAVE
	void* data;
    char file_name[256];

	data=SND_fn_pvMallocSnd(disk->ulDataSize);
    SND_fn_vResolveFileName(disk->uRes.stSample.czFileName,file_name);
	if (!SND_fn_bLoadDataInMemAndSwap(data,disk->ulDataSize,file_name,disk->ulDataOffset,(disk->uRes.stSample.eZip==SAMPLE_AIFF)))
	{
		SND_fn_vDisplayError(E_uwSndErrorReadingFile,file_name);
		if (data!=NULL) SND_fn_vFreeSnd(data);
		data=NULL;
	}

	SND_fn_vConvertResDiskToMemSample(disk,mem,data);

	//en script, chaque ressources charge ces propres data (on ne tient pas compte de eStorage)
	mem->eStorage=TYPE_EXTERNAL;

	return TRUE;
#else
	return FALSE;
#endif
}



SndBool SND_fn_bLoadResBinarySample(tdstBlockResourceDisk *_pBRDisk,tdstBlockResourceMem *_pBRMem,char *pDataBloc)
{
#ifndef DISABLE_WAVE
	void* pData;
    char file_name[256];

	snd_assert(_pBRDisk->uRes.stSample.eZip!=SAMPLE_AIFF);
	//binary load is disabled for AIFF sample

	// Sample is not streaming: must be really loaded in memory
	if (_pBRDisk->eStorage!=TYPE_EXTERNAL)
	{	// Sample is in the bank: its position is known relatively to pDataBloc:
		pData=(void *)(pDataBloc +_pBRDisk->ulDataOffset);
	}
	else 
	{	// Sample is external: it must be loaded from a file:
		pData=SND_fn_pvMallocSnd(_pBRDisk->ulDataSize);
		SND_fn_vResolveFileName(_pBRDisk->uRes.stSample.czFileName,file_name);
		if (!SND_fn_bLoadDataInMem(pData,_pBRDisk->ulDataSize,file_name,_pBRDisk->ulDataOffset))
		{
			SND_fn_vDisplayError(E_uwSndErrorReadingFile,file_name);
			SND_fn_vFreeSnd(pData);
			pData=NULL;
		}
	}

	SND_fn_vConvertResDiskToMemSample(_pBRDisk,_pBRMem,pData);

	return TRUE;
#else
	return FALSE;
#endif
}




//-------------------------------------------------------
//	IsResLoadedSample: teste si une ressource est exploitable
//	Entrée: block mem valide
//	Sortie:TRUE si un PlaySnd est tolérable
//-------------------------------------------------------
SndBool SND_fn_bIsResLoadedSample(tdstBlockResourceMem *mem)
{
#ifndef DISAVLE_WAVE
	if 	(mem->uRes.stSample.bStream)
		return TRUE;// Streaming sample is always "loaded"
	else  return (mem->uRes.stSample.uData.stMem.pvPtrFirst!=NULL);
#else
    return FALSE;
#endif
}

/*
void SND_fn_vSetResUnloadedSample(tdstBlockResourceMem *pResMem)
{
#ifndef DISABLE_WAVE
	pResMem->uRes.stSample.bStream=FALSE;
	pResMem->uRes.stSample.uData.stMem.pvPtrFirst=NULL;
#endif
}
*/

//---------------------------------------------------------
//      GetResForVoiceSample: retourner la ressource associée à une voie
//      Entrée:voie=concernée
//      Sortie:ressource associée
//---------------------------------------------------------
tduRefRes SND_fn_tdGetResForVoiceSample(long voice)
{
#ifndef DISABLE_WAVE
        return buffer_dispo[voice].sample;
#else
        tduRefRes r;
        return r;
#endif
}



//----------------------------------------------------------------------
//  Gestion des buffers
//----------------------------------------------------------------------
void SND_fn_vInitBufferSample(void)
{
#ifndef DISABLE_WAVE
    SNDLST2_M_DynamicInitAnchor(&buffers_clients.pList);
#endif
}

//creation d'un buffer client asynchrone
long CreateNewBufferExAsynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
#ifndef DISABLE_WAVE
    p_tdstBufferClient buffer;
	tdstBufferClientAsynchrone *buffer_client;

    snd_assert(pformat->eZip==SAMPLE_PCM);snd_assert(pCallback->eType==BUFFER_ASYNCHRONE);

    if ((pformat->uFormat.stPCM.uwResolution!=16) || !driver_SND_OK)
		return C_PLAY_FAILED;

    buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient));    
	buffer_client=&buffer->stAsynchrone;
    SNDLST2_M_DynamicInitElement(buffer);
	buffer->eType=BUFFER_ASYNCHRONE;
    buffer->uPseudoRes.pstPtr=&(buffer->tdstPseudoRes);
    buffer_client->ulSamples=pformat->uFormat.stPCM.ulNbSamples;
    buffer_client->ulDecalBytes=((pformat->uFormat.stPCM.uwResolution==8) ? 0 : 1) + ((pformat->uFormat.stPCM.uwNbChannels==1) ? 0 : 1);
    buffer_client->pfnCallback=pCallback->uCallback.CallbackASynchrone;
    buffer_client->lUserId=user_id;
    buffer_client->ulNextToCompute=0;
	buffer->bPaused=FALSE;
    //--
    //SNDLST2_M_StaticInitElement(&buffer->tdstPseudoRes);
    buffer->tdstPseudoRes.Id=0xFFFFFFFF;
    buffer->tdstPseudoRes.eType=TYPE_SAMPLE;
    buffer->tdstPseudoRes.eStorage=TYPE_EXTERNAL;
    buffer->tdstPseudoRes.ucVolume=127;
    buffer->tdstPseudoRes.bIsLoaded=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bPitchable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bVolable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bPanable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bSpacable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bReverbable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bStream=FALSE;
    buffer->tdstPseudoRes.uRes.stSample.bLoop=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst=SND_fn_pvMallocSnd(buffer_client->ulSamples<<buffer_client->ulDecalBytes);
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.ulNbEchLoop=0;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.ulNbEch=pformat->uFormat.stPCM.ulNbSamples;
    buffer->tdstPseudoRes.uRes.stSample.ulInc0=(pformat->uFormat.stPCM.ulFreq<<OFFSET_BIT_INC)/g_stFormatOutput.nSamplesPerSec;
    buffer->tdstPseudoRes.uRes.stSample.ulFreq=pformat->uFormat.stPCM.ulFreq;
    buffer->tdstPseudoRes.uRes.stSample.uwResolution=pformat->uFormat.stPCM.uwResolution;
    buffer->tdstPseudoRes.uRes.stSample.uwNbChannels=pformat->uFormat.stPCM.uwNbChannels;
    memset(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst,0,(buffer_client->ulSamples<<buffer_client->ulDecalBytes));
    
	//1° appel à la callback client avant de lancer le son
	(*(buffer_client->pfnCallback))(user_id
        ,0
        ,pformat->uFormat.stPCM.ulNbSamples
        ,(void*)buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst);

    buffer->lVoice=PlaySample(buffer->uPseudoRes,par,0,NULL,0,TRUE);

    WaitForSingleObject(hMutexBuffer,INFINITE);
    mouchard=eCreateNewBufferExAsynchrone;
    
    SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);

    ReleaseMutex(hMutexBuffer);

    return (long)buffer;
#else
    return 0;
#endif
}

//creation d'un buffer client synchrone
long CreateNewBufferExSynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
#ifndef DISABLE_WAVE
    p_tdstBufferClient buffer;
	tdstBufferClientSynchrone *buffer_client;

    snd_assert(pformat->eZip==SAMPLE_PCM);snd_assert(pCallback->eType==BUFFER_SYNCHRONE);

    if ((pformat->uFormat.stPCM.uwResolution!=16) || !driver_SND_OK)
		return C_PLAY_FAILED;

    buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient));  
	buffer_client=&buffer->stSynchrone;
    SNDLST2_M_DynamicInitElement(buffer);
	buffer->eType=BUFFER_SYNCHRONE;
    buffer->uPseudoRes.pstPtr=&(buffer->tdstPseudoRes);
    buffer_client->ulSamples=(unsigned long)((double)size_sound_block*pformat->uFormat.stPCM.ulFreq/((double)g_stFormatOutput.nBlockAlign*g_stFormatOutput.nSamplesPerSec));
    buffer_client->ulDecalBytes=((pformat->uFormat.stPCM.uwResolution==8) ? 0 : 1) + ((pformat->uFormat.stPCM.uwNbChannels==1) ? 0 : 1);
    buffer_client->pfnCallback=pCallback->uCallback.CallbackSynchrone;
    buffer_client->lUserId=user_id;
	buffer->bPaused=FALSE;
    //--
    //SNDLST2_M_StaticInitElement(&buffer->tdstPseudoRes);
    buffer->tdstPseudoRes.Id=0xFFFFFFFF;
    buffer->tdstPseudoRes.eType=TYPE_SAMPLE;
    buffer->tdstPseudoRes.eStorage=TYPE_EXTERNAL;
    buffer->tdstPseudoRes.ucVolume=127;
    buffer->tdstPseudoRes.bIsLoaded=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bPitchable=TRUE;//impossible à pitcher pour l'instant
    buffer->tdstPseudoRes.uRes.stSample.bVolable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bPanable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bSpacable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bReverbable=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.bStream=FALSE;
    buffer->tdstPseudoRes.uRes.stSample.bLoop=TRUE;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst=SND_fn_pvMallocSnd(buffer_client->ulSamples<<buffer_client->ulDecalBytes);
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.ulNbEchLoop=0;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.ulNbEch=buffer_client->ulSamples;
    buffer->tdstPseudoRes.uRes.stSample.ulInc0=(pformat->uFormat.stPCM.ulFreq<<OFFSET_BIT_INC)/g_stFormatOutput.nSamplesPerSec;
    buffer->tdstPseudoRes.uRes.stSample.ulFreq=pformat->uFormat.stPCM.ulFreq;
    buffer->tdstPseudoRes.uRes.stSample.uwResolution=pformat->uFormat.stPCM.uwResolution;
    buffer->tdstPseudoRes.uRes.stSample.uwNbChannels=pformat->uFormat.stPCM.uwNbChannels;
    memset(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst,0,(buffer_client->ulSamples<<buffer_client->ulDecalBytes));
    
    buffer->lVoice=PlaySample(buffer->uPseudoRes,par,0,NULL,0,TRUE);

    WaitForSingleObject(hMutexBuffer,INFINITE);
    mouchard=eCreateNewBufferExSynchrone;
    
    SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);

    ReleaseMutex(hMutexBuffer);

    return (long)buffer;
#else
    return 0;
#endif
}

//obsolete
long SND_fn_lCreateNewBufferSample(unsigned long nb_samples,unsigned short uwResolution,unsigned short uwNbChannels,unsigned long ulFreq,SampleParam* par,td_pfn_vRefreshBufferClient callback,long user_id)
{
#ifndef DISABLE_WAVE
	SND_tdstFormat format;
	SND_tdstCallback Callback;

	memset(&format,0,sizeof(format));
	format.eZip=SAMPLE_PCM;
	format.uFormat.stPCM.ulNbSamples=nb_samples;	
	format.uFormat.stPCM.uwResolution=uwResolution;	
	format.uFormat.stPCM.uwNbChannels=uwNbChannels;	
	format.uFormat.stPCM.ulFreq=ulFreq;

	memset(&Callback,0,sizeof(Callback));
	Callback.eType=BUFFER_ASYNCHRONE;
	Callback.uCallback.CallbackASynchrone=callback;

	return SND_fn_lCreateNewBufferExSample(&format,&Callback,par,user_id);
#endif
}

void SND_fn_vSetParamBufferSample(long id_buffer,SampleParam *par)
{
#ifndef DISABLE_WAVE
	if (id_buffer!=C_PLAY_FAILED)
	{
		if (par!=NULL)
			SND_fn_bSetParamSample(((p_tdstBufferClient)id_buffer)->lVoice,par);
		else
	        SND_fn_bSetParamSample(((p_tdstBufferClient)id_buffer)->lVoice,&C_SAMPLEPARAM_NEUTRE);
	}
#endif
}

void SND_fn_vDeleteBufferSample(long id_buffer)
{   
#ifndef DISABLE_WAVE
    WaitForSingleObject(hMutexBuffer,INFINITE);
	mouchard=eDeleteBufferSample;

	if (id_buffer!=C_PLAY_FAILED)
	{
		StopSample(((p_tdstBufferClient)id_buffer)->lVoice);

		if (((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst!=NULL)
			SND_fn_vFreeSnd(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.pvPtrFirst);

		SNDLST2_M_DynamicIsolate((p_tdstBufferClient)id_buffer);
		SND_fn_vFreeSnd((p_tdstBufferClient)id_buffer);
	}

    ReleaseMutex(hMutexBuffer);
#endif
}

void SND_fn_vPauseBufferSample(long id_buffer)
{
#ifndef DISABLE_WAVE
	if (id_buffer!=C_PLAY_FAILED)
	{
		((p_tdstBufferClient)id_buffer)->bPaused=TRUE;
		SND_fn_vPauseSample(((p_tdstBufferClient)id_buffer)->lVoice);
	}
#endif
}

void SND_fn_vResumeBufferSample(long id_buffer)
{
#ifndef DISABLE_WAVE
	if (id_buffer!=C_PLAY_FAILED)
	{
		((p_tdstBufferClient)id_buffer)->bPaused=FALSE;
		SND_fn_vResumeSample(((p_tdstBufferClient)id_buffer)->lVoice);
	}
#endif
}

//obsolete
void* SND_fn_pvGetPtrBufferSample(long id_buffer)
{
    return NULL;
}

//retourne le delta entre dernier sample calculer et sample audible
SndReal SND_fn_rGetPosBufferSample(long id_buffer)
{
#ifndef DISABLE_WAVE
	if (id_buffer!=C_PLAY_FAILED)
	{
		switch (((tdstBufferClient*)id_buffer)->eType)
		{
		case BUFFER_ASYNCHRONE:
			return SND_fn_rGetPosSample(((tdstBufferClient*)id_buffer)->lVoice);
			break;
		case BUFFER_SYNCHRONE:
			return SND_fn_rGetPosSample(((tdstBufferClient*)id_buffer)->lVoice);
			break;
		default:
			return SND_fn_rGetPosSample(((tdstBufferClient*)id_buffer)->lVoice);
		}
	}
#endif
	return SND_C_POS_ENDED;
}

long SND_fn_lCreateNewBufferExSample(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
	switch (pCallback->eType)
	{
	case BUFFER_ASYNCHRONE:
		return CreateNewBufferExAsynchrone(pformat,pCallback,par,user_id);
	case BUFFER_SYNCHRONE:
		return CreateNewBufferExSynchrone(pformat,pCallback,par,user_id);
	default:
		return C_PLAY_FAILED;
	}
}

long SND_fn_lGetPosBufferExSample(long id_buffer)
{
	return SND_C_POS_UNKNOWN;
}

long SND_fn_lPushBufferExSample(long id_buffer,SND_tdstStackBuffer* pStack)
{
	return C_PLAY_FAILED;
}


//----THEME
/*--------------------------------------------------------------------------------
PlayTransition:lance une mélodie dynamique (suite continue de samples s'enchainant
Entrées:FirstRes=1° ressource à jouée immédiatement
    NextRes=ressource devant s'enchainer à la fin de la 1° sauf contre-ordre par SetNextTrtansition
    callback=fonction a appeler chaque fois qu'une tranistion à lieu (c.a.d. qu'en on vodrait
        savoir quel sera le prochain élément à jouée après celui qui vient d'être lancé)
Sortie:ID de la voie (pour les fonctions suivantes)
-------------------------------------------------------------------------------------*/
long SND_fn_lPlayTransitionSample(tduRefRes FirstRes,tduRefRes NextRes,SND_td_pfn_vSoundCallback transition_callback,long param_callback,SampleParam* par)
{
    return C_PLAY_FAILED;
}

SndBool SND_fn_bSetParamTransitionSample(long voice,SampleParam* par)
{
	return FALSE;
}

/*--------------------------------------------------------------------------------
SetNextTransition:Modifie la ressource devant s’enchainer à la fin de la ressource en cours de restitution ; l’effet de cette fonction ne se fera donc sentir qu’à la fin de la ressource courante. De plus, si plusieurs appels ont lieu durant la restitution de la même ressource, le dernier annule les effets des précedents.
Entrées:voice=voie concernée par la remise à jour
    new_res=nouvelle ressource à lancer par défaut à la fin de celle en cours
Retour:TRUE si OK, FALSE si voie morte (plus d'autre SetNextTransition nécessaire)
-----------------------------------------------------------------------------------*/
SndBool SND_fn_bSetNextTransitionSample(long voice,tduRefRes new_res)
{
    return FALSE;
}

SndBool SND_fn_bDoTransitionWithFadeSample(long voice,tduRefRes new_res)
{
	return FALSE;
}

SndBool SND_fn_bDoTransitionWithFadeSample2(long voice,tduRefRes new_res,SndReal rFadeDuration)
{
	return FALSE;
}

/*--------------------------------------------------------------------------------
StopTransition:Stop une mélodie/theme en cours
Entrées:voice=voie devant être stoppée
-----------------------------------------------------------------------------------*/
void SND_fn_vStopTransitionSample(long voice)
{
}

void SND_fn_vPauseTransitionSample(long voice)
{
}

void SND_fn_vResumeTransitionSample(long voice)
{
}

#ifndef NO_ACP_LDBIN
__declspec(dllexport) SndBool SND_CALL SND_fn_bCanFreeDataSample(void);

SndBool SND_CALL SND_fn_bCanFreeDataSample(void)
{
	return FALSE;
}
#endif

/*--------------------------------------------------
Release/restore of sound driver.
SND_fn_vReleaseDriverSample: is called for Focus lost
SND_fn_vForceReleaseDriverSample: is called when waveOut is needed for an other module
	The first only of both does release driver (second don't let driver released)
SND_fn_vRestoreDriverSample: is called for Focus gain
SND_fn_vRestoreReleaseDriverSample: is called when waveOut is no more needed 
	When both SND_fn_vReleaseDriverSample & SND_fn_vForceReleaseDriverSample have been called
		, driver is restored only when both SND_fn_vRestoreDriverSample & SND_fn_vForceRestoreDriverSample have been called
bDriverReleased is set to TRUE between SND_fn_vReleaseDriverSample & SND_fn_vRestoreDriverSample
bDriverForcedReleased is set to TRUE between SND_fn_vForceReleaseDriverSample & SND_fn_vForceRestoreDriverSample
---------------------------------------------------------*/
void RestoreDriver()
{
	int ret,i;

	PrendDriver();

	while (1)
	{
		if (!driver_SND_OK && wanna_driver_SND)
		{
			ret=snd_erreur_init(SND_ERREUR_INIT_SNDDRIVERBUSY);

			switch (ret)
			{
			case INIT_FAILED:
				wanna_driver_SND=FALSE;
				exit(0);
				break;
			case INIT_RETRY:
				break;
			case INIT_IGNORE:
				wanna_driver_SND=FALSE;
				break;
			default:
				SND_fn_vDisplayError(E_uwSndResourceMissing,"");
				wanna_driver_SND=FALSE;
				break;
			}
		}
		if (driver_SND_OK || !wanna_driver_SND)
		{
			if (!wanna_driver_SND)
			{//destruction de toutes les voies; driver kaput
				for (i=0;i<NB_VOICE;i++) SND_fn_vStopSample(i);
			}
			break;//sortie de la boucle infinie
		}
		else
			PrendDriver();
	}
}

void SND_fn_vReleaseDriverSample(void)
{
	if (!bDriverReleased)
	{
		bDriverReleased=TRUE;

		LibereDriver();
	}
}

void SND_fn_vForceReleaseDriverSample(void)
{
	if (!bDriverForcedReleased)
	{
		bDriverForcedReleased=TRUE;

		LibereDriver();
	}
}

void SND_fn_vRestoreDriverSample(void)
{
	if (bDriverReleased)
	{
		bDriverReleased=FALSE;

		if (!bDriverForcedReleased)
		{
			RestoreDriver();
		}
	}
}

void SND_fn_vForceRestoreDriverSample(void)
{
	if (bDriverForcedReleased)
	{
		bDriverForcedReleased=FALSE;

		if (!bDriverReleased)
                {
			RestoreDriver();
                }
        }
}


//---------------------------------------------------------
//	GetPos: retourne la position "théorique" de la ressource
//			exprimé en seconde depuis le début
//			différent du temps absolu (car pitch, dérive..)
//	Entrée: voie concernée
//	Sortie: temps en S (SndReal)
//----------------------------------------------------------
SndReal SND_fn_rGetPosSample(long voie)
{
    BUFFER_DISPO* buffer;

    buffer=&(buffer_dispo[voie]);

	if (buffer->is_playing)
	{
		if (buffer->pt_sign>>OFFSET_BIT_INC>(unsigned long)ech_avance)
			return SND_fn_rDivRealRealSnd(
				(buffer->pt_sign>>OFFSET_BIT_INC)-ech_avance
				,buffer->sample.pstPtr->uRes.stSample.ulFreq);
		else
			return 0;//décalage entre mixage et audition
	}
	else
		return SND_C_POS_ENDED;
}

//---------------------------------------------------------
//	GetLength: retourne la durée "théorique" de la ressource
//			exprimé en seconde (constant pour une ressource donnée)
//	Entrée: voie concernée
//	Sortie: durée en S (SndReal)
//----------------------------------------------------------
SndReal SND_fn_rGetLengthSample(long voie)
{
    BUFFER_DISPO* buffer;

    buffer=&(buffer_dispo[voie]);

	if (buffer->is_playing)
		return SND_fn_rDivRealRealSnd(
			buffer->sample.pstPtr->uRes.stSample.uData.stMem.ulNbEch
			,buffer->sample.pstPtr->uRes.stSample.ulFreq);
	else
		return SND_C_LENGTH_UNKNOWN;
}

void SND_fn_vSetSoundVolumeSample(unsigned char vol)
{
		SND_fn_vSetSampleValue((long)100*vol/127);
}

unsigned char SND_fn_ucGetSoundVolumeSample()
{
	return (unsigned char)(SND_fn_dwGetSampleValue()*127/100);
}

//-------------------------------------------------
// Fonction de synchro moteur
//-------------------------------------------------
void SND_fn_vSynchroSample()
{
}

void SND_fn_vSetNbVoiceWishedSample(long nb)
{
}

long SND_fn_lGetNbVoiceWishedSample()
{
	return 0x7FFFFFFF;
}

long SND_fn_lGetNbVoiceOptimumSample()
{
	return 0x7FFFFFFF;
}



SndBool SND_fn_bIsResourceLoopingSample(tduRefRes res)
{
	return res.pstPtr->uRes.stSample.bLoop;
}

SndBool  SND_fn_bSetResourceStaticVolumeSample(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	pstRes->ucVolume=ucVolume;

	return TRUE;
}

