/*---------------------------------------------------------------
FILE:   client3d.c
ROLE:   management of client buffers for DSound3d

MODIFICATIONS: 
     - J.Vibert - 10/12/98 - small modif on errors returned
     - J.Vibert - 07/12/98 - test on StaticBuffer+StreaminigBuffer in DSCAPS test
							+ dsound buffer pointers checked
     - J.Vibert - 01/12/98 - extracted from sndxdwav.c
----------------------------------------------------------------*/

#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
#include "SNDinc.h"
#include "sndthrd.h"

#include "sndxd.h"
#include "sndres.h"
#include "sndhardw.h"

#if defined(_DLL_COMPILATION_MODE) && defined(_DLL_MODULE) 
#include "snddll.h"
#endif

#include "priv3dfl.h"

//to debug
#ifdef DBG_NB_BUF
extern int dbgBuffAtt,dbgBuffLoop,dbgBuff,dbgBuffExt;
#endif

#if (defined(DBG_NB_BUF)&&!defined(USE_DEBUGFILE))
#undef DBG_NB_BUF
#endif


extern long          vol_2_hth_dB[128];
extern LPDIRECTSOUND lpDirectSoundObject;
extern SndBool		 driver_SND_OK;
extern SndBool		 bReleasedForced;

//	M_AdaptVolume macro:
//	compute SND_volume to get DS_volume
//	Due to mixing level compatibility with WinMM DLL, Stereo ClientBuffers should twice as loud as any other buffers
#define M_AdaptVolume(SND_volume,iNbChannels,bClientBuffer)	\
( (((int)iNbChannels==2) && bClientBuffer) ? (unsigned char)SND_volume : (unsigned char)(SND_volume/2))


//buffers clients
/*
typedef struct _tdstStackElement *p_tdstStackElement;
SNDLST2_M_DynamicUseListOf(p_tdstStackElement);
typedef struct _tdstStackElement {

} tdstStackElement;
*/
typedef struct _tdstBufferClientStack {
	td_pfn_vBufferClientPoped pfnCallback;
} tdstBufferClientStack;

typedef struct _tdstBufferClientAsynchrone {
    unsigned long				ulSamples;		//size of buffer (in samples)
    unsigned long				ulNextToCompute;//id of the next sample to compute
    td_pfn_vRefreshBufferClient pfnCallback;    //callback function for client buffer
} tdstBufferClientAsynchrone;

typedef struct _tdstBufferClient *p_tdstBufferClient;

SNDLST2_M_DynamicUseListOf(p_tdstBufferClient);

typedef struct _tdstBufferClient {
	SND_tduTypeCallback		eType;
    long                    lVoice;				//associated sample voice
	//SndBool					bPaused;//TRUE si buffer paused
	unsigned long           dwFlags;			//FLAG_PAUSED|FLAG_VIRTUAL//FLAG_SETVIRTFREQ
	DWORD					pos_when_paused;
    long					lUserId;		
    tduRefResDX3D			uPseudoRes;			//uPseudoRes=&tdstPseudoRes
    tdstBlockResourceMemDX3D	tdstPseudoRes;		//pseudo resource to mix samples
    unsigned long			ulDecalBytes;		//nb_sample<<buffer->ulDecalBytes=nb_bytes
	union {
		tdstBufferClientAsynchrone	stAsynchrone;
		tdstBufferClientStack		stStack;
	} uParam;
	DWORD					ulTimeNextToCompute;//time measured when updating
	double					dLastDeltaT;		//last Dt used to extrapolate (GetPos)

    unsigned long           ulFreq; //added 02/09/98 to restore frequency if needed after a forceRestore
    SNDLST2_M_DynamicElementDeclaration(p_tdstBufferClient)
} tdstBufferClient;

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

static tdstBufferClientList buffers_clients;
//tdstBufferClientList buffers_clients;
/*
typedef struct _tdstBufferClient *p_tdstBufferClient;
SNDLST2_M_DynamicUseListOf(p_tdstBufferClient);
typedef struct _tdstBufferClient tdstBufferClient;
struct _tdstBufferClient  {
    tduRefResDX3D               uPseudoRes;
    tdstBlockResourceMemDX3D    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;
	DWORD						ulTimeNextToCompute;//temps mesuré au moment de la remise à jour de ulNextToCompute
	double						dLastDeltaT;		//dernier Dt utilisé pour l'extrapolation de GetPos
	BOOL						paused;
    SNDLST2_M_DynamicElementDeclaration(p_tdstBufferClient)
};

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

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

	//jvdbg("refresh async");
	if (buffer_client==NULL)
		return; //04/12/98
	if (buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop==NULL)
		return; //04/12/98

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

	    IDirectSoundBuffer_GetCurrentPosition(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,&playptr,&writeptr);
        playptr=(playptr>>buffer_client->ulDecalBytes)<<buffer_client->ulDecalBytes;
        nbbytes_to_calcul=playptr-(buffer_asynchron->ulNextToCompute<<buffer_client->ulDecalBytes);

        if ((long)nbbytes_to_calcul<0) nbbytes_to_calcul+=buffer_asynchron->ulSamples<<buffer_client->ulDecalBytes;
        
		buffer_client->dLastDeltaT-=(double)(nbbytes_to_calcul>>buffer_client->ulDecalBytes)/buffer_client->tdstPseudoRes.uRes.stSample.ulFreq;
		if (buffer_client->dLastDeltaT<0.0)
			buffer_client->dLastDeltaT=0.0;

        if (nbbytes_to_calcul)
        {
			result=
	        IDirectSoundBuffer_Lock(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
                ,buffer_asynchron->ulNextToCompute<<buffer_client->ulDecalBytes,nbbytes_to_calcul
                ,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0);
			while (result==DSERR_BUFFERLOST)
			{
				result=IDirectSoundBuffer_Restore(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
				if (result!=DSERR_BUFFERLOST)
					result=
						IDirectSoundBuffer_Lock(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
						,buffer_asynchron->ulNextToCompute<<buffer_client->ulDecalBytes,nbbytes_to_calcul
						,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0);
			}
			if (result != DS_OK)
				DebugBreak();

            if (sizeptr1!=nbbytes_to_calcul)
            {//wrap out

				(*(buffer_asynchron->pfnCallback))(
                    buffer_client->lUserId
                    ,buffer_asynchron->ulNextToCompute
                    ,sizeptr1>>buffer_client->ulDecalBytes
                    ,ptr1);
                snd_assert(ptr2);
                (*(buffer_asynchron->pfnCallback))(
                    buffer_client->lUserId
                    ,0
                    ,sizeptr2>>buffer_client->ulDecalBytes
                    ,ptr2);
                snd_assert(sizeptr2==(nbbytes_to_calcul-sizeptr1));
                buffer_asynchron->ulNextToCompute=sizeptr2>>buffer_client->ulDecalBytes;
            }
            else
            {
                (*(buffer_asynchron->pfnCallback))(
                    buffer_client->lUserId
                    ,buffer_asynchron->ulNextToCompute
                    ,sizeptr1>>buffer_client->ulDecalBytes
                    ,ptr1);
                buffer_asynchron->ulNextToCompute+=nbbytes_to_calcul>>buffer_client->ulDecalBytes;
                if (buffer_asynchron->ulNextToCompute==buffer_asynchron->ulSamples) 
                    buffer_asynchron->ulNextToCompute=0;
            }

	        IDirectSoundBuffer_Unlock(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2);
			buffer_client->ulTimeNextToCompute=timeGetTime();
        }
}

// we manage a stack of buffers to be played one after another
void refresh_buffer_stack(p_tdstBufferClient buffer)
{
}


void updateClients()
{
	p_tdstBufferClient buffer_client;
	int i;

   //--- buffers clients
    //update buffers clients
    SNDLST2_M_DynamicForEachElementOf(&buffers_clients.pList,buffer_client,i)
    {
		if (!(buffer_client->dwFlags & FLAG_PAUSED)) //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;
			}
		}
    }
}

void fn_vSetVirtualVolume(unsigned long *pdwFlags, unsigned char ucVolume);
void SND_fn_vSetParamBufferSxdWave(long id_buffer,SoundParam *par)
{
#ifndef DISABLE_WAVE
    p_tdstBufferClient buffer;
	DWORD dwPitch;
	unsigned char	ucDSvolume;

	if ((id_buffer == -1)||(id_buffer == 0)) return;

	buffer = (p_tdstBufferClient)id_buffer;
	if (buffer->dwFlags&FLAG_VIRTUAL)
	{
		//jvdbg("SetParamBufferSxdWav (virtual)");
		buffer->dwFlags|=FLAG_SETVIRTFREQ;
		if (par)
		{
			fn_vSetVirtualVolume(&buffer->dwFlags, par->ucVol);
			dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(buffer->tdstPseudoRes.uRes.stSample.ulFreq));
		} else
		{
			fn_vSetVirtualVolume(&buffer->dwFlags, 127);
			dwPitch=buffer->tdstPseudoRes.uRes.stSample.ulFreq;
		}
		buffer->ulFreq=dwPitch;

		return;
	}

	if (driver_SND_OK == FALSE)
		return ;

	if (buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop==NULL)
		return; //04/12/98
	

	//jvdbg("SetParamBufferSxdWav");

	if (par) 
	{
		ucDSvolume=M_AdaptVolume(par->ucVol
			,buffer->tdstPseudoRes.uRes.stSample.uwNbChannels
			,TRUE);
		IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[ucDSvolume]);
		dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(buffer->tdstPseudoRes.uRes.stSample.ulFreq));
	} 
	else 
	{
		ucDSvolume=M_AdaptVolume(127
			,buffer->tdstPseudoRes.uRes.stSample.uwNbChannels
			,TRUE);
		IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[ucDSvolume]);
		dwPitch=buffer->tdstPseudoRes.uRes.stSample.ulFreq;
	}
	IDirectSoundBuffer_SetFrequency(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,dwPitch);
#endif
}

void SND_fn_vDeleteBufferSxdWave(long id_buffer)
{
#ifndef DISABLE_WAVE
#ifdef DBG_NB_BUF
	char s[80];
#endif //DBG_NB_BUF

	//jvdbg("delete buff SxdWav");
	if (driver_SND_OK == FALSE)
		return ;
	
	//jvdbg("delete2 buff SxdWav");

#ifdef DBG_NB_BUF
		dbgBuffExt--;
		sprintf(s,"  delete=>ext %d",dbgBuffExt);
		//jvdbg(s);
#endif //DBG_NB_BUF

	if ((id_buffer == -1)||(id_buffer == 0)) return;

    SND_fn_vEnterCriticalSectionThreadSnd();

    SNDLST2_M_DynamicIsolate((p_tdstBufferClient)id_buffer);

	if (!(((p_tdstBufferClient)id_buffer)->dwFlags&FLAG_VIRTUAL)) //forceRelease 01/09/98
	{
		if (((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop) //precaution 01/09/98
		{
			IDirectSoundBuffer_Stop(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
			IDirectSoundBuffer_Release(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
			((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop=NULL;
		}
	}
    SND_fn_vFreeSnd((void*)id_buffer);

    SND_fn_vQuitCriticalSectionThreadSnd();
#endif //DISABLE_WAVE

}

void SND_fn_vPauseBufferSxdWave(long id_buffer)
{
#ifndef DISABLE_WAVE
	if ((driver_SND_OK == FALSE)||id_buffer==0) { //id_buffer: 04/12/98
		return ;
	}
	//jvdbg("pause buff SxdWav");
	if (
		(id_buffer!=C_PLAY_FAILED)
		&& (!((p_tdstBufferClient)id_buffer)->dwFlags&FLAG_PAUSED) //bPaused
		)
	{
		//((p_tdstBufferClient)id_buffer)->bPaused=TRUE;	
		((p_tdstBufferClient)id_buffer)->dwFlags|=FLAG_PAUSED;
		if (!((p_tdstBufferClient)id_buffer)->dwFlags&FLAG_VIRTUAL)
		{
			if (((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop!=NULL) //04/12/98
				IDirectSoundBuffer_Stop(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
		}
	}
#endif
}

void SND_fn_vResumeBufferSxdWave(long id_buffer)
{
#ifndef DISABLE_WAVE

	if (driver_SND_OK == FALSE) {
		return ;
	}
	//jvdbg("resume buff SxdWav");
	if (id_buffer!=C_PLAY_FAILED)
	{
		if (((p_tdstBufferClient)id_buffer)->dwFlags&FLAG_PAUSED) //bPaused)
		{
			if (! (((p_tdstBufferClient)id_buffer)->dwFlags&FLAG_VIRTUAL))
				if (((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop!=NULL) //04/12/98
					IDirectSoundBuffer_Play(((p_tdstBufferClient)id_buffer)->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,0,DSBPLAY_LOOPING);

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

			((p_tdstBufferClient)id_buffer)->dwFlags&=~FLAG_PAUSED; //bPaused=FALSE;	
		}
	}
#endif
}

void* SND_fn_pvGetPtrBufferSxdWave(long id_buffer)
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return NULL;
	}
#endif
    return NULL;
}


SndReal SND_fn_rGetPosBufferSxdWave(long id_buffer)
{
#ifndef DISABLE_WAVE
    tdstBufferClient *buffer;
	DWORD			lT;
	double			deltaT;
	double			rT;

	if (driver_SND_OK == FALSE) {
		return SND_C_POS_UNKNOWN;
	}

	if (id_buffer==C_PLAY_FAILED)
		return SND_C_POS_UNKNOWN;

	buffer=(tdstBufferClient*)id_buffer;

	//if (!buffer->bPaused)
	if (!(buffer->dwFlags&FLAG_PAUSED))
	{
		//deltaT=real time between now and the last computed sample
		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)
			{//big looping problem => slow slow slow...
				buffer->dLastDeltaT=0.0;
			}
		}
	}

	//rT=last coherent value (ulNextToCompute) + time interpolation (dLastDeltaT)
	rT=(double)buffer->uParam.stAsynchrone.ulNextToCompute/buffer->tdstPseudoRes.uRes.stSample.ulFreq
		+buffer->dLastDeltaT;

	//modulo T (duration of 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);
#endif
	return SND_C_POS_UNKNOWN;
}

/*---------------------------------------------------------------------------
FUNCTION: fn_lCreateDxBufferForClientBuffer
ROLE:     Create directsound buffer for client buffer
		  (for internal use only)
MODIFICATIONS:
		  01/09/98 - Joel Vibert - isolated from CreateNewBufferExAsynchrone
---------------------------------------------------------------------------*/
#ifndef DISABLE_WAVE
SndBool fn_bFreeBuffer(void);
long fn_lCreateDxBufferForClientBuffer(p_tdstBufferClient buffer, SoundParam* par, tdstBufferClientAsynchrone *buffer_asynchron)
{
	DWORD sizeptr1,sizeptr2;
	LPVOID ptr1,ptr2;
	DWORD  dwPitch;
	PCMWAVEFORMAT pcmwf;
	DSCAPS dscaps; //to test available buffers
    DSBUFFERDESC dsbdesc;
    HRESULT res;
#ifdef USE_DEBUGFILE
	char s[150];
#endif
	unsigned char	ucDSvolume;

	// Set up wave format structure.
	memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
	pcmwf.wf.wFormatTag=WAVE_FORMAT_PCM;
	pcmwf.wf.nChannels=buffer->tdstPseudoRes.uRes.stSample.uwNbChannels;   //pformat->uFormat.stPCM.uwNbChannels;
	pcmwf.wf.nSamplesPerSec=buffer->tdstPseudoRes.uRes.stSample.ulFreq;    //pformat->uFormat.stPCM.ulFreq;
	pcmwf.wBitsPerSample=buffer->tdstPseudoRes.uRes.stSample.uwResolution; //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
#ifdef AVOID_FOCUS_PB
		|DSBCAPS_GLOBALFOCUS
#endif
		; 
    dsbdesc.dwBufferBytes=buffer_asynchron->ulSamples<<buffer->ulDecalBytes;
	dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;

		//memset(&dscaps,0,sizeof(DSCAPS));
		dscaps.dwSize=sizeof(DSCAPS);
		res=IDirectSound_GetCaps(lpDirectSoundObject,&dscaps);
		if (res!=DS_OK)
		{
#ifdef USE_DEBUGFILE
			jvdbg("ERROR ON GETCAPS:");
			GetErrorDSound(res,s,sizeof(s));
			jvdbg(s);
#endif
		}// else break;
	//}
	

	if (res==DS_OK)
	{
#ifdef USE_DEBUGFILE
		jvdbg("getcaps=>ok:");
		sprintf(s,"MxSt=%ld; FrMStr=%ld; FrMSt=%ld; FrStr=%ld; FrST=%ld",
			dscaps.dwMaxHw3DStaticBuffers,
			dscaps.dwFreeHwMixingStreamingBuffers,
			dscaps.dwFreeHwMixingStaticBuffers,
			dscaps.dwFreeHw3DStreamingBuffers,
			dscaps.dwFreeHw3DStaticBuffers
			);
		jvdbg(s);
#endif
		if (dscaps.dwMaxHw3DStaticBuffers<=0) //2D card=>emulation
			dscaps.dwFreeHw3DStaticBuffers=CRITICAL_BUFF_NB+2; //dummy value to simulate free buffers			
		else
		{
			#ifdef SIMU_CRITICAL_BUFF_NB
			dscaps.dwFreeHw3DStaticBuffers-=SIMU_BUFF_NB;
			if ((long int)dscaps.dwFreeHw3DStaticBuffers<0) dscaps.dwFreeHw3DStaticBuffers=0;
			#endif
			if ((dscaps.dwFreeHwMixingStreamingBuffers<1)&& //no available buffer
			    (dscaps.dwFreeHwMixingStaticBuffers<1))
			{
				if (fn_bFreeBuffer()==FALSE) //free one or two buffer(s) from buffer_dispo
				return C_PLAY_FAILED;
			}
		}
	}

	// create buffer
	if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject
            ,&dsbdesc
            ,&buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
            ,NULL))==DS_OK) {
#ifdef DBG_NB_BUF
		dbgBuffExt++;
		sprintf(s,"  Create ext=> ext %d",dbgBuffExt);
		jvdbg(s);
#endif //DBG_NB_BUF

		IDirectSoundBuffer_Lock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,dsbdesc.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0);
		//pCallback->uCallback.CallbackASynchrone
		buffer_asynchron->pfnCallback(buffer->lUserId,0,sizeptr1>>buffer->ulDecalBytes,ptr1);
		IDirectSoundBuffer_Unlock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,ptr1,sizeptr1,ptr2,sizeptr2);
		
		IDirectSoundBuffer_Play(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,0,DSBPLAY_LOOPING);

		if (par) {
			ucDSvolume=M_AdaptVolume(par->ucVol,buffer->tdstPseudoRes.uRes.stSample.uwNbChannels,TRUE);
			IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[ucDSvolume]);
			dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(buffer->tdstPseudoRes.uRes.stSample.ulFreq));
		}
		else {
			ucDSvolume=M_AdaptVolume(127,buffer->tdstPseudoRes.uRes.stSample.uwNbChannels,TRUE);
			IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[ucDSvolume]);
			dwPitch=buffer->tdstPseudoRes.uRes.stSample.ulFreq;
		}
		IDirectSoundBuffer_SetFrequency(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,dwPitch);
	} 
	else 
	{
		//GetErrorDSound(res,texte,sizeof(texte));
		//SND_fn_vDisplayError(E_uwSndSystemError,texte);
		return C_PLAY_FAILED;
	}

    return (long)buffer;
}
#endif


long CreateNewBufferExAsynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SoundParam* par,long user_id)
{
#ifndef DISABLE_WAVE
    p_tdstBufferClient buffer;
	tdstBufferClientAsynchrone *buffer_asynchron;
	long res;

	if (driver_SND_OK == FALSE)
		return C_PLAY_FAILED;

	jvdbg("CreateNewBuffExAsync");

    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->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->dwFlags=0;
	if (bReleasedForced) buffer->dwFlags|=FLAG_VIRTUAL;
    //--
    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.lSizeAttackBuff=0;

	if (bReleasedForced)
	{
		SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);
		return (long)buffer;
	} else
	{
		res=fn_lCreateDxBufferForClientBuffer(buffer, par, buffer_asynchron);
		if (res==C_PLAY_FAILED)
			return res;

		
		
			SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);
		
		return res; //(long)buffer
	}

#else //DISABLE_WAVE	
    return C_PLAY_FAILED;
#endif
}

//synchronous buffer= simulation by a small asynchronous buffer
long CreateNewBufferExSynchrone(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SoundParam* par,long user_id)
{
	SND_tdstFormat		stFormatSynchrone;
	SND_tdstCallback	stCallbackSynchrone;
	long				id_buffer;

	//jvdbg("CreateNewBuffExSync");
	//no difference between synchronous and asynchronous versions
	memcpy(&stFormatSynchrone,pformat,sizeof(stFormatSynchrone));
	stFormatSynchrone.uFormat.stPCM.ulNbSamples=stFormatSynchrone.uFormat.stPCM.ulFreq*400/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;
}

//creation of a synchronous client buffer
long CreateNewBufferExStack(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SoundParam* par,long user_id)
{
#ifndef DISABLE_WAVE
	/*
    snd_assert(pformat->eZip==SAMPLE_PCM);snd_assert(pCallback->eType==BUFFER_STACK);

	if (driver_SND_OK == FALSE)
		return C_PLAY_FAILED;

    buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient));    
	buffer_stack=&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->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->ulDecalBytes;
	dsbdesc.lpwfxFormat=(LPWAVEFORMATEX)&pcmwf;
*/
#endif
	return C_PLAY_FAILED;
}


long SND_fn_lCreateNewBufferExSxdWave(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SoundParam* par,long user_id)
{
	//jvdbg("CreateNewBuffExSxdWav");
	switch (pCallback->eType)
	{
	case BUFFER_ASYNCHRONE:
		return CreateNewBufferExAsynchrone(pformat,pCallback,par,user_id);
	case BUFFER_SYNCHRONE:
		//no difference between synchronous and asynchronous versions
		return CreateNewBufferExSynchrone(pformat,pCallback,par,user_id);
	case BUFFER_STACK:
		return CreateNewBufferExStack(pformat,pCallback,par,user_id);
	default:
		//jvdbg("CreateNewBuffExSxdWav=>FAILED");
		return C_PLAY_FAILED;
	}
}

//obsolete: emulation via new fonction
long SND_fn_lCreateNewBufferSxdWave(unsigned long nb_samples,unsigned short uwResolution,unsigned short uwNbChannels,unsigned long ulFreq,SoundParam* par,td_pfn_vRefreshBufferClient callback,long user_id)
{
#ifndef DISABLE_WAVE
	SND_tdstFormat format;
	SND_tdstCallback Callback;

	//jvdbg("createNew buff SxdWav");

	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_lCreateNewBufferExSxdWave(&format,&Callback,par,user_id);
#endif
}

long SND_fn_lGetPosBufferExSxdWave(long id_buffer)
{
	return SND_C_POS_UNKNOWN;
}

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

void fn_vForceReleaseClientBuffers(void)
{
	p_tdstBufferClient buffer_client;
	int i;

		//force release of client buffers
	SNDLST2_M_DynamicForEachElementOf(&buffers_clients.pList,buffer_client,i)
    {
		if ((buffer_client!=NULL)&&((long)buffer_client!=-1))
		{
			if (!(buffer_client->dwFlags&FLAG_VIRTUAL)) //forceRelease 01/09/98
			{
				buffer_client->dwFlags|=FLAG_VIRTUAL;
				if (buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop) //precaution 01/09/98
				{
					IDirectSoundBuffer_Stop(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
					IDirectSoundBuffer_Release(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop);
					buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop=NULL;
				} //Loop
			} //!FLAG_VIRTUAL
		} //valid buffer_client
    } //SNDLST2_M_DynamicForEachElementOf
}

void fn_vForceRestoreClientBuffers(void)
{
	p_tdstBufferClient buffer_client;
	int i;
	unsigned char ucDSvolume;

		//reactivate client buffers
	SNDLST2_M_DynamicForEachElementOf(&buffers_clients.pList,buffer_client,i)
    {
		if (fn_lCreateDxBufferForClientBuffer(buffer_client, //create directsound buffer
				NULL , //SoundParam* par, 
				&buffer_client->uParam.stAsynchrone)!=C_PLAY_FAILED)
		{
			//jvdbg("BufferClientReactivated");
			if (buffer_client->dwFlags&FLAG_PAUSED) //paused => pause again
			{
				buffer_client->dwFlags&=~(FLAG_PAUSED|FLAG_VIRTUAL);
				SND_fn_vPauseBufferSxdWave((long)buffer_client);
			}

			if (buffer_client->dwFlags&FLAG_SETVIRTVOL) /* restore volume */
			{							
				buffer_client->dwFlags&=~FLAG_SETVIRTVOL;
				//if (buffer->Attack)
				//	IDirectSoundBuffer_SetVolume(buffer->Attack,uwVolume);
				if (buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop)
				{
					ucDSvolume=M_AdaptVolume(((buffer_client->dwFlags&C_VIRTUAL_VOL_MASK)>>C_VIRTUAL_VOL_SHIFT)
						,buffer_client->tdstPseudoRes.uRes.stSample.uwNbChannels
						,TRUE);
					IDirectSoundBuffer_SetVolume(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[ucDSvolume]);
				}
			} //FLAG_SETVIRTVOL

			if (buffer_client->dwFlags&FLAG_SETVIRTFREQ) /* restore frequency */
			{							
				buffer_client->dwFlags&=~FLAG_SETVIRTFREQ;
				if (buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop)
					IDirectSoundBuffer_SetFrequency(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,
							buffer_client->ulFreq);
			} //FLAG_SETVIRTFREQ
		} //else jvdbg("Pb reactivate buffer client!!!!");
    }
}