/*
//////////////////////////////////////////////////////////////
//      SNDXDWAV.C                                                                                                 //
//////////////////////////////////////////////////////////////
//      Management of samples 3D                            //
//		Version DirectSound3D or A3D						//
//															//
//////////////////////////////////////////////////////////////
MODIFIED:
        27/01/98
		    Joel Vibert
			* Corrections in ForceReleaseDriver/ForceRestoreDriver.
			  It does not crash any longer, but it is not completely bug free yet.
			* small modification in timer + init/exit
        29/12/98 + 27/01/98
		    Stephane Ronse
			* SetMode to solve 2D/3D problem with sounds that are not located
        17/12/98
		    Joel Vibert:
			* release of timer checked
        10/12/98
		    Joel Vibert: minor modification on main volume initialization
        07/12/98
            Joel Vibert
			* test on StaticBuffer+StreamingBuffer instead of just staticBuffer in DSCAPS tests
			* init cleaned
        01/12/98
		    Joel Vibert
			* cut into pieces, to make updating easier
        30/11/98
		    Joel Vibert
			* a few error tests added+call to exit removed
        23/11/98 11:35
		    Joel Vibert
			* volume bug corrected on attack2loop (volume macro applied on dB value)
		20/11/98 12:15
		    Joel Vibert
			* snd_cpa.ini: Platform=WinNT... instead of 2..., 
			  so that it's easier to read and homogenous with directx2d
        20/11/98 10:15
		    Joel Vibert
			* bug of looping buffer offset corrected
			* correction of GetGUIDList
        16/11/98 15:45
		    Dominique Dumont
			* eax effects (define USE_EAX in SndDef.h)
        13/11/98 16:45
		    Joël Vibert:
			* Some more tests+cleaning for GUID/forceReleaseDriver
			* Some cleaning for volumes: 
				- removed SetVolume, useless and with bad parameter
				- removed useless SetParameter
			* Reset position if re-use of allocated buffers
		06/11/98 11:35
		    Joël Vibert
			* Set Doppler factor implemented
			* end of correction for memory problem
        05/11/98 19:00
		    Joël Vibert
			* memory problem corrected
			* compilation flag to allow to use the dll if the handle of the window is not correct
			  (the sounds are still played when the application loses the focus)
			* correction of default parameter for microphone creation
		04/11/98 15:55
		    Joël Vibert
			* End of correction for callbacks
			* Correction of bug on flags for ForceReleaseDriver 
		04/11/98
		    Joël Vibert
			* Bug corrected for callbacks (called too late on non looping sounds)
        02/11/98
		    Joël Vibert
			* Bug corrected on looping point on initial creation of looping buffer with attack
			* Bug corrected on size of attack+check sizes when loading
        10/09/98
		    Joël Vibert
			* SND_fn_bIsDirect3DOptimum updated
		02/09/98
		    Joël Vibert
		    * ForceReleaseDriver/ForceRestoreDriver implemented (untested)			
		10/07/98 Stéphane RONSE:
			* SND_fn_vConvertResDiskToMemSxdWave could no be called outside LoadRes to 
				generate a valid BlockResourceMem (used for SND_fn_uGenerateEventPlaySound)
			* SND_fn_bLoadResScriptSxdWave and SND_fn_bLoadResBinarySxdWave only load data,
				then call SND_fn_vConvertResDiskToMemSxdWave
        03/04/98
		    Joel Vibert:
			   * SND_fn_vConvertResDiskToMemSxdWave data parameter now is
			     the address of the data and not the address of a conversion
				 parameter (untested). NB: This function was duplicated for
				 internal use when loading with scripts (will be updated later).
        28/03/98
		    Joel Vibert:
			   * max number of voices reduced if high (never reached on F1 on the Maxisound,
			     but precaution if the number of sounds increase).
        27/03/98
		    Joel Vibert:
			   * emulation accepted again on 2D cards only (8 voices by default)
			   * SND_fn_bLoadResScriptSxdWave works again
			     (untested and not working in previous version)
			   * fn_wFreeHalfBuffer removed
			   * a few modifs in defines (debugging file+no more NEW_FLAGS_MANAGEMENT...)
        26/03/98
		    Joel Vibert:
			   * new function to free directsound buffers when number of buffers is very
			     critical: fn_wFreeHalfBuffer(more tests are needed)
				 +minor modifs in fn_bFindPlayBuff
		       * warnings when compiling to prevent from using temporary code when this is not
			     wanted
	    26/03/98    
		    Joel Vibert:
			   * margin for allocation of directsound buffers set to 2 instead of 1
			     => suppresses definitively emulation on MonsterSound cards
			   * modification of default voice number for virtual voices to avoid
			     lack of buffers in F1 with the new margin (experimental value)
        25/03/98
		    Joel Vibert:
			   * default voice number for virtual voices estimated from dwMaxHw3DStaticBuffers
			   * GetCaps: dwFreeHwMixingStaticBuffers replaced by 
					dwFreeHw3DStaticBuffers; seems to have the same behaviour
					(returned value too high on monstersound=>emulated if voice number in .ini
					 is too high)
        24/03/98 
            Joel Vibert:
			   * buffers may not be filled any longer during loading if it may lead to 
			     directsound emulation.
			   * separate allocation for attacks and loops if the number of buffers is critical
			   * buffers may be created, duplicated or simply reused during play,
			     to get better performances, and avoid directsound emulation when
				 number of 3d buffers is critical.
			   * the time of access to a buffer is now taken into account when allocating
			     buffers if their number is critical, and there are now two deactivation
				 functions, to manage final deactivation and temporary allocation
				 ("cache" system).
			   * simulation of allocated buffers for tests+debugging file
*/


// INCLUDES SYSTEMES
#define INITGUID
#include <windows.h>
#include <mmsystem.h>
//#include <crtdbg.h>
#include <dsound.h>
//A3D: doesn't compile, and untested
#ifdef A3D
#include "ia3d.h"
#endif

// INCLUDES SONS
#include "SNDinc.h"
#include "sndthrd.h"
#if defined(_DLL_COMPILATION_MODE) && defined(_DLL_MODULE) 
#include "snddll.h"
#endif
#include "sndxd.h"
#include "sndres.h"
#include "sndhardw.h"
#include "sndxdwav.h"
#include "sndini.h"
#include "sndspace.h"

#ifndef DISABLE_WAVE
#include "maxi3d.h" //to free MIDI memory on maxisound cards
#endif

//###DOM
#ifdef USE_EAX
#pragma message ("--------------------------")
#pragma message ("Because you want the best")
#pragma message ("EAX is enabled!!!")
#pragma message ("--------------------------")
#include "eax.h"
#include "sndeax.h"
#endif

#include "priv3dfl.h"

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

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


//--------------------------------------------------------------------------
//							WARNINGS
//--------------------------------------------------------------------------
//macros to put any result of macro into a string
#define TOSTRING(a) #a					 //convert to string without expansion
#define STRINGVALUE(a) TOSTRING(a)       //convert to string with expansion
//concatenation of warning parameters (=>string)
#define WARNING_S(s1) "WARNING: " s1
#define WARNING_SIS(s1,a,s2) "WARNING: " s1 STRINGVALUE(a) s2

//WARNINGS when compiling (precautions)
#ifdef  USE_DEBUGFILE
#pragma message(WARNING_S("DEBUGGING FILE ACTIVATED !!!"))
#endif

#ifdef SIMU_CRITICAL_BUFF_NB
#pragma message(WARNING_SIS("SIMULATION OF ",SIMU_BUFF_NB," ALLOCATED BUFFERS !!!"))
#endif

#ifdef USE_TEST_CODE
#pragma message(WARNING_S("TEST CODE STILL IN USE !!!"))
#endif


//--------------------------------------------------------------------------
//							STRUCTURES
//--------------------------------------------------------------------------

//description of a voice
typedef struct {

		DWORD			dwFlags;					// flags (+volume index when driver released)
		unsigned long	ulFreq;						// original frequency
		unsigned long	ulVol;						// original volume

		tduRefResDX3D   uRes;						// associated resource

		LPDIRECTSOUNDBUFFER Attack;					// attack buffer (never played when looping)
        LPDIRECTSOUNDBUFFER Loop;					// looping buffer 
        LPDIRECTSOUND3DBUFFER Attack3D;				// attack buffer (never played when looping)
        LPDIRECTSOUND3DBUFFER Loop3D;				// looping buffer

		//###DOM
		#ifdef USE_EAX
		LPKSPROPERTYSET		AttackEAX;
		LPKSPROPERTYSET		LoopEAX;
		#endif
        
		DS3DBUFFER		parameters;					// last parameters sent
        float			fLastFreq;					// last frequency sent

		DWORD           dwSizeAttack;               // number of bytes of attack
        SND_td_pfn_vSoundCallback fn_callback;		// callback function
        long            par_callback;				// parameter for the callback function
		
		long            lAccessTime;				// used to allocate buffers (cache)
} BUFFER_DISPO;

typedef struct {
        BOOL  bAttack;
		BOOL  bLoop;
} tdstConvertParam;

enum {
    TIMER_OFF,
    TIMER_ON,
} eTimerState;
static BOOL bTimerInstance=FALSE;

// VARIABLES GLOBALES
#define USE_MAXREALFREQ
//#ifdef USE_MAXREALFREQ
//#undef USE_MAXREALFREQ
//#endif

#ifdef USE_MAXREALFREQ
#define MAXREALFREQ 22050
#endif

extern long			idMainMicro;

SndBool				g_b_ChooseOutputFormat=TRUE;	//TRUE if we want to impose DSound output format
int					OutputFreqChosen=22050;		// the A3D API was optimized for this frequency
int					OutputResChosen=16;			// idem
int					OutputChanChosen=2;			// idem

static unsigned long ulForcedFlags=0;			//global flags for ForceRestore
SndBool				bReleasedForced=FALSE;
LPDIRECTSOUND		lpDirectSoundObject=NULL;		//DIRECTSOUND object
DSBUFFERDESC		DSPrimBufferDesc;			// description of primary buffer
LPDIRECTSOUNDBUFFER lpDSPrimBuffer=NULL;		// primary buffer
LPDIRECTSOUND3DLISTENER lpListener3D=NULL;			// the only listener 3d (micro)
LPWAVEFORMATEX		lpWaveFormatPrimBuffer=NULL;		// primary buffer format
int					sizeoflpWaveFormatPrimBuffer;	

SndBool				stereo_reverse=TRUE;
unsigned char		ucMainVol=64;
int					nb_voices_activated=0;
BUFFER_DISPO buffer_dispo[NB_VOICES];

SND_tdstInitStruct  InitStructSaved;//copy of SND_tdstInitStruct from init

int init_DSoundChosen(SND_tdstInitStruct *pInitStructSaved);
void init_listGUID(void);
void destroy_listGUID();
LPGUID evaluate_bestGUID(char* description,int size);
LPGUID find_GUID(char* GUID_wanted,char* GUID_choosen,int size);
LPGUID auto_chooseGUID();
LPDIRECTSOUND create_DSOUND(LPGUID lpGuid,SndBool retry);
int init_DSOUND(SND_tdstInitStruct *pInitStruct,LPGUID lpGuid);
void desinit_DSOUND(LPDIRECTSOUND *lplpDirectSoundObject); //parameter 08/09/98

HRESULT DSoundTry(HRESULT res);

SndBool				driver_SND_OK = FALSE;			// indicate a good init

#ifdef A3D
LPIA3D pIA3d = NULL;
#endif


UINT snd_msg_ExitThread=0;
HANDLE handle_snd_thread=NULL;
DWORD id_snd_thread;
HANDLE hEvtQuitThread=NULL;
MMRESULT timerId;


//default rolloff
D3DVALUE g_d3dDistSatur=5.0f;
D3DVALUE g_d3dDistBackGround=DS3D_DEFAULTMAXDISTANCE;
	

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

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


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

/*---------------------------------------------------------------
FUNCTION: jvdbg
ROLE:   Send debugging info to file if USE_DEBUGFILE was defined
		(the file is never erased; strings are just written at the end of the file)
AUTHOR: Joel Vibert
----------------------------------------------------------------*/
#ifdef USE_DEBUGFILE
void jvdbg(char *s)
{
	char texte[24];
    HANDLE   f;
	char*  pfilename = "c:\\sndtest.txt"; //file name
	DWORD nb;

    f = CreateFile( pfilename, GENERIC_WRITE|GENERIC_READ ,0,NULL,OPEN_ALWAYS ,0,NULL);
	SetFilePointer(f,0,NULL,FILE_END);

	if (f==NULL) return;

	WriteFile(f,s,strlen(s),&nb,NULL);
	sprintf(texte,"at %d\n",timeGetTime());
	WriteFile(f,texte,strlen(texte),&nb,NULL);
    CloseHandle(f);
}
#else
#define jvdbg(a)
#endif


void SND_fn_vSetSoundVolumeSxdWave(unsigned char vol);


void GetErrorDSound(HRESULT err,char* texte,int size_texte)
{
        switch (err)
        {
		case DS_OK					:strncpy(texte,"No error",size_texte);
			break;
		case DSERR_ALLOCATED		:strncpy(texte,"Alloc err",size_texte);
			break;
		case DSERR_CONTROLUNAVAIL	:strncpy(texte,"Control unavail",size_texte);
			break;
		case DSERR_INVALIDPARAM		:strncpy(texte,"Invalid param",size_texte);
			break;
		case DSERR_INVALIDCALL		:strncpy(texte,"Invalid call",size_texte);
			break;
		case DSERR_GENERIC			:strncpy(texte,"Generic err",size_texte);
			break;
		case DSERR_PRIOLEVELNEEDED	:strncpy(texte,"Priority level needed",size_texte);
			break;
		case DSERR_OUTOFMEMORY		:strncpy(texte,"Out of mem",size_texte);
			break;
		case DSERR_BADFORMAT		:strncpy(texte,"Bad format",size_texte);
			break;
		case DSERR_UNSUPPORTED		:strncpy(texte,"Unsupported",size_texte);
			break;
		case DSERR_NODRIVER			:strncpy(texte,"No driver",size_texte);
			break;
		case DSERR_ALREADYINITIALIZED:strncpy(texte,"Already initialized",size_texte);
			break;
		case DSERR_NOAGGREGATION	:strncpy(texte,"Aggregation not supported",size_texte);
			break;
		case DSERR_BUFFERLOST		:strncpy(texte,"Buffer lost",size_texte);
			break;
		case DSERR_OTHERAPPHASPRIO	:strncpy(texte,"Other app has priority",size_texte);
			break;
		case DSERR_UNINITIALIZED	:strncpy(texte,"Uninit err",size_texte);
			break;
		default						:strncpy(texte,"DEFAULT err!!!",size_texte);			
		}
}


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

#ifdef A3D
BOOL CALLBACK DSEnumCallback(GUID FAR * lpGuid, LPSTR lpstrDescription, LPSTR lpstrModule, LPVOID param)
{
    HRESULT res;
	SndBool	bFound = FALSE;

	if (lpGuid == NULL) return TRUE;

	//gpguid[iNumGUID]= lpGuid;
//
	if (nb_GUIDSound)
	{
		pGUIDAvailable=SND_fn_pvReAllocSnd(pGUIDAvailable,sizeof(stGUIDAvailable)*(nb_GUIDSound+1));
	}
	else
		pGUIDAvailable=SND_fn_pvMallocSnd(sizeof(stGUIDAvailable));

	if (pGUIDAvailable==NULL) return FALSE; //precaution

	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));
//
	lpDirectSoundObject =  NULL;
	//_RPT3(_CRT_WARN,"driver %d:%s %s\n",iNumGUID,lpstrModule,lpstrDescription);
	if (A3dCreate(lpGuid,&lpDirectSoundObject,NULL)==A3D_OK)
	{
		//_RPT1(_CRT_WARN,"driver choisi:%d\n",iNumGUID);
		bFound = TRUE;
		//-- creation and management of primary buffer
		memset(&DSPrimBufferDesc,0,sizeof(DSPrimBufferDesc));
		DSPrimBufferDesc.dwSize=sizeof(DSPrimBufferDesc);
		DSPrimBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D|DSBCAPS_CTRLVOLUME;

		if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&DSPrimBufferDesc,&lpDSPrimBuffer,NULL))!=DS_OK) {
			// Can't create A3D PRIMARY BUFFER
			lpDSPrimBuffer=NULL;
			bFound = FALSE;

		} else {
			// GET THE LISTENER INTERFACE...
			if ((res = IDirectSoundBuffer_QueryInterface(lpDSPrimBuffer, &IID_IDirectSound3DListener,&lpListener3D)) != DS_OK) {
				// Can't get A3D PRIMARY BUFFER 3D INTERFACE
				IDirectSoundBuffer_Release(lpDSPrimBuffer);
				lpDSPrimBuffer = NULL;
				bFound = FALSE;
			} else {
				if (g_b_ChooseOutputFormat)
				{//prioritylevel ->possible choice of format
					if ((res=IDirectSound_SetCooperativeLevel(lpDirectSoundObject,((SND_tdstInitStruct*)param)->hMainWindow,
							//DSSCL_EXCLUSIVE ))!=DS_OK) {
						   DSSCL_PRIORITY))!=DS_OK) {
						// Cooperative Level error
						IDirectSound3DBuffer_Release(lpListener3D);
						lpListener3D = NULL;
						IDirectSoundBuffer_Release(lpDSPrimBuffer);
						lpDSPrimBuffer = NULL;
						bFound = FALSE;
					}
				}
				else
				{//non priority-level ->no choice for format
					if ((res=IDirectSound_SetCooperativeLevel(lpDirectSoundObject,((SND_tdstInitStruct*)param)->hMainWindow,DSSCL_NORMAL))!=DS_OK) {
						// Cooperative Level error
						IDirectSound3DBuffer_Release(lpListener3D);
						lpListener3D = NULL;
						IDirectSoundBuffer_Release(lpDSPrimBuffer);
						lpDSPrimBuffer = NULL;
						bFound = FALSE;
					}
				}
				if (bFound) {
					
					res = IA3d_QueryInterface(lpDirectSoundObject,&IID_IA3d,&pIA3d);
					if (res >=0) {
						// change the resource manager mode
						IA3d_SetResourceManagerMode(pIA3d,A3D_RESOURCE_MODE_NOTIFY);
					} else {
						// Can't change ressource manager mode
						IDirectSound3DBuffer_Release(lpListener3D);
						lpListener3D = NULL;
						IDirectSoundBuffer_Release(lpDSPrimBuffer);
						lpDSPrimBuffer = NULL;
						bFound = FALSE;
					}
					
				}
			}

		}
	} else {
		if (lpDirectSoundObject) {
			IDirectSound_Release(lpDirectSoundObject);
			lpDirectSoundObject = NULL;
		}
	}
	//iNumGUID++;
	nb_GUIDSound++;

    return !bFound; // return TRUE if we have not found
					// return FALSE if we have found
}

#else
void CreatePrimaryBuffer(SND_tdstInitStruct *param)
{

	HRESULT res;
	
	//lpDirectSoundObject = aDirectSoundObject[iGUID];
	if (lpDirectSoundObject) {
		//-- creation and management of primary buffer
		memset(&DSPrimBufferDesc,0,sizeof(DSPrimBufferDesc));
		DSPrimBufferDesc.dwSize=sizeof(DSPrimBufferDesc);
		DSPrimBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D|DSBCAPS_CTRLVOLUME;
			//DSBCAPS_GLOBALFOCUS => can't create primary buffer

		jvdbg("create prim buff");
		if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&DSPrimBufferDesc,&lpDSPrimBuffer,NULL))!=DS_OK) {
			// Can't create PRIMARY BUFFER
			IDirectSound_Release(lpDirectSoundObject);
			lpDirectSoundObject = NULL;

		} else {
			jvdbg("get listener interf");
			// GET THE LISTENER INTERFACE...
			if ((res = IDirectSoundBuffer_QueryInterface(lpDSPrimBuffer, &IID_IDirectSound3DListener,&lpListener3D)) != DS_OK) {
				// Can't get A3D PRIMARY BUFFER 3D INTERFACE
				IDirectSoundBuffer_Release(lpDSPrimBuffer);
				lpDSPrimBuffer = NULL;
				IDirectSound_Release(lpDirectSoundObject);
				lpDirectSoundObject = NULL;
			} else {
				jvdbg("prim buff:tt OK");
				// OK !!!
			}
		}
	}
}

/*
BOOL CALLBACK DSEnumCallback(GUID FAR * lpGuid, LPSTR lpstrDescription, LPSTR lpstrModule, LPVOID param)
{
    HRESULT res;
	DSCAPS	caps;

	SndBool	bOk = TRUE;
	aDirectSoundObject[iNumGUID] = NULL;
	
	if ((res=DirectSoundCreate(lpGuid,&aDirectSoundObject[iNumGUID],NULL))==DS_OK)
	{
		//_RPT3(_CRT_WARN,"driver %d:%s %s\n",iNumGUID,lpstrModule,lpstrDescription);

		gpguid[iNumGUID]= lpGuid;
		if (g_b_ChooseOutputFormat)
		{//prioritylevel ->choix du format possible
			if ((res=IDirectSound_SetCooperativeLevel(aDirectSoundObject[iNumGUID],((SND_tdstInitStruct*)param)->hMainWindow,DSSCL_PRIORITY))!=DS_OK) {
				bOk = FALSE;
			}
		}
		else
		{//non priority-level ->pas de choix du format
			if ((res=IDirectSound_SetCooperativeLevel(aDirectSoundObject[iNumGUID],((SND_tdstInitStruct*)param)->hMainWindow,DSSCL_NORMAL))!=DS_OK) {
				bOk = FALSE;
			}
		}
		if (bOk) {
			memset(&caps,0,sizeof(DSCAPS));
			caps.dwSize=sizeof(DSCAPS);
			if ((res = IDirectSound_GetCaps(aDirectSoundObject[iNumGUID], &caps)) == DS_OK) {
				iNumHard3DMix[iNumGUID] = caps.dwFreeHw3DAllBuffers;
			} else {
				iNumHard3DMix[iNumGUID] = 0;
			}
		} else iNumHard3DMix[iNumGUID] = 0;


		if (iNumHard3DMix[iNumGUID] > iMax3DBuffer) {
			iMax3DBuffer = 	iNumHard3DMix[iNumGUID];
			if (iMaxDriver != -1) {
				IDirectSound_Release(aDirectSoundObject[iMaxDriver]);
			}

			iMaxDriver = iNumGUID;

		} else {
			IDirectSound_Release(aDirectSoundObject[iNumGUID]);
		}
		iNumGUID++;
	}

    return TRUE;
}
*/
   
/*
void SearchBestDriver(SND_tdstInitStruct *param)
{
	if (iMaxDriver != -1) {
		//_RPT1(_CRT_WARN,"driver choisi:%d\n",iMaxDriver);
		
		CreatePrimaryBuffer(param);
	} 
}
*/
#endif

//------------------------------
//      completely deactivate a voice (Release DSBuf)
//------------------------------
void fn_vDeactivateVoice(BUFFER_DISPO* buffer)
{
#ifdef USE_DEBUGFILE
	char s[100];
#endif

	if (buffer==NULL)
	{
		jvdbg(" deactivate voice(NULL)!");
		return;
	}
#ifdef USE_DEBUGFILE
	sprintf(s,"deactivate voice %d (F%ld; A%d;L%d)", buffer-buffer_dispo,buffer->dwFlags,
		buffer->Attack!=NULL, buffer->Loop!=NULL);
	jvdbg(s);
#endif
	buffer->dwFlags = 0;
	buffer->fn_callback=NULL;
    buffer->par_callback = 0;

    //added to avoid crash if the resource changes
	//,due to initialization during 1st loading
	//(to be able to test the resource to allocate the buffer in the play function)
	buffer->uRes.pstPtr=NULL;
	buffer->lAccessTime=0;

    if (buffer->Attack)
    {
#ifdef USE_DEBUGFILE
	    sprintf(s,"  DEACTIVATE attack(%ld; buf No %d)",buffer->dwSizeAttack, buffer-buffer_dispo);
	    jvdbg(s);
#endif
        IDirectSoundBuffer_Stop(buffer->Attack);
		if (buffer->Attack3D)
		{
			IDirectSound3DBuffer_Release(buffer->Attack3D);
			buffer->Attack3D = NULL;
		}
        IDirectSoundBuffer_Release(buffer->Attack);
        buffer->Attack=NULL;
#ifdef DBG_NB_BUF
		dbgBuffAtt--; dbgBuff--;
		sprintf(s,"  DEACTIVATE attack=>%d/%d + ext %d",	dbgBuffAtt,dbgBuff,dbgBuffExt);
		jvdbg(s);
#endif //DBG_NB_BUF
    }
    if (buffer->Loop)
    {
#ifdef USE_DEBUGFILE
	    sprintf(s,"  DEACTIVATE loop(buf No %d))", buffer-buffer_dispo);
	    jvdbg(s);
#endif
        IDirectSoundBuffer_Stop(buffer->Loop);
		if (buffer->Loop3D)
		{
			IDirectSound3DBuffer_Release(buffer->Loop3D);
			buffer->Loop3D=NULL;
		}
        IDirectSoundBuffer_Release(buffer->Loop);
        buffer->Loop=NULL;
#ifdef DBG_NB_BUF
		dbgBuffLoop--; dbgBuff--;
		sprintf(s,"  DEACTIVATE loop=>%d/%d + ext %d",	dbgBuffLoop,dbgBuff,dbgBuffExt);
		jvdbg(s);
#endif //DBG_NB_BUF
    }
	//reduce nb_voice_activated
	if ((nb_voices_activated==buffer-buffer_dispo+1)&&(nb_voices_activated>0))
		nb_voices_activated--;
}

/*---------------------------------------------------------------
FUNCTION: fn_lGetAccessTime
ROLE: returns a value, to be used in buffer_dispo only,
	that always increases (and updates buffer_dispo when
	the values get too high, so that values can always be compared)
MODIFIED:
	- 24/03/98 - Joel Vibert - Created
//---------------------------------------------------------------*/
long fn_lGetAccessTime()
{
	static long cpt=0;
	int i;

	cpt++;
	 //after a very long time, shift all values to avoid pb with signed numbers+overflow
	if (cpt==0x7FFFFFFF)
	{
		for (i=0; i<NB_VOICES; i++)
		{
			if (buffer_dispo[i].lAccessTime<0x3FFFFFF)
				buffer_dispo[i].lAccessTime=0; //very old
			else buffer_dispo[i].lAccessTime-=0x3FFFFFE;
		}
		cpt=0x3FFFFFF;
	}
    return cpt;
}

/*-------------------------------------------------
FUNCTION: fn_vDeactivateVoiceTmp
ROLE: temporarily deactivate a voice
		(do not release anything, but stop the buffers)
MODIFIED:
     - 24/03/98 - Joel Vibert - created
---------------------------------------------------*/
void fn_vDeactivateVoiceTmp(BUFFER_DISPO* buffer)
{
#ifdef USE_DEBUGFILE
	char s[80];
#endif
	if (driver_SND_OK == FALSE) {
		return ;
	}
#ifdef USE_DEBUGFILE
	sprintf(s,"deactmp voice %d (F%ld; A%d;L%d)=>F%d", buffer-buffer_dispo,buffer->dwFlags,
		buffer->Attack!=NULL, buffer->Loop!=NULL, buffer->dwFlags&FLAG_BUFFS);
	jvdbg(s);
#endif

	buffer->fn_callback=NULL;
	 //only keep these flags, to keep track of what's allocated
	buffer->dwFlags &= (FLAG_BUFFS|FLAG_VIRTUAL);
    buffer->lAccessTime=fn_lGetAccessTime();

	if (buffer->dwFlags&FLAG_VIRTUAL)
	{
		//fn_vDeactivateVoice(buffer);
		return;
	}
	
    if (buffer->Attack)
    {
#ifdef USE_DEBUGFILE
	    sprintf(s,"   deactivate tmp attack(%ld; buf No %d)",buffer->dwSizeAttack, buffer-buffer_dispo);
	    jvdbg(s);
#endif
        IDirectSoundBuffer_Stop(buffer->Attack);
    }
    if (buffer->Loop)
    {
#ifdef USE_DEBUGFILE
	    sprintf(s,"   deactivate tmp loop(buf No %d)",buffer-buffer_dispo);
	    jvdbg(s);
#endif
        IDirectSoundBuffer_Stop(buffer->Loop);
    }
}


//------------------------------
//      pause a voice 
//------------------------------
void pause_voice(BUFFER_DISPO* buffer)
{
	if (driver_SND_OK == FALSE) {
		return ;
	}
	if ((buffer->dwFlags & FLAG_PAUSED) || (!(buffer->dwFlags & FLAG_PLAYING))) {
		return ;
	}
	jvdbg("pause");
	buffer->dwFlags |= FLAG_PAUSED;

	if (!(buffer->dwFlags & FLAG_LOOPING)) {
		if (buffer->Attack) IDirectSoundBuffer_Stop(buffer->Attack);
	} else	{
		if (buffer->Loop)	IDirectSoundBuffer_Stop(buffer->Loop);
	}

}

//------------------------------
//      resume a voice 
//------------------------------
void resume_voice(BUFFER_DISPO* buffer)
{
	if (driver_SND_OK == FALSE) {
		return ;
	}
	if ((!(buffer->dwFlags & FLAG_PAUSED)) || (!(buffer->dwFlags & FLAG_PLAYING))) {
		return ;
	}
	jvdbg("resume");
	buffer->dwFlags &= ~FLAG_PAUSED;

	//if (buffer->dwFlags&FLAG_VIRTUAL) return;

	if (!(buffer->dwFlags & FLAG_LOOPING))
	{
		if (buffer->Attack)
			IDirectSoundBuffer_Play(buffer->Attack,0,0,0);
	}
	else
	{
		if (buffer->Loop)
			IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING);
	}
}

// returns TRUE if the voice is being played
BOOL test_voice_is_active(BUFFER_DISPO* buffer)
{
	DWORD status;
	
	if (buffer->dwFlags & FLAG_PAUSED) return TRUE;
	if (buffer->dwFlags & FLAG_VIRTUAL)
	{
		if (buffer->uRes.pstPtr->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDLOOP)
			if (buffer->dwFlags & FLAG_PLAYING)
				return TRUE;
		return FALSE; //Attack or problem while loading
	}
	if (buffer->Attack)
	{
		if (buffer->dwFlags & FLAG_WAITINGLOOP) return TRUE;
	
		if(IDirectSoundBuffer_GetStatus(buffer->Attack,&status) == DS_OK) {
			if (status & DSBSTATUS_PLAYING) return TRUE; // voice is playing
			if (status & DSBSTATUS_BUFFERLOST) return FALSE;
		}
	}
	if (buffer->Loop)
	{
		if(IDirectSoundBuffer_GetStatus(buffer->Loop,&status) == DS_OK) {
			if (status & DSBSTATUS_PLAYING) return TRUE; // voice is playing
			if (status & DSBSTATUS_BUFFERLOST) return FALSE;
 		}
	}
	return FALSE;
}


/*---------------------------------------------------------
FUNCTION:	fn_vSetVirtualVolume
ROLE:		update of buffer volume after forceRelease
MODIFICATIONS:
			- 01/09/97 - Joel Vibert - Created
----------------------------------------------------------*/
void fn_vSetVirtualVolume(unsigned long *pdwFlags, unsigned char ucVolume)
{
	(*pdwFlags)&=~C_VIRTUAL_VOL_MASK;
	(*pdwFlags)|=((unsigned long)ucVolume)<<C_VIRTUAL_VOL_SHIFT;
	(*pdwFlags)|=FLAG_SETVIRTVOL;
}

/*---------------------------------------------------------
FUNCTION:	setVirtualparam_DSBuffer
ROLE:		update of buffer parameters after forceRelease
MODIFICATIONS:
			- 01/09/97 - Joel Vibert - Created
----------------------------------------------------------*/
void setVirtualparam_DSBuffer(BUFFER_DISPO* pBuffer, SoundParam *par)
{
	unsigned short uwVolume;
	unsigned char  ucVolume,ucDefaultVolume;
	unsigned long  *pdwFlags;

	ucDefaultVolume=(unsigned char)pBuffer->ulVol;
	pdwFlags=&pBuffer->dwFlags;

	if (par->iFlags & C_SOUNDPARAM_COEF_VOL)
	{
		// dynamic volume
		uwVolume = (short)par->ucVol;
		uwVolume *= (short)ucDefaultVolume;
		uwVolume >>=7;
		if (uwVolume>127) ucVolume=127;
		else ucVolume=(unsigned char)uwVolume;
		fn_vSetVirtualVolume(pdwFlags,ucVolume);
	} else 
	{
		if (!(par->iFlags & C_SOUNDPARAM_NO_POSITION))
		{
			fn_vSetVirtualVolume(pdwFlags,ucDefaultVolume);
		}
	}

	
	// update frequency if necessary.. 
	//dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(pBuffer->ulFreq));
	//if ((pBuffer->fLastFreq == 0.f) || (pBuffer->fLastFreq != (float)dwPitch))  
	//{
		pBuffer->fLastFreq = //(float)dwPitch;
			(float)(M_RealToDoubleSnd(par->Freq)*(double)(pBuffer->ulFreq));
	//	IDirectSoundBuffer_SetFrequency(buffer,dwPitch);
	//}
}

// unconditional update of buffer parameters
void set_param_DSBuffer(LPDIRECTSOUNDBUFFER buffer,LPDIRECTSOUND3DBUFFER interface3D,SoundParam *par,BUFFER_DISPO* pBuffer, SndBool bImmediate)
{
    unsigned char   ucDefaultVolume = (unsigned char)pBuffer->ulVol; // default volume for resource: max volume !!!
    unsigned short  uwVolume;
	RollOffParam	*pRollOff;
	SndBool			bAudible=TRUE;
	int				j;
    DWORD dwPitch;
	unsigned char	ucDSvolume;
	SndBool			b3DParameters;

	if ((par==NULL) || (par->iFlags & C_SOUNDPARAM_NO_POSITION))
		b3DParameters=FALSE;
	else
		b3DParameters=TRUE;

	//1st parameter invalid
	if (buffer==NULL)
	{
		jvdbg("setparamDS(NULL!)");
		if (pBuffer) 
		{
			if (pBuffer->dwFlags&FLAG_VIRTUAL)
			{
				/*
				if (par==NULL)
					setVirtualparam_DSBuffer(pBuffer,&C_SAMPLEPARAM_NEUTRE); 
				else */
				if (par!=NULL) //07/09/98
					setVirtualparam_DSBuffer(pBuffer,par);				
			}
		}
		return;
	}

	if (!b3DParameters)
	{//no 3D
		if (interface3D)
			IDirectSound3DBuffer_SetMode(interface3D,DS3DMODE_DISABLE,(bImmediate) ? DS3D_IMMEDIATE : DS3D_DEFERRED);
	}
	else
	{//3D parameters
		if (interface3D)
			IDirectSound3DBuffer_SetMode(interface3D,DS3DMODE_NORMAL,(bImmediate) ? DS3D_IMMEDIATE : DS3D_DEFERRED);
	}

	if (par==NULL)
	{
		fn_vSetVirtualVolume(&pBuffer->dwFlags,ucDefaultVolume); //27/01/99

		ucDSvolume=M_AdaptVolume(ucDefaultVolume
			,pBuffer->uRes.pstPtr->uRes.stSample.uwNbChannels
			,FALSE);
		IDirectSoundBuffer_SetVolume(buffer,vol_2_hth_dB[ucDSvolume]);
		return;
	}

    if (!b3DParameters)
	{//not spatially located
		if (par->iFlags & C_SOUNDPARAM_COEF_VOL) 
		{
			// dynamic volume
			uwVolume = (short)par->ucVol;
			uwVolume *= (short)ucDefaultVolume;
			uwVolume >>=7;
			if (uwVolume>127) uwVolume=127; //precaution 01/09/98
		}
		else 
			uwVolume = (short) ucDefaultVolume;

		// update frequency if necessary.. 
		dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(pBuffer->ulFreq));
		if ((pBuffer->fLastFreq == 0.f) || (pBuffer->fLastFreq != (float)dwPitch))  
		{
			pBuffer->fLastFreq = (float)dwPitch;
			IDirectSoundBuffer_SetFrequency(buffer,dwPitch);
		}

		fn_vSetVirtualVolume(&pBuffer->dwFlags,(unsigned char)uwVolume); //27/01/99
		ucDSvolume=M_AdaptVolume(uwVolume
			,pBuffer->uRes.pstPtr->uRes.stSample.uwNbChannels
			,FALSE);
		IDirectSoundBuffer_SetVolume(buffer,vol_2_hth_dB[ucDSvolume]);
	} 
	else 
	{//spatially located
		if (idMainMicro!=C_CREATEMICRO_FAILED)
		{//at least 1 micro
			bAudible=FALSE;

			for (j=0;j<par->iNbLinks;j++)
			{
				if (par->astMicro[j].lId==idMainMicro) break;
			}
			if (j<par->iNbLinks)
			{
				if (par->astMicro[j].bLinked)
					bAudible=TRUE;
			}
		}

		if (bAudible)
		{
			// update position...      
			if (par->iFlags & C_SOUNDPARAM_COEF_VOL) 
			{
				// dynamic volume
				uwVolume = (short)par->ucVol;
				uwVolume *= (short)ucDefaultVolume;
				uwVolume >>=7;
				if (uwVolume>127) uwVolume=127;

				fn_vSetVirtualVolume(&pBuffer->dwFlags,(unsigned char)uwVolume); //27/01/99

				ucDSvolume=M_AdaptVolume(uwVolume
					,pBuffer->uRes.pstPtr->uRes.stSample.uwNbChannels
					,FALSE);
				IDirectSoundBuffer_SetVolume(buffer,vol_2_hth_dB[ucDSvolume]);
			}

			if (!stereo_reverse) 
			{
				pBuffer->parameters.vPosition.x = (float)M_RealToDoubleSnd(par->Pos.x);
				pBuffer->parameters.vPosition.y = (float)M_RealToDoubleSnd(par->Pos.y);
				pBuffer->parameters.vPosition.z = (float)M_RealToDoubleSnd(par->Pos.z);
				pBuffer->parameters.vVelocity.dvX = (float)M_RealToDoubleSnd(par->Vit.x);
				pBuffer->parameters.vVelocity.dvY = (float)M_RealToDoubleSnd(par->Vit.y);
				pBuffer->parameters.vVelocity.dvZ = (float)M_RealToDoubleSnd(par->Vit.z);
			} 
			else 
			{
				pBuffer->parameters.vPosition.x = (float)M_RealToDoubleSnd(par->Pos.y);
				pBuffer->parameters.vPosition.y = (float)M_RealToDoubleSnd(par->Pos.x);
				pBuffer->parameters.vPosition.z = (float)M_RealToDoubleSnd(par->Pos.z);
				pBuffer->parameters.vVelocity.dvX = (float)M_RealToDoubleSnd(par->Vit.y);
				pBuffer->parameters.vVelocity.dvY = (float)M_RealToDoubleSnd(par->Vit.x);
				pBuffer->parameters.vVelocity.dvZ = (float)M_RealToDoubleSnd(par->Vit.z);
			}

			// Rolloff			
			if (par->iFlags & C_SOUNDPARAM_ROLLOFF)
			{//explicit rolloff 
				//pRollOff=SND_M_GetRollOffFromSoundParam(par);
				pRollOff=&(SND_M_GetExtraParamFromSoundParam(par))->stRollOff;
				snd_assert(pRollOff->rDistSatur!=0);

				pBuffer->parameters.flMinDistance=(D3DVALUE)M_RealToDoubleSnd(pRollOff->rDistSatur);

				if (pRollOff->rDistBackGround<pRollOff->rDistSatur)
					pBuffer->parameters.flMaxDistance=DS3D_DEFAULTMAXDISTANCE;
				else
					pBuffer->parameters.flMaxDistance=(D3DVALUE)M_RealToDoubleSnd(pRollOff->rDistBackGround);
			}
			else			
			{//preset in case of obsolete DLL (SND_fn_vGetDefaultRollOff undefined)
				pBuffer->parameters.flMinDistance=g_d3dDistSatur;
				pBuffer->parameters.flMaxDistance=g_d3dDistBackGround;
			}

			if (interface3D==NULL)
			{
				jvdbg("set_param_DSBuffer=>interface3D==NULL!!!!");
			}
			else
			IDirectSound3DBuffer_SetAllParameters(interface3D,&(pBuffer->parameters),(bImmediate) ? DS3D_IMMEDIATE : DS3D_DEFERRED);

			// update frequency if necessary.. 
			dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(pBuffer->ulFreq));
			if ((pBuffer->fLastFreq == 0.f) || (pBuffer->fLastFreq != (float)dwPitch))  
			{
				pBuffer->fLastFreq = (float)dwPitch;
				IDirectSoundBuffer_SetFrequency(buffer,dwPitch);
			}
		}
		else
		{// not audible
				fn_vSetVirtualVolume(&pBuffer->dwFlags,0); //27/01/99
				IDirectSoundBuffer_SetVolume(buffer,vol_2_hth_dB[0]);
		}
	}
}

void Attack2Loop(BUFFER_DISPO* buffer, int iGo, DWORD dwAttackVol, DWORD dwAttackFreq)
{
	//DWORD dwTemp
	DWORD status;
    SND_td_pfn_vSoundCallback fnCB;
    long    lParam;
	SndBool bNeedCallback;
    HRESULT res;
	//unsigned char	ucDSvolume;

	jvdbg("attack2loop");
	if (buffer->Attack==NULL)
	{
		jvdbg("  attack2loop: Attack==NULL!!");		
	}
	if (buffer->Loop==NULL)
	{
		/*
		if (buffer->dwFlags&FLAG_VIRTUAL) //simulated transition attack->loop; unused yet
		{
			if (buffer->uRes.pstPtr->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDLOOP)
				if ((buffer->dwFlags & FLAG_PLAYING)&!(buffer->dwFlags & FLAG_LOOPING))
				{
					jvdbg("  simulated attack2loop");
					buffer->dwFlags|=FLAG_LOOPING;
					buffer->dwFlags&=~FLAG_WAITINGLOOP;
				}
		} else
		*/
		{
			jvdbg("  attack2loop: Loop==NULL!!=>return!!!!");
		}
		return;
	}

	//transition attack->loop
	// we stop the attack sound and start the looping sound with the same parameters
	if (buffer->Attack)
		IDirectSoundBuffer_Stop(buffer->Attack);
	
	if (buffer->dwFlags & FLAG_3D) {
		IDirectSound3DBuffer_SetAllParameters(buffer->Loop3D,&(buffer->parameters),DS3D_IMMEDIATE);
	}


/*	if (buffer->Attack)
	{
		IDirectSoundBuffer_GetVolume(buffer->Attack,&dwTemp);
		IDirectSoundBuffer_SetVolume(buffer->Loop,dwTemp);
		IDirectSoundBuffer_GetFrequency(buffer->Attack,&dwTemp);
		IDirectSoundBuffer_SetFrequency(buffer->Loop,dwTemp);
	} else */

	
	/*ucDSvolume=M_AdaptVolume(dwAttackVol
				,buffer->uRes.pstPtr->uRes.stSample.uwNbChannels
				,FALSE);
		IDirectSoundBuffer_SetVolume(buffer->Loop,vol_2_hth_dB[ucDSvolume]);
	*/
	IDirectSoundBuffer_SetVolume(buffer->Loop,dwAttackVol);
	IDirectSoundBuffer_SetFrequency(buffer->Loop,dwAttackFreq);

	if (buffer->Loop3D)
			IDirectSound3DBuffer_SetMode(buffer->Loop3D,DS3DMODE_NORMAL,DS3D_IMMEDIATE);

//NB: buffers not released, to allow duplication later

	res = IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING);
	buffer->dwFlags |= FLAG_LOOPING;
	buffer->dwFlags &= ~FLAG_WAITINGLOOP;
	if (res == DS_OK) {
		IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,iGo);
		
		IDirectSoundBuffer_GetStatus(buffer->Loop,&status);
		if (status & DSBSTATUS_PLAYING)	buffer->dwFlags |= FLAG_PLAYING;
		else {
			fnCB = buffer->fn_callback;
			lParam = buffer->par_callback;
        
			fn_vDeactivateVoice(buffer);
			if (fnCB) (*fnCB)(lParam);
		}
		
	} else {

        fnCB = buffer->fn_callback;
        lParam = buffer->par_callback;
		bNeedCallback=(buffer->dwFlags&FLAG_WAITINGCALLBACK);
        
		fn_vDeactivateVoice(buffer);
		if (bNeedCallback)
			if (fnCB)
				(*fnCB)(lParam);
	}
}

/*
    //--- buffers clients
    SNDLST2_M_DynamicForEachElementOf(&buffers_clients.pList,buffer_client,i)
    {
	    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_client->ulNextToCompute<<buffer_client->ulDecalBytes);

        if ((long)nbbytes_to_calcul<0) nbbytes_to_calcul+=buffer_client->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)
        {
	        IDirectSoundBuffer_Lock(buffer_client->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
                ,buffer_client->ulNextToCompute<<buffer_client->ulDecalBytes,nbbytes_to_calcul
                ,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0);

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

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

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

/*-------------------------------------------------
fn_bCreate2ndBuffer
ROLE: create a secondary buffer,
	and fills it with data
last modification:  24/03/98
by: Joel Vibert /put into a function+flags 2d/3d
---------------------------------------------------*/
SndBool fn_bCreate2ndBuffer(tdstTypeSampleMemDX3D *pSample, DWORD dwSize,
							char *pcData, LPDIRECTSOUNDBUFFER *pBuffer
							,SndBool bUse3d
							)
{
#ifndef DISABLE_WAVE
	DSBCAPS caps;
	WAVEFORMATEX pcmwf;
	DSBUFFERDESC dsbdesc;	
	DWORD   sizeptr1,sizeptr2;
    LPVOID  ptr1,ptr2;
	HRESULT res;
#ifdef USE_DEBUGFILE
	char s[100];
#endif

    // set up wave format structure.
    memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
    pcmwf.wFormatTag=WAVE_FORMAT_PCM;
    pcmwf.nChannels=pSample->uwNbChannels;
    pcmwf.nSamplesPerSec=pSample->ulFreq;
    pcmwf.wBitsPerSample=pSample->uwResolution;
    pcmwf.nBlockAlign=pcmwf.nChannels*pcmwf.wBitsPerSample/8;
    pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
    // set up DSBUFFERDESC structure.
    memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
    dsbdesc.dwSize=sizeof(DSBUFFERDESC);
                
//#ifdef NEW_FLAGS_MANAGEMENT
    /*dsbdesc.dwFlags= DSBCAPS_STATIC; 
	if (pSample->bPitchable) dsbdesc.dwFlags |= DSBCAPS_CTRLFREQUENCY;
	if (pSample->bVolable  ) dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
	if (pSample->bPanable  ) dsbdesc.dwFlags |= DSBCAPS_CTRLPAN;
	if (pSample->bSpacable ) dsbdesc.dwFlags |= DSBCAPS_CTRL3D;
	*/
	if (bUse3d) dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRL3D |DSBCAPS_CTRLDEFAULT
#ifdef AVOID_FOCUS_PB
		|DSBCAPS_GLOBALFOCUS 
#endif
		; 
	else dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRLDEFAULT
#ifdef AVOID_FOCUS_PB
		|DSBCAPS_GLOBALFOCUS
#endif
		;
//#else
//    dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRL3D |DSBCAPS_CTRLDEFAULT; 
//#endif
								
	dsbdesc.dwBufferBytes=dwSize;
    dsbdesc.lpwfxFormat=&pcmwf;

    // create buffer
	//jvdbg("create 2nd buff");
	res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&dsbdesc,pBuffer,NULL);
				    
    if (res==DS_OK){
#ifdef USE_DEBUGFILE
				sprintf(s,"buff created(size=%ld)",dwSize);
				jvdbg(s);
#endif

		//fill buffer
		memset(&caps,0,sizeof(DSBCAPS));
		caps.dwSize=sizeof(DSBCAPS);
		IDirectSoundBuffer_GetCaps(*pBuffer,&caps);				
                       
		if(IDirectSoundBuffer_Lock(*pBuffer,0,caps.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0)!=DS_OK)
		{
#ifdef USE_DEBUGFILE
				sprintf(s,"buff: pb lock(sizecaps=%ld)",caps.dwBufferBytes);
				jvdbg(s);
#endif
#ifdef DBG_NB_BUF
				dbgBuff++;
#endif //DBG_NB_BUF
				return FALSE;
		};

		memcpy(ptr1, pcData, sizeptr1);

		if(IDirectSoundBuffer_Unlock(*pBuffer,ptr1,sizeptr1,ptr2,sizeptr2)!=DS_OK)
		{
#ifdef USE_DEBUGFILE
			sprintf(s,"buff: pb unlock(sizecaps=%ld)",caps.dwBufferBytes);
			jvdbg(s);
#endif
#ifdef DBG_NB_BUF
			dbgBuff++;
#endif //DBG_NB_BUF
			return FALSE;
		}
#ifdef USE_DEBUGFILE
		if (caps.dwFlags&DSBCAPS_LOCSOFTWARE)
			sprintf(s,"buff filled: l1=%ld; l2=%ld/size=%ld; sizecaps=%ld EMULATED!!!",sizeptr1,sizeptr2,dwSize,caps.dwBufferBytes);
		else
			sprintf(s,"buff filled: l1=%ld; l2=%ld/size=%ld; sizecaps=%ld HARD",sizeptr1,sizeptr2,dwSize,caps.dwBufferBytes);
		jvdbg(s);
#endif
		return TRUE;
	}
#ifdef USE_DEBUGFILE
	sprintf(s,"buff not created(%ld)",dwSize);
	jvdbg(s);
#endif
	*pBuffer=NULL;
	return FALSE;
#endif
}



/*-------------------------------------------------
fn_bGetInterf3d
ROLE:     Gets the 3d interface
last modification: 24/03/98
by: Joel Vibert / put into a function+flags 2d/3d
---------------------------------------------------*/
BOOL fn_bGetInterf3d(BUFFER_DISPO *Buffer,
					 LPDIRECTSOUNDBUFFER Buffer2D,
					 LPDIRECTSOUND3DBUFFER *pBuffer3D,
					 BOOL b3d
					 )
{
//#ifdef NEW_FLAGS_MANAGEMENT
				// In this mode, we don't ask for 3D interface when the sound is in 2D
				if (b3d==FALSE) {
					jvdbg("fn_bGetInterf3d: no_pos");
					if (*pBuffer3D) IDirectSound3DBuffer_Release(*pBuffer3D);
					*pBuffer3D = NULL;
					return TRUE;
				} else {
					if(IDirectSoundBuffer_QueryInterface(Buffer2D,&IID_IDirectSound3DBuffer,(void**)pBuffer3D)) {
					   // Can't get 3d interface
						jvdbg("fn_bGetInterf3d: pb 3dInterf");
						fn_vDeactivateVoice(Buffer);
						return FALSE;
					} else {
						jvdbg("    newQuery3d OK");
						IDirectSound3DBuffer_SetMode(*pBuffer3D,DS3DMODE_NORMAL,DS3D_IMMEDIATE);
						return TRUE;
					}
				}
//#else 
/*				// in this mode, we always ask for a 3D interface
				if (IDirectSoundBuffer_QueryInterface(Buffer2D,&IID_IDirectSound3DBuffer,(void**)&buffer->Attack3D)) {
				   // Can't get 3d interface
					jvdbg("fn_bGetInterf3d: pb 3dInterf");
					fn_vDeactivateVoice(Buffer);
					return FALSE;
				} else {
					if (par->iFlags & C_SOUNDPARAM_NO_POSITION) {
						jvdbg("fn_bGetInterf3d: no_pos(Query3d ok)");
						IDirectSound3DBuffer_SetMode(*pBuffer3D,DS3DMODE_DISABLE,DS3D_IMMEDIATE);
					} else {						
						jvdbg("    Query3d OK");
						IDirectSound3DBuffer_SetMode(*pBuffer3D,DS3DMODE_NORMAL,DS3D_IMMEDIATE);
					}
					return TRUE;
				}
*/
//#endif
}


/*-------------------------------------------------
FUNCTION:  Wave3DTimerSynchro
ROLE:      Timer function that manages loops and callbacks
MODIFICATIONS: 
   - 24/03/98 - Joel Vibert - When the number of buffers is critical,
                              buffers and 3d interface may be created here
   - ??/??/?? - S.Ronse - created
---------------------------------------------------*/
void updateClients();
void CALLBACK Wave3DTimerSynchro(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2)
{
    int i,bOK;
    BUFFER_DISPO *buffer;
    DWORD play,write,status;	
	DWORD dwAttackVol,dwAttackFreq;
    

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

    SND_fn_vEnterCriticalSectionThreadSnd();
	if (eTimerState==TIMER_OFF)
	{
		bTimerInstance=FALSE;
		SND_fn_vQuitCriticalSectionThreadSnd();
		return;
	}

	updateClients();
	
    //--- transitions attack->loop
    for (i=0;i<nb_voices_activated;i++) {
		buffer=&buffer_dispo[i];
        if ((buffer->dwFlags & FLAG_PLAYING)&&(!(buffer->dwFlags & FLAG_PAUSED)))
        {
            if (buffer->dwFlags & FLAG_WAITINGLOOP)
            {
				if ((buffer->Attack==NULL)||(buffer->dwFlags&FLAG_VIRTUAL))
				{
					continue;
				} else
				{
					if(IDirectSoundBuffer_GetCurrentPosition(buffer->Attack,&play,&write) == DS_OK)
						bOK=TRUE;
					else bOK=FALSE;
				}

				if (bOK)
                    if (play>=buffer->dwSizeAttack)
                    {
						IDirectSoundBuffer_GetVolume(buffer->Attack,&dwAttackVol);
						IDirectSoundBuffer_GetFrequency(buffer->Attack,&dwAttackFreq);

						if (buffer->Loop==NULL) //(critical number of available buffers)
						{
							jvdbg("transitions attack->loop >attack: critical loop");
							//IDirectSoundBuffer_GetVolume(buffer->Attack,&dwAttackVol);
							//IDirectSoundBuffer_GetFrequency(buffer->Attack,&dwAttackFreq);
							//free attack buffer to be sure to have enough buffers
							if (buffer->Attack3D) IDirectSound3DBuffer_Release(buffer->Attack3D);
							#ifdef DBG_NB_BUF
								dbgBuffAtt--;
							#endif
							IDirectSoundBuffer_Release(buffer->Attack);
							buffer->Attack=NULL;
							buffer->Attack3D=NULL;
							buffer->dwFlags&=~FLAG_BUFFATT;

							//create Loop buffer
							if (fn_bCreate2ndBuffer(
										&buffer->uRes.pstPtr->uRes.stSample,
										//buffer->uRes.pstPtr->uRes.stSample.uData.stMem.data->iSizeBuffLoop,
										buffer->uRes.pstPtr->uRes.stSample.uData.stMem.lSizeLoopBuff,
										buffer->uRes.pstPtr->uRes.stSample.uData.stMem.pcDataLoop,
										&buffer->Loop
//#ifdef NEW_FLAGS_MANAGEMENT			
										,(buffer->dwFlags & FLAG_3D)!=0
//#endif
										)==FALSE)
								fn_vDeactivateVoice(buffer);
							else //launch loop on new buffer
							{
								#ifdef DBG_NB_BUF
									dbgBuffLoop++;
								#endif
								buffer->dwFlags|=FLAG_BUFFLOOP;

								if ((fn_bGetInterf3d(buffer,buffer->Loop,&buffer->Loop3D,(buffer->dwFlags & FLAG_3D)!=0)) 
									#ifdef USE_EAX
									&&
									(fn_bGetInterfEAX(buffer->Loop,&buffer->LoopEAX)!=0)
									#endif
									)

								{
									Attack2Loop(buffer,play-buffer->dwSizeAttack,dwAttackVol,dwAttackFreq);
								} else fn_vDeactivateVoice(buffer);
							} //launch loop
						} else  //loop!=NULL
							//Attack2Loop(buffer,play-buffer->dwSizeAttack,0,0);
							Attack2Loop(buffer,play-buffer->dwSizeAttack,dwAttackVol,dwAttackFreq);
					} else //(play<buffer->dwSizeAttack)
					{
						if (play == 0) {
							IDirectSoundBuffer_GetStatus(buffer->Attack,&status);
							if (!(status & DSBSTATUS_PLAYING)){ //Stopped=>trtansition								

							IDirectSoundBuffer_GetVolume(buffer->Attack,&dwAttackVol);
							IDirectSoundBuffer_GetFrequency(buffer->Attack,&dwAttackFreq);

							if (buffer->Loop==NULL) //(critical number of available buffers)
							{
								jvdbg("transitions attack->loop 0 critical loop");
								//IDirectSoundBuffer_GetVolume(buffer->Attack,&dwAttackVol);
								//IDirectSoundBuffer_GetFrequency(buffer->Attack,&dwAttackFreq);
								//free attack buffer to be sure to have enough buffers
								if (buffer->Attack3D)
									IDirectSound3DBuffer_Release(buffer->Attack3D);
								#ifdef DBG_NB_BUF
									dbgBuffAtt--;
								#endif
								IDirectSoundBuffer_Release(buffer->Attack);
								buffer->Attack=NULL;
								buffer->Attack3D=NULL;
								buffer->dwFlags&=~FLAG_BUFFATT;

								//create Loop buffer
								if (fn_bCreate2ndBuffer(
										&buffer->uRes.pstPtr->uRes.stSample,
										//buffer->uRes.pstPtr->uRes.stSample.uData.stMem.data->iSizeBuffLoop,
										buffer->uRes.pstPtr->uRes.stSample.uData.stMem.lSizeLoopBuff,
										buffer->uRes.pstPtr->uRes.stSample.uData.stMem.pcDataLoop,
										&buffer->Loop
//#ifdef NEW_FLAGS_MANAGEMENT			
										,(buffer->dwFlags & FLAG_3D)!=0
//#endif
										)==FALSE)
									fn_vDeactivateVoice(buffer);
								else //get interf3d+launch loop on new buffer
									{
										#ifdef DBG_NB_BUF
											dbgBuffLoop++;
										#endif
										buffer->dwFlags|=FLAG_BUFFLOOP;

										if ((fn_bGetInterf3d(
												buffer,
												buffer->Loop,
												&buffer->Loop3D,
												(buffer->dwFlags & FLAG_3D)!=0))
												#ifdef USE_EAX
												&&
												(fn_bGetInterfEAX(buffer->Loop,&buffer->LoopEAX)!=0)
												#endif
												)
										{
											Attack2Loop(buffer,0,dwAttackVol,dwAttackFreq);
										} else fn_vDeactivateVoice(buffer);
									} //get interf3d+launch loop on new buffer
								} else //loop!=NULL=>launch loop
									//Attack2Loop(buffer,0,0,0);
									Attack2Loop(buffer,0,dwAttackVol,dwAttackFreq);

							} //not playing
						}//play==0
					}//(play<buffer->dwSizeAttack)
			}//FLAG_WAITINGLOOP
			else
			{
				//04/11/98
				if (!test_voice_is_active(buffer))				
				{
					//fn_vDeactivateVoiceTmp(buffer);

					if (buffer->dwFlags & FLAG_WAITINGCALLBACK)
					{
						buffer->dwFlags &=~FLAG_WAITINGCALLBACK;
						if (buffer->fn_callback)
							(buffer->fn_callback) (buffer->par_callback);
					}
				}//!PLAYING				
			}//WAITING_LOOP
		}//FLAG_PLAYING&&!FLAG_PAUSED
	}//for

    bTimerInstance=FALSE;

    SND_fn_vQuitCriticalSectionThreadSnd();
  //launch next timer
	if (eTimerState!=TIMER_OFF)
		timerId = timeSetEvent(10,1,Wave3DTimerSynchro,0,TIME_ONESHOT);
}

DWORD WINAPI ThreadSnd(HANDLE h_event)
{
    MSG msg;

    timerId = timeSetEvent(10,1,(LPTIMECALLBACK)Wave3DTimerSynchro,0,TIME_ONESHOT);
    if(timerId) {
		while (GetMessage(&msg,NULL,0,0))
		{
			if (msg.message==snd_msg_ExitThread) {
				break;
			}
		}
	}

	//do not intend to kill the timer since the critical section
	// may prevent the instance of the timer to end
 	
	//timeKillEvent(timerId);
	eTimerState=TIMER_OFF;
	Sleep(100);
	while (bTimerInstance)	
		Sleep(0);
	
    ExitThread(0);
	return 0;
}

SndBool SND_CALL SND_fn_bTestIsPlayingSxdWave(long voice)
{
#ifndef DISABLE_WAVE
	BUFFER_DISPO*				pVoice = ((BUFFER_DISPO*)voice);
	SndBool						bPlaying;
	SndBool						bNeedCallback;
	SND_td_pfn_vSoundCallback   fnCB;
	long						lParam;
	
	if (driver_SND_OK == FALSE) {
		return FALSE;
	}

	snd_assert(pVoice);

	bPlaying = test_voice_is_active(pVoice); 
	if (!bPlaying) {
		fnCB=pVoice->fn_callback;
		lParam=pVoice->par_callback;
		bNeedCallback=(pVoice->dwFlags&FLAG_WAITINGCALLBACK);

		fn_vDeactivateVoiceTmp(pVoice);

		if (bNeedCallback)
		{			
			if (fnCB)
				(*fnCB)(lParam);
		}
	}

    return bPlaying;
#else
	return FALSE;
#endif
}

///////////////////////////////////////////////////////
// NAME: fn_bFindPlayBuff
//
// ROLE: returns an available buffer for play function, a source buffer if duplication
//       is possible, and frees memory if needed (used to choose between duplication/creation/
//       reuse)
//       NB: this function may return true if several buffers are requested
//           although there are not enough buffers, since buffers may be created later
//           (in attack2loop) if the number of buffers is critical 
// IN:   pointer to a place where to put the buffers
//       and pointer to description of sample in mem
// OUT:  boolean (available buffer found)
// AUTHOR: Joel Vibert
///////////////////////////////////////////////////////

SndBool fn_bFindPlayBuff(BUFFER_DISPO **pBuffer,
#ifndef CREATE_ONLY
						 BUFFER_DISPO **pSourceBuffer,
#endif						 
						 tdstBlockResourceMemDX3D* memDX3D,
						 DWORD  dwFreeBuff,
						 DWORD  dwDesiredBuff			
//					     ,SndBool bUse3d
						 ,DWORD    *dwTotalFree
)
{
	int i,j,k,iAvail1=0,iAvail2=0,iAvail1b,iAvail2b;
	DWORD		 freed=0,freed1=0,freed2=0,Avail2Flags=0,AvailFlags=0;
	BUFFER_DISPO *AvailBuffer=NULL; //available buffer without directsound buffers
	BUFFER_DISPO *AvailbBuffer=NULL;//available buffer without directsound buffers (never activated)
	BUFFER_DISPO *NewAvail,*NewAvail1; //idem after freed

	BUFFER_DISPO *Avail2Buffer[4]; //available buffers with 2 directsound buffers
	BUFFER_DISPO *Avail1Buffer[4]; //available buffers with 1 directsound buffer
	BUFFER_DISPO *buff,*buffTmp;
	
	DWORD		 dwFlags=0;
	BOOL bMemOk;

#ifdef USE_DEBUGFILE
	char s[100];
	int nb_alloc, nb_alloc_busy;
#endif

	*dwTotalFree=dwFreeBuff;

	//--look for an available buffer structure (buffer_dispo) among all voices	
#ifdef USE_DEBUGFILE
	sprintf(s,"fn_bFindPlayBuff(%d)",nb_voices_activated);
	jvdbg(s);
	nb_alloc=0;
	nb_alloc_busy=0;
#endif

#ifndef CREATE_ONLY
	*pSourceBuffer=NULL;
#endif
	*pBuffer=NULL;

	for (i=0; i<nb_voices_activated; i++)
    {
		dwFlags=buffer_dispo[i].dwFlags;
#ifdef USE_DEBUGFILE
		if (dwFlags & FLAG_BUFFS)
			nb_alloc_busy++;
#endif
		if (!(dwFlags & FLAG_PLAYING)) //Available buffer
		{
			if (dwFlags & FLAG_BUFFS)
			{
#ifdef USE_DEBUGFILE
				nb_alloc++;
				nb_alloc_busy--;
#endif
				if (memDX3D==buffer_dispo[i].uRes.pstPtr) //check if same resource
				{
					 //Available buffer with same resource
					*pBuffer=&(buffer_dispo[i]);

					//ok if all buffers created					
					//if ((dwFlags&FLAG_BUFFS)==memDX3D->uRes.stSample.uData.stMem.data->wBuffFlags)
					if ((long)(dwFlags&FLAG_BUFFS)==((memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags)&FLAG_LOADEDBUFFS))
					{
						#ifndef CREATE_ONLY
						*pSourceBuffer=*pBuffer;
						#endif
						#ifdef USE_DEBUGFILE
						sprintf(s,"%d<voice activated; same resource",i);
						jvdbg(s);
						#endif
						return TRUE;
					}

					//ok if 1st buffer created (attack) and buffer number is critical
					if ((dwFlags&FLAG_BUFFATT)&&(dwFreeBuff<=CRITICAL_BUFF_NB))
					{
					    #ifndef CREATE_ONLY
						*pSourceBuffer=*pBuffer;
						#endif
						#ifdef USE_DEBUGFILE
						sprintf(s,"%d<voice activated; same resource (critical=>Attack)",i);
						jvdbg(s);
						#endif
						return TRUE;
					}
				} //same resource
			} //with allocated buffer(s)


			//if (dwFreeBuff<=CRITICAL_BUFF_NB) //few buffers left=>we should try to free more buffers
			{
				switch (dwFlags & FLAG_BUFFS)
				{
					case 0: AvailBuffer=&(buffer_dispo[i]);
						break;
					case FLAG_BUFFS: //uses 2 buffers
						  //#ifdef USE_DEBUGFILE
						  //sprintf(s,"found avail2: %d; acces: %d",i,buffer_dispo[i].lAccessTime);
						  //jvdbg(s);
						  //#endif
						  if (iAvail2<4)
						     Avail2Buffer[iAvail2++]=&(buffer_dispo[i]);
						  else //only keep the first accessed
						  {
							  buff=&buffer_dispo[i];
							  for (k=0; k<4; k++)
								  if (buff->lAccessTime<Avail2Buffer[k]->lAccessTime)
								  {   //swap pointers
									  buffTmp=Avail2Buffer[k];
									  Avail2Buffer[k]=buff;
									  buff=buffTmp;
									  break;
								  }
						  }
						break;
					default: //FLAG_BUFFLOOP,FLAG_BUFFATT //uses one buffer
						//#ifdef USE_DEBUGFILE
						//  sprintf(s,"found avail1: %d; acces: %d",i,buffer_dispo[i].lAccessTime);
						//  jvdbg(s);
						// #endif
						if (iAvail1<4)
						    Avail1Buffer[iAvail1++]=&(buffer_dispo[i]);
						  else //only keep the first accessed
						  {
							  buff=&buffer_dispo[i];
							  for (k=0; k<4; k++)
								  if (buff->lAccessTime<Avail1Buffer[k]->lAccessTime)
								  {   //swap pointers
									  buffTmp=Avail1Buffer[k];
									  Avail1Buffer[k]=buff;
									  buff=buffTmp;
									  break;
								  }
						  }
				}	//switch			
			}
			/*	else
			{
				//change AvailBuffer to keep the oldest one
				if (buffer_dispo[i].lAccessTime<AvailBuffer->lAccessTime)
				{				
					Avail1Buffer=AvailBuffer;
					AvailBuffer=&(buffer_dispo[i]);
					AvailFlags=dwFlags;
				} else if (Avail1Buffer==NULL) Avail1Buffer=&(buffer_dispo[i]);
			}*/			
		} //not playing
#ifndef CREATE_ONLY
		else //playing
		{
			if (dwFlags & FLAG_BUFFS) //precaution
				if (memDX3D==buffer_dispo[i].uRes.pstPtr) //check if same resource
				{
					//ok only if buffers created (otherwise: risk of lack of buffers)
					//if ((dwFlags&FLAG_BUFFS)==memDX3D->uRes.stSample.uData.stMem.data->wBuffFlags)
					if ((long)(dwFlags&FLAG_BUFFS)==((memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags)&FLAG_LOADEDBUFFS))
							*pSourceBuffer=&(buffer_dispo[i]); //for duplication
				}
		}//playing
#endif
	}//for

	
	//jvdbg("fn_bFindPlayBuff: find among deactivated voices");
    //check among the deactivated voices if one has been loaded with the resource
    if (nb_voices_activated<NB_VOICES)
	{
		iAvail1b=0;
		iAvail2b=0;
		//Avail2Buffer=&(buffer_dispo[nb_voices_activated]);
		//Avail2Flags=buffer_dispo[nb_voices_activated].dwFlags;
#ifdef USE_DEBUGFILE
		jvdbg("DBG nb_voices_activated<NB_VOICES");
		for (j=0; j<iAvail2; j++)
		{
			sprintf(s,"  Avail2[%d]=buf %d",j,Avail2Buffer[j]-buffer_dispo);
			jvdbg(s);
		}
		for (j=0; j<iAvail1; j++)
		{
			sprintf(s,"  Avail1[%d]=buf %d",j,Avail1Buffer[j]-buffer_dispo);
			jvdbg(s);
		}
#endif


		for (j=nb_voices_activated; j<NB_VOICES; j++)
		{
			if (buffer_dispo[j].dwFlags & FLAG_BUFFS) //buffer(s) already created
			{
#ifdef USE_DEBUGFILE
				nb_alloc++;
#endif
				if (memDX3D==buffer_dispo[j].uRes.pstPtr)
				{
					//nb_voices_activated=j+1; //updated later
					 //Available buffer with same resource=>OK
#ifdef USE_DEBUGFILE
				    sprintf(s,"%d<voice activated2; same resource",j);
					jvdbg(s);
#endif
					//ok only if buffers created (otherwise: risk of lack of buffers)
					//if ((buffer_dispo[j].dwFlags&FLAG_BUFFS)==memDX3D->uRes.stSample.uData.stMem.data->wBuffFlags)
					if ((long)(buffer_dispo[j].dwFlags&FLAG_BUFFS)==((memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags)&FLAG_LOADEDBUFFS))
					{
						jvdbg("Same resource with buffers");
						*pBuffer=&(buffer_dispo[j]);
#ifndef CREATE_ONLY
						*pSourceBuffer=*pBuffer;
#endif
						return TRUE;
					}
				}

				if (dwFreeBuff<=CRITICAL_BUFF_NB) //few buffers left=>we should try to free more buffers
				{

					switch (buffer_dispo[j].dwFlags & FLAG_BUFFS)
					{
						case 0: if (AvailBuffer==NULL) AvailBuffer=&(buffer_dispo[j]);
							break;
						case FLAG_BUFFS: //uses 2 buffers
							if (iAvail2<4)
								 Avail2Buffer[iAvail2++]=&(buffer_dispo[j]);
							  else //never activated=>should be freed first
								 if (iAvail2b<4)
								    Avail2Buffer[iAvail2b++]=&(buffer_dispo[j]);								
							break;
						default: //FLAG_BUFFLOOP,FLAG_BUFFATT //uses one buffer
							if (iAvail1<4)
								Avail1Buffer[iAvail1++]=&(buffer_dispo[j]);
							else //never activated=>should be freed first
								 if (iAvail1b<4)
								    Avail1Buffer[iAvail1b++]=&(buffer_dispo[j]);
					}
				} else
				{
					if (AvailbBuffer==NULL) AvailbBuffer=&(buffer_dispo[j]);
				}

			}//buffer(s) exists
			else
			{				
				if (AvailBuffer==NULL) AvailBuffer=&(buffer_dispo[j]);				
			}
		}//for

#ifdef USE_DEBUGFILE
	   sprintf(s,"nb alloc=%d+%d busy; dwFree=%d; dwCritical=%d",nb_alloc,nb_alloc_busy,
		   dwFreeBuff,CRITICAL_BUFF_NB);
	   jvdbg(s);
#endif
	}
    else //nb_voices_activated==NB_VOICES
	{
		jvdbg("nb_voices_activated==NB_VOICES!");
		if (AvailBuffer)
		{
			fn_vDeactivateVoice(AvailBuffer); //precaution
			*pBuffer=AvailBuffer;
			if (dwFreeBuff>=CRITICAL_BUFF_NB) 
			{
				jvdbg("nb buffer not critical but max nb voices");
				return TRUE;
			}
		}

        //now free mem	if necessary

		if ((dwFreeBuff<=CRITICAL_BUFF_NB)||(*pBuffer==NULL)) //few buffers left=>free
		{
			jvdbg("critical");
			while (iAvail2>0) //maybe already activated with 2 buffers
			{
				freed+=2;				
				*dwTotalFree+=2;
				*pBuffer=Avail2Buffer[--iAvail2]; //buffer to be used				
				fn_vDeactivateVoice(Avail2Buffer[iAvail2]);

				if (dwFreeBuff+freed>dwDesiredBuff*2) return TRUE; //margin
			}
			while (iAvail1>0) //maybe already activated with 1 buffer
			{
				freed+=1;
				*dwTotalFree+=1; /* added 31/08/98 */
				*pBuffer=Avail1Buffer[--iAvail1];
				fn_vDeactivateVoice(Avail1Buffer[iAvail1]);
				if (dwFreeBuff+freed>dwDesiredBuff*2) return TRUE; //margin
			}

			#ifdef USE_DEBUGFILE
			sprintf(s,"freed %ld; free %ld; req %ld",
				freed,dwFreeBuff,dwDesiredBuff);
			jvdbg(s);
			#endif

			if (*pBuffer)
			{
				if (dwFreeBuff+freed>2) return TRUE;
				//unable to free enough buffers=>last try
				//freed+=fn_wFreeHalfBuffer();
				//if (dwFreeBuff+freed>2) return TRUE;
			}

			#ifdef USE_DEBUGFILE
			sprintf(s,"unable to free enough buffers(freed %ld; free %ld; req %ld)",
				freed,dwFreeBuff,dwDesiredBuff);
			jvdbg(s);
			#endif

			return FALSE;
		}//critical
		jvdbg("not critical");
		return TRUE;
	}  //nb_voices_activated==NB_VOICES

	 //increase the number of voices (free and never activated)
	if (AvailBuffer==NULL) AvailBuffer=AvailbBuffer;

//now free mem and return freed buffer if there are enough buffers left
	*pBuffer=NULL;

	if (dwFreeBuff<=CRITICAL_BUFF_NB) //few buffers left=>free
	{
#ifdef USE_DEBUGFILE
		sprintf(s,"dwFree<=CRITICAL; iavail2b=%d; iavail1b=%d",
			iAvail2b,iAvail1b);
		jvdbg(s);
		if (iAvail2b>0)
		{
			sprintf(s,"Avail2[0]=buff %d",
				Avail2Buffer[iAvail2]-buffer_dispo);
			jvdbg(s);
		}
		if (iAvail1b>0)
		{
			sprintf(s,"Avail1[%d]=buff %d",
				Avail1Buffer[iAvail1]-buffer_dispo);
			jvdbg(s);
		}
#endif
		/*if (AvailBuffer==NULL);
		if (bAvailNull)
		{
			AvailBuffer=Avail2Buffer[iAvail2b];	
		}*/
		NewAvail=NULL;
		while (iAvail2b>0) //never activated with 2 buffers
		{
#ifdef USE_DEBUGFILE
			if (Avail2Buffer[iAvail2b-1]==NULL)
				 sprintf(s,"iAvail2b=%d=>Free 2 NULL!",iAvail2b);
			else sprintf(s,"iAvail2b=%d=>Free 2 (buff %d)",iAvail2b,
				Avail2Buffer[iAvail2b-1]-buffer_dispo);
			jvdbg(s);		
#endif
			freed+=2;

			if (Avail2Buffer[iAvail2b-1])
				*dwTotalFree+=2; /* added 31/08/98 */
			fn_vDeactivateVoice(Avail2Buffer[--iAvail2b]);
			NewAvail=Avail2Buffer[iAvail2b];
			if (iAvail2)
			   Avail2Buffer[iAvail2b]=Avail2Buffer[--iAvail2];
		}
		NewAvail1=NULL;
		while (iAvail1b>0) //never activated with 1 buffer
		{
#ifdef USE_DEBUGFILE
			if (Avail1Buffer[iAvail1b-1]==NULL)
				 sprintf(s,"iAvail1b=%d=>Free 1 NULL!",iAvail1b);
			else sprintf(s,"iAvail1b=%d=>Free 1 (buff %d)",iAvail1b,
				Avail1Buffer[iAvail1b-1]-buffer_dispo);
			jvdbg(s);		
#endif
			freed+=1;

			if (Avail1Buffer[iAvail1b-1])
				*dwTotalFree+=2; /* added 31/08/98 */

			fn_vDeactivateVoice(Avail1Buffer[--iAvail1b]);
			NewAvail1=Avail1Buffer[iAvail1b];
			if (iAvail1)
				Avail1Buffer[iAvail1b]=Avail1Buffer[--iAvail1];
		}
		
		bMemOk=FALSE;
	    //if (bUse3d)
		//{
				if (dwFreeBuff+freed>dwDesiredBuff*2) bMemOk=TRUE; //margin
		//} else
		//		if (dwFreeBuff+freed>dwDesiredBuff) bMemOk=TRUE;
		if (bMemOk)
		{
#ifdef USE_DEBUGFILE
			sprintf(s,"freed %ld; free %ld; req %ld; dwTotalFree %ld",
				freed,dwFreeBuff,dwDesiredBuff,dwTotalFree);
			jvdbg(s);
#endif
			if (AvailBuffer)
			{
				*pBuffer=AvailBuffer;
				return TRUE;
			} else
				if (NewAvail&&NewAvail1)
				{
					if (NewAvail1-NewAvail>0) *pBuffer=NewAvail; //the smallest one
					else *pBuffer=NewAvail1;
					return TRUE;
				} else if (NewAvail) 
					{
						*pBuffer=NewAvail;
						return TRUE;
					}
					else if (NewAvail1)
						{
							*pBuffer=NewAvail1;
							return TRUE;
						}
						else 
						{
							jvdbg("internal pb: return FALSE while mem sufficient");
							return FALSE;
						}
			//return TRUE;
		}

		while (iAvail2>0) //maybe already activated with 2 buffers
		{
			freed+=2;
			iAvail2--;
#ifdef USE_DEBUGFILE
			sprintf(s,"maybe already activated: destr avail2[%d]= buf %d",
				iAvail2,Avail2Buffer[iAvail2]-buffer_dispo);
			jvdbg(s);
#endif
			if (*pBuffer==NULL) *pBuffer=Avail2Buffer[iAvail2]; //buffer to be used
			else if ((*pBuffer)->lAccessTime>Avail2Buffer[iAvail2]->lAccessTime)
				*pBuffer=Avail2Buffer[iAvail2]; //first accessed
			fn_vDeactivateVoice(Avail2Buffer[iAvail2]);
		}
		while (iAvail1>0) //maybe already activated with 1 buffer
		{
			freed+=1;
			iAvail1--;
#ifdef USE_DEBUGFILE
			sprintf(s,"maybe already activated: destr avail1[%d]= buf %d",
				iAvail1,Avail1Buffer[iAvail1]-buffer_dispo);
			jvdbg(s);
#endif
			if (*pBuffer==NULL) *pBuffer=Avail1Buffer[iAvail1]; //buffer to be used
			else if ((*pBuffer)->lAccessTime>Avail1Buffer[iAvail1]->lAccessTime)
				*pBuffer=Avail1Buffer[iAvail1]; //first accessed
			fn_vDeactivateVoice(Avail1Buffer[iAvail1]);
		}
#ifdef USE_DEBUGFILE
			sprintf(s,"freed %ld; free %ld; req %ld",
				freed,dwFreeBuff,dwDesiredBuff);
			jvdbg(s);
#endif
		if (*pBuffer==NULL) *pBuffer=AvailBuffer;

		if (*pBuffer)
		{
			//actually we only need one, but on the MonsterSound card,
			//the creation of one single small buffer may lead to
			//software emulation, even with two free 3D buffers (=>2 buffers are wasted!)
			if ((*dwTotalFree=dwFreeBuff+freed)>2) return TRUE;
		}

		
		//unable to free enough buffers=>last try
		//freed+=fn_wFreeHalfBuffer();
		//if (dwFreeBuff+freed>2) return TRUE;

	    #ifdef USE_DEBUGFILE
	    sprintf(s,"unable to free enough buffers(freed %ld; free %ld; req %ld)",
		   freed,dwFreeBuff,dwDesiredBuff);
	    jvdbg(s);
		#endif
		return FALSE;
	} //critical

	jvdbg("Not critical");
	if (AvailBuffer)
	{
		*pBuffer=AvailBuffer;
		return TRUE;
	}
	if (iAvail1b)
	{
		*pBuffer=Avail1Buffer[0];
		return TRUE;
	}
	if (iAvail2b)
	{
		*pBuffer=Avail2Buffer[0];
		return TRUE;
	}

#ifdef USE_DEBUGFILE
	   sprintf(s,"unable to free enough buffers(freed %ld; free %ld; req %ld)",
		   freed,dwFreeBuff,dwDesiredBuff);
	   jvdbg(s);
#endif

	*pBuffer=NULL;
	return FALSE;
}

///////////////////////////////////////////////////////
// NAME: fn_bFreeBuffer
//
// ROLE: free one (at least) buffer when not enough available buffers
// OUT:  boolean (successfully freed)
// AUTHOR: Joel Vibert
///////////////////////////////////////////////////////
SndBool fn_bFreeBuffer(void)
{
	int i;
	BUFFER_DISPO *AvailBuffer =NULL;
	DWORD		 dwFlags=0;
	BOOL         NoneAllocated=TRUE;

	//--look for an available buffer structure (buffer_dispo) among all voices	
	jvdbg("freeBuffer");

//first check voices that have never been activated
	for (i=nb_voices_activated; i<NB_VOICES; i++)
    {
		dwFlags=buffer_dispo[i].dwFlags;

		if (dwFlags & FLAG_BUFFS) //with directsound buffer(s) allocated
		{
			fn_vDeactivateVoice(&buffer_dispo[i]);				
			return TRUE;
		} //allocated
	}//for

//check voices that have already been activated
	for (i=nb_voices_activated-1; i>=0; i--) //backward to reduce nb_voices_activated
    {
		dwFlags=buffer_dispo[i].dwFlags;

		if (!(dwFlags & FLAG_PLAYING)) //Available buffer
		{
			if (dwFlags & FLAG_BUFFS)
			{
				if (AvailBuffer)
				{
					//change AvailBuffer to keep the oldest one
					if (buffer_dispo[i].lAccessTime<AvailBuffer->lAccessTime)					
						AvailBuffer=&(buffer_dispo[i]);											
				} else
				{
					if (NoneAllocated)
					{
						nb_voices_activated=i+1;
						NoneAllocated=FALSE;
					}
					AvailBuffer=&(buffer_dispo[i]);					
				}
			} //allocated
			else
			{
				if (NoneAllocated)				
					nb_voices_activated=i;				
			}
		} 
		else //playing
		{
			if (NoneAllocated)
			{
				nb_voices_activated=i+1;
				NoneAllocated=FALSE;
			}
		}
	}//for
	
	if (AvailBuffer)
	{
		fn_vDeactivateVoice(AvailBuffer);
		return TRUE;
	}
	jvdbg("FreeBuffer=>FALSE!!!");
	return FALSE;
}

///////////////////////////////////////////////////////
// NAME: fn_bFindInitialBuff
//
// ROLE: returns an available buffer for loading functions
// IN:   pointer to place where to put the buffer
// OUT:  boolean (available buffer found)
// AUTHOR: Joel Vibert
///////////////////////////////////////////////////////
SndBool fn_bFindInitialBuff(BUFFER_DISPO **pBuffer)
{
	int i;
	//--look for an available buffer structure (buffer_dispo) among all voices
	for (i=0;i<NB_VOICES;i++)
    {
		*pBuffer=&(buffer_dispo[i]);
		if (!((*pBuffer)->dwFlags & FLAG_BUFFS)) break;	
	}

    if (i==NB_VOICES)
    {    
        return FALSE;        
    }

	*pBuffer=&(buffer_dispo[i]); //pointer to the buffer to be used	
	(*pBuffer)->lAccessTime=0; //fn_lGetAccessTime();

	return TRUE;
}

///////////////////////////////////////////////////////
// NAME: fn_bCreateInitialBuffers
//
// ROLE: initially create directsound buffers if there are enough available ones
// AUTHOR: Joel Vibert
///////////////////////////////////////////////////////
SndBool fn_bCreateInitialBuffers(DWORD dwMaxFreeBuff, 
								 tdstBlockResourceMemDX3D *memDX3D,								 
								 SndBool bLoop, SndBool bAttack)
{
	tduResMemDX3D    *pRes;
	DWORD			 dwToBeCreated=0;
	BUFFER_DISPO     *buffer;
	//tduSampleMemBuff *pSampleData;
	tdstSampleMemoryDX3D *pSampleMem;
#ifdef USE_DEBUGFILE
	char s[100];
#endif

	if (bAttack) dwToBeCreated++;
	if (bLoop)   dwToBeCreated++;

	if (dwToBeCreated==0)
	{
		jvdbg("toBeCreated==0!!!");
		return TRUE;
	}
		
	//can be created (available buffers)
	//if (dwMaxFreeBuff>=dwToBeCreated)
	if (dwMaxFreeBuff>=2) // precaution
	{					
		//choice of buffer
		if (fn_bFindInitialBuff(&buffer)==FALSE)
		{
			jvdbg("findinitialbuffer=>FALSE!!!!!");
			return FALSE;
		}

		buffer->dwFlags=0;
		buffer->Attack = buffer->Loop = NULL;  //precaution
		buffer->Attack3D = buffer->Loop3D = NULL;
		
		pRes=&memDX3D->uRes;
		//pSampleData=pRes->stSample.uData.stMem.data;
		pSampleMem=&pRes->stSample.uData.stMem;
		if (bAttack)
		{			
#ifdef USE_DEBUGFILE
			sprintf(s,"init Create2nd Attack(%d); free: %ld; buff No %d",				
				//pSampleData->iSizeBuffAttack,
				pSampleMem->lSizeAttackBuff,
				dwMaxFreeBuff,
				buffer-buffer_dispo);
			jvdbg(s);
#endif
			if (fn_bCreate2ndBuffer(&pRes->stSample,				
				//pSampleData->iSizeBuffAttack,
				pSampleMem->lSizeAttackBuff,
				//pRes->stSample.uData.stMem.pcDataAttack,
				pSampleMem->pcDataAttack,
				&buffer->Attack		
				,TRUE //3d
				)==FALSE)
			{
				//buffer->dwFlags=0;
				//buffer->Attack=0;
				fn_vDeactivateVoice(buffer);
				return FALSE;
			} else
			{
#ifdef DBG_NB_BUF
		dbgBuffAtt++; dbgBuff++;
		sprintf(s,"  Create2 att=>%d/%d + ext %d",	dbgBuffAtt,dbgBuff,dbgBuffExt);
		jvdbg(s);
#endif //DBG_NB_BUF
			}			
			buffer->dwFlags|=FLAG_BUFFATT;
		}

		if (bLoop&&
			!((dwMaxFreeBuff<=CRITICAL_BUFF_NB)&&(bAttack))
			)
		{
#ifdef USE_DEBUGFILE
			sprintf(s,"init Create2nd Loop(%d); free: %ld; buff No %d",				
				//pSampleData->iSizeBuffLoop,
				pSampleMem->lSizeLoopBuff,
				dwMaxFreeBuff,
				buffer-buffer_dispo);
			jvdbg(s);
#endif
			if (fn_bCreate2ndBuffer(&pRes->stSample,				
				//pSampleData->iSizeBuffLoop,
				pSampleMem->lSizeLoopBuff,
				//pRes->stSample.uData.stMem.pcDataLoop,
				//bAttack?pSampleMem->pcDataLoop+pSampleMem->lSizeAttackBuff:pSampleMem->pcDataLoop,
				pSampleMem->pcDataLoop, //20/11/98
				&buffer->Loop
				,TRUE //3d			
				)==FALSE)
			{
				fn_vDeactivateVoice(buffer); 
				return FALSE;
			} else
			{
#ifdef DBG_NB_BUF
				dbgBuffLoop++; dbgBuff++;
				sprintf(s,"  Create2 loop=>%d/%d + ext %d",	dbgBuffLoop,dbgBuff,dbgBuffExt);
				jvdbg(s);
#endif //DBG_NB_BUF
			}		
			buffer->dwFlags|=FLAG_BUFFLOOP;
		} //(loop+critical buff number) or (critical buff number+loop without attack)
		else
		{
			if (bLoop)
			{
				jvdbg("ATT+LOOP: Loop buffer not created because of critical number of buffers!");
			}
		}

		//added to be able to test the resource while seeking a buffer for play function
		buffer->uRes.pstPtr=memDX3D;

		return TRUE;
	} else 
	{
#ifdef USE_DEBUGFILE
			sprintf(s,"(dwMaxFreeBuff=%ld<2; dwToBeCreated=%ld)!!!",
				dwMaxFreeBuff, dwToBeCreated);
			jvdbg(s);
#endif
		return FALSE;
	}
}

/*------------------------------------------------------------------------------
    FUNCTION: fn_lVirtualPlay
    ROLE: Virtual management of SND_fn_lPlaySxdWave after ForceRelease	
	MODIFICATION:
	- 27/01/99 - Joel Vibert - parameter
	- 28/08/98 - Joel Vibert - Created
------------------------------------------------------------------------------*/
long fn_lVirtualPlay(BUFFER_DISPO *buffer, BOOL bAttack, BOOL bLoop, SoundParam *par)
{
#ifdef USE_DEBUGFILE
	char s[100];
	
	sprintf(s,"Play Virtual : create attack %d loop %d",bAttack,bLoop);
	jvdbg(s);
#endif

	if (bAttack)
	{
		buffer->dwFlags|=(FLAG_PLAYING|FLAG_VIRTUAL);
	}
	if (bLoop)
	{
		buffer->dwFlags|=(FLAG_LOOPING|FLAG_VIRTUAL);
	}
	if (par!=NULL)
		setVirtualparam_DSBuffer(buffer,par);	//27/01/99

	return (long)buffer;
}

///////////////////////////////////////////////////////
// NAME: SND_fn_lPlaySxdWave
//
// ROLE: plays a 3D sample:
//       finds or creates buffer(s), freeing other ones if necessary,
//       initilizes them if necessary, initializes flags, and launches the sound
// IN:   pointer to place where to put the buffer
// OUT:  boolean (available buffer found)
// MODIFIED: 24/03/98 / Joel Vibert / modified to have as many buffers as available when
//             the number of buffers is critical:
//       instead of having all buffers duplicated from one associated with the
//       samples (and that is never used), buffers may be created here, duplicated,
//       or simply reused. Furthermore allocation of looping buffers may be postponed
//       if the number of buffers is critical.
///////////////////////////////////////////////////////
long SND_CALL SND_fn_lPlaySxdWave(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
#ifndef DISABLE_WAVE
    int						i;
	int						dwDesiredBuff;
    BUFFER_DISPO            *buffer;
    HRESULT                 result;
    tduRefResDX3D			resDX3D;

	tduResMemDX3D			*pRes;
	DWORD					dwFreeBuff,dwTotalFreeBuff;
	BOOL					bAttack,bLoop;
	static	BOOL			bAllowDuplication=TRUE;
	DSCAPS					dscaps;
#ifndef CREATE_ONLY
	BUFFER_DISPO            *SourceBuffer;	
#endif
#ifdef USE_DEBUGFILE
	char s[100];
	DSBCAPS					caps;
	HRESULT					dbgRes=DS_OK;
#endif
//	unsigned char			ucDSvolume;

    resDX3D.pstPtr=(tdstBlockResourceMemDX3D*)(res.pstPtr);

	if (driver_SND_OK == FALSE) {
		jvdbg("play: driver_SND_OK == FALSE");
		return C_PLAY_FAILED;
	}	
	pRes=&((tdstBlockResourceMemDX3D*)res.pstPtr)->uRes;
	//

	//---release buffers	
	for (i=0;i<nb_voices_activated;i++)
    {
			buffer=&(buffer_dispo[i]);
			if (buffer->dwFlags & FLAG_PLAYING)
                if (!SND_fn_bTestIsPlayingSxdWave((long)buffer))
                {
					// The voice is stopped and freed
					// (the callback is called in SND_fn_bTestIsPlayingSxdWave)
#ifdef USE_DEBUGFILE
					sprintf(s,"(play:release buff: att%ld)",buffer->dwSizeAttack);
					jvdbg(s);
#endif
                }
    }//for
	//jvdbg("play: after release");	


	//bAttack=pRes->stSample.uData.stMem.data->bAttack; //resource with attack
	//bLoop=pRes->stSample.uData.stMem.data->bLoop; //looping resource
	//bAttack=((pRes->stSample.uData.stMem.data->wBuffFlags&FLAG_BUFFATT)!=0); //resource with attack
	//bLoop=((pRes->stSample.uData.stMem.data->wBuffFlags&FLAG_BUFFLOOP)!=0); //looping resource
	bAttack=((pRes->stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDATT)!=0); //resource with attack
	bLoop=((pRes->stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDLOOP)!=0); //looping resource
	
	 //update number of directsound buffers to be created
	if (bAttack&&bLoop)
	{
		   dwDesiredBuff=2;
		  // dwDesiredFlags=FLAG_BUFFS;
	} else 
	{
		dwDesiredBuff=1;
		//if (bAttack) dwDesiredFlags=FLAG_BUFFATT;
		//else dwDesiredFlags=FLAG_BUFFLOOP;
	}

	if (bReleasedForced)
	{
		/* 
		We cannot get info on it, so we try to be careful
		by specifying not too many free buffers. We suppose everything is OK.
		We'll only get the errors when recreating the buffers.
		*/
		dwFreeBuff=dwDesiredBuff+1;
	} else
	{
		dscaps.dwSize=sizeof(DSCAPS);
			result=IDirectSound_GetCaps(lpDirectSoundObject,&dscaps);
			if (result!=DS_OK)
			{
		#ifdef USE_DEBUGFILE
					jvdbg("ERROR ON GETCAPS:");
					GetErrorDSound(result,s,sizeof(s));
					jvdbg(s);
		#endif
					dwFreeBuff=0;
			} else 
			{
				//tests modified 07/12/98
				if (dscaps.dwMaxHw3DStaticBuffers<=0)
				{
					if	(dscaps.dwMaxHw3DStreamingBuffers<=0)  //2D card=>emulation;
						dscaps.dwFreeHw3DStreamingBuffers=CRITICAL_BUFF_NB+2; //dummy value to simulate free buffers
					dwFreeBuff=dscaps.dwFreeHw3DStreamingBuffers;
				}
				else
				{
		#ifdef SIMU_CRITICAL_BUFF_NB
					dscaps.dwFreeHw3DStaticBuffers-=SIMU_BUFF_NB;
					if ((long int)dscaps.dwFreeHw3DStaticBuffers<0)
						dscaps.dwFreeHw3DStaticBuffers=0;
		#endif
					dwFreeBuff=dscaps.dwFreeHw3DStaticBuffers;
				}				
			}
	}//bReleasedForced

    //--look for a free "buffer_dispo" among all voices...
#ifdef CREATE_ONLY	
		if (fn_bFindPlayBuff(&buffer,resDX3D.pstPtr,dwFreeBuff,dwDesiredBuff				
		//,(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0 //use 3d
		,&dwTotalFreeBuff	
			)==FALSE)
			return C_PLAY_FAILED;
#else //!CREATE_ONLY
		if (fn_bFindPlayBuff(&buffer,&SourceBuffer,resDX3D.pstPtr,dwFreeBuff,dwDesiredBuff				
		//,(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0 //use 3d
		,&dwTotalFreeBuff	
			)==FALSE)
			return C_PLAY_FAILED;
#endif //!CREATE_ONLY

        //the buffer to be used is *buffer
		buffer->dwFlags &= FLAG_BUFFS; //only keep these flags
		buffer->lAccessTime=fn_lGetAccessTime();
		if (buffer->dwFlags==0)
		{
			buffer->Attack = buffer->Loop = NULL;
			buffer->Attack3D = buffer->Loop3D = NULL;
		}
		buffer->uRes=resDX3D;

		buffer->parameters.dwSize = sizeof(DS3DBUFFER);
		buffer->parameters.dwInsideConeAngle = 360;
		buffer->parameters.dwOutsideConeAngle = 360;
		buffer->parameters.vConeOrientation.x = 0;
		buffer->parameters.vConeOrientation.y = 0;
		buffer->parameters.vConeOrientation.z = 1;
		buffer->parameters.lConeOutsideVolume = 0;
		buffer->parameters.flMinDistance = 1.;
		buffer->parameters.flMaxDistance = 1E9;
		buffer->parameters.dwMode = DS3DMODE_NORMAL;		

        //buffer->dwSizeAttack=((tdstBlockResourceMemDX3D*)res.pstPtr)->uRes.stSample.uData.stMem.iSizeAttack;
		buffer->ulVol = res.pstPtr->ucVolume;
		buffer->ulFreq = res.pstPtr->uRes.stSample.ulFreq;

#ifdef USE_DEBUGFILE
		sprintf(s,"(PLAY bAtt: %d; bLoop: %d)",bAttack, bLoop);
		jvdbg(s);
#endif
		 			
		if (bReleasedForced)
			return fn_lVirtualPlay(buffer, bAttack, bLoop, par );

		if (bAttack)
		{			
			//buffer->dwSizeAttack=((tdstBlockResourceMemDX3D*)res.pstPtr)->uRes.stSample.uData.stMem.data->iSizeBuffAttack;
			buffer->dwSizeAttack=((tdstBlockResourceMemDX3D*)res.pstPtr)->uRes.stSample.uData.stMem.lSizeAttackBuff;

			result=~DS_OK; //=>create if no other way of getting the buffer is available
#ifdef CREATE_ONLY
			if (buffer->dwFlags & FLAG_BUFFS)
#else
			if (SourceBuffer==buffer) //(=>allocated)
#endif
			{
				/*if (buffer==NULL)
				{
					jvdbg("ALLOC att:NULL!!!");
					return C_PLAY_FAILED;
				}*/
				if (buffer->Attack)
				{
					#ifdef USE_DEBUGFILE
					memset(&caps,0,sizeof(DSBCAPS));
					caps.dwSize=sizeof(DSBCAPS);
					dbgRes=IDirectSoundBuffer_GetCaps(buffer->Attack,&caps);
					if (dbgRes==DS_OK)
					{
						if (caps.dwFlags&DSBCAPS_LOCSOFTWARE)
							sprintf(s,"play: Attack allocated(%d) EMULATED!!!",buffer->dwSizeAttack);
						else
							sprintf(s,"play: Attack allocated(%d) HARD",buffer->dwSizeAttack);	
					} else 
					{
						sprintf(s,"play: Attack allocated(%d):err/getcaps!",buffer->dwSizeAttack);
						jvdbg(s);
						GetErrorDSound(dbgRes,s,sizeof(s));
						jvdbg(s);
					}
					#endif

					IDirectSoundBuffer_SetCurrentPosition(buffer->Attack,0); //12/11/98
					result=DS_OK; //=> do not create
				}
					// (else create because !DS_OK)
			} //else //not allocated
#ifndef CREATE_ONLY
			else //not allocated
			//if (buffer->Attack!=NULL)
			if ((SourceBuffer!=NULL)&&(SourceBuffer->dwFlags&FLAG_BUFFATT)&&bAllowDuplication)
			{
#ifdef USE_DEBUGFILE
				sprintf(s,"play: Attack duplicate(%d; No %d->%d)",SourceBuffer->dwSizeAttack,
					SourceBuffer-buffer_dispo,
					buffer-buffer_dispo);
				jvdbg(s);
#endif
				buffer->Attack = buffer->Loop = NULL;
				buffer->Attack3D = buffer->Loop3D = NULL;
				buffer->dwFlags&=~FLAG_BUFFS;
				//result=IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject ,Attack,&buffer->Attack);
				result=IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject ,SourceBuffer->Attack,&buffer->Attack);
				if (result==DS_OK)
				{
				#ifdef USE_DEBUGFILE
					memset(&caps,0,sizeof(DSBCAPS));
					caps.dwSize=sizeof(DSBCAPS);
					dbgRes=IDirectSoundBuffer_GetCaps(buffer->Attack,&caps);
					if (dbgRes==DS_OK)
					{
						if (caps.dwFlags&DSBCAPS_LOCSOFTWARE)
							jvdbg("play: Attack dupl EMULATED!!!");
						else
							jvdbg("play: Attack dupl HARD");
					#ifdef DBG_NB_BUF
						dbgBuffAtt++; dbgBuff++;
						sprintf(s,"  dup att=>%d/%d + ext %d",	dbgBuffAtt,dbgBuff,dbgBuffExt);
						jvdbg(s);
					#endif
					} else
					{
						jvdbg("play: Attack dupl ???");
						GetErrorDSound(dbgRes,s,sizeof(s));
						jvdbg(s);
					}
				#endif	
				    IDirectSoundBuffer_SetCurrentPosition(buffer->Attack,0);					
					buffer->dwFlags|=FLAG_BUFFATT;
					dwTotalFreeBuff-=2; //leave less memory for loop
				}				
				else //duplication failed
				{
					jvdbg("duplicate attack failed=>bAllowDuplication=FALSE");
#ifdef USE_DEBUGFILE					
					GetErrorDSound(result,s,sizeof(s));
					jvdbg(s);
#endif
					buffer->Attack =NULL;          //precaution
					buffer->Attack3D = NULL;
					buffer->dwFlags&=~FLAG_BUFFATT;
					  // fn_vDeactivateVoice(buffer);
					  // return C_PLAY_FAILED;
					//duplication error=>do not try again later
							     //[some cards do not support duplication of playing buffers, 
							     // and it may slow down the game]
					bAllowDuplication=FALSE;
				}//duplication failed
			}
			//else
			//if (result!=DS_OK)
#endif //create_only
			if (result!=DS_OK) //no buffer yet (failed or no loaded buffer available)=>create
			{
#ifdef USE_DEBUGFILE
				if (pRes->stSample.uData.stMem.lSizeAttackBuff==0)
				{
					jvdbg("PLAY:iSizeBuffAttack=0!!!!!!");
					return C_PLAY_FAILED;
				}
				sprintf(s,"play: Attack Create2nd(%d); buff No %d",buffer->dwSizeAttack, buffer-buffer_dispo);
				jvdbg(s);
#endif
				buffer->dwFlags&=~FLAG_BUFFS;
				if (fn_bCreate2ndBuffer(&pRes->stSample,
					//pRes->stSample.uData.stMem.data->iSizeBuffAttack,
					pRes->stSample.uData.stMem.lSizeAttackBuff,
					pRes->stSample.uData.stMem.pcDataAttack,
					&buffer->Attack
					,(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0))
				{
					buffer->dwFlags|=FLAG_BUFFATT;
					result=DS_OK;
#ifdef DBG_NB_BUF
		dbgBuffAtt++; dbgBuff++;
		sprintf(s,"  Create2 att=>%d/%d + ext %d",	dbgBuffAtt,dbgBuff,dbgBuffExt);
		jvdbg(s);
#endif //DBG_NB_BUF
					dwTotalFreeBuff-=2;
				} else
				{
					fn_vDeactivateVoice(buffer);
					return C_PLAY_FAILED;				
				}
			}
		} else //bAttack
		{
			buffer->dwFlags&=~FLAG_BUFFATT;
			buffer->dwSizeAttack=0;
		}

        if (buffer->dwFlags&FLAG_BUFFATT) {
		   //result=DS_OK;
			if(result==DS_OK)
			{
				if (nb_voices_activated<(buffer-buffer_dispo)+1) //update nb_voices_activated
				   nb_voices_activated=(buffer-buffer_dispo)+1;
				// Volume de la ressource
				/*  comment 12/11/98: done later by SetParam (+take into account parameter volume)
				ucDSvolume=M_AdaptVolume(buffer->ulVol
					,buffer->uRes.pstPtr->uRes.stSample.uwNbChannels
					,FALSE);
				IDirectSoundBuffer_SetVolume(buffer->Attack,vol_2_hth_dB[ucDSvolume]);
				*/

			   if (buffer->Attack3D==NULL)
			   {
				    if ((fn_bGetInterf3d(
							buffer,
							buffer->Attack,
							&buffer->Attack3D,									
							/*(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0*/TRUE	//as buffer are created with 3D capacities, 3D interface must always be created
							)==FALSE)
#ifdef USE_EAX
							||
							(fn_bGetInterfEAX(buffer->Attack,&buffer->AttackEAX)==FALSE)
#endif
							)
					{
						jvdbg("play: attack3d query failed!!!");
						fn_vDeactivateVoice(buffer);
						return C_PLAY_FAILED;
					}
					else jvdbg("play: attack3d query tt ok");
			   }//not allocated
           } else {
				fn_vDeactivateVoice(buffer);
				return C_PLAY_FAILED;
           }
        } else buffer->Attack=NULL;

		//if (pRes->stSample.uData.stMem.bLoop)
		if (bLoop)
		{
			jvdbg("play:loop");

			result=~DS_OK;
#ifdef CREATE_ONLY
			if (buffer->dwFlags & FLAG_BUFFS)
#else
			if (SourceBuffer==buffer) //(=>allocated)
#endif
			{
				if (buffer->Loop)
				{
				#ifdef USE_DEBUGFILE
					memset(&caps,0,sizeof(DSBCAPS));
					caps.dwSize=sizeof(DSBCAPS);
					dbgRes=IDirectSoundBuffer_GetCaps(buffer->Loop,&caps);
					if (dbgRes==DS_OK)
					{
						if (caps.dwFlags&DSBCAPS_LOCSOFTWARE)
							jvdbg("play: Loop allocated EMULATED!!!");
						else
							jvdbg("play: Loop allocated HARD");	
					} else 
					{
						jvdbg("play: Loop allocated ???");
						GetErrorDSound(dbgRes,s,sizeof(s));
						jvdbg(s);
					}
				#endif
					IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,0); //12/11/98
					result=DS_OK;
				} //else   create (!DS_OK)
			} //else
#ifndef CREATE_ONLY
			else
			//if (buffer->Loop!=NULL)
			if ((SourceBuffer!=NULL)&&(SourceBuffer->dwFlags&FLAG_BUFFLOOP)&&bAllowDuplication)
				//duplicate
			{
			#ifdef USE_DEBUGFILE
				sprintf(s,"play: Loop duplicate(No %d->%d)",
					SourceBuffer-buffer_dispo, buffer-buffer_dispo);
				jvdbg(s);
			#endif
				buffer->Loop = NULL;
				buffer->Loop3D = NULL;				

				//if (dwTotalFreeBuff<2)
				if ( ((buffer->Attack)&&(dwTotalFreeBuff<CRITICAL_BUFF_NB)) //will be created in attack2loop
					||(dwTotalFreeBuff<=2) )
					result=DS_OK; //do not try to create
				else
				{
					if (SourceBuffer->Loop)
					{
						//result=IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject ,Loop,&buffer->Loop);
						result=IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject ,SourceBuffer->Loop,&buffer->Loop);
						if (result==DS_OK)
						{
						#ifdef USE_DEBUGFILE //to debug hard/emulated+buffers nb
							memset(&caps,0,sizeof(DSBCAPS));
							caps.dwSize=sizeof(DSBCAPS);
							dbgRes=IDirectSoundBuffer_GetCaps(buffer->Loop,&caps);
							if (dbgRes==DS_OK)
							{
								if (caps.dwFlags&DSBCAPS_LOCSOFTWARE)
									jvdbg("play: Loop dupl EMULATED!!!");
								else
									jvdbg("play: Loop dupl HARD");
							#ifdef DBG_NB_BUF
								dbgBuffLoop++; dbgBuff++;
								sprintf(s,"  dup loop=>%d/%d + ext %d",	dbgBuffLoop,dbgBuff,dbgBuffExt);
								jvdbg(s);
							#endif
							} else //getcaps=>!ok
							{
								jvdbg("play: Loop dupl ???");
								GetErrorDSound(dbgRes,s,sizeof(s));
								jvdbg(s);
							}
						#endif //USE_DEBUGFILE
							IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,0);					
							buffer->dwFlags|=FLAG_BUFFLOOP;
						} else //duplication failed
						{
							jvdbg("duplicate loop failed:");
						#ifdef USE_DEBUGFILE					
							GetErrorDSound(result,s,sizeof(s));
							jvdbg(s);
						#endif
							buffer->Loop = NULL; //precaution
							buffer->dwFlags&=~FLAG_BUFFLOOP;
							jvdbg("duplicate attack failed");

							    // fn_vDeactivateVoice(buffer);
							    // return C_PLAY_FAILED;
							//duplication error=>do not try again later
							     //[some cards do not support duplication of playing buffers, 
							     // and it may slow down the game]
							bAllowDuplication=FALSE;
							//result!=DS_OK => create
						}//duplication failed
					} else result=~DS_OK; //source->Loop==NULL=>create
				}//dwTotalFreeBuff
			}//((SourceBuffer!=NULL)&&(SourceBuffer->dwFlags&FLAG_BUFFLOOP)) //duplicate		
		//else
		//if (result!=DS_OK) //dupl failed=>create
#endif //create_only
		if (result!=DS_OK) //not available or failed=>create
		{ //create
			#ifdef USE_DEBUGFILE
				sprintf(s,"play: Loop Create2nd(%d); buff No %d",pRes->stSample.uData.stMem.lSizeLoopBuff,
					buffer-buffer_dispo);
				jvdbg(s);
			#endif

			//if (dwTotalFreeBuff<2)
			if ( ((buffer->Attack)&&(dwTotalFreeBuff<CRITICAL_BUFF_NB)) //will be created in attack2loop
					||(dwTotalFreeBuff<=2) )
			{
				result=DS_OK;
				buffer->dwFlags&=~FLAG_BUFFLOOP;
			} else
			{
				buffer->dwFlags&=~FLAG_BUFFLOOP;
				if (fn_bCreate2ndBuffer(&pRes->stSample,
						//pRes->stSample.uData.stMem.data->iSizeBuffLoop,
						pRes->stSample.uData.stMem.lSizeLoopBuff,
						//pRes->stSample.uData.stMem.data->pcDataLoop+pRes->stSample.uData.stMem.iSizeAttack,
						//pRes->stSample.uData.stMem.pcDataLoop+pRes->stSample.uData.stMem.iSizeAttack,
						pRes->stSample.uData.stMem.pcDataLoop//+pRes->stSample.uData.stMem.lSizeAttackBuff, //20/11/98
						,&buffer->Loop
						,(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0))
				{				
					buffer->dwFlags|=FLAG_BUFFLOOP;
					result=DS_OK;
				#ifdef DBG_NB_BUF
					dbgBuffLoop++; dbgBuff++;
					sprintf(s,"  Create2 loop=>%d/%d + ext %d",	dbgBuffLoop,dbgBuff,dbgBuffExt);
					jvdbg(s);
				#endif //DBG_NB_BUF
				} else //fn_bCreate2ndBuffer
				{
					fn_vDeactivateVoice(buffer);
					return C_PLAY_FAILED;				
				}
			}//dwTotalFreeBuff
		} //create

		} else //bLoop
			buffer->dwFlags&=~FLAG_BUFFLOOP;

		//Loop = ((tdstBlockResourceMemDX3D*)res.pstPtr)->uRes.stSample.uData.stMem.lpBufferLoop;
        if (buffer->dwFlags&FLAG_BUFFLOOP) {			
            //if((result=IDirectSound_DuplicateSoundBuffer(lpDirectSoundObject,Loop,&buffer->Loop)) ==DS_OK){
			if (result==DS_OK)
			{
				if (nb_voices_activated<(buffer-buffer_dispo)+1) //update nb_voices_activated
				   nb_voices_activated=(buffer-buffer_dispo)+1;
				// Volume de la ressource
				/*  comment 12/11/98: done later by SetParam (+take into account parameter volume)
				ucDSvolume=M_AdaptVolume(buffer->ulVol
					,buffer->uRes.pstPtr->uRes.stSample.uwNbChannels
					,FALSE);
				IDirectSoundBuffer_SetVolume(buffer->Loop,vol_2_hth_dB[ucDSvolume]);
				*/

				if (buffer->Loop3D==NULL)
				{
					if ((fn_bGetInterf3d(
									buffer,
									buffer->Loop,
									&buffer->Loop3D,									
									(par->iFlags & C_SOUNDPARAM_NO_POSITION)==0
										)==FALSE)
										#ifdef USE_EAX
										||(fn_bGetInterfEAX(buffer->Loop,&buffer->LoopEAX)==FALSE)
										#endif
										)
					{
						jvdbg("play: loop3d query failed!!!");
						fn_vDeactivateVoice(buffer);
						return C_PLAY_FAILED;
					}
					else jvdbg("play: loop3d tt ok");
				}//Loop3D==NULL
            } else { //result==DS_OK
				//Can't duplicate sound
				fn_vDeactivateVoice(buffer);
				return C_PLAY_FAILED;
            }
        } else  //LOOP!=NULL
			buffer->Loop=NULL;

		if (par->iFlags & C_SOUNDPARAM_NO_POSITION) {
			buffer->dwFlags &= ~FLAG_3D;
		} else {
			buffer->dwFlags |= FLAG_3D;
		}

		buffer->fLastFreq = 0.f;

        //c'est parti
        if (buffer->Attack)
        {
			jvdbg("play:goAttack");
           // Il n'y a pas de micro -> dernier parametre NULL...
			
			set_param_DSBuffer(buffer->Attack,buffer->Attack3D,par,buffer,TRUE);
			//###DOM
			#ifdef USE_EAX
			set_param_eax(buffer->AttackEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
			#endif
			buffer->dwFlags |= FLAG_PLAYING;
		    IDirectSoundBuffer_SetCurrentPosition(buffer->Attack,0);					
			if(IDirectSoundBuffer_Play(buffer->Attack,0,0,0) == DS_OK) {
				//comment 12/11/98 (once is enough)
				//set_param_DSBuffer(buffer->Attack,buffer->Attack3D,par,buffer,TRUE);
				//###DOM
				//#ifdef USE_EAX
				//set_param_eax(buffer->AttackEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
				//#endif

				//IDirectSoundBuffer_GetStatus(buffer->Attack,&status); //16/03
				//if (status & DSBSTATUS_PLAYING)				
				//if (buffer->Loop != NULL)
				if (bLoop) buffer->dwFlags |= FLAG_WAITINGLOOP;				
			} else
			{
				jvdbg("Pb play attack=>deactivate!!!");
				fn_vDeactivateVoice(buffer);
                return C_PLAY_FAILED;
			}
            
            /*if (!(buffer->dwFlags & FLAG_PLAYING)) {
				fn_vDeactivateVoice(buffer);
                return C_PLAY_FAILED;
            }
			*/
        }
        else if (buffer->Loop)
        {			
			jvdbg("play:goLoop");
			// Il n'y a pas de micro -> dernier parametre NULL...			
			set_param_DSBuffer(buffer->Loop,buffer->Loop3D,par,buffer,TRUE);
			//###DOM
			#ifdef USE_EAX
			set_param_eax(buffer->LoopEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
			#endif
			buffer->dwFlags |= FLAG_PLAYING;
			buffer->dwFlags |= FLAG_LOOPING;
			IDirectSoundBuffer_SetCurrentPosition(buffer->Loop,0);					
			if(IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING) == DS_OK) {
				//comment 12/11/98 (once is enough)
				//set_param_DSBuffer(buffer->Loop,buffer->Loop3D,par,buffer,TRUE);
				//###DOM
				//#ifdef USE_EAX
				//set_param_eax(buffer->LoopEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
				//#endif

				//IDirectSoundBuffer_GetStatus(buffer->Loop,&status);
				//if (status & DSBSTATUS_PLAYING)				
			} else
			{
				jvdbg("PB play loop=>deactivate!!!");
				fn_vDeactivateVoice(buffer);
                return C_PLAY_FAILED;
			}

			if (!(buffer->dwFlags & FLAG_PLAYING)) {
				jvdbg("Loop: not playing: Pb!!!");
				fn_vDeactivateVoice(buffer);
				return C_PLAY_FAILED;
			}

        } else {
			// no attack buffer and no loop buffer-> sound error
			jvdbg("play:Pb (NoAttack NoLoop)");
			//buffer->dwFlags = 0;
			//buffer->fn_callback=NULL; 
			fn_vDeactivateVoice(buffer); //precaution
            return C_PLAY_FAILED;
        }

        if (fn_callback!=NULL)
        {
            buffer->dwFlags |= FLAG_WAITINGCALLBACK;
            buffer->fn_callback=fn_callback;
            buffer->par_callback=par_callback;
        }

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

void SND_CALL SND_fn_vRemoveCallbackSxdWave(long voice)
{
#ifndef DISABLE_WAVE
    BUFFER_DISPO* pBuffer = (BUFFER_DISPO*)voice;
 	
	if (driver_SND_OK == FALSE) {
		return ;
	}
    if (pBuffer) {
        pBuffer->dwFlags &= ~FLAG_WAITINGCALLBACK;
        pBuffer->fn_callback=NULL;
        pBuffer->par_callback = 0;
    }
#endif
}

SndBool SND_CALL SND_fn_bSetParamSxdWave(long voice,SoundParam *par)
{
#ifndef DISABLE_WAVE
	BUFFER_DISPO*	buffer = ((BUFFER_DISPO*)voice);
    SND_td_pfn_vSoundCallback fnCB;
    long			lParam;
	SndBool			bNeedCallback;

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

	snd_assert(buffer);

	//jvdbg("SetParSxdWave");

	if (buffer->dwFlags & FLAG_PLAYING) {
		if (test_voice_is_active(buffer))
		{
			if (buffer->dwFlags&FLAG_VIRTUAL)
			{
				setVirtualparam_DSBuffer(buffer,par);
			} else
			{
				if ((buffer->Attack) && (!(buffer->dwFlags & FLAG_LOOPING)))
				{
					set_param_DSBuffer(buffer->Attack,buffer->Attack3D,par,buffer,FALSE);
					//##DOM
					#ifdef USE_EAX
					set_param_eax(buffer->AttackEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
					#endif
				}
        
				if ((buffer->Loop) &&(buffer->dwFlags & FLAG_LOOPING))
				{
					set_param_DSBuffer(buffer->Loop,buffer->Loop3D,par,buffer,FALSE);
					//###DOM
					#ifdef USE_EAX
					set_param_eax(buffer->LoopEAX,par->ucReverb,buffer->uRes.pstPtr->uRes.stSample.bReverbable);
					#endif

				}
			}
        
			return TRUE;
		}
		else
		{
			// voice finished
			jvdbg("voice finished");	
			fnCB=buffer->fn_callback;
			lParam=buffer->par_callback;
			bNeedCallback=buffer->dwFlags & FLAG_WAITINGCALLBACK;

			fn_vDeactivateVoiceTmp(buffer);

			if (bNeedCallback)
			{
				if (fnCB)
					(*fnCB)(lParam);
			}
				
			return FALSE;
		}
	} else	
		return FALSE;
#else
	return FALSE;
#endif
}

/*------------------------------------------------------------------------------
    FUNCTION: fn_vVirtualStopSxdWave
    ROLE: Virtual management of SND_fn_vStopSxdWave after ForceRelease	
	MODIFICATION:
	- 28/08/98 - Joel Vibert - Created
------------------------------------------------------------------------------*/
void fn_vVirtualStopSxdWave(BUFFER_DISPO* buffer)
{
	buffer->dwFlags&=~FLAG_PLAYING;    //indicate that the sound stopped
	buffer->dwFlags&=~FLAG_WAITINGLOOP;

	if (buffer->dwFlags&FLAG_WAITINGCALLBACK) //call callback if necessary and possible
	{
		buffer->dwFlags&=~FLAG_WAITINGCALLBACK;	
		if (buffer->fn_callback)
		{
			(*buffer->fn_callback)(buffer->par_callback);
		} //fnCB
	}//FLAG_WAITINGCALLBACK
}

void SND_CALL SND_fn_vStopSxdWave(long voice)
{
#ifndef DISABLE_WAVE
	BUFFER_DISPO*				buffer = ((BUFFER_DISPO*)voice);
	SND_td_pfn_vSoundCallback	fnCB;
    long						lParam;
	SndBool						bNeedCallback;

	snd_assert(buffer);

	if (buffer->dwFlags&FLAG_VIRTUAL)
	{
		//Virtual management of SND_fn_vStopSxdWave after ForceRelease
		fn_vVirtualStopSxdWave(buffer);
		return;
	}
 
	if (driver_SND_OK == FALSE)
		return ;	

	jvdbg("StopSxdWav");

	if (buffer->dwFlags & FLAG_PLAYING)
	{
		fnCB=buffer->fn_callback;
		lParam=buffer->par_callback;
		bNeedCallback=(buffer->dwFlags & FLAG_WAITINGCALLBACK);

		fn_vDeactivateVoiceTmp(buffer);

		if (bNeedCallback)
		{
			if (fnCB)
				(*fnCB)(lParam);
		}
	}
#endif
}

void SND_CALL SND_fn_vPauseSxdWave(long voice)
{
#ifndef DISABLE_WAVE
	snd_assert(voice);
	if (driver_SND_OK == FALSE) {
		return ;
	}
	jvdbg("Pause SxdWave");
    pause_voice((BUFFER_DISPO*)voice);
#endif
}

void SND_CALL SND_fn_vResumeSxdWave(long voice)
{
#ifndef DISABLE_WAVE
	snd_assert(voice);
	if (driver_SND_OK == FALSE) {
		return ;
	}
	jvdbg("Resume SxdWave");
   resume_voice((BUFFER_DISPO*)voice);
#endif
}

void SND_fn_vDesInitSxdWave(void)
{
#ifndef DISABLE_WAVE
	jvdbg("desinitSxdWav");
	if (driver_SND_OK) {
		if (eTimerState == TIMER_ON)
		{
			eTimerState=TIMER_OFF;
			PostThreadMessage(id_snd_thread,snd_msg_ExitThread,0,0);
			WaitForSingleObject(handle_snd_thread,100);
			CloseHandle(handle_snd_thread);
		}

		//###DOM
		#ifdef USE_EAX
		SND_fn_vDesInitEaxModule();
		#endif

		if(lpListener3D) {
			IDirectSound3DBuffer_Release(lpListener3D);
			lpListener3D = NULL;
		}
		if(lpDSPrimBuffer) {
			IDirectSoundBuffer_Release(lpDSPrimBuffer);
			lpDSPrimBuffer = NULL;
		}

		if (lpWaveFormatPrimBuffer)
		{
			SND_fn_vFreeSnd(lpWaveFormatPrimBuffer);
			lpWaveFormatPrimBuffer=NULL;
		}
		destroy_listGUID();

#ifdef A3D
		if (pIA3d) {
			IA3d_Release(pIA3d);
			pIA3d = NULL;
		}
#endif
				
		desinit_DSOUND(&lpDirectSoundObject); //free DirectSound object
		
		/*if (lpDirectSoundObject) {
			IDirectSound_Release(lpDirectSoundObject);
			lpDirectSoundObject = NULL;
		}*/
		driver_SND_OK = FALSE;
	}

#endif
}

///////////////////////////////////////////////////////
// NAME: SND_fn_bFreeMemOnMaxiSound
//
// ROLE: frees MIDI memory on maxisound cards, to get a maximum number of buffers;
// returns true if it has been freed
// AUTHOR: Joel Vibert
///////////////////////////////////////////////////////
#ifndef DISABLE_WAVE
BOOL SND_fn_bFreeMemOnMaxiSound()
{	 
     LPKSPROPERTYSET lpKsPropertySet;
	 ULONG ulSupport;

	 if (lpDSPrimBuffer==NULL) return FALSE;
	 if (IDirectSoundBuffer_QueryInterface(lpDSPrimBuffer,&IID_IKsPropertySet,(void**)&lpKsPropertySet)
	                   !=S_OK) return FALSE;
     if (IKsPropertySet_QuerySupport(lpKsPropertySet,&DSPROPERTY_Dream3D,
									   DSPROPERTY_DREAM3D_NOMIDI,&ulSupport)
	              ==E_NOTIMPL) return FALSE;
	 return (IKsPropertySet_Set(lpKsPropertySet,&DSPROPERTY_Dream3D,
							  DSPROPERTY_DREAM3D_NOMIDI, NULL, 0, NULL, 0)
					==DS_OK)? TRUE: FALSE;
}
#endif

#ifndef DISABLE_WAVE
int fn_iInitExSxdWave(SND_tdstInitStruct *pInitStruct, int bRestart)
{
    HRESULT			res;
    //LPGUID			lpGuid;
	DWORD			dwSpeakerConfig,dwSpeakerGeometry;
	DWORD			OutputAvgBytesPerSec;
	int i;
#ifdef USE_DEBUGFILE
	char s[100];
#endif

    //lpGuid=NULL;
	//iNumGUID = 0;
	driver_SND_OK = FALSE;
	lpListener3D = NULL;

	jvdbg("init sxdWav");

	if (!bRestart)
	{
		bReleasedForced=FALSE;
		ulForcedFlags=0;	
		memcpy(&InitStructSaved,pInitStruct,sizeof(InitStructSaved));
	} else
	{
		destroy_listGUID(); //added 13/11/98
	}

	init_listGUID();

	if (init_DSoundChosen(&InitStructSaved)==C_INIT_FAILED)
	{
		return C_INIT_FAILED;
	}	

	CreatePrimaryBuffer(&InitStructSaved);
	if (lpDirectSoundObject ==NULL)
	{		
		destroy_listGUID();
		return C_INIT_FAILED;
	} 

	if (!bRestart)
	{
		for (i=0; i<NB_VOICES; i++) //reset buffer content
		{
			memset(&buffer_dispo[i],0,sizeof(BUFFER_DISPO));
		}
		jvdbg("apres memset buff_dispo");
	}

    if (SND_fn_bGetWantedSpeakerConfigurationSxdWave(&dwSpeakerConfig,&dwSpeakerGeometry))
	{
		SND_fn_bChangeCurrentSpeakerConfigurationSxdWave(dwSpeakerConfig,dwSpeakerGeometry);
	}
	else
	{
		SND_fn_bGetCurrentSpeakerConfigurationSxdWave(&dwSpeakerConfig,&dwSpeakerGeometry);
		SND_fn_vChangeWantedSpeakerConfigurationSxdWave(dwSpeakerConfig,dwSpeakerGeometry);
	}

	if (!bRestart)
	{
		//ucMainVol = 127;
    				//free MIDI memory on maxi sounds cards
		if (SND_fn_bFreeMemOnMaxiSound())
			jvdbg("mem freed on MaxiSound");
	}

	// Set the listener Rolloff factor
    IDirectSound3DListener_SetDistanceFactor(lpListener3D,DISTANCE_FACTOR,DS3D_IMMEDIATE);
    IDirectSound3DListener_SetRolloffFactor(lpListener3D,ROLLOFF_FACTOR,DS3D_IMMEDIATE);
    IDirectSound3DListener_SetDopplerFactor(lpListener3D,DOPPLER_FACTOR,DS3D_IMMEDIATE);

    IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,NULL,0,&sizeoflpWaveFormatPrimBuffer);
    lpWaveFormatPrimBuffer=(LPWAVEFORMATEX)SND_fn_pvMallocSnd(sizeoflpWaveFormatPrimBuffer);
    IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer,sizeoflpWaveFormatPrimBuffer,NULL);

    if (g_b_ChooseOutputFormat) //apply chosen format with maximal frequency set to MAXREALFREQ
    {
#ifdef USE_MAXREALFREQ
		if (OutputResChosen>MAXREALFREQ) OutputResChosen=MAXREALFREQ;
#endif

        lpWaveFormatPrimBuffer->wBitsPerSample=OutputResChosen;
        lpWaveFormatPrimBuffer->nChannels=OutputChanChosen;
        lpWaveFormatPrimBuffer->nSamplesPerSec=OutputFreqChosen;
        lpWaveFormatPrimBuffer->nBlockAlign=lpWaveFormatPrimBuffer->nChannels*lpWaveFormatPrimBuffer->wBitsPerSample/8;
		OutputAvgBytesPerSec=lpWaveFormatPrimBuffer->nSamplesPerSec*lpWaveFormatPrimBuffer->nBlockAlign;
        lpWaveFormatPrimBuffer->nAvgBytesPerSec=OutputAvgBytesPerSec;
	} else //apply default format with maximal frequency set to MAXREALFREQ
	{
		if (IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer,sizeoflpWaveFormatPrimBuffer,NULL)==DS_OK)
		{
			#ifdef USE_DEBUGFILE
			sprintf(s,"freq buff prim=%d",lpWaveFormatPrimBuffer->nSamplesPerSec);
			jvdbg(s);
			#endif
			OutputResChosen=lpWaveFormatPrimBuffer->wBitsPerSample;			
			OutputChanChosen=lpWaveFormatPrimBuffer->nChannels;
			OutputFreqChosen=lpWaveFormatPrimBuffer->nSamplesPerSec;
#ifdef USE_MAXREALFREQ
			if (OutputFreqChosen>MAXREALFREQ) OutputFreqChosen=MAXREALFREQ;
#endif
			lpWaveFormatPrimBuffer->nBlockAlign=lpWaveFormatPrimBuffer->nChannels*lpWaveFormatPrimBuffer->wBitsPerSample/8;
			OutputAvgBytesPerSec=lpWaveFormatPrimBuffer->nSamplesPerSec*lpWaveFormatPrimBuffer->nBlockAlign;
			lpWaveFormatPrimBuffer->nAvgBytesPerSec=OutputAvgBytesPerSec;
		} else
		{			
			SND_fn_vDesInitSxdWave();
			driver_SND_OK = FALSE;
            return C_INIT_FAILED;        
		}
	}

	//apply format to primary buffer
    while (((res=IDirectSoundBuffer_SetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer))!=DS_OK)
#ifdef USE_MAXREALFREQ
		|| (OutputFreqChosen>MAXREALFREQ)
#endif
		)
    {
		if ((res==DSERR_BADFORMAT) &&  (OutputFreqChosen>11025))
        { //bad format
				OutputFreqChosen/=2;
				lpWaveFormatPrimBuffer->nSamplesPerSec=OutputFreqChosen;
				OutputAvgBytesPerSec/=2;
                lpWaveFormatPrimBuffer->nAvgBytesPerSec=OutputAvgBytesPerSec;
        } else
		{
			if ((res==DSERR_BADFORMAT) &&  (OutputFreqChosen<=11025))
				break; //cannot recover from error
			//good format
			if (IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer,sizeoflpWaveFormatPrimBuffer,NULL)==DS_OK)
			{
					
				#ifdef USE_DEBUGFILE
				sprintf(s,"freq buff prim=%d",lpWaveFormatPrimBuffer->nSamplesPerSec);
				jvdbg(s);
				#endif
#ifdef USE_MAXREALFREQ
				if ((lpWaveFormatPrimBuffer->nSamplesPerSec>MAXREALFREQ)&& (OutputFreqChosen>11025))
				{
					OutputFreqChosen/=2;
					lpWaveFormatPrimBuffer->nSamplesPerSec=OutputFreqChosen;
					OutputAvgBytesPerSec/=2;
					lpWaveFormatPrimBuffer->nAvgBytesPerSec=OutputAvgBytesPerSec;
				} else
#endif
					break; //Ok
			} else break; //format changed Ok, but unable to check it=>Ok
		} //good format
    } //while

#ifdef USE_DEBUGFILE
	if (IDirectSoundBuffer_GetFormat(lpDSPrimBuffer,lpWaveFormatPrimBuffer,sizeoflpWaveFormatPrimBuffer,NULL)==DS_OK)
	{					
			sprintf(s,"LastCheck: freq buff prim=%d",lpWaveFormatPrimBuffer->nSamplesPerSec);
			jvdbg(s);
	}
	if (sizeof(tdstBlockResourceMem)<sizeof(tdstBlockResourceMemDX3D))
	{
		sprintf(s,"sizeof(tdstBlockResourceMem)=%d<sizeof(tdstBlockResourceMemDX3D)=%d!!!!!!!!!",
			sizeof(tdstBlockResourceMem),sizeof(tdstBlockResourceMemDX3D)
			);
		jvdbg(s);
	} else
	{
		sprintf(s,"NB: sizeof(tdstBlockResourceMem)=%d; sizeof(tdstBlockResourceMemDX3D)=%d",
			sizeof(tdstBlockResourceMem),sizeof(tdstBlockResourceMemDX3D)
			);
		jvdbg(s);
	}
#endif

    if (res!=DS_OK)
    {
		SND_fn_vDesInitSxdWave();
		driver_SND_OK = FALSE;
        return C_INIT_FAILED;
    }
    
	jvdbg("create sound thread");

    stereo_reverse = TRUE;
	eTimerState=TIMER_ON;
	if ((!bRestart)||(handle_snd_thread==NULL))
		if (!(handle_snd_thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadSnd,0,0,&id_snd_thread)))
    {
		//eTimerState=TIMER_OFF;
		SND_fn_vDesInitSxdWave();
		driver_SND_OK = FALSE;
        return C_INIT_FAILED;
    }

	//###DOM
	#ifdef USE_EAX
	jvdbg("init eax..");
	SND_fn_vInitEaxModule(lpDirectSoundObject);
	#endif

	driver_SND_OK = TRUE;

	jvdbg("End initSxdWav:OK");
	SND_fn_vSetSoundVolumeSxdWave(ucMainVol);

	return C_INIT_OK;
}
#endif

///////////////////////////////////////////////////////
// NAME: SND_fn_iInitSxdWave
//
// ROLE: general init
// returns C_INIT_FAILED if failed
// Last modification: Joel Vibert 24/03/97 format of primary buffer
//         changed to allow more buffers on some PCI cards (maximum frequency)
///////////////////////////////////////////////////////
int SND_fn_iInitSxdWave(SND_tdstInitStruct *pInitStruct)
{
#ifndef DISABLE_WAVE
	return fn_iInitExSxdWave(pInitStruct, FALSE);
#else
	driver_SND_OK = FALSE;
    return C_INIT_FAILED;
#endif
}

SndBool SND_fn_bTestInitSxdWave(void)
{
    return driver_SND_OK;
}

//--------------------------------------------------
//      SND_fnvConvertRsvDiskToMemSxd
//      Entrée:disk=bloxkResDisk à convertir
//              mem=blockMemDisk à générer
//              ptrBegin=pointer onto raw data
//      Retour:néant
//  last modif: 
//		Stéphane RONSE 10/07/98: SND_fnvConvertRsvDiskToMemSxd do all necessary stuff
//			to create a valid BlockResourceMem (i.e. DSound init..)
//     Joel Vibert 03/04/98: ptrBegin is now the adress where the data
//            have really been loaded
//------------------------------------------------
void SND_fn_vConvertResDiskToMemSxdWave(tdstBlockResourceDisk *disk
                                                 ,tdstBlockResourceMem *mem
                                                 ,void* ptrBegin)
{
	DSCAPS  dscaps;
    HRESULT res;
	DWORD   dwMaxBuff;
	tdstSampleMemoryDX3D *pSampleMem;
	DWORD   dwSizeOneShot=0,dwSizeLoop=0;
	BOOL    bAttack,bLoop; //resource with loop/attack
    tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
    snd_assert((disk->uRes.stSample.eZip==SAMPLE_PCM)||(disk->uRes.stSample.eZip==SAMPLE_AIFF));

    memDX3D->Id=disk->Id;
    memDX3D->eType=disk->eType;
    memDX3D->eStorage=disk->eStorage;
    memDX3D->ucVolume=disk->ucVolume;
    memDX3D->bIsLoaded=TRUE;
    //specific to type of resource
    //
    memDX3D->uRes.stSample.eZip=SAMPLE_PCM;
    memDX3D->uRes.stSample.bPitchable=disk->uRes.stSample.bPitchable;
    memDX3D->uRes.stSample.bVolable=disk->uRes.stSample.bVolable;
    memDX3D->uRes.stSample.bPanable=disk->uRes.stSample.bPanable;
    memDX3D->uRes.stSample.bSpacable=disk->uRes.stSample.bSpacable;
    memDX3D->uRes.stSample.bReverbable=disk->uRes.stSample.bReverbable;
    memDX3D->uRes.stSample.bStream=disk->uRes.stSample.bStream;
    memDX3D->uRes.stSample.bLoop=disk->uRes.stSample.bLoop;
    memDX3D->uRes.stSample.ulFreq=disk->uRes.stSample.ulFreq;
    memDX3D->uRes.stSample.uwResolution=disk->uRes.stSample.uwResolution;
    memDX3D->uRes.stSample.uwNbChannels=disk->uRes.stSample.uwNbChannels;
	if (disk->uRes.stSample.bStream) 
	{
		strcpy(memDX3D->uRes.stSample.uData.stStream.szFileName,disk->uRes.stSample.czFileName);
        memDX3D->uRes.stSample.uData.stStream.ulOffsetFirst=disk->ulDataOffset;
		if (!disk->uRes.stSample.bLoop)
		{
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLoop=0;
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataSize+disk->ulDataOffset;
		}
		else
		{
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLoop=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop;
			if (disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength<disk->ulDataSize) //02/11/98
				memDX3D->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength;
			else
				memDX3D->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataOffset+disk->ulDataSize;
		}
	} //stream
	else 
	{
		memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags=0; //&=~FLAG_LOADEDBUFFS;

		if (ptrBegin!=NULL)
		{
			memDX3D->uRes.stSample.uData.stMem.lpBufferLoop=NULL;
			pSampleMem=&((tdstBlockResourceMemDX3D*)mem)->uRes.stSample.uData.stMem;

	       //--------buffer one-shot or attack...		
		    if ((!disk->uRes.stSample.bLoop) || (disk->uRes.stSample.ulStartLoop>0))
			{
				jvdbg("(one-shot)");
 			
				if (disk->uRes.stSample.bLoop)                        
					dwSizeOneShot=
						disk->uRes.stSample.ulStartLoop; //+disk->uRes.stSample.uwNbChannels*(disk->uRes.stSample.uwResolution/8)/4; 
	            else
		            dwSizeOneShot=disk->ulDataSize;
			
				pSampleMem->pcDataAttack=ptrBegin;
				if (pSampleMem->pcDataAttack==NULL)
				{
					jvdbg("pbAlloc!!!");				
					pSampleMem->lSizeAttackBuff=0;
					bAttack=FALSE;
				}
				else
				{
					//memcpy(pSampleMem->pcDataAttack,ptrBegin,dwSizeOneShot);				
					pSampleMem->lSizeAttackBuff=dwSizeOneShot;
					bAttack=TRUE;
				}
			} 
			else
			{
				bAttack=FALSE;
				pSampleMem->lSizeAttackBuff=0;
			}

			//------looping buffer
			if (disk->uRes.stSample.bLoop)
			{
				jvdbg("(looping buffer)");

				if (disk->uRes.stSample.bLoop)
				{
					if (disk->uRes.stSample.ulLoopLength+disk->uRes.stSample.ulStartLoop<disk->ulDataSize) //02/11/98
						dwSizeLoop=disk->uRes.stSample.ulLoopLength; 
					else
					{
						dwSizeLoop=disk->ulDataSize-disk->uRes.stSample.ulStartLoop;
						if (dwSizeLoop<0) dwSizeLoop=0;
					}
				}
        
				pSampleMem->pcDataLoop=ptrBegin; //SND_fn_pvMallocSnd(sizeptr1);
				if (pSampleMem->pcDataLoop==NULL)
				{
					jvdbg("pbAllocLoop!!!");			
					pSampleMem->lSizeLoopBuff=0;
					bLoop=FALSE;
				}
				else
				{
					//memcpy((char *)(pSampleMem->pcDataLoop)+pSampleMem->lSizeAttackBuff,
					//	(char *)(ptrBegin)+pSampleMem->lSizeAttackBuff,dwSizeLoop);
					
					if (bAttack)
						pSampleMem->pcDataLoop+=pSampleMem->lSizeAttackBuff; //20/11/98
					memcpy((char *)pSampleMem->pcDataLoop,
						(char *)(ptrBegin)+pSampleMem->lSizeAttackBuff,dwSizeLoop);


					pSampleMem->lSizeLoopBuff=dwSizeLoop;
					bLoop=TRUE;
				}			
			} else
		{
			pSampleMem->lSizeLoopBuff=0;
			bLoop=FALSE;
		}
//
		if (bAttack)
			memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_LOADEDATT;			
		if (bLoop)
			memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_LOADEDLOOP;

		}
	} //ptrBegin

	//--2° part: prepare buffers
	bAttack=((memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDATT)!=0);
	bLoop=((memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDLOOP)!=0);

	memset(&dscaps,0,sizeof(DSCAPS));
	dscaps.dwSize=sizeof(DSCAPS);
	res=DSoundTry(IDirectSound_GetCaps(lpDirectSoundObject,&dscaps));


	//tests modified 07/12/98
	if (dscaps.dwMaxHw3DStaticBuffers<=0)
	{
		if (dscaps.dwMaxHw3DStreamingBuffers<=0)  //2D card=>emulation;
			dscaps.dwFreeHw3DStreamingBuffers=CRITICAL_BUFF_NB+2; //dummy value to simulate free buffers
		dwMaxBuff=dscaps.dwFreeHw3DStreamingBuffers;
	}
	else
	{
#ifdef SIMU_CRITICAL_BUFF_NB
		dscaps.dwFreeHw3DStaticBuffers-=SIMU_BUFF_NB;
		if ((long int)dscaps.dwFreeHw3DStaticBuffers<0)
			dscaps.dwFreeHw3DStaticBuffers=0;
#endif
		dwMaxBuff=dscaps.dwFreeHw3DStaticBuffers;		
	}

	if (fn_bCreateInitialBuffers(dwMaxBuff,
			(tdstBlockResourceMemDX3D*)mem,			
			bLoop, bAttack)==FALSE)
	{
			jvdbg("fn_bCreateInitialBuffers=>FALSE!!!");
			//snd_assert(FALSE);
	}	
}

//old version of SND_fn_vConvertResDiskToMemSxdWave 
//for scripts loading: hybrid version (buffers already created by CreateSample)
//should be updated in the future
/*
void SND_fn_vConvertResScriptsToMemSxdWave(tdstBlockResourceDisk *disk
                                                 ,tdstBlockResourceMem *mem
                                                 ,void* ptrBegin)
{
#ifdef USE_DEBUGFILE
	char s[200];
#endif
    tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
    snd_assert((disk->uRes.stSample.eZip==SAMPLE_PCM)||(disk->uRes.stSample.eZip==SAMPLE_AIFF));

    memDX3D->Id=disk->Id;
    memDX3D->eType=disk->eType;
    memDX3D->eStorage=disk->eStorage;
    memDX3D->ucVolume=disk->ucVolume;
    memDX3D->bIsLoaded=TRUE;
    //specific to type of resource
    //
    memDX3D->uRes.stSample.eZip=SAMPLE_PCM;
    memDX3D->uRes.stSample.bPitchable=disk->uRes.stSample.bPitchable;
    memDX3D->uRes.stSample.bVolable=disk->uRes.stSample.bVolable;
    memDX3D->uRes.stSample.bPanable=disk->uRes.stSample.bPanable;
    memDX3D->uRes.stSample.bSpacable=disk->uRes.stSample.bSpacable;
    memDX3D->uRes.stSample.bReverbable=disk->uRes.stSample.bReverbable;
    memDX3D->uRes.stSample.bStream=disk->uRes.stSample.bStream;
    memDX3D->uRes.stSample.bLoop=disk->uRes.stSample.bLoop;
    memDX3D->uRes.stSample.ulFreq=disk->uRes.stSample.ulFreq;
    memDX3D->uRes.stSample.uwResolution=disk->uRes.stSample.uwResolution;
    memDX3D->uRes.stSample.uwNbChannels=disk->uRes.stSample.uwNbChannels;
	if (disk->uRes.stSample.bStream) {
		strcpy(memDX3D->uRes.stSample.uData.stStream.szFileName,disk->uRes.stSample.czFileName);
        memDX3D->uRes.stSample.uData.stStream.ulOffsetFirst=disk->ulDataOffset;
		if (!disk->uRes.stSample.bLoop)
		{
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLoop=0;
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataSize+disk->ulDataOffset;
		}
		else
		{
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLoop=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop;
			memDX3D->uRes.stSample.uData.stStream.ulOffsetLast=disk->ulDataOffset+disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength;
		}
	} else {
		if (ptrBegin!=NULL)
		{
			memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags=0; //&=~FLAG_LOADEDBUFFS;
			if (((tdstConvertParam*)ptrBegin)->bAttack)
				memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_LOADEDATT;
			if (((tdstConvertParam*)ptrBegin)->bLoop)
				memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_LOADEDLOOP;

			memDX3D->uRes.stSample.uData.stMem.lpBufferLoop=NULL;
		}
	}
}
*/

//--------------------------------------------------
//      SND_fnvLoadRsvDisk
//      Entrée:disk=blockResDisk à convertir
//              mem=blockMemDisk à générer
//      Retour:néant
//------------------------------------------------
void SND_fn_vLoadResFromDiskSxdWave(tdstBlockResourceDisk* disk,tdstBlockResourceMem* mem)
{
}


//  Creation of DirectX buffers; data will be loaded later
//	Parem :disk=blockresourceDisk (input)
//		ptrpar=pointer on an array of 2 LPSOUNDBUFFER	
//joel: no longer called for bin loading; to be tested otherwise
SndBool fn_CreateResSample(tdstBlockResourceDisk *disk,void **ptrpar)
{
    WAVEFORMATEX pcmwf;
    HRESULT res;
	LPDIRECTSOUNDBUFFER lpBufAttack=NULL,lpBufLoop=NULL;
    DSBUFFERDESC dsbdesc;

	((LPDIRECTSOUNDBUFFER*)(*ptrpar))[0]=NULL;
	((LPDIRECTSOUNDBUFFER*)(*ptrpar))[1]=NULL;

    if (!disk->uRes.stSample.bStream)
    {
		jvdbg("create resSample");
            //--------buffer one-shot
            if ((!disk->uRes.stSample.bLoop) || (disk->uRes.stSample.ulStartLoop>0))
            {
                // Set up wave format structure.
                memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
                pcmwf.wFormatTag=WAVE_FORMAT_PCM;
                pcmwf.nChannels=disk->uRes.stSample.uwNbChannels;
                pcmwf.nSamplesPerSec=disk->uRes.stSample.ulFreq;
                pcmwf.wBitsPerSample=disk->uRes.stSample.uwResolution;
                pcmwf.nBlockAlign=pcmwf.nChannels*pcmwf.wBitsPerSample/8;
                pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
                // Set up DSBUFFERDESC structure.
                memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
                dsbdesc.dwSize=sizeof(DSBUFFERDESC);
                
//#ifdef NEW_FLAGS_MANAGEMENT
                /*dsbdesc.dwFlags= DSBCAPS_STATIC; 
				if (disk->uRes.stSample.bPitchable) dsbdesc.dwFlags |= DSBCAPS_CTRLFREQUENCY;
				if (disk->uRes.stSample.bVolable) dsbdesc.dwFlags |= DSBCAPS_CTRLVOLUME;
				if (disk->uRes.stSample.bPanable) dsbdesc.dwFlags |= DSBCAPS_CTRLPAN;
				if (disk->uRes.stSample.bSpacable) dsbdesc.dwFlags |= DSBCAPS_CTRL3D;*/
				//if (bUse3d)
					dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRL3D |DSBCAPS_CTRLDEFAULT
#ifdef AVOID_FOCUS_PB
						|DSBCAPS_GLOBALFOCUS
#endif
						; 
				//else dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRLDEFAULT;
//#else
//                dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRL3D |DSBCAPS_CTRLDEFAULT; 
//#endif
				
                if (disk->uRes.stSample.bLoop)
				{
                    //dsbdesc.dwBufferBytes=disk->uRes.stSample.ulStartLoop+pcmwf.nAvgBytesPerSec/4;
					if (disk->uRes.stSample.ulStartLoop<disk->ulDataSize) //corrected+checked 02/11/98
						dsbdesc.dwBufferBytes=disk->uRes.stSample.ulStartLoop;
					else dsbdesc.dwBufferBytes=disk->ulDataSize;
				}
                else
                    dsbdesc.dwBufferBytes=disk->ulDataSize;
                dsbdesc.lpwfxFormat=&pcmwf;
                // create buffer
                if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&dsbdesc,&lpBufAttack,NULL))!=DS_OK)
				{
					((LPDIRECTSOUNDBUFFER*)(*ptrpar))[0]=NULL;
					((LPDIRECTSOUNDBUFFER*)(*ptrpar))[1]=NULL;
					return FALSE;
				}
            }

            //------buffer bouclant
            if (disk->uRes.stSample.bLoop)
            {//buffer de loop
                // Set up wave format structure.
                memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
                pcmwf.wFormatTag=WAVE_FORMAT_PCM;
                pcmwf.nChannels=disk->uRes.stSample.uwNbChannels;
                pcmwf.nSamplesPerSec=disk->uRes.stSample.ulFreq;
                pcmwf.wBitsPerSample=disk->uRes.stSample.uwResolution;
                pcmwf.nBlockAlign=pcmwf.nChannels*pcmwf.wBitsPerSample/8;
                pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
                // Set up DSBUFFERDESC structure.
                memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
                dsbdesc.dwSize=sizeof(DSBUFFERDESC);

				dsbdesc.dwFlags= DSBCAPS_STATIC | DSBCAPS_CTRL3D |DSBCAPS_CTRLDEFAULT
#ifdef AVOID_FOCUS_PB
					|DSBCAPS_GLOBALFOCUS
#endif
					;
                
				//check total size 02/11/98
				if (disk->uRes.stSample.ulStartLoop>0)
				{
					if (disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength<disk->ulDataSize)
						dsbdesc.dwBufferBytes=disk->uRes.stSample.ulLoopLength;
					else
					{   //size: too big!
						dsbdesc.dwBufferBytes=disk->ulDataSize
								-disk->uRes.stSample.ulStartLoop;
						if (dsbdesc.dwBufferBytes<0)
							dsbdesc.dwBufferBytes=0;
					}
				} else
					dsbdesc.dwBufferBytes=disk->uRes.stSample.ulLoopLength; 

				//if (dsbdesc.dwBufferBytes>uRes.stSample.ulLoopLength)


                dsbdesc.lpwfxFormat=&pcmwf;
                // create buffer
				lpBufLoop=NULL;
				if (dsbdesc.dwBufferBytes) //added 02/11/98
                if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject,&dsbdesc,&lpBufLoop,NULL))!=DS_OK)
				{
					if (lpBufAttack) IDirectSound_Release(lpBufAttack);
					((LPDIRECTSOUNDBUFFER*)(*ptrpar))[0]=NULL;
					((LPDIRECTSOUNDBUFFER*)(*ptrpar))[1]=NULL;
					return FALSE;
				}
            }

            ((LPDIRECTSOUNDBUFFER*)(*ptrpar))[0]=lpBufAttack;
            ((LPDIRECTSOUNDBUFFER*)(*ptrpar))[1]=lpBufLoop;
    } else jvdbg("create res: Stream");

    return TRUE;
}


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

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

		return FALSE;
	}

	SND_fn_vConvertResDiskToMemSxdWave(disk,mem,pData);

	//05/11/98
	((tdstBlockResourceMemDX3D*)mem)->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_ALLOCATED;

    return TRUE;
#else 
	return FALSE;
#endif
}


///////////////////////////////////////////////////////
// NAME: SND_fn_bLoadResBinarySxdWave
//
// ROLE: load samples into memory
// Last modification:
// Joel Vibert 06/11/98: allocated flag
// Joel Vibert 03/04/98: normal use of SND_fn_vConvertResDiskToMemSxdWave
//                       but buffer creation still postponed
// Joel Vibert 24/03/98: buffers may be created later (during play or attack2loop)
//      and samples are kept into memory
///////////////////////////////////////////////////////
SndBool SND_fn_bLoadResBinarySxdWave(tdstBlockResourceDisk* disk,tdstBlockResourceMem* mem, char* pDataBlock)
{
#ifndef DISABLE_WAVE 
    char    file_name[256];
	void    *pData;
	tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
	tdstTypeSampleDisk* pSample = &disk->uRes.stSample;

	if (driver_SND_OK == FALSE) {
		return FALSE;
	}
 
	// Initialize possibilities of sound...
	memset(mem,0,sizeof(tdstBlockResourceMem));

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

	if (disk->eStorage == TYPE_MEGAFILE) 
	{
		pData = (void*)(pDataBlock+disk->ulDataOffset);
	} 
	else 
	{
		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_vFreeSnd(pData);

			return FALSE;
		} else
		{
			((tdstBlockResourceMemDX3D*)mem)->uRes.stSample.uData.stMem.ucLoadedFlags|=FLAG_ALLOCATED;
		}
	}
	
    SND_fn_vConvertResDiskToMemSxdWave(disk,mem,pData);

return TRUE;
#else
	return FALSE;
#endif
}


//--------------------------------------------------
//      SND_fnvUnLoadRsvDisk
//      IN:mem=blockMemDisk to be freed
//      OUT: none
//      last modification: Joel Vibert 24/03/98
//------------------------------------------------
void SND_fn_vUnLoadResSxdWave(tdstBlockResourceMem* mem)
{
#ifndef DISABLE_WAVE
	int i;
    tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
	char *pData;
#ifdef USE_DEBUGFILE
	char s[80];
#endif

	if (driver_SND_OK == FALSE) {
		return ;
	}
	jvdbg("unloadResSxdWav");
	snd_assert(!memDX3D->uRes.stSample.bStream);
	
	for (i=0; i<NB_VOICES; i++)
	{
		if (memDX3D==buffer_dispo[i].uRes.pstPtr)
		{
			if (buffer_dispo[i].dwFlags)
			{
#ifdef USE_DEBUGFILE
				sprintf(s,"  => Unload %d",i);
				jvdbg(s);
#endif
				fn_vDeactivateVoice(&buffer_dispo[i]);				
			}
		}
	}

	memDX3D->uRes.stSample.uData.stMem.lpBufferLoop=NULL;

//  05/11/98
	if (memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_ALLOCATED)
	{
		pData=memDX3D->uRes.stSample.uData.stMem.pcDataAttack;
		if (pData)
		{
			SND_fn_vFreeSnd(pData);
			memDX3D->uRes.stSample.uData.stMem.pcDataAttack=NULL;
		} else
		{
			pData=memDX3D->uRes.stSample.uData.stMem.pcDataAttack;
			if (pData)
			{
				SND_fn_vFreeSnd(pData);
				memDX3D->uRes.stSample.uData.stMem.pcDataAttack=NULL;
			}
		}
	}	
#endif
}

//---------------------------------------------------------
//      IsResLoadedSample: test whether a resource is operational
//      Input: valid blockmem
//      Output:TRUE si SndPlay acceptable
//      modified: Joel Vibert flags 24/03/98
//---------------------------------------------------------
SndBool SND_fn_bIsResLoadedSxdWave(tdstBlockResourceMem *mem)
{
#ifndef DISABLE_WAVE
	tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
	if (driver_SND_OK == FALSE) {
		return FALSE;
	}
	if (memDX3D->uRes.stSample.uData.stMem.ucLoadedFlags)
		return TRUE;
	else
		return FALSE;
#else
	return FALSE;
#endif
}

void SND_fn_vSetResUnloadedSxdWave(tdstBlockResourceMem *pResMem)
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return ;
	}
#endif
}



//----------------------------------------------------------------------
//  management of client buffers
//----------------------------------------------------------------------
/*
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
	WAVEFORMATEX pcmwf;
    DSBUFFERDESC dsbdesc;
    HRESULT res;
	DWORD sizeptr1,sizeptr2,dwPitch;
	LPVOID ptr1,ptr2;
    p_tdstBufferClient buffer;
    
	if (driver_SND_OK == FALSE) {
		return -1;
	}

	buffer=SND_fn_pvMallocSnd(sizeof(tdstBufferClient));    
    SNDLST2_M_DynamicInitElement(buffer);
    buffer->lUserId=user_id;
    buffer->uPseudoRes.pstPtr=&(buffer->tdstPseudoRes);
    buffer->ulSamples=nb_samples;
    buffer->ulDecalBytes=((uwResolution==8) ? 0 : 1) + ((uwNbChannels==1) ? 0 : 1);
    buffer->pfnCallbackBuffer=callback;
    buffer->ulNextToCompute=0;
    buffer->ulTimeNextToCompute=0;
	buffer->dLastDeltaT=0.0;
	buffer->paused=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=ulFreq;
    buffer->tdstPseudoRes.uRes.stSample.uwResolution=uwResolution;
    buffer->tdstPseudoRes.uRes.stSample.uwNbChannels=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(WAVEFORMATEX));
	pcmwf.wFormatTag=WAVE_FORMAT_PCM;
	pcmwf.nChannels=uwNbChannels;
	pcmwf.nSamplesPerSec=ulFreq;
	pcmwf.wBitsPerSample=uwResolution;
	pcmwf.nBlockAlign=pcmwf.nChannels*pcmwf.wBitsPerSample/8;
	pcmwf.nAvgBytesPerSec=pcmwf.nSamplesPerSec*pcmwf.nBlockAlign;
	// Set up DSBUFFERDESC structure.
	memset(&dsbdesc,0,sizeof(DSBUFFERDESC));
	dsbdesc.dwSize=sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags=DSBCAPS_CTRLDEFAULT; 

    dsbdesc.dwBufferBytes=buffer->ulSamples<<buffer->ulDecalBytes;
	dsbdesc.lpwfxFormat=&pcmwf;
	// create buffer
	if ((res=IDirectSound_CreateSoundBuffer(lpDirectSoundObject
            ,&dsbdesc
            ,&buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop
            ,NULL))==DS_OK) {

		IDirectSoundBuffer_Lock(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,0,dsbdesc.dwBufferBytes,&ptr1,&sizeptr1,&ptr2,&sizeptr2,0);
		callback(user_id,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) {
			IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[par->ucVol]);
			dwPitch=(DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(buffer->tdstPseudoRes.uRes.stSample.ulFreq));
		}
		else {
			IDirectSoundBuffer_SetVolume(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,vol_2_hth_dB[127]);
			dwPitch=buffer->tdstPseudoRes.uRes.stSample.ulFreq;
		}
		IDirectSoundBuffer_SetFrequency(buffer->tdstPseudoRes.uRes.stSample.uData.stMem.lpBufferLoop,dwPitch);

		SNDLST2_M_DynamicAddTail(&buffers_clients.pList,buffer);
	}


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


/*--------------------------------------------------
Release/restore of sound driver.
SND_fn_vReleaseDriverSample: is called for Focus lost
SND_fn_vForceReleaseDriverSample: is called when waveOut is needed for an other module
	The first only of both does release driver (second don't let driver released)
SND_fn_vRestoreDriverSample: is called for Focus gain
SND_fn_vRestoreReleaseDriverSample: is called when waveOut is no more needed 
	When both SND_fn_vReleaseDriverSample & SND_fn_vForceReleaseDriverSample have been called
		, driver is restored only when both SND_fn_vRestoreDriverSample & SND_fn_vForceRestoreDriverSample have been called
bDriverReleased is set to TRUE between SND_fn_vReleaseDriverSample & SND_fn_vRestoreDriverSample
bDriverForcedReleased is set to TRUE between SND_fn_vForceReleaseDriverSample & SND_fn_vForceRestoreDriverSample
---------------------------------------------------------*/
void SND_fn_vReleaseDriverSxdWave(void)
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return ;
	}
#endif
}

void SND_fn_vRestoreDriverSxdWave(void)
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return ;
	}
#endif
}


/*--------------------------------------------------------------------
FUNCTION: SND_fn_vForceReleaseDriverSxdWave
ROLE:     destroy all directsound buffers (to be able to record if half-duplex...) 
MODIFICATIONS:
      - 27/01/99 - Joel Vibert - debugged
      - 28/08/98 - Joel Vibert - 1st implementation
--------------------------------------------------------------------*/
void fn_vForceReleaseClientBuffers(void);
void SND_fn_vForceReleaseDriverSxdWave(void)
{
	BUFFER_DISPO* buffer;
	int j;
#ifdef USE_DEBUGFILE
	char s[100];
#endif

	if (bReleasedForced) return;

	bReleasedForced=TRUE;
	jvdbg("Force release");

	while (bTimerInstance) //27/01/99
		; 

	SND_fn_vEnterCriticalSectionThreadSnd();//27/01/99

	jvdbg("in critical section Force release");

    fn_vForceReleaseClientBuffers();

	jvdbg("after force release client");
	
	for (j=0; j<NB_VOICES; j++)		//deactivate activated buffers + unused ones
	{
		#ifdef USE_DEBUGFILE
			sprintf(s,"(Force release %d) flags: /*%lX*/", j/*,buffer->dwFlags*/);
			jvdbg(s);
		#endif
		buffer=&buffer_dispo[j];
		if (buffer->dwFlags & FLAG_BUFFS) //allocated buffers
		{									
		#ifdef USE_DEBUGFILE
			sprintf(s,"Force release deactivate voice %d (F%ld; A%d;L%d)", buffer-buffer_dispo,buffer->dwFlags,
			buffer->Attack!=NULL, buffer->Loop!=NULL);
			jvdbg(s);
		#endif
			//buffer->dwFlags = 0;
			buffer->dwFlags&=~(FLAG_BUFFS);
			buffer->dwFlags|=FLAG_VIRTUAL;
			//buffer->fn_callback=NULL;
			//buffer->par_callback = 0;
			//buffer->uRes.pstPtr=NULL;
			//buffer->lAccessTime=0;

			if (buffer->Attack) //deactivate attack directsound buffer
			{
			#ifdef USE_DEBUGFILE
				sprintf(s,"  Force DEACTIVATE attack(%ld; buf No %d)",buffer->dwSizeAttack, buffer-buffer_dispo);
				jvdbg(s);
			#endif
				IDirectSoundBuffer_Stop(buffer->Attack);
				if (buffer->Attack3D)
				{
					IDirectSound3DBuffer_Release(buffer->Attack3D);
					buffer->Attack3D = NULL;
				}
				IDirectSoundBuffer_Release(buffer->Attack);
				buffer->Attack=NULL;
			#ifdef DBG_NB_BUF
				dbgBuffAtt--; dbgBuff--;
				sprintf(s,"  DEACTIVATE attack=>%d/%d + ext %d",	dbgBuffAtt,dbgBuff,dbgBuffExt);
				jvdbg(s);
			#endif //DBG_NB_BUF
			} //deactivate attack directsound buffer

			if (buffer->Loop) //deactivate looping directsound buffer
			{
			#ifdef USE_DEBUGFILE
				sprintf(s,"  Force DEACTIVATE loop(buf No %d))", buffer-buffer_dispo);
				jvdbg(s);
			#endif
				IDirectSoundBuffer_Stop(buffer->Loop);
				if (buffer->Loop3D)
				{	
					IDirectSound3DBuffer_Release(buffer->Loop3D);
					buffer->Loop3D=NULL;
				}
				IDirectSoundBuffer_Release(buffer->Loop);
				buffer->Loop=NULL;
			#ifdef DBG_NB_BUF
				dbgBuffLoop--; dbgBuff--;
				sprintf(s,"  DEACTIVATE loop=>%d/%d + ext %d",	dbgBuffLoop,dbgBuff,dbgBuffExt);
				jvdbg(s);
			#endif //DBG_NB_BUF

			}//deactivate looping directsound buffer
		
		}//FLAG_BUFFS (allocated buffer)

	}//for: deactivate activated buffers + unused ones	
	
	//deactivate client buffers

	
	//deactivate listener+primary buffer
	if(lpListener3D)
	{
		IDirectSound3DBuffer_Release(lpListener3D);
		lpListener3D = NULL;
	}
	if(lpDSPrimBuffer)
	{
		IDirectSoundBuffer_Release(lpDSPrimBuffer);
		lpDSPrimBuffer = NULL;
	}

	desinit_DSOUND(&lpDirectSoundObject); //free DirectSound object

	SND_fn_vQuitCriticalSectionThreadSnd(); //27/01/99
}

/*--------------------------------------------------------------------
FUNCTION: SND_fn_vForceRestoreDriverSxdWave
ROLE:     rebuilds directsound buffers after SND_fn_vForceReleaseDriverSxdWave
MODIFICATIONS:
      - 27/01/99 - Joel Vibert - debugged
      - 28/08/98 -> 01/09/98 - Joel Vibert - 1st implementation
--------------------------------------------------------------------*/
void fn_vForceRestoreClientBuffers(void);
void SND_fn_vForceRestoreDriverSxdWave(void)
{
	BUFFER_DISPO* buffer;

	int j;
	//unsigned char ucSaveMainVol;     //to prevent loss of main volume after new call to SND_fn_iInitSxdWave
	//unsigned long ulSaveForcedFlags; //idem for flags
	unsigned char	ucDSvolume;

#ifdef USE_DEBUGFILE
	char s[100];
	jvdbg("!!!SND_fn_vForceRestoreDriverSxdWave!!!");
#endif
	//ucSaveMainVol = ucMainVol; //init updates volume to default value (max value)=>keep original value
	//ulSaveForcedFlags=ulForcedFlags;

	if (!bReleasedForced) return;
	//if (SND_fn_iInitSxdWave(&InitStructSaved)) return;
	if (fn_iInitExSxdWave(&InitStructSaved, TRUE)==C_INIT_FAILED) return;

	//ucMainVol=ucSaveMainVol;
	//ulForcedFlags=ulSaveForcedFlags;

	SND_fn_vEnterCriticalSectionThreadSnd();
	bReleasedForced=FALSE; //27/01/99

	fn_vForceRestoreClientBuffers();

	for (j=0; j<NB_VOICES; j++)		//reactivate (non client) buffers
	{
		buffer=&buffer_dispo[j];
		if (buffer->dwFlags & FLAG_VIRTUAL) //freed buffer
		{
			if ((buffer->uRes.pstPtr->uRes.stSample.uData.stMem.ucLoadedFlags&FLAG_LOADEDLOOP) //looping
				&&(buffer->dwFlags & (FLAG_PLAYING|FLAG_WAITINGLOOP|FLAG_LOOPING)))  //playing
			{	// the buffer should be reactivated (looping sound that was played)			
					#ifdef USE_DEBUGFILE
					sprintf(s,"Force restore looping voice %d (F%ld; A%d;L%d)", buffer-buffer_dispo,buffer->dwFlags,
						  (buffer->dwFlags & FLAG_PLAYING)!=0,
						  (buffer->dwFlags & (FLAG_WAITINGLOOP|FLAG_LOOPING))!=0
						  );
					jvdbg(s);
					#endif			
					//create secondary buffer again
					if (fn_bCreate2ndBuffer(
							&buffer->uRes.pstPtr->uRes.stSample,
							//buffer->uRes.pstPtr->uRes.stSample.uData.stMem.data->iSizeBuffLoop,
							buffer->uRes.pstPtr->uRes.stSample.uData.stMem.lSizeLoopBuff,
							buffer->uRes.pstPtr->uRes.stSample.uData.stMem.pcDataLoop,
							&buffer->Loop
//#ifdef NEW_FLAGS_MANAGEMENT			
							,(buffer->dwFlags & FLAG_3D)!=0
//#endif
							)==FALSE)
								fn_vDeactivateVoice(buffer);
					else
					{
						buffer->dwFlags|=FLAG_BUFFLOOP; //27/01/99
						if (buffer->dwFlags&FLAG_SETVIRTVOL) /* restore volume */
						{							
							buffer->dwFlags&=~FLAG_SETVIRTVOL;
							//if (buffer->Attack)
							//	IDirectSoundBuffer_SetVolume(buffer->Attack,uwVolume);
/*							if (buffer->Loop)
							{
								ucDSvolume=M_AdaptVolume((buffer->dwFlags&C_VIRTUAL_VOL_MASK)>>C_VIRTUAL_VOL_SHIFT
									,buffer->uRes.pstPtr->uRes.stSample.uwNbChannels
									,FALSE);
								IDirectSoundBuffer_SetVolume(buffer->Loop,vol_2_hth_dB[ucDSvolume]);
							}
							// also update frequency if possible 
							if (buffer->fLastFreq != 0.f)  
							{								
								IDirectSoundBuffer_SetFrequency(buffer->Loop,(DWORD)buffer->fLastFreq);
							}*/
						} //FLAG_SETVIRTVOL
						if (buffer->dwFlags&(FLAG_WAITINGLOOP|FLAG_LOOPING))
							if (buffer->Loop)
							{
								ucDSvolume=M_AdaptVolume((buffer->dwFlags&C_VIRTUAL_VOL_MASK)>>C_VIRTUAL_VOL_SHIFT
									,buffer->uRes.pstPtr->uRes.stSample.uwNbChannels
									,FALSE);
								buffer->dwFlags&=~FLAG_WAITINGLOOP;
								buffer->dwFlags|=FLAG_LOOPING;
								IDirectSoundBuffer_Play(buffer->Loop,0,0,DSBPLAY_LOOPING);

								IDirectSoundBuffer_SetVolume(buffer->Loop,vol_2_hth_dB[ucDSvolume]);
															/* also update frequency if possible */
								if (buffer->fLastFreq != 0.f)  
								{								
									IDirectSoundBuffer_SetFrequency(buffer->Loop,(DWORD)buffer->fLastFreq);
								}
							}

					} //fn_bCreate2ndBuffer
				} //looping
			    else buffer->dwFlags&=~FLAG_PLAYING; //do not restart stop one-shot sounds			
			buffer->dwFlags&=~FLAG_VIRTUAL;
		} //FLAG_VIRTUAL
	} //for
	SND_fn_vQuitCriticalSectionThreadSnd();

	if (ulForcedFlags) /* restore some parameters set when buffers were unavailable */
	{
		if (ulForcedFlags&SND_FLAG_FORCEVOL) /* Main volume */
		{
			if (lpDSPrimBuffer)
			{
					IDirectSoundBuffer_SetVolume(lpDSPrimBuffer,vol_2_hth_dB[ucMainVol]);
			}
		}//SND_FLAG_FORCEVOL
		ulForcedFlags=0;
	}//ulForcedFlags
}


//---------------------------------------------------------
//	GetPos: Returns the "theorical" position in resource
//			expressed in seconds since the beginning;
//			different from absolute time (pitch, time shift ..)
//	Input:  Voice concerned
//	Output: Time in s (SndReal)
//----------------------------------------------------------
SndReal SND_CALL SND_fn_rGetPosSxdWave(long voie)
{
#ifndef DISABLE_WAVE
	BUFFER_DISPO *buffer;
	DWORD play,write;

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

	buffer=(BUFFER_DISPO*)voie;

	if (test_voice_is_active(buffer))
	{
		if (buffer->Loop && (buffer->dwFlags & FLAG_LOOPING))
		{
			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->dwFlags & FLAG_LOOPING))
			{
				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;
#endif
	return SND_C_POS_UNKNOWN;
}

//---------------------------------------------------------
//	GetLength: Returns the "theorical" duration of resource
//			expressed in seconds (constant for a given resource)
//	Input:  Voice concerned
//	Output: Time in s (SndReal)
//----------------------------------------------------------
SndReal SND_CALL SND_fn_rGetLengthSxdWave(long voie)
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return SND_C_POS_UNKNOWN;
	}
#endif
	return SND_C_POS_UNKNOWN;
}


#ifndef NO_ACP_LDBIN
SndBool SND_CALL SND_fn_bCanFreeDataSample(void)
{
	return FALSE; //TRUE;
}
#endif


void SND_fn_vGetTargetLabelSxdWave(char* name,int nb_char)
{
#ifdef A3D
	strncpy(name,"A3",nb_char);
#else
	strncpy(name,"D3",nb_char);
#endif
}

void SND_fn_vSetupTargetSxdWave()
{
}

SndBool SND_fn_bCanSetupTargetSxdWave()
{
	return FALSE;
}

void SND_fn_vSetReverseStereoSxdWave(SndBool active)
{
	stereo_reverse = active;
}

SndBool	SND_fn_bGetReverseStereoSxdWave(void)
{
	return stereo_reverse;
}

void SND_fn_vSetEffectSxdWave(long effect)
{
	//###DOM
#ifdef USE_EAX
	SND_fn_vSetPreset((unsigned long)effect);
#endif
}

void SND_fn_vSetSoundVolumeSxdWave(unsigned char vol)
{
#ifndef DISABLE_WAVE
	jvdbg("setSoundVol");
	ucMainVol = vol;

	if (driver_SND_OK == FALSE) {
		return ;
	}

	if (bReleasedForced||(lpDSPrimBuffer==NULL))
	{
		ulForcedFlags|=SND_FLAG_FORCEVOL;
	}
	else
		IDirectSoundBuffer_SetVolume(lpDSPrimBuffer,vol_2_hth_dB[vol]);
#endif
}

unsigned char SND_fn_ucGetSoundVolumeSxdWave(void)
{
	return ucMainVol;
}

//-------------------------------------------------
// Function of engine synchro
//-------------------------------------------------
void SND_fn_vSynchroSxdWave()
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return ;
	}
	if ((lpListener3D == NULL)||(bReleasedForced)) return;
	IDirectSound3DListener_CommitDeferredSettings(lpListener3D);
#endif
}


void SND_fn_vSetDefaultRollOffSxdWave(RollOffParam* rolloff)
{
	if ((rolloff->rDistSatur!=C_ROLLOFFPARAM_DEFAULT) && (rolloff->rDistSatur!=0))
		g_d3dDistSatur=(D3DVALUE)M_RealToDoubleSnd(rolloff->rDistSatur);

	if ((rolloff->rDistBackGround>rolloff->rDistSatur)
		&& (rolloff->rDistBackGround!=C_ROLLOFFPARAM_INFINITE) 
		&& (rolloff->rDistBackGround!=C_ROLLOFFPARAM_DEFAULT))
		g_d3dDistBackGround=(D3DVALUE)M_RealToDoubleSnd(rolloff->rDistBackGround);
	else
		g_d3dDistBackGround=DS3D_DEFAULTMAXDISTANCE;
}


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

SndBool SND_fn_b3DOneShootShouldBeConvertedSxdWave()
{
	return TRUE;
}

SndBool  SND_fn_bSetResourceStaticVolumeSxdWave(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	BUFFER_DISPO *buffer;
	int	i;

	pstRes->ucVolume=ucVolume;

	for (i=0,buffer=&buffer_dispo[0];i<NB_VOICES;i++,buffer++)
	{
		if (buffer->uRes.pstPtr!=NULL)
			if ((tdstBlockResourceMem*)buffer->uRes.pstPtr==pstRes)
			{
				buffer->ulVol=ucVolume;
			}
	}

	return TRUE;
}
