/*
//////////////////////////////////////////////////////////////
//      SNDWAVE - DirectX2D									//
//////////////////////////////////////////////////////////////
//      Module d'exploitation des samples simples           //
//      Version												//
//////////////////////////////////////////////////////////////
*/

#define INITGUID
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
//#include <crtdbg.h>
#include <stdio.h>

//#define DISABLE_RECORD

#include "SNDinc.h"
#include "sndres.h"
#include "sndxd.h"
#include "sndwave.h"
#include "sndstrm.h"
#include "sndthrd.h"
#include "sndini.h"
#include "sndrec.h"

#include "effects.h"

#include "sndhardw.h"

#if defined(_DLL_COMPILATION_MODE)
#include "snddll.h"
#else
#include "snddbg.h"
#endif

LPDIRECTSOUNDNOTIFY lpDsNotify;

static BOOL stereo_reverse=FALSE;
static BOOL stereo_active=TRUE;

static BOOL driver_SND_OK=FALSE;//TRUE si objet DX OK

//----
static BOOL g_b_ChooseOutputFormat=TRUE; //TRUE si on veut imposer à DSound le format Output
static int OutputFreqChoosen=22050;
static int OutputResChoosen=16;
static int OutputChanChoosen=2; //format souhaité

//--liste des GUIDs dispos
static int nb_GUIDSound=0;
static pstGUIDAvailable pGUIDAvailable;

//-------	  
static LPDIRECTSOUND lpDirectSoundObject;
static DSCAPS DSCapsInfos;
static LPGUID lpGuidChoosen=NULL;

static DSBUFFERDESC DSPrimBufferDesc;
static LPDIRECTSOUNDBUFFER lpDSPrimBuffer;
static LPWAVEFORMATEX lpWaveFormatPrimBuffer;
static DWORD dwSizeoflpWaveFormatPrimBuffer;

//------------------
typedef struct BUFFER_DISPO_ BUFFER_DISPO;
struct BUFFER_DISPO_{
	BOOL	        is_playing;					//TRUE si valid
	BOOL	        is_looping;					//TRUE si bouclant
	BOOL	        is_mixing;					//TRUE si réellement audible 
	tduRefResDX2D   uRes;						//ressource associée
	LPDIRECTSOUNDBUFFER Attack;			//buffer d'attaque (jamais joué en bouclant)
	LPDIRECTSOUNDBUFFER Loop;			//buffer de boucle (toujours 
	BOOL	        waiting_callback;           //TRUE si une callback doit être appelée à la fin du son
	BOOL	        waiting_loop;               //TRUE si on doit enchainer sur la boucle
	DWORD           dwSizeAttack;                //nb d'octets de l'attaque
	SND_td_pfn_vSoundCallback fn_callback;
	long            par_callback;
	BOOL			paused;					//TRUE entre pause et resume
	DWORD			pos_when_paused;		//currentposition au moement du pause
} ;
#define NB_VOICE 64
static int nb_voice_actived=0;
static BUFFER_DISPO buffer_dispo[NB_VOICE];

//buffer clients
//buffer clients
typedef struct _tdstBufferClientAsynchrone {
    unsigned long				ulSamples;		//taille du buffer en sample
    unsigned long				ulDecalBytes;	//nb_sample<<buffer->ulDecalBytes=nb_octet
    unsigned long				ulNextToCompute;//id du prochaine sample àcalculer
    td_pfn_vRefreshBufferClient pfnCallback;//fonction callback buffer client
} tdstBufferClientAsynchrone;

typedef struct _tdstBufferClient *p_tdstBufferClient;

SNDLST2_M_DynamicUseListOf(p_tdstBufferClient);

typedef struct _tdstBufferClient {
	SND_tduTypeCallback		eType;
    long                    lVoice;			//voie sample associée
	SndBool					bPaused;		//TRUE si buffer paused
	DWORD					pos_when_paused;
    long					lUserId;		
    tduRefResDX2D				uPseudoRes;		//uPseudoRes=&tdstPseudoRes
    tdstBlockResourceMemDX2D	tdstPseudoRes;	//pseudo ressource pour le mixage sample
	union {
		tdstBufferClientAsynchrone stAsynchrone;
	} uParam;
	DWORD						ulTimeNextToCompute;//temps mesuré au moment de la remise à jour de ulNextToCompute
	double						dLastDeltaT;		//dernier Dt utilisé pour l'extrapolation de GetPos
    SNDLST2_M_DynamicElementDeclaration(p_tdstBufferClient)
} tdstBufferClient;

typedef struct _tdstBufferClientList {
    SNDLST2_M_DynamicAnchorDeclaration(p_tdstBufferClient) pList;
} tdstBufferClientList;

static tdstBufferClientList buffers_clients;

/*
typedef struct _tdstBufferClient *p_tdstBufferClient;

SNDLST2_M_DynamicUseListOf(p_tdstBufferClient);

typedef struct _tdstBufferClient tdstBufferClient;
struct _tdstBufferClient  {
    tduRefResDX2D               uPseudoRes;
    tdstBlockResourceMemDX2D    tdstPseudoRes;
    unsigned long               ulSamples;//nb SAMPLE
    unsigned long               ulDecalBytes;//(SAMPLE<<ulDecalBytes)->BYTES
    unsigned long               ulNextToCompute;//next SAMPLE to compute
    td_pfn_vRefreshBufferClient pfnCallbackBuffer;
    long                        lUserId;
	BOOL						paused;
	DWORD						pos_when_paused;
	DWORD						ulTimeNextToCompute;//temps mesuré au moment de la remise à jour de ulNextToCompute
	double						dLastDeltaT;		//dernier Dt utilisé pour l'extrapolation de GetPos
    SNDLST2_M_DynamicElementDeclaration(p_tdstBufferClient)
};

typedef struct _tdstBufferClientList {
    SNDLST2_M_DynamicAnchorDeclaration(p_tdstBufferClient) pList;
} tdstBufferClientList;

tdstBufferClientList buffers_clients;
*/
enum {
    TIMER_OFF,
    TIMER_ON,
} eTimerState;
static BOOL bTimerInstance=FALSE;

static UINT snd_msg_ExitThread=0;
static HANDLE handle_snd_thread=NULL;
static DWORD id_snd_thread;
static HANDLE hEvtQuitThread=NULL;

static SND_tdstInitStruct InitStructSaved;//copie du SND_tdstInitStruct de l'init

//res en mémoire
typedef struct _tdstResInMem *p_tdstResInMem;

SNDLST2_M_DynamicUseListOf(p_tdstResInMem);

typedef struct _tdstResInMem tdstResInMem;
struct _tdstResInMem  {
    tdstBlockResourceMem	*pstRes;
    SNDLST2_M_DynamicElementDeclaration(p_tdstResInMem)
};

typedef struct _tdstResInMemList {
    SNDLST2_M_DynamicAnchorDeclaration(p_tdstResInMem) pList;
} tdstResInMemList;

static tdstResInMemList res_in_mem;

//----
SampleParam C_SAMPLEPARAM_MUET={C_SNDREAL_1,C_SOUNDVOL_MINI,C_SOUNDPAN_MEDIAN,C_SOUNDSPACE_FRONT,NO_REVERB};
SampleParam C_SAMPLEPARAM_NEUTRE={C_SNDREAL_1,C_SOUNDVOL_MAXI,C_SOUNDPAN_MEDIAN,C_SOUNDSPACE_FRONT,NO_REVERB};

//#include "sndwavmx.cxx"

//---------------- FONCTIONS PRIVEES ---------------------------
#ifndef DISABLE_WAVE

//tables log. de conversion Midi->DSound
long vol_2_hth_dB[128]={-10000,
        -4206,-3605,-3253,-3003,-2809,-2651,-2517,-2401,-2299,-2207,
        -2124,-2049,-1979,-1915,-1855,-1799,-1746,-1697,-1650,-1605,
        -1563,-1522,-1484,-1447,-1411,-1377,-1344,-1313,-1282,-1253,
        -1224,-1197,-1170,-1144,-1119,-1094,-1071,-1048,-1025,-1003,
        -982,-961,-940,-920,-901,-882,-863,-845,-827,-809,
        -792,-775,-759,-742,-726,-711,-695,-680,-665,-651,
        -636,-622,-608,-595,-581,-568,-555,-542,-529,-517,
        -505,-492,-480,-469,-457,-445,-434,-423,-412,-401,
        -390,-379,-369,-359,-348,-338,-328,-318,-308,-299,
        -289,-280,-270,-261,-252,-243,-234,-225,-216,-207,
        -198,-190,-181,-173,-165,-156,-148,-140,-132,-124,
        -116,-109,-101,-93,-86,-78,-71,-63,-56,-49,
        -42,-34,-27,-20,-13,-6,0};
long pan_2_hth_dB[129]={-9999,
        -6900,-5848,-5198,-4725,-4355,-4049,-3789,-3564,-3364,-3185,
        -3022,-2874,-2737,-2610,-2492,-2381,-2277,-2179,-2086,-1998,
        -1914,-1834,-1757,-1684,-1613,-1546,-1481,-1418,-1358,-1299,
        -1243,-1188,-1135,-1083,-1033,-984,-937,-891,-846,-802,
        -760,-718,-677,-637,-599,-561,-523,-487,-451,-416,
        -382,-348,-316,-283,-251,-220,-190,-159,-130,-101,
        -72,-44,-16,16,44,72,101,130,159,190,
        220,251,283,316,348,382,416,451,487,523,
        561,599,637,677,718,760,802,846,891,937,
        984,1033,1083,1135,1188,1243,1299,1358,1418,1481,
        1546,1613,1684,1757,1834,1914,1998,2086,2179,2277,
        2381,2492,2610,2737,2874,3022,3185,3364,3564,3789,
        4049,4355,4725,5198,5848,6900,9999,9999};

LPDIRECTSOUND create_DSOUND(LPGUID lpGuid,SndBool retry);
int init_DSOUND(SND_tdstInitStruct *pInitStruct,LPGUID lpGuid);
void desinit_DSOUND();


static void GetErrorDSound(HRESULT err,char* texte,int size_texte)
{
    if (err!=DS_OK)
    {
        switch (err)
        {
                case DSERR_CONTROLUNAVAIL:
                        strncpy(texte,"not available",size_texte);break;
                case DSERR_ALLOCATED:
                        strncpy(texte,"allocated",size_texte);break;
                case DSERR_BADFORMAT:
                        strncpy(texte,"bad format",size_texte);break;
                case DSERR_INVALIDPARAM:
                        strncpy(texte,"invalid param",size_texte);break;
                case DSERR_NOAGGREGATION:       
                        strncpy(texte,"no aggregation",size_texte);break;
                case DSERR_OUTOFMEMORY: 
                        strncpy(texte,"out of memory",size_texte);break;
                case DSERR_BUFFERLOST:
                        strncpy(texte,"bufferlost",size_texte);break;
                case DSERR_PRIOLEVELNEEDED:
                        strncpy(texte,"priolevelneeded",size_texte);break;
                case DSERR_INVALIDCALL:
                        strncpy(texte,"invalidcall",size_texte);break;
                default:
                        strncpy(texte,"Please, re-install your sound-driver",size_texte);
        }
	}
	else
		strncpy(texte,"No error",size_texte);
}

/*----------------------------------------------------------------
        Analyse une erreur DirectSound
        Si err=DS_OK, ne fait rien
        Entrées:fonction=id de la fonction (cf liste au dessus)
                err=code renvoyé par DSound
        Sortie: renvoie err tel quel
        Appelé par: les macros de la mort (DSound_xx)
----------------------------------------------------------------*/
void DisplayErrorDSound(HRESULT err)
{
    char texte[80];

	GetErrorDSound(err,texte,sizeof(texte));

	SND_fn_vDisplayError(E_uwSndSystemError,texte);
}

//------------- DSoundTry(fct(x,y)) appelle fct(x,y) et affiche une éventuelle erreur
static HRESULT DSoundTry(HRESULT res)
{									
    char texte[80];

	if (res!=DS_OK)			
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
		//DisplayErrorDSound(res);	
	}
	return res;
}

//-------callback enumGuid
BOOL CALLBACK GUID_enum_callback(GUID FAR * lpGuid,LPSTR lpstrDescription,LPSTR lpstrModule,LPVOID lpContext)
{
	HRESULT res;
	char texte[80];

	if (nb_GUIDSound)
		pGUIDAvailable=SND_fn_pvReAllocSnd(pGUIDAvailable,sizeof(stGUIDAvailable)*(nb_GUIDSound+1));
	else
		pGUIDAvailable=SND_fn_pvMallocSnd(sizeof(stGUIDAvailable));

	if (lpGuid!=NULL)
	{
		memcpy(&pGUIDAvailable[nb_GUIDSound].Guid,lpGuid,sizeof(GUID));
		pGUIDAvailable[nb_GUIDSound].bGuidNULL=FALSE;
	}
	else
		pGUIDAvailable[nb_GUIDSound].bGuidNULL=TRUE;
	strncpy(pGUIDAvailable[nb_GUIDSound].description,lpstrDescription,sizeof(pGUIDAvailable[nb_GUIDSound].description));
	strncpy(pGUIDAvailable[nb_GUIDSound].module,lpstrModule,sizeof(pGUIDAvailable[nb_GUIDSound].module));

	//évaluation du GUID
	lpDirectSoundObject=create_DSOUND(
		pGUIDAvailable[nb_GUIDSound].bGuidNULL ? NULL : &pGUIDAvailable[nb_GUIDSound].Guid
		,FALSE);

	if (lpDirectSoundObject)
	{
		memset(&DSCapsInfos,0,sizeof(DSCapsInfos));
		DSCapsInfos.dwSize=sizeof(DSCapsInfos);
		if ((res=IDirectSound_GetCaps(lpDirectSoundObject,&DSCapsInfos))!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
		}

		//stock caractéristique
		memcpy(&pGUIDAvailable[nb_GUIDSound].caps,&DSCapsInfos,sizeof(DSCapsInfos));

		desinit_DSOUND();
	}
	else
	{//driver indisponible 
		memset(&pGUIDAvailable[nb_GUIDSound].caps,0,sizeof(DSCapsInfos));
	}

	nb_GUIDSound++;

	return TRUE;
}

//------- choix du GUID pour DirectSound
void init_listGUID(void)
{
//        int context=0;
	HRESULT res;
	char texte[80];
	DWORD context=0;

	if (nb_GUIDSound)
	{
		SND_fn_vFreeSnd(pGUIDAvailable);
		nb_GUIDSound=0;
	}

	if ((res=DirectSoundEnumerate((LPDSENUMCALLBACK)&GUID_enum_callback,&context))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
		//DisplayErrorDSound(res);
	}
}

void destroy_listGUID()
{
	if (nb_GUIDSound)
	{
		SND_fn_vFreeSnd(pGUIDAvailable);
		nb_GUIDSound=0;
	}
}

//return le lpGUID du meilleur driver; ne profite pour retourner le nom de celui-ci
LPGUID evaluate_bestGUID(char* description,int size)
{
#define DSCAPS_FLAG1 DSCAPS_CERTIFIED+DSCAPS_PRIMARY16BIT+DSCAPS_PRIMARYSTEREO+DSCAPS_SECONDARY16BIT+DSCAPS_SECONDARYMONO+DSCAPS_SECONDARYSTEREO 

	int i,best;
	DWORD dwMaxHwMixingStaticBuffers=0;
	
	best=-1;
	for (i=0;i<nb_GUIDSound;i++)
	{//cherche le 1° hardware accelered
		if ((pGUIDAvailable[i].caps.dwSize!=0)
			&& (pGUIDAvailable[i].caps.dwFlags & DSCAPS_FLAG1)
			&& !(pGUIDAvailable[i].caps.dwFlags & DSCAPS_EMULDRIVER)
			&& (pGUIDAvailable[i].caps.dwMaxHwMixingStaticBuffers>dwMaxHwMixingStaticBuffers))
		{
			dwMaxHwMixingStaticBuffers=pGUIDAvailable[i].caps.dwMaxHwMixingStaticBuffers;
			best=i;
		}
	}
	if (best==-1)
	{
		for (i=0;i<nb_GUIDSound;i++)
		{//cherche le 1° non émulé
			if ((pGUIDAvailable[i].caps.dwSize!=0)
				&& (pGUIDAvailable[i].caps.dwFlags & DSCAPS_FLAG1)
				&& !(pGUIDAvailable[i].caps.dwFlags & DSCAPS_EMULDRIVER)
				&& !pGUIDAvailable[i].bGuidNULL)
			{
				best=i;
			}
		}

		if ((best==-1) && nb_GUIDSound)
			best=0;

	}

	if (best>-1)
	{
		strncpy(description,pGUIDAvailable[best].description,size);
		return (pGUIDAvailable[best].bGuidNULL ? NULL : &pGUIDAvailable[best].Guid);
	}

	return NULL;
}

//recherche d'un GUID à partir de sa description
LPGUID find_GUID(char* GUID_wanted,char* GUID_choosen,int size)
{
	int i;

	//recherche du bon GUID
	if (GUID_wanted[0]!=0)
	{//INI OK?
		for (i=0;i<nb_GUIDSound;i++)
		{
			if (!strcmp(pGUIDAvailable[i].description,GUID_wanted))
			{//found
				strncpy(GUID_choosen,GUID_wanted,size);
				return (pGUIDAvailable[i].bGuidNULL ? NULL : &pGUIDAvailable[i].Guid);
			}
		}
	}
	
	//not found
	return evaluate_bestGUID(GUID_choosen,size);
}

void get_keyvalue(char *section,DWORD dwPlatformId)
{
	char texte[64];

	strcpy(section,DX2D_GUID_KEY);

	switch (dwPlatformId)
	{
	case VER_PLATFORM_WIN32_WINDOWS:
		strcat(section,"Win95");
		break;
	case VER_PLATFORM_WIN32_NT:
		strcat(section,"WinNT");
		break;
	default:
		sprintf(texte,"%d",dwPlatformId);
		strcat(section,texte);
		break;
	}
}

void SND_fn_vSetWantedGUID(char* GUID_choosen)
{
	char section[128];
	OSVERSIONINFO info;

	memset(&info,0,sizeof(OSVERSIONINFO));
	info.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
	GetVersionEx(&info);
	get_keyvalue(section,info.dwPlatformId);

	if (SND_fn_bCanSetOptions())
	{
		SND_fn_vSetOptions(DX2D_SECTION,section,GUID_choosen);
	}
	else
	{
		WritePrivateProfileString(DX2D_SECTION,section,GUID_choosen,INI_FILENAME);
	}
}

//extraction du GUID souhaité du fichier INI
void SND_CALL SND_fn_vGetWantedGUID(char* GUID_wanted,int size)
{
	char section[128];
	OSVERSIONINFO info;

	memset(&info,0,sizeof(OSVERSIONINFO));
	info.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
	GetVersionEx(&info);
	get_keyvalue(section,info.dwPlatformId);

	//extraction du fichier INI
	if (SND_fn_bCanSetOptions())
	{
		if (!SND_fn_bGetOptions(DX2D_SECTION,section,GUID_wanted,size))
		{//section inéxistante
			GUID_wanted[0]=0;			
		}
	}
	else
	{
		if (!GetPrivateProfileString(DX2D_SECTION,section,"NULL",GUID_wanted,size,INI_FILENAME))
		{//section inéxistante
			GUID_wanted[0]=0;			
		}
	}
}

//choix automoatique du GUID
LPGUID auto_chooseGUID()
{
	char GUID_wanted[128],GUID_choosen[128];
	LPGUID lpGuid;

	SND_fn_vGetWantedGUID(GUID_wanted,sizeof(GUID_wanted));

	//recherche du bon GUID
	lpGuid=find_GUID(GUID_wanted,GUID_choosen,sizeof(GUID_choosen));
	if ((GUID_wanted[0]!=0) && !strcmp(GUID_choosen,GUID_wanted))
		return lpGuid;

	//raj du fichier INI
	SND_fn_vSetWantedGUID(GUID_choosen);

	return lpGuid; 
}

//exportation de la liste des GUID; retourne le nb de GUID
int SND_CALL SND_fn_iGetGUIDList(pstGUIDAvailable lpGuidDescrption,int size)
{
	memcpy(lpGuidDescrption,pGUIDAvailable,size);
	return nb_GUIDSound;
}

//obtenir le GUID actuel
void SND_CALL  SND_fn_vGetCurrentGUID(char* description,int size)
{
	int i;

	strncpy(description,"None",size);

	for (i=0;i<nb_GUIDSound;i++)
	{
		if ((pGUIDAvailable[i].bGuidNULL) && (lpGuidChoosen==NULL)
			|| (!pGUIDAvailable[i].bGuidNULL && (lpGuidChoosen==&pGUIDAvailable[i].Guid)))
		{//found
			strncpy(description,pGUIDAvailable[i].description,size);
			return;
		}
	}
}

//sauvagarde le GUID souhaité(avec vérification) sans toucher au GUID courant
void SND_CALL SND_fn_vChangeWantedGUID(char* wanted,char* selected,int size)
{
	LPGUID lpGuid;

	lpGuid=find_GUID(wanted,selected,size);

	SND_fn_vSetWantedGUID(selected);
}

//sauvagarde le GUID courant(avec vérification) sans toucher au ficheir INI
void SND_CALL SND_fn_vChangeCurrentGUID(char* wanted,char* selected,int size)
{
	LPGUID lpGuid;

	lpGuid=find_GUID(wanted,selected,size);

	desinit_DSOUND();

	init_DSOUND(&InitStructSaved,lpGuid);
}

//--------------------
DWORD WaitForSingleObjectL(HANDLE h,DWORD inf)
{
        DWORD retWait;

        if ((retWait=WaitForSingleObject(h,INFINITE))==WAIT_TIMEOUT) 
                DebugBreak();
        return retWait;
}

// raj inconditionnelle des paramètres d'un buffer
void set_param_DSBuffer(LPDIRECTSOUNDBUFFER buffer,tduRefResDX2D res,SampleParam *par,BOOL bClientBuffer)
{
	long	lDSvolume;
	DWORD freq=(DWORD)(M_RealToDoubleSnd(par->Freq)*res.pstPtr->uRes.stSample.ulFreq);

	snd_assert(freq<100000);snd_assert(freq>100);
	freq=max(100,min(100000,freq));

	DSoundTry(IDirectSoundBuffer_SetFrequency(buffer,freq));

	lDSvolume=((long)res.pstPtr->ucVolume*(long)par->ucVol)>>7;
	if (!bClientBuffer || (res.pstPtr->uRes.stSample.uwNbChannels!=2))
		lDSvolume/=2;
	DSoundTry(IDirectSoundBuffer_SetVolume(buffer,vol_2_hth_dB[lDSvolume]));
	if (stereo_reverse)
		DSoundTry(IDirectSoundBuffer_SetPan(buffer,pan_2_hth_dB[128-par->ucPan]));
	else
		DSoundTry(IDirectSoundBuffer_SetPan(buffer,pan_2_hth_dB[par->ucPan]));
}

// renvoie TRUE si la voie est en cours de jeu
BOOL test_voice_is_active(BUFFER_DISPO* buffer)
{
	DWORD status;
	BOOL ret=FALSE;

	if (buffer->Attack)
	{
		if (buffer->waiting_loop) return TRUE;
		else {
			DSoundTry(IDirectSoundBuffer_GetStatus(buffer->Attack,&status));
			if ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_PLAYING))
				ret=TRUE;
		}
	}
	if (buffer->Loop)
	{
		DSoundTry(IDirectSoundBuffer_GetStatus(buffer->Loop,&status));
		if ((status & DSBSTATUS_PLAYING) || (status & DSBSTATUS_PLAYING))
			ret=TRUE;
	}

	return ret;
}

//------------------------------
//	désactive complétement une voie (Release des DSBuf)
//	ne touche pas à la callback
//------------------------------
void desactive_voice(BUFFER_DISPO* buffer)
{
	//remettre is_playing à 0 en 1° pour éviter les interférences avec des callback-timer
	buffer->is_playing=FALSE;
	//-----------------------------------------
	buffer->waiting_loop=FALSE;
	buffer->is_looping=FALSE;
	if (buffer->Attack)
	{
		DSoundTry(IDirectSoundBuffer_Release(buffer->Attack));
		buffer->Attack=NULL;
	}
	if (buffer->Loop)
	{
		DSoundTry(IDirectSoundBuffer_Release(buffer->Loop));
		buffer->Loop=NULL;
	}
}


void refresh_buffer_asynchrone(p_tdstBufferClient buffer)
{
	tdstBufferClientAsynchrone *buffer_asynchron=&buffer->uParam.stAsynchrone;
    DWORD sizeptr1,sizeptr2,*ptr1,*ptr2;
    DWORD playptr,writeptr;
    DWORD nbbytes_to_calcul;

	//_RPT4(_CRT_WARN,"%d-%d-%f-%x\n",buffer->ulNextToCompute,nbbytes_to_calcul,buffer->dLastDeltaT,SND_fn_rGetPosBufferSample((long)buffer));

	DSoundTry(IDirectSoundBuffer_GetCurrentPosition(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,&playptr,&writeptr));
    playptr=(playptr>>buffer_asynchron->ulDecalBytes)<<buffer_asynchron->ulDecalBytes;
    nbbytes_to_calcul=playptr-(buffer_asynchron->ulNextToCompute<<buffer_asynchron->ulDecalBytes);
    if ((long)nbbytes_to_calcul<0) nbbytes_to_calcul+=buffer_asynchron->ulSamples<<buffer_asynchron->ulDecalBytes;
    
    buffer->dLastDeltaT-=(double)(nbbytes_to_calcul>>buffer_asynchron->ulDecalBytes)/buffer->tdstPseudoRes.uRes.stSample.ulFreq;
	if (buffer->dLastDeltaT<0.0)
		buffer->dLastDeltaT=0.0;

    if (nbbytes_to_calcul)
    {
	    DSoundTry(IDirectSoundBuffer_Lock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
            ,buffer_asynchron->ulNextToCompute<<buffer_asynchron->ulDecalBytes,nbbytes_to_calcul
            ,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0));

        //memset(ptr1,0,sizeptr1);
        if (sizeptr1!=nbbytes_to_calcul)
        {//wrap out
            (*(buffer_asynchron->pfnCallback))(buffer->lUserId
                ,buffer_asynchron->ulNextToCompute
                ,sizeptr1>>buffer_asynchron->ulDecalBytes
                ,ptr1);
            snd_assert(ptr2);
            (*(buffer_asynchron->pfnCallback))(buffer->lUserId
                ,0
                ,sizeptr2>>buffer_asynchron->ulDecalBytes
                ,ptr2);
            snd_assert(sizeptr2==(nbbytes_to_calcul-sizeptr1));
            buffer_asynchron->ulNextToCompute=sizeptr2>>buffer_asynchron->ulDecalBytes;
        }
        else
        {
            (*(buffer_asynchron->pfnCallback))(buffer->lUserId
                ,buffer_asynchron->ulNextToCompute
                ,sizeptr1>>buffer_asynchron->ulDecalBytes
                ,ptr1);
            buffer_asynchron->ulNextToCompute+=nbbytes_to_calcul>>buffer_asynchron->ulDecalBytes;
            if (buffer_asynchron->ulNextToCompute==buffer_asynchron->ulSamples) 
                buffer_asynchron->ulNextToCompute=0;
        }

	    DSoundTry(IDirectSoundBuffer_Unlock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2));       

		buffer->ulTimeNextToCompute=timeGetTime();
    }
}

// on gère une pile de buffer à enchainer
void refresh_buffer_stack(p_tdstBufferClient buffer)
{
}

//---------------------------------------------------
//  Fonction timer gérant les loops, les callback
//---------------------------------------------------
void CALLBACK TimerSynchro(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2)
{
    int i;
    BUFFER_DISPO *buffer;
    DWORD play,write,status;
    p_tdstBufferClient buffer_client;

    if (bTimerInstance || (eTimerState==TIMER_OFF))
        return;
    bTimerInstance=TRUE;

    SND_fn_vEnterCriticalSectionThreadSnd();
    if (eTimerState==TIMER_OFF)
		return;

    //--- buffers clients
    //raj des buffers clients
    SNDLST2_M_DynamicForEachElementOf(&buffers_clients.pList,buffer_client,i)
    {
		if (!buffer_client->bPaused)
		{
			switch (buffer_client->eType)
			{
			case BUFFER_ASYNCHRONE:
			case BUFFER_SYNCHRONE:
				refresh_buffer_asynchrone(buffer_client);
				break;
			case BUFFER_STACK:
				refresh_buffer_stack(buffer_client);
				break;
			}
		}
    }

    //--- transitions attack->loop
    for (i=0,buffer=&buffer_dispo[0];i<nb_voice_actived;i++,buffer++)
        if (buffer->is_playing)
        {
            if (buffer->waiting_loop)
            {
                DSoundTry(IDirectSoundBuffer_GetCurrentPosition(buffer->Attack,&play,&write));
                if (play>=buffer->dwSizeAttack)
                {//transition attack->loop
                    DSoundTry(IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,play-buffer->dwSizeAttack));
                    DSoundTry(IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING));
                    DSoundTry(IDirectSoundBuffer_Stop(buffer->Attack));

                    buffer->is_looping=TRUE;
					buffer->waiting_loop=FALSE;
                } else {
                    DSoundTry(IDirectSoundBuffer_GetStatus(buffer->Attack,&status));
					if (!(status & DSBSTATUS_PLAYING)) {
						DSoundTry(IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,0));
						DSoundTry(IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING));
						DSoundTry(IDirectSoundBuffer_Stop(buffer->Attack));

						buffer->is_looping=TRUE;
						buffer->waiting_loop=FALSE;
					}
				} 


            }

            if (buffer->waiting_callback)
            {
                if (buffer->is_looping)
                    DSoundTry(IDirectSoundBuffer_GetStatus(buffer->Loop,&status));
                else
                    DSoundTry(IDirectSoundBuffer_GetStatus(buffer->Attack,&status));
                if (!(status & DSBSTATUS_PLAYING))
                {//sample fini->callback
                    buffer->is_playing=FALSE;
            		(*(buffer->fn_callback))(buffer->par_callback);
                    desactive_voice(buffer);
                }
            }
        }

    SND_fn_vQuitCriticalSectionThreadSnd();

    bTimerInstance=FALSE;

    //lacement du timer suivant
    timeSetEvent(10,1,TimerSynchro,0,TIME_ONESHOT);
}

DWORD WINAPI ThreadSnd(HANDLE h_event)
{
    MSG msg;

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

    timeSetEvent(10,1,TimerSynchro,0,TIME_ONESHOT);

    while (GetMessage(&msg,NULL,0,0))
    {
#ifndef DISABLE_RECORD
		if (!SND_fn_bManageMsgForRecord((long)&msg))
#endif
		{
	        if (msg.message==snd_msg_ExitThread)
		        break;
		}
    }

    eTimerState=TIMER_OFF;

    ExitThread(0);

	return 0;
}

//--------------------------
//	jouer un son en mémoire
//-----------------------
long PlaySample(tduRefRes res,SampleParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
	int i;
	BUFFER_DISPO *buffer;
    tduRefResDX2D resDX2D;
    resDX2D.pstPtr=(tdstBlockResourceMemDX2D*)(res.pstPtr);

	if (!driver_SND_OK)
		return C_PLAY_FAILED;

    //---release des buffers
    for (i=0,buffer=&buffer_dispo[0];i<nb_voice_actived;i++,buffer++)
    {
        if (buffer->is_playing)
            if (!SND_fn_bTestIsPlayingSample((long)buffer))
        {
            desactive_voice(buffer);
        }
    }

    //--recherche d'un buffer_dispo libre
	for (i=0,buffer=&buffer_dispo[0];i<nb_voice_actived;i++,buffer++)
		if (!buffer->is_playing) break;

	if (i==nb_voice_actived)
	{
		if (nb_voice_actived<NB_VOICE)
			nb_voice_actived++;
		else
		{
			SND_fn_vDisplayError(E_uwSndSystemError,"Too many voices for SNDWAVE");
			return C_PLAY_FAILED;
		}
	}

	//le buffer à utiliser est *buffer
	buffer->is_playing=FALSE;
	buffer->is_looping=FALSE;
	buffer->is_mixing=TRUE;
	buffer->uRes=resDX2D;
	buffer->paused=FALSE;
	if (resDX2D.pstPtr->uRes.stSample.uData.stMem.lpBufferAttack)
		DSoundTry(
            IDirectSound_DuplicateSoundBuffer(
                lpDirectSoundObject
                ,resDX2D.pstPtr->uRes.stSample.uData.stMem.lpBufferAttack
                ,&buffer->Attack
                )
            );
	else
		buffer->Attack=NULL;
	if (resDX2D.pstPtr->uRes.stSample.uData.stMem.lpBufferLoop)
		DSoundTry(IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject,resDX2D.pstPtr->uRes.stSample.uData.stMem.lpBufferLoop,&buffer->Loop));
	else
		buffer->Loop=NULL;
	buffer->dwSizeAttack=resDX2D.pstPtr->uRes.stSample.uData.stMem.iSizeAttack;

	//c'est parti
	if (buffer->Attack)
	{
		set_param_DSBuffer(buffer->Attack,resDX2D,par,FALSE);
		DSoundTry(IDirectSoundBuffer_Play(buffer->Attack,0,0,0));
		set_param_DSBuffer(buffer->Attack,resDX2D,par,FALSE);
		buffer->is_playing=TRUE;

		if (buffer->Loop)
		{
			buffer->waiting_loop=TRUE;
			set_param_DSBuffer(buffer->Loop,resDX2D,par,FALSE);
		}
		else
			buffer->waiting_loop=FALSE;
	}
	else
	{
		set_param_DSBuffer(buffer->Loop,resDX2D,par,FALSE);
		DSoundTry(IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING));
		set_param_DSBuffer(buffer->Loop,resDX2D,par,FALSE);
		buffer->is_playing=TRUE;
		buffer->is_looping=TRUE;
		buffer->waiting_loop=FALSE;
	}

	if (fn_callback!=NULL)
	{
		buffer->waiting_callback=TRUE;
		buffer->fn_callback=fn_callback;
		buffer->par_callback=par_callback;
	}
	else
		buffer->waiting_callback=FALSE;

	#ifdef SUPERVISATER
	dbgSND_fn_vAddVoiceSample((long)buffer,res,par);
	#endif
	
	return (long)buffer;
}

//---------------------
//	stopper une voie
//---------------------
void StopSample(long voice)
{
	if (((BUFFER_DISPO*)voice)->waiting_callback)
	{
		(*(((BUFFER_DISPO*)voice)->fn_callback))(((BUFFER_DISPO*)voice)->par_callback);
		((BUFFER_DISPO*)voice)->waiting_callback=FALSE;
	}
	desactive_voice((BUFFER_DISPO*)voice);
}

//--------------------------
//	remmettre à jour une voie
//-----------------------
SndBool SetParamSample(long voice,SampleParam *par)
{
	#ifdef SUPERVISATER
	dbgSND_fn_vSetParVoiceSample(voice,par);
	#endif

	if (test_voice_is_active((BUFFER_DISPO*)voice))
	{
		if (((BUFFER_DISPO*)voice)->Attack)
			set_param_DSBuffer(((BUFFER_DISPO*)voice)->Attack,((BUFFER_DISPO*)voice)->uRes,par,FALSE);
		if (((BUFFER_DISPO*)voice)->Loop)
			set_param_DSBuffer(((BUFFER_DISPO*)voice)->Loop,((BUFFER_DISPO*)voice)->uRes,par,FALSE);

		return TRUE;
	}
	else
	{
		((BUFFER_DISPO*)voice)->is_playing=FALSE;
		#ifdef SUPERVISATER
		dbgSND_fn_vDelVoiceSample(voice);
		#endif
		return FALSE;
	}
}

#endif //DISABLE_WAVE


/*------------------------------------------------------------------
        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)
{
    stereo_reverse=reverse;
}

/*------------------------------------------------------------------
        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)
{
    return stereo_reverse;
}

//***************************************************************
//********************* 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);

        if (!res.pstPtr->uRes.stSample.bStream && res.pstPtr->bIsLoaded)
                return PlaySample(res,par,prio,fn_callback,par_callback);

#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
	return SetParamSample(voice,par);
#else
    return TRUE;
#endif
}

//-----------------------------------------------------------
//      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
    ((BUFFER_DISPO*)voice)->is_playing=test_voice_is_active((BUFFER_DISPO*)voice);
	#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
    StopSample(voice);

	#ifdef SUPERVISATER
	dbgSND_fn_vDelVoiceSample(voice);
	#endif
#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)
{
	DWORD write;

	if (test_voice_is_active((BUFFER_DISPO*)voice) && !((BUFFER_DISPO*)voice)->paused)
	{
		((BUFFER_DISPO*)voice)->paused=TRUE;
		if ((((BUFFER_DISPO*)voice)->Attack) && !((BUFFER_DISPO*)voice)->is_looping)
		{
			DSoundTry(IDirectSoundBuffer_GetCurrentPosition(((BUFFER_DISPO*)voice)->Attack,&((BUFFER_DISPO*)voice)->pos_when_paused,&write));
			DSoundTry(IDirectSoundBuffer_Stop(((BUFFER_DISPO*)voice)->Attack));
		}
		if ((((BUFFER_DISPO*)voice)->Loop) && ((BUFFER_DISPO*)voice)->is_looping)
		{
			DSoundTry(IDirectSoundBuffer_GetCurrentPosition(((BUFFER_DISPO*)voice)->Loop,&((BUFFER_DISPO*)voice)->pos_when_paused,&write));
			DSoundTry(IDirectSoundBuffer_Stop(((BUFFER_DISPO*)voice)->Loop));
		}
	}

	#ifdef SUPERVISATER
	dbgSND_fn_vPauseVoiceSample(voice);
	#endif
}

//-----------------------------------------------------------
//      ResumeSample: reprendre une voie
//      Entrées:voice=voie concernée
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vResumeSample(long voice)
{
	if (((BUFFER_DISPO*)voice)->is_playing && ((BUFFER_DISPO*)voice)->paused)
	{
		if ((((BUFFER_DISPO*)voice)->Attack) && !((BUFFER_DISPO*)voice)->is_looping)
		{
			DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((BUFFER_DISPO*)voice)->Attack,((BUFFER_DISPO*)voice)->pos_when_paused));
			DSoundTry(IDirectSoundBuffer_Play(((BUFFER_DISPO*)voice)->Attack,0,0,0));
			DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((BUFFER_DISPO*)voice)->Attack,((BUFFER_DISPO*)voice)->pos_when_paused));
		}
		if ((((BUFFER_DISPO*)voice)->Loop) && ((BUFFER_DISPO*)voice)->is_looping)
		{
			DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((BUFFER_DISPO*)voice)->Loop,((BUFFER_DISPO*)voice)->pos_when_paused));
			DSoundTry(IDirectSoundBuffer_Play(((BUFFER_DISPO*)voice)->Loop,0,0,DSBPLAY_LOOPING));
			DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((BUFFER_DISPO*)voice)->Loop,((BUFFER_DISPO*)voice)->pos_when_paused));
		}
		((BUFFER_DISPO*)voice)->paused=FALSE;
	}

	#ifdef SUPERVISATER
	dbgSND_fn_vResumeVoiceSample(voice);
	#endif
}


//-----------------------------------------------------------
//      SetSoundvolumeSample: modifier le volume sonore
//      Entrées:vol=nouveau volume
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vSetSoundVolumeSample(unsigned char vol)
{
}

//-----------------------------------------------------------
//      GetSOundVolumeSample: obtenir le volume sonore
//      Entrées:néant
//      Sortie:volume au format MIDI
//-----------------------------------------------------------   
unsigned char SND_fn_ucGetSoundVolumeSample(void)
{
        return 0;
}


//-----------------------------------------------------------
//      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
}

#ifndef DISABLE_WAVE
//creation d'un objet DSOUND (boucle si driver occupé er retry=TRUE)
LPDIRECTSOUND create_DSOUND(LPGUID lpGuid,SndBool retry)
{
	LPDIRECTSOUND lpDirectSoundObject;
	HRESULT res;
	int ret;
	char texte[80];
	SndBool out=FALSE;

	driver_SND_OK=FALSE;

	//--crétation et initialisation globales
    while (!out)
    {
		if ((res=DirectSoundCreate(lpGuid,&lpDirectSoundObject,NULL))!=DS_OK)
		{//impossible de créer l'objet DX
			if (retry)
			{
				if (res==DSERR_ALLOCATED)
				{//driver busy
					ret=snd_erreur_init(SND_ERREUR_INIT_SNDDRIVERBUSY);
					switch (ret)
					{
					case INIT_FAILED:
						exit(0);
						break;
					case INIT_RETRY:
						break;
					case INIT_IGNORE:
						driver_SND_OK=FALSE;
						out=TRUE;
						break;
					default:
						SND_fn_vDisplayError(E_uwSndResourceMissing,"");
						driver_SND_OK=FALSE;
						out=TRUE;
						break;
					}
				}
				else
				{//erreur non prévue
					GetErrorDSound(res,texte,sizeof(texte));
					SND_fn_vDisplayError(E_uwSndSystemError,texte);
					//DisplayErrorDSound(res);
					driver_SND_OK=FALSE;
					out=TRUE;
				}
			}
			else
			{
				driver_SND_OK=FALSE;
				out=TRUE;
			}
		}
		else
		{//DX OK
                driver_SND_OK=TRUE;
                out=TRUE;
		}
	}

	if (!driver_SND_OK)
		return NULL;
	else
		return lpDirectSoundObject;
}

//init géné de l'objet DSOUND
int init_DSOUND(SND_tdstInitStruct *pInitStruct,LPGUID lpGuid)
{
	HRESULT res;
	BOOL out=FALSE;
	char texte[80];

	driver_SND_OK=FALSE;

	//--crétation et initialisation globales
	lpDirectSoundObject=create_DSOUND(lpGuid,TRUE);

	/*
    while (!out)
    {
		if ((res=DirectSoundCreate(lpGuid,&lpDirectSoundObject,NULL))!=DS_OK)
		{//impossible de créer l'objet DX
			if (res==DSERR_ALLOCATED)
            {//driver busy
                ret=snd_erreur_init(SND_ERREUR_INIT_SNDDRIVERBUSY);
                switch (ret)
                {
                case INIT_FAILED:
                    exit(0);
                    break;
                case INIT_RETRY:
                    break;
                case INIT_IGNORE:
                    driver_SND_OK=FALSE;
					out=TRUE;
                    break;
                default:
                    SND_fn_vDisplayError(E_uwSndResourceMissing,"");
                    driver_SND_OK=FALSE;
                    out=TRUE;
                    break;
                }
			}
			else
			{//erreur non prévue
				GetErrorDSound(res,texte,sizeof(texte));
				SND_fn_vDisplayError(E_uwSndSystemError,texte);
				//DisplayErrorDSound(res);
                driver_SND_OK=FALSE;
                out=TRUE;
			}
		}
		else
		{//DX OK
                driver_SND_OK=TRUE;
                out=TRUE;
		}
	}
	*/

	if (!driver_SND_OK || !lpDirectSoundObject)
		return C_INIT_FAILED;
	
	memset(&DSCapsInfos,0,sizeof(DSCapsInfos));
	DSCapsInfos.dwSize=sizeof(DSCapsInfos);
	if ((res=IDirectSound_GetCaps(lpDirectSoundObject,&DSCapsInfos))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
		//DisplayErrorDSound(res);
	}

	if (g_b_ChooseOutputFormat)
	{//prioritylevel ->choix du format possible
		if ((res=IDirectSound_SetCooperativeLevel(lpDirectSoundObject,pInitStruct->hMainWindow,DSSCL_PRIORITY))!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
			//DisplayErrorDSound(res);
		}
	}
	else
	{//non priority-level ->pas de choix du format
		if ((res=IDirectSound_SetCooperativeLevel(lpDirectSoundObject,pInitStruct->hMainWindow,DSSCL_NORMAL))!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
			//DisplayErrorDSound(res);
		}
	}

	//-- création et gestion du buffer primaire
	memset(&DSPrimBufferDesc,0,sizeof(DSPrimBufferDesc));
	DSPrimBufferDesc.dwSize=sizeof(DSPrimBufferDesc);
	DSPrimBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER;
	if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&DSPrimBufferDesc,&lpDSPrimBuffer,NULL))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
	}

    if ((res=IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,NULL,0,&dwSizeoflpWaveFormatPrimBuffer))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
	}
    lpWaveFormatPrimBuffer=(LPWAVEFORMATEX)SND_fn_pvMallocSnd(dwSizeoflpWaveFormatPrimBuffer);
    if ((res=IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer,dwSizeoflpWaveFormatPrimBuffer,NULL))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
	}

	if (g_b_ChooseOutputFormat)
	{
		if ((DSCapsInfos.dwFlags & DSCAPS_PRIMARY16BIT) && (OutputResChoosen==16))
			lpWaveFormatPrimBuffer->wBitsPerSample=16;
		else
			lpWaveFormatPrimBuffer->wBitsPerSample=8;

		if ((DSCapsInfos.dwFlags & DSCAPS_PRIMARYSTEREO) && (OutputChanChoosen==2))
			lpWaveFormatPrimBuffer->nChannels=2;
		else
			lpWaveFormatPrimBuffer->nChannels=1;

		lpWaveFormatPrimBuffer->nSamplesPerSec=OutputFreqChoosen;
		lpWaveFormatPrimBuffer->nBlockAlign=lpWaveFormatPrimBuffer->nChannels*lpWaveFormatPrimBuffer->wBitsPerSample/8;
		lpWaveFormatPrimBuffer->nAvgBytesPerSec=lpWaveFormatPrimBuffer->nSamplesPerSec*lpWaveFormatPrimBuffer->nBlockAlign;

		while ((res=IDirectSoundBuffer_SetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer))!=DS_OK)
		{
			if ((res==DSERR_BADFORMAT) &&  (lpWaveFormatPrimBuffer->nSamplesPerSec>11025))
			{
				lpWaveFormatPrimBuffer->nSamplesPerSec/=2;
				lpWaveFormatPrimBuffer->nAvgBytesPerSec/=2;
			}
			else
				break;
		}

		if (res!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
			return C_INIT_FAILED;
		}
	}

	DSoundTry(IDirectSoundBuffer_Play(lpDSPrimBuffer,0,0,DSBPLAY_LOOPING));

	return C_INIT_OK;
}
#endif

//  Création des Buffer DirectX; le chargement se fera plus tard
//	Par:disk=blockresourceDisk entrée
//		ptrpar=pointer sur un tableau de 2 LPSOUNDBUFFER	
SndBool fn_CreateResSample(tdstBlockResourceDisk *disk,tdstBlockResourceMemDX2D *mem)
{
#ifndef DISABLE_WAVE
    PCMWAVEFORMAT pcmwf;
    HRESULT res;
	LPDIRECTSOUNDBUFFER lpBufAttack=NULL,lpBufLoop=NULL;
    DSBUFFERDESC dsbdesc;
	char texte[80];

	if (!driver_SND_OK)
		return FALSE;

    snd_assert(!disk->uRes.stSample.bStream);

    //--------buffer one-shot
    if (!(disk->uRes.stSample.bLoop) || (disk->uRes.stSample.ulStartLoop>0))
    {
        // Set up wave format structure.
        memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
        pcmwf.wf.wFormatTag=WAVE_FORMAT_PCM;
        pcmwf.wf.nChannels=disk->uRes.stSample.uwNbChannels;
        pcmwf.wf.nSamplesPerSec=disk->uRes.stSample.ulFreq;
        pcmwf.wBitsPerSample=disk->uRes.stSample.uwResolution;
        pcmwf.wf.nBlockAlign=pcmwf.wf.nChannels*pcmwf.wBitsPerSample/8;
        pcmwf.wf.nAvgBytesPerSec=pcmwf.wf.nSamplesPerSec*pcmwf.wf.nBlockAlign;
        // Set up DSBUFFERDESC structure.
        memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
        dsbdesc.dwSize=sizeof(DSBUFFERDESC);
        dsbdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_LOCSOFTWARE; 
        if (disk->uRes.stSample.bLoop)
                dsbdesc.dwBufferBytes=disk->uRes.stSample.ulStartLoop+pcmwf.wf.nAvgBytesPerSec/4; 
        else
                dsbdesc.dwBufferBytes=disk->ulDataSize;
        dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;
        // create buffer
        if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&dsbdesc,&lpBufAttack,NULL))!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
		}

    }

    //------buffer bouclant
    if (disk->uRes.stSample.bLoop)
    {//buffer de loop
        // Set up wave format structure.
        memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
        pcmwf.wf.wFormatTag=WAVE_FORMAT_PCM;
        pcmwf.wf.nChannels=disk->uRes.stSample.uwNbChannels;
        pcmwf.wf.nSamplesPerSec=disk->uRes.stSample.ulFreq;
        pcmwf.wBitsPerSample=disk->uRes.stSample.uwResolution;
        pcmwf.wf.nBlockAlign=pcmwf.wf.nChannels*pcmwf.wBitsPerSample/8;
        pcmwf.wf.nAvgBytesPerSec=pcmwf.wf.nSamplesPerSec*pcmwf.wf.nBlockAlign;
        // Set up DSBUFFERDESC structure.
        memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
        dsbdesc.dwSize=sizeof(DSBUFFERDESC);
        dsbdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_LOCSOFTWARE; 
        dsbdesc.dwBufferBytes=disk->uRes.stSample.ulLoopLength; 
        dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;
        // create buffer
        if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&dsbdesc,&lpBufLoop,NULL))!=DS_OK)
		{
			GetErrorDSound(res,texte,sizeof(texte));
			SND_fn_vDisplayError(E_uwSndSystemError,texte);
		}

    }

    mem->uRes.stSample.uData.stMem.lpBufferAttack=lpBufAttack;
    mem->uRes.stSample.uData.stMem.lpBufferLoop=lpBufLoop;

    return TRUE;
#else
	return FALSE;
#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
	LPGUID lpGuid;

    snd_msg_ExitThread=RegisterWindowMessage("MSG_ExitThread");

	memcpy(&InitStructSaved,pInitStruct,sizeof(InitStructSaved));

	init_listGUID();

	lpGuid=auto_chooseGUID();

	if (init_DSOUND(&InitStructSaved,lpGuid)==C_INIT_FAILED)
	{
		destroy_listGUID();
		return C_INIT_FAILED;
	}

	lpGuidChoosen=lpGuid;

	SNDLST2_M_DynamicInitAnchor(&res_in_mem.pList);

    eTimerState=TIMER_ON;
    if (!(handle_snd_thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadSnd,0,0,&id_snd_thread)))
    {
        SND_fn_vDisplayError(E_uwSndWaveCreateThread,"");
		destroy_listGUID();
        return C_INIT_FAILED;
    }

	return C_INIT_OK;
#else
	return C_INIT_FAILED;
#endif 
}

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

#ifndef DISABLE_WAVE
void desinit_DSOUND()
{
		if (lpDirectSoundObject!=NULL)
			DSoundTry(IDirectSound_Release(lpDirectSoundObject));
		lpDirectSoundObject=NULL;
}
#endif

//-----------------------------------------------------------
//      DesInitSample; désinitialisation du module
//      Entrées:néant
//      Sortie:néant
//-----------------------------------------------------------   
void SND_fn_vDesInitSample(void)
{
	if (driver_SND_OK)
	{
	    eTimerState=TIMER_OFF;
	    PostThreadMessage(id_snd_thread,snd_msg_ExitThread,0,0);
	    WaitForSingleObject(handle_snd_thread,100);

		CloseHandle(handle_snd_thread);

		desinit_DSOUND();
		SND_fn_vFreeSnd(lpWaveFormatPrimBuffer);

		destroy_listGUID();
	}
}


//-----------------------------------------------------------
//      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)
{
        Sound_Local_Effect = NO_EFFECT;
        switch (num)
        {
                case 0 :      Sound_Global_Effect = NO_EFFECT;break;
                /*
                default:    Sound_Global_Effect=GREVERB1;
                    DialogBox(SND_g_hResourceHandle,"DIALOG_REVERB",HWND_DESKTOP,(DLGPROC)ReverbDialog);
                    break;
                */
                case 1 :      Sound_Global_Effect = GREVERB1;break;
                case 2 :      Sound_Global_Effect = GREVERB2;break;
                case 3 :      Sound_Global_Effect = GREVERB3;break;
                case 4 :      Sound_Global_Effect = GREVERB4;break;
                case 5 :      Sound_Global_Effect = GFLANGE;break;
                case 6 :      Sound_Global_Effect = LOCAL;break;
        }
}

//---------------------------------------------------
//	  LoadResFromDiskSample:rendre une ressource opérationnelle
//	Entrées:blockdisk à exploiter
//		blockmem à remplir
//--------------------------------------------------------------
/*
void SND_fn_vLoadResFromDiskSample(tdstBlockResourceDisk* disk,tdstBlockResourceMem* mem)
{
}
*/

//----------------------------------------------
//	  UnLoadResSample:libérer une ressource opérationnelle
//	Entrées:blockmem correspondant
//--------------------------------------------------------------
void SND_fn_vUnLoadResSample(tdstBlockResourceMem* mem)
{
#ifndef DISABLE_WAVE
	int i;
	p_tdstResInMem pResInMem,pResInMemNext;
    tdstBlockResourceMemDX2D* memDX2D=(tdstBlockResourceMemDX2D*)mem;

	if (!memDX2D->uRes.stSample.bStream)
	{
		if (memDX2D->uRes.stSample.uData.stMem.lpBufferAttack)
        {
			DSoundTry(IDirectSoundBuffer_Stop(memDX2D->uRes.stSample.uData.stMem.lpBufferAttack));
			DSoundTry(IDirectSoundBuffer_Release(memDX2D->uRes.stSample.uData.stMem.lpBufferAttack));
        }
		if (memDX2D->uRes.stSample.uData.stMem.lpBufferLoop)
        {
			DSoundTry(IDirectSoundBuffer_Stop(memDX2D->uRes.stSample.uData.stMem.lpBufferLoop));
			DSoundTry(IDirectSoundBuffer_Release(memDX2D->uRes.stSample.uData.stMem.lpBufferLoop));
        }
		memDX2D->uRes.stSample.uData.stMem.lpBufferAttack=NULL;
		memDX2D->uRes.stSample.uData.stMem.lpBufferLoop=NULL;
	}

	SNDLST2_M_DynamicForEachMovingElementOf(&res_in_mem.pList,pResInMem,pResInMemNext,i)
	{
		if (pResInMem->pstRes==mem)
		{
			SNDLST2_M_DynamicIsolate(pResInMem);
			SND_fn_vFreeSnd(pResInMem);
		}
	}
#endif
}

//-----------------------------------------------------------
//      ConvertRsvDiskToMem: convertir in BlockResourceDisk en Mem
//      Entrées:
//			disk: resource to be converted (in)
//			mem: resource to be generated (out)
//			ptrBegin: pointer onto raw data
//------------------------------------------------------------
void SND_fn_vConvertResDiskToMemSample(tdstBlockResourceDisk *disk
                                                 ,tdstBlockResourceMem *mem
                                                 ,void* ptrBegin)
{       
#ifndef DISABLE_WAVE
	    DSBCAPS caps;
		DWORD sizeptr1,sizeptr2;
		LPVOID ptr1,ptr2;
		p_tdstResInMem pResInMem;
        tdstBlockResourceMemDX2D* memDX2D=(tdstBlockResourceMemDX2D*)mem;

        snd_assert(disk->eType==TYPE_SAMPLE);

	    fn_CreateResSample(disk,memDX2D);

		//--------buffer one-shot
        if (!(disk->uRes.stSample.bLoop) || (disk->uRes.stSample.ulStartLoop>0))
        {
            // Set up wave format structure.
            memset(&caps,0,sizeof(DSBCAPS));
            caps.dwSize=sizeof(DSBCAPS);
            DSoundTry(IDirectSoundBuffer_GetCaps(memDX2D->uRes.stSample.uData.stMem.lpBufferAttack,&caps));

            DSoundTry(IDirectSoundBuffer_Lock(memDX2D->uRes.stSample.uData.stMem.lpBufferAttack,0,caps.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0));

			memcpy(ptr1,ptrBegin,sizeptr1);

            DSoundTry(IDirectSoundBuffer_Unlock(memDX2D->uRes.stSample.uData.stMem.lpBufferAttack,ptr1,sizeptr1,ptr2,sizeptr2));
        }

        //------buffer bouclant
        if (disk->uRes.stSample.bLoop)
        {//buffer de loop
            memset(&caps,0,sizeof(DSBCAPS));
            caps.dwSize=sizeof(DSBCAPS);
            DSoundTry(IDirectSoundBuffer_GetCaps(memDX2D->uRes.stSample.uData.stMem.lpBufferLoop,&caps));

            DSoundTry(IDirectSoundBuffer_Lock(memDX2D->uRes.stSample.uData.stMem.lpBufferLoop,0,caps.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0));

			memcpy(ptr1,(char*)ptrBegin+disk->uRes.stSample.ulStartLoop,sizeptr1);

			DSoundTry(IDirectSoundBuffer_Unlock(memDX2D->uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2));
        }


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

		//partie spécifique au type de ressource
       
        //
        memDX2D->uRes.stSample.eZip=SAMPLE_PCM;
        memDX2D->uRes.stSample.bPitchable=disk->uRes.stSample.bPitchable;
        memDX2D->uRes.stSample.bVolable=disk->uRes.stSample.bVolable;
        memDX2D->uRes.stSample.bPanable=disk->uRes.stSample.bPanable;
        memDX2D->uRes.stSample.bSpacable=disk->uRes.stSample.bSpacable;
        memDX2D->uRes.stSample.bReverbable=disk->uRes.stSample.bReverbable;
        memDX2D->uRes.stSample.bStream=disk->uRes.stSample.bStream;
        memDX2D->uRes.stSample.bLoop=disk->uRes.stSample.bLoop;
		memDX2D->uRes.stSample.ulFreq=disk->uRes.stSample.ulFreq;
        memDX2D->uRes.stSample.uwResolution=disk->uRes.stSample.uwResolution;
		memDX2D->uRes.stSample.uwNbChannels=disk->uRes.stSample.uwNbChannels;

		snd_assert(!disk->uRes.stSample.bStream);

		memDX2D->uRes.stSample.uData.stMem.iSizeAttack=disk->uRes.stSample.ulStartLoop;

		pResInMem=SND_fn_pvMallocSnd(sizeof(tdstResInMem));
		SNDLST2_M_DynamicInitElement(pResInMem);
		pResInMem->pstRes=mem;
		SNDLST2_M_DynamicAddTail(&res_in_mem.pList,pResInMem);


#endif
}

//---------------------------------------------------------
//      IsResLoadedSample: teste si une ressource est opérationnelle
//      Entrée:blockmem valide
//      Sortie:TRUE si SndPlay accéptable
//---------------------------------------------------------
SndBool SND_fn_bIsResLoadedSample(tdstBlockResourceMem *mem)
{
#ifndef DISABLE_WAVE

    tdstBlockResourceMemDX2D* memDX2D=(tdstBlockResourceMemDX2D*)mem;

    if (!memDX2D->uRes.stSample.bStream)
	{
		if ((memDX2D->uRes.stSample.uData.stMem.lpBufferAttack) || (memDX2D->uRes.stSample.uData.stMem.lpBufferLoop))
			return TRUE;
		else
			return FALSE;
	}

	return TRUE;
#else
	return FALSE;
#endif
}

//---------------------------------------------------------
//      GetResForVoiceSample: retourner la ressource associée à une voie
//      Entrée:voie=concernée
//      Sortie:ressource associée
//---------------------------------------------------------
tduRefRes SND_fn_tdGetResForVoiceSample(long voice)
{
	tduRefRes rien={0};

        return rien;
}

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

	if (!driver_SND_OK)
		return FALSE;

    snd_assert(!disk->uRes.stSample.bStream);

	ptrpar=SND_fn_pvMallocSnd(disk->ulDataSize);

    SND_fn_vResolveFileName(disk->uRes.stSample.czFileName,file_name);

    if (!SND_fn_bLoadDataInMem(ptrpar,disk->ulDataSize,file_name,disk->ulDataOffset))
    {
		SND_fn_vDisplayError(E_uwSndErrorReadingFile,"");
		SND_fn_vFreeSnd(ptrpar);
		ptrpar=NULL;
    }
	
	SND_fn_vConvertResDiskToMemSample(disk,mem,ptrpar);

	SND_fn_vFreeSnd(ptrpar);

    return TRUE;
#else
	return FALSE;
#endif
}



SndBool SND_fn_bLoadResBinarySample(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
{
#ifndef DISABLE_WAVE
    char file_name[256];
    void* pData=NULL;

	if (!driver_SND_OK)
		return FALSE;

	snd_assert(!disk->uRes.stSample.bStream);

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

	SND_fn_vConvertResDiskToMemSample(disk,mem,pData);

    //déchargement des données
    if (pData && (disk->eStorage==TYPE_EXTERNAL))
    {
		SND_fn_vFreeSnd(pData);
		pData=NULL;
    }

    return TRUE;
#else
	return FALSE;
#endif
}




/*
void SND_fn_vSetResUnloadedSample(tdstBlockResourceMem *pResMem)
{
    SND_fn_vUnLoadResSample(pResMem);
}
*/


//---buffer clients
//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
}

//creation d'un buffer client synchrone
long CreateNewBufferExStack(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
#ifndef DISABLE_WAVE
    snd_assert(pformat->eZip==SAMPLE_PCM);snd_assert(pCallback->eType==BUFFER_STACK);
#endif
	return C_PLAY_FAILED;
}

long CreateNewBufferExAsynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
#ifndef DISABLE_WAVE
	PCMWAVEFORMAT pcmwf;
    DSBUFFERDESC dsbdesc;
    HRESULT res;
	DWORD sizeptr1,sizeptr2;
	LPVOID ptr1,ptr2;
    p_tdstBufferClient buffer;
	char texte[80];
	tdstBufferClientAsynchrone *buffer_asynchron;

	if (!driver_SND_OK)
		return C_PLAY_FAILED;

    buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient));    
	buffer_asynchron=&buffer->uParam.stAsynchrone;

    SNDLST2_M_DynamicInitElement(buffer);
	buffer->eType=BUFFER_ASYNCHRONE;
    buffer->lUserId=user_id;
    buffer->uPseudoRes.pstPtr=&(buffer->tdstPseudoRes);
    buffer_asynchron->ulSamples=pformat->uFormat.stPCM.ulNbSamples;
    buffer_asynchron->ulDecalBytes=((pformat->uFormat.stPCM.uwResolution==8) ? 0 : 1) + ((pformat->uFormat.stPCM.uwNbChannels==1) ? 0 : 1);
    buffer_asynchron->pfnCallback=pCallback->uCallback.CallbackASynchrone;
    buffer_asynchron->ulNextToCompute=0;
    buffer->ulTimeNextToCompute=0;
	buffer->dLastDeltaT=0.0;
	buffer->bPaused=FALSE;
    //--
    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.ulFreq=pformat->uFormat.stPCM.ulFreq;
    buffer->tdstPseudoRes.uRes.stSample.uwResolution=pformat->uFormat.stPCM.uwResolution;
    buffer->tdstPseudoRes.uRes.stSample.uwNbChannels=pformat->uFormat.stPCM.uwNbChannels;
    buffer->tdstPseudoRes.uRes.stSample.eZip=SAMPLE_PCM;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferAttack=NULL;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.iSizeAttack=0;

	// Set up wave format structure.
	memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
	pcmwf.wf.wFormatTag=WAVE_FORMAT_PCM;
	pcmwf.wf.nChannels=pformat->uFormat.stPCM.uwNbChannels;
	pcmwf.wf.nSamplesPerSec=pformat->uFormat.stPCM.ulFreq;
	pcmwf.wBitsPerSample=pformat->uFormat.stPCM.uwResolution;
	pcmwf.wf.nBlockAlign=pcmwf.wf.nChannels*pcmwf.wBitsPerSample/8;
	pcmwf.wf.nAvgBytesPerSec=pcmwf.wf.nSamplesPerSec*pcmwf.wf.nBlockAlign;
	// Set up DSBUFFERDESC structure.
	memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
	dsbdesc.dwSize=sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_LOCSOFTWARE; 
    dsbdesc.dwBufferBytes=buffer_asynchron->ulSamples<<buffer_asynchron->ulDecalBytes;
	dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;
	// create buffer
	if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject
            ,&dsbdesc
            ,&buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
            ,NULL))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
	}

	DSoundTry(IDirectSoundBuffer_Lock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,dsbdesc.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0));

	pCallback->uCallback.CallbackASynchrone(user_id,0,sizeptr1>>buffer_asynchron->ulDecalBytes,ptr1);

	DSoundTry(IDirectSoundBuffer_Unlock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2));

    if (par)
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,par,TRUE);
    else
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,&C_SAMPLEPARAM_NEUTRE,TRUE);

    DSoundTry(IDirectSoundBuffer_Play(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,0,DSBPLAY_LOOPING));

    if (par)
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,par,TRUE);
    else
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,&C_SAMPLEPARAM_NEUTRE,TRUE);

    SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);

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

//buffer synchrone= simulation par petit buffer asynchrone
long CreateNewBufferExSynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SampleParam* par,long user_id)
{
	SND_tdstFormat		stFormatSynchrone;
	SND_tdstCallback	stCallbackSynchrone;
	long				id_buffer;

	//aucune différence entre synchrone et asynchrone
	memcpy(&stFormatSynchrone,pformat,sizeof(stFormatSynchrone));
	stFormatSynchrone.uFormat.stPCM.ulNbSamples=stFormatSynchrone.uFormat.stPCM.ulFreq*200/1000;
	memcpy(&stCallbackSynchrone,pCallback,sizeof(stCallbackSynchrone));

	id_buffer=CreateNewBufferExAsynchrone(&stFormatSynchrone,&stCallbackSynchrone,par,user_id);

	if (id_buffer!=C_PLAY_FAILED)
		((p_tdstBufferClient)id_buffer)->eType=BUFFER_SYNCHRONE;

	return id_buffer;
/*
	PCMWAVEFORMAT pcmwf;
    DSBUFFERDESC dsbdesc;
    HRESULT res;
	DWORD sizeptr1,sizeptr2;
	LPVOID ptr1,ptr2;
    p_tdstBufferClient buffer;
	tdstBufferClientAsynchrone *buffer_asynchron;
	char texte[80];

	if (!driver_SND_OK)
		return C_PLAY_FAILED;

    buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient)); 
	buffer_asynchron=&buffer->uParam.stAsynchrone;

    SNDLST2_M_DynamicInitElement(buffer);
    buffer->lUserId=user_id;
    buffer->uPseudoRes.pstPtr=&(buffer->tdstPseudoRes);
    buffer_asynchron->ulSamples=pformat->uFormat.stPCM.ulFreq*100/1000;//pour 1/10 sec. d'autonomie
    buffer_asynchron->ulDecalBytes=((pformat->uFormat.stPCM.uwResolution==8) ? 0 : 1) + ((pformat->uFormat.stPCM.uwNbChannels==1) ? 0 : 1);
    buffer_asynchron->pfnCallback=pCallback->uCallback.CallbackSynchrone;
    buffer_asynchron->ulNextToCompute=0;
    buffer->ulTimeNextToCompute=0;
	buffer->dLastDeltaT=0.0;
	buffer->bPaused=FALSE;
    //--
    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.ulFreq=pformat->uFormat.stPCM.ulFreq;
    buffer->tdstPseudoRes.uRes.stSample.uwResolution=pformat->uFormat.stPCM.uwResolution;
    buffer->tdstPseudoRes.uRes.stSample.uwNbChannels=pformat->uFormat.stPCM.uwNbChannels;
    buffer->tdstPseudoRes.uRes.stSample.eZip=SAMPLE_PCM;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferAttack=NULL;
    buffer->tdstPseudoRes.uRes.stSample.uData.stMem.iSizeAttack=0;

	// Set up wave format structure.
	memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
	pcmwf.wf.wFormatTag=WAVE_FORMAT_PCM;
	pcmwf.wf.nChannels=pformat->stPCM.uwNbChannels;
	pcmwf.wf.nSamplesPerSec=pformat->uFormat.stPCM.ulFreq;
	pcmwf.wBitsPerSample=pformat->uFormat.stPCM.uwResolution;
	pcmwf.wf.nBlockAlign=pcmwf.wf.nChannels*pcmwf.wBitsPerSample/8;
	pcmwf.wf.nAvgBytesPerSec=pcmwf.wf.nSamplesPerSec*pcmwf.wf.nBlockAlign;
	// Set up DSBUFFERDESC structure.
	memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
	dsbdesc.dwSize=sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags=DSBCAPS_CTRLDEFAULT|DSBCAPS_LOCSOFTWARE; 
    dsbdesc.dwBufferBytes=buffer_asynchron->ulSamples<<buffer_asynchron->ulDecalBytes;
	dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;
	// create buffer
	if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject
            ,&dsbdesc
            ,&buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
            ,NULL))!=DS_OK)
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
	}
	DSoundTry(IDirectSoundBuffer_Lock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,dsbdesc.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0));

	pCallback->uCallback.CallbackSynchrone(user_id,0,sizeptr1>>buffer_asynchron->ulDecalBytes,ptr1);

	DSoundTry(IDirectSoundBuffer_Unlock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2));

    if (par)
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,par);
    else
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,&C_SAMPLEPARAM_NEUTRE);

    DSoundTry(IDirectSoundBuffer_Play(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,0,DSBPLAY_LOOPING));

    if (par)
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,par);
    else
	    set_param_DSBuffer(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,buffer->uPseudoRes,&C_SAMPLEPARAM_NEUTRE);

    SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);

    return (long)buffer;
#else
    return C_PLAY_FAILED;
#endif
	*/
}

void SND_fn_vSetParamBufferSample(long id_buffer,SampleParam *par)
{
    if (par!=NULL)
		set_param_DSBuffer(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
			,((p_tdstBufferClient)id_buffer)->uPseudoRes
			,par
			,TRUE);
}

void SND_fn_vDeleteBufferSample(long id_buffer)
{
#ifndef DISABLE_WAVE
	if (id_buffer!=C_PLAY_FAILED)
	{
		SNDLST2_M_DynamicIsolate((p_tdstBufferClient)id_buffer);
		DSoundTry(IDirectSoundBuffer_Release(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop));
		SND_fn_vFreeSnd((void*)id_buffer);
	}
#endif
}

void SND_fn_vPauseBufferSample(long buffer)
{
	DWORD write;

	if (
		(buffer!=C_PLAY_FAILED)
		&& !((p_tdstBufferClient)buffer)->bPaused
		)
	{
		((p_tdstBufferClient)buffer)->bPaused=TRUE;	
		DSoundTry(IDirectSoundBuffer_GetCurrentPosition(((p_tdstBufferClient)buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
			,&((p_tdstBufferClient)buffer)->pos_when_paused,&write));
		DSoundTry(IDirectSoundBuffer_Stop(((p_tdstBufferClient)buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop));
	}
}

void SND_fn_vResumeBufferSample(long buffer)
{
	if (buffer!=C_PLAY_FAILED)
	{
	if (((p_tdstBufferClient)buffer)->bPaused)
	{
		DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((p_tdstBufferClient)buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
			,((p_tdstBufferClient)buffer)->pos_when_paused));
		DSoundTry(IDirectSoundBuffer_Play(((p_tdstBufferClient)buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,0,DSBPLAY_LOOPING));
		DSoundTry(IDirectSoundBuffer_SetCurrentPosition(((p_tdstBufferClient)buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
			,((p_tdstBufferClient)buffer)->pos_when_paused));

		//raj de l'horloge d'interpolation
		((p_tdstBufferClient)buffer)->ulTimeNextToCompute=timeGetTime();

		((p_tdstBufferClient)buffer)->bPaused=FALSE;	
	}
	}
}

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

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:
		//aucune différence entre synchrone et asynchrone
		return CreateNewBufferExSynchrone(pformat,pCallback,par,user_id);
	case BUFFER_STACK:
		return CreateNewBufferExStack(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
SndBool SND_CALL SND_fn_bCanFreeDataSample(void)
{
	return TRUE;
}
#endif


//---pour test
__declspec(dllexport) BOOL SND_CALL SND_fn_bTestSnd_DX2D(void);

BOOL SND_CALL SND_fn_bTestSnd_DX2D(void)
{
    return FALSE;
}

tdstBufferClientList buffers_clients_sauv;
BOOL bReleaseDone=FALSE;
BUFFER_DISPO* buffer_saved[NB_VOICE];
int nb_buffer_saved=0;


/*--------------------------------------------------
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 SND_fn_vForceReleaseDriverSample(void)
{
	snd_assert(FALSE);
}

void SND_fn_vForceRestoreDriverSample(void)
{
	snd_assert(FALSE);
}

/*--------------------------------------------------
relache-reprise a chaud des drives (cas ou cela est automatique)
---------------------------------------------------------*/
void SND_fn_vReleaseDriverSample(void)
{
	//rien à faire, automatique pour Directx2D
}

void SND_fn_vRestoreDriverSample(void)
{
	//rien à faire, automatique pour Directx2D
}

//---------------------------------------------------------
//	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;
	DWORD play,write;

	if (voie==C_PLAY_FAILED)
		return SND_C_POS_UNKNOWN;

	buffer=(BUFFER_DISPO*)voie;

	if (test_voice_is_active(buffer))
	{
		if (buffer->Loop && buffer->is_looping)
		{
			DSoundTry(IDirectSoundBuffer_GetCurrentPosition(buffer->Loop,&play,&write));
			//conv->ech.
			play=(buffer->dwSizeAttack+play)>>(
				((buffer->uRes.pstPtr->uRes.stSample.uwResolution==8)?0:1)
				+((buffer->uRes.pstPtr->uRes.stSample.uwNbChannels==1)?0:1));

			return M_DoubleToRealSnd((double)play/buffer->uRes.pstPtr->uRes.stSample.ulFreq);
		}
		else
			if (buffer->Attack && !buffer->is_looping)
			{
				DSoundTry(IDirectSoundBuffer_GetCurrentPosition(buffer->Attack,&play,&write));
				//conv->ech.
				play=play>>(
					((buffer->uRes.pstPtr->uRes.stSample.uwResolution==8)?0:1)
					+((buffer->uRes.pstPtr->uRes.stSample.uwNbChannels==1)?0:1));

				return M_DoubleToRealSnd((double)play/buffer->uRes.pstPtr->uRes.stSample.ulFreq);
			}
			else
				return SND_C_POS_UNKNOWN;
	}
	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;

	if (voie==C_PLAY_FAILED)
		return SND_C_LENGTH_UNKNOWN;

	buffer=(BUFFER_DISPO*)voie;

	if (buffer->is_playing)
		return 0x100000;
	else
		return SND_C_LENGTH_UNKNOWN;
}


//*************************************************************
//  Renvoie la position en sec (SndReal) du sample courament audible dans le buffer
//	!!!!! La valeur retournée évolue entre 0 et T (durée du buffer)
//			Il ne doit jamais y avoir de "sauts arrières" sinon le client ne
//			pourra faire la différence entre ce saut et un bouclage complet
//			soit une erreur de positionnement de T (une boucle complète)
//
//	La seule valeur sure pour la synchro SON est ulNextToCompute
//	Entre 2 remise à jour de ulNextToCompute, on suppose que le son évolue
//		de manière similaire au temps vrai (utilisation de timGetTime).
//	A chaque remise à jour de ulNextToCompute, on corrige l'erreur d'interpolation
//		en s'assurant qu'on ait jamais de petit saut de rattrapage en arrière.
//*******************************************************************
SndReal SND_fn_rGetPosBufferSample(long id_buffer)
{
    tdstBufferClient *buffer;
	DWORD			lT;
	double			deltaT;
	double			rT;

	if (id_buffer==C_PLAY_FAILED)
		return SND_C_POS_UNKNOWN;

	buffer=(tdstBufferClient*)id_buffer;

	if (!buffer->bPaused)
	{
		//deltaT=temps vrai entre maintenant et le dernier sample calculé
		lT=timeGetTime();
		if (buffer->ulTimeNextToCompute)
			deltaT=(double)(lT-buffer->ulTimeNextToCompute)/1000.0;
		else
			deltaT=0.0;

		if (deltaT>buffer->dLastDeltaT)
		{
			buffer->dLastDeltaT=deltaT;
			if (buffer->dLastDeltaT>=(double)buffer->uParam.stAsynchrone.ulSamples/buffer->tdstPseudoRes.uRes.stSample.ulFreq)
			{//gros problème de rebouclage=mega ramante
				buffer->dLastDeltaT=0.0;
			}
		}
	}

	//rT=dernière valeur sure (ulNextToCompute) + interpolation temporelle (dLastDeltaT)
	rT=(double)buffer->uParam.stAsynchrone.ulNextToCompute/buffer->tdstPseudoRes.uRes.stSample.ulFreq
		+buffer->dLastDeltaT;

	//modulo T (durée du buffer)
	while (rT>=(double)buffer->uParam.stAsynchrone.ulSamples/buffer->tdstPseudoRes.uRes.stSample.ulFreq)
		rT-=(double)buffer->uParam.stAsynchrone.ulSamples/buffer->tdstPseudoRes.uRes.stSample.ulFreq;

	return M_DoubleToRealSnd(rT);
}


//-----------------------------------------------------
//	Retourne le label désignant la target associée à la DLL
//	Entrée:name=cible de 256 char
//-----------------------------------------------------
void SND_fn_vGetTargetLabelSample(char* name,int nb_char)
{
	strncpy(name,"x2",nb_char);
}

void SND_fn_vSetupTargetSample(void)
{
//	MessageBox(HWND_DESKTOP,"Directx2D version","Sound Module",MB_OK);
	//rien
}

SndBool SND_fn_bCanSetupTargetSample(void)
{
	return FALSE;
}

//-------------------------------------------------
// Fonction de synchro moteur
//	ne sert à rien pour DX2D
//-------------------------------------------------
void SND_fn_vSynchroSample()
{
}

///////////////////////////////////////////////////////
// NAME: SND_fn_vSetNbVoiceWishedSxdWave
//
// ROLE: modify value for number of voices stored in INI file
// AUTHOR: Stéphane Ronse
///////////////////////////////////////////////////////
void SND_fn_vSetNbVoiceWishedSample(long nb)
{
	char texte[256];

	sprintf(texte,"%d",nb);

	if (SND_fn_bCanSetOptions())
	{
		SND_fn_vSetOptions(DX2D_SECTION,DX2D_NBVOICES_KEY,texte);
	}
	else
		WritePrivateProfileString(DX2D_SECTION,DX2D_NBVOICES_KEY,texte,INI_FILENAME);
}

///////////////////////////////////////////////////////
// NAME: SND_fn_lGetNbVoiceWishedSxdWave
//
// ROLE: returns value for SNDVIRT's number of voices
//       (checked value found in .ini or estimated optimal value)
// AUTHOR: Stéphane Ronse
///////////////////////////////////////////////////////
long SND_fn_lGetNbVoiceWishedSample()
{
	char texte[256];
	long nb;

	if (SND_fn_bCanSetOptions())
	{
		if (SND_fn_bGetOptions(DX2D_SECTION,DX2D_NBVOICES_KEY,texte,sizeof(texte)))
			sscanf(texte,"%d",&nb);
		else
		{
			nb=SND_fn_lGetNbVoiceOptimumSample();
			SND_fn_vSetNbVoiceWishedSample(nb);
		}
	}
	else
	{
		if (GetPrivateProfileString(DX2D_SECTION,DX2D_NBVOICES_KEY,"8",texte,sizeof(texte),INI_FILENAME))
			sscanf(texte,"%d",&nb);
		else
		{
			nb=SND_fn_lGetNbVoiceOptimumSample();
			SND_fn_vSetNbVoiceWishedSample(nb);
		}
	}

	return nb;
}

///////////////////////////////////////////////////////
// NAME: SND_fn_lGetNbVoiceOptimumSxdWave
//
// ROLE: returns best value for SNDVIRT's number of voices
//       (always estimated optimal value)
// AUTHOR: Stéphane Ronse
// MODIFICATIONS:
///////////////////////////////////////////////////////
long SND_fn_lGetNbVoiceOptimumSample()
{
	return 8;
}


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;
}
