/*
//////////////////////////////////////////////////////////////
//      SNDXDWAV.C                                                                                                 //
//////////////////////////////////////////////////////////////
//      Management of samples 3D                            //
//		Version A3D1.2/DirectSound3D						//
//															//
//////////////////////////////////////////////////////////////
MODIFIED:
		29/05/99
			Stéphane Ronse
			* created from previous Joel Vibert's version

  */

// INCLUDES SYSTEMES
#define INITGUID
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>

#include <crtdbg.h>

int nb_dsbuffer=0;

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

#include "sndvrt3D.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"



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

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		g_lpDirectSoundObject=NULL;		//DIRECTSOUND object
SndBool				g_lA3D=DSERR_NODRIVER;				//TRUE if an A3D hw is used
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=FALSE;
unsigned char		ucMainVol=64;
int					nb_voices_activated=0;

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);
void DSoundError(char* message);

SndBool				driver_SND_OK = FALSE;			// indicate a good init


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


//snd_assert cannot be used with SPEEDBUSTER
#ifdef SPEEDBUSTER
#define DX_snd_assert(test) if (!test) DebugBreak();
#else
#define DX_snd_assert(test) snd_assert(test)
#endif


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


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)
{									
#ifdef DEBUG
    char texte[80];

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

void DSoundError(char* message)
{
#ifdef DEBUG
	MessageBox(NULL,message,"Problem with DirectSound3D",MB_OK);
#endif
}


/* convert SND parameters to DS3D parameters*/
void generate_DS3DBUFFERparameters(DS3DBUFFER* DS3Dpar,SoundParam* par)
{
	RollOffParam	*pRollOff;

	memset(DS3Dpar,0,sizeof(DS3DBUFFER));

	DS3Dpar->dwSize = sizeof(DS3DBUFFER);
	if (!stereo_reverse) 
	{
		/* Be Careful: DS3D use left-handed  coordinate system while CPA use right-handed coordinate system*/
		DS3Dpar->vPosition.x = (float)M_RealToDoubleSnd(par->Pos.y);
		DS3Dpar->vPosition.y = (float)M_RealToDoubleSnd(par->Pos.x);
		DS3Dpar->vPosition.z = (float)M_RealToDoubleSnd(par->Pos.z);
		DS3Dpar->vVelocity.dvX = (float)M_RealToDoubleSnd(par->Vit.y);
		DS3Dpar->vVelocity.dvY = (float)M_RealToDoubleSnd(par->Vit.x);
		DS3Dpar->vVelocity.dvZ = (float)M_RealToDoubleSnd(par->Vit.z);
	} 
	else 
	{
		/* Be Careful: DS3D use left-handed  coordinate system while CPA use right-handed coordinate system*/
		DS3Dpar->vPosition.x = (float)M_RealToDoubleSnd(par->Pos.x);
		DS3Dpar->vPosition.y = (float)M_RealToDoubleSnd(par->Pos.y);
		DS3Dpar->vPosition.z = (float)M_RealToDoubleSnd(par->Pos.z);
		DS3Dpar->vVelocity.dvX = (float)M_RealToDoubleSnd(par->Vit.x);
		DS3Dpar->vVelocity.dvY = (float)M_RealToDoubleSnd(par->Vit.y);
		DS3Dpar->vVelocity.dvZ = (float)M_RealToDoubleSnd(par->Vit.z);
	}

	DS3Dpar->dwInsideConeAngle = 360;
	DS3Dpar->dwOutsideConeAngle = 360;
	DS3Dpar->vConeOrientation.x = 0;
	DS3Dpar->vConeOrientation.y = 0;
	DS3Dpar->vConeOrientation.z = 1;
	DS3Dpar->lConeOutsideVolume = 0;

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

		DS3Dpar->flMinDistance=(D3DVALUE)M_RealToDoubleSnd(pRollOff->rDistSatur);

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

	DS3Dpar->dwMode = DS3DMODE_NORMAL;		
}

//get volume parameter for a DSBuffer
long get_DSvolume(SoundParam* par,p_audible_voice3D pAudibleVoice)
{
	unsigned long ulVolume;
	int iIndexVol;

	ulVolume = pAudibleVoice->voice->tduRes.pstPtr->ucVolume;

	if (par!=NULL)
	{
		// dynamic volume
		ulVolume *= (short)par->ucVol;
		ulVolume >>=7;
		if (ulVolume>127) ulVolume=127;
	}

	iIndexVol=M_AdaptVolume(ulVolume
			,pAudibleVoice->voice->tduRes.pstPtr->uRes.stSample.uwNbChannels
			,FALSE);

	return vol_2_hth_dB[iIndexVol];
}

//get frequency paramater for a DSBuffer
long get_DSfrequency(SoundParam* par,p_audible_voice3D pAudibleVoice)
{
	return (DWORD)(M_RealToDoubleSnd(par->Freq)*(double)(pAudibleVoice->voice->tduRes.pstPtr->uRes.stSample.ulFreq));
}

// unconditional update of buffer parameters
// Parameters:pres3D=dsbuffer to be updated
//	par=new dynamic parameters
//	attack=TRUE is attack have to be update;FALSE if loop have to be updated
//  bImmediate=TRUE if change should apply immediatly
//  bInconditionnal=TRUE if update MUST be done; FALSE if update should only be done with new values
void set_param_DSBuffer(p_allocated_resource3D pres3D,SoundParam *par,SndBool bImmediate,SndBool bInconditionnal)
{
	SndBool			bAudible=TRUE;
	int				j;
	SndBool			b3DParameters;
    LPDIRECTSOUNDBUFFER		dsbuf;
    LPDIRECTSOUND3DBUFFER	dsbuf3D;
	DWORD			lVolume,lPitch;
	DS3DBUFFER		DS3Dparam;

	if (pres3D->pAudibleVoice->voice->bWaitingLoop || !pres3D->Loop)
	{
		dsbuf=pres3D->Attack;
		dsbuf3D=pres3D->Attack3D;
	}
	else
	{
		dsbuf=pres3D->Loop;
		dsbuf3D=pres3D->Loop3D;
	}

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

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

	//update volume
	lVolume=get_DSvolume(par,pres3D->pAudibleVoice);

	if (bInconditionnal || lVolume!=pres3D->lLastVolume)
	{
		IDirectSoundBuffer_SetVolume(dsbuf,lVolume);
		pres3D->lLastVolume=lVolume;
	}

	//update frequency
	lPitch=get_DSfrequency(par,pres3D->pAudibleVoice);

	if (bInconditionnal || (pres3D->lLastPitch == 0) || (pres3D->lLastPitch != lPitch))  
	{
		pres3D->lLastPitch = lPitch;
		IDirectSoundBuffer_SetFrequency(dsbuf,lPitch);
	}

	//update 3D location
    if (b3DParameters)
	{//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 (dsbuf3D)
			{
				generate_DS3DBUFFERparameters(&DS3Dparam,par);

				IDirectSound3DBuffer_SetAllParameters(dsbuf3D,&DS3Dparam,(bImmediate) ? DS3D_IMMEDIATE : DS3D_DEFERRED);
			}
		}
		else
		{// not audible
				IDirectSoundBuffer_SetVolume(dsbuf,vol_2_hth_dB[0]);
		}
	}
}

//create the Primary buffer abd get Listener interface
SndBool bCreatePrimaryBuffer(SND_tdstInitStruct *param)
{

	HRESULT res;
	
	if (g_lpDirectSoundObject) {
		//-- 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(g_lpDirectSoundObject,&DSPrimBufferDesc,&lpDSPrimBuffer,NULL))!=DS_OK) {
			// Can't create PRIMARY BUFFER
			IDirectSound_Release(g_lpDirectSoundObject);
			g_lpDirectSoundObject = NULL;

		} 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;
				IDirectSound_Release(g_lpDirectSoundObject);
				g_lpDirectSoundObject = NULL;
			}
		}
	}

	if (lpDSPrimBuffer)
		return TRUE;
	else
		return FALSE;
}

void fn_vStartCallBack(virtual_voice3D* pVoice)
{
	(*pVoice->pfnCallback)(pVoice->lParamCallback);
}



/*-------------------------------------------------
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)
{
    p_audible_voice3D pAudibleVoice,pAudibleVoiceNext;
	int i;
	SndBool bOK;
	DWORD play,write;
    
    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
	SNDLST2_M_DynamicForEachMovingElementOf(&g_VirtualModule3D.audible_voices.pList,pAudibleVoice,pAudibleVoiceNext,i)
	{
		if (pAudibleVoice->voice->bWaitingLoop)
		{//should this voice transit from attack to loop ?
			bOK=FALSE;

			if (pAudibleVoice->allocated_res->Attack)
				if(IDirectSoundBuffer_GetCurrentPosition(pAudibleVoice->allocated_res->Attack,&play,&write) == DS_OK)
					bOK=TRUE;

			if (bOK)
	            if (play>=pAudibleVoice->allocated_res->pstRes3D->uRes.stSample.uData.stMem.lAttackToLoopPoint)
                {
					IDirectSoundBuffer_Stop(pAudibleVoice->allocated_res->Attack);

					pAudibleVoice->voice->bWaitingLoop=FALSE;

					set_param_DSBuffer(pAudibleVoice->allocated_res,pAudibleVoice->voice->pstParam,TRUE,TRUE);

					#ifdef USE_EAX
					set_param_eax(pAudibleVoice->allocated_res->LoopEAX,pAudibleVoice->voice->pstParam->ucReverb,pAudibleVoice->allocated_res->pstRes3D->uRes.stSample.bReverbable);
					#endif

					IDirectSoundBuffer_SetCurrentPosition(pAudibleVoice->allocated_res->Loop,0);					

					if (IDirectSoundBuffer_Play(pAudibleVoice->allocated_res->Loop,0,0,DSBPLAY_LOOPING ) != DS_OK) 
					{//transition failed->voice become virtual
						kill_virtual_voice3D(pAudibleVoice->voice);
					}
				}							
		}
		else
		{//is it waiting for a callback ?
			if (pAudibleVoice->voice->pfnCallback)
			{
				if (!pAudibleVoice->allocated_res->Loop)
					if (!fn_bTestDSBufferIsActive(pAudibleVoice))
						kill_virtual_voice3D(pAudibleVoice->voice);
			}
		}
	}

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

	eTimerState=TIMER_OFF;
	Sleep(100);
	
    ExitThread(0);
	return 0;
}

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

/* create and initialize DiretcSound object and primary buffer */
int fn_iInitExSxdWave(SND_tdstInitStruct *pInitStruct)
{
    HRESULT			res;
	DWORD			dwSpeakerConfig,dwSpeakerGeometry;
	DWORD			OutputAvgBytesPerSec;

	driver_SND_OK = FALSE;
	lpListener3D = NULL;

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

	if (!bCreatePrimaryBuffer(&InitStructSaved))
	{
		destroy_listGUID();
		return C_INIT_FAILED;
	} 

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

	//free MIDI memory on maxi sounds cards
	SND_fn_bFreeMemOnMaxiSound();

	// 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
    {
        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)
		{
			OutputResChosen=lpWaveFormatPrimBuffer->wBitsPerSample;			
			OutputChanChosen=lpWaveFormatPrimBuffer->nChannels;
			OutputFreqChosen=lpWaveFormatPrimBuffer->nSamplesPerSec;
			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))
    {
		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_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

    if (res!=DS_OK)
    {
		SND_fn_vDesInitSxdWave();
		driver_SND_OK = FALSE;
        return C_INIT_FAILED;
    }
    
	eTimerState=TIMER_ON;
	if (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
	SND_fn_vInitEaxModule(g_lpDirectSoundObject);
	#endif

	driver_SND_OK = TRUE;

	SND_fn_vSetSoundVolumeSxdWave(ucMainVol);

	return C_INIT_OK;
}

//release directsound object
void fn_vDesInitExSxdWave()
{
		//###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;
		}
}

//release an audible voice(transform it in pure virtual); update any list and elements
/*
void fn_vReleaseAudibleVoice(p_audible_voice3D voice)
{
	//stop playing DSbuffers
	if (voice->allocated_res->Loop)
		IDirectSoundBuffer_Stop(voice->allocated_res->Loop);
	if (voice->allocated_res->Attack)
		IDirectSoundBuffer_Stop(voice->allocated_res->Attack);

	//release interfaces
	if (voice->allocated_res->Attack3D)
	{
		IDirectSound3DBuffer_Release(voice->allocated_res->Attack3D);
		voice->allocated_res->Attack3D = NULL;
	}
	if (voice->allocated_res->Loop3D)
	{
		IDirectSound3DBuffer_Release(voice->allocated_res->Loop3D);
		voice->allocated_res->Loop3D = NULL;
	}
}
*/
/*reinsert an audible voice in the list*/
/*
void reinsert_audible_voice3D(p_audible_voice3D audible)
{
	p_virtual_voice3D voice;
	p_audible_voice3D tmp_audible;
 
	//parcours en ariere pour retrouver la voie virtuelle audible precedent _voice
	voice=SNDLST2_M_DynamicGetPrevElement(audible->voice);
	while ((voice!=NULL) && (voice->audible==NULL))
	{
		voice=SNDLST2_M_DynamicGetPrevElement(voice);
	}
	//-voice->audible pointe alors sur l'elemenet apres lequel il faut inserer audible
	if (voice==NULL)
	{//audible est inserer au debut de la liste
		SNDLST2_M_DynamicAddHead(&g_AudibleVoices.pList,audible);
	}
	else
	{//audible est inserer apres voice->audible
		tmp_audible=SNDLST2_M_DynamicGetNextElement(voice->audible);
		SNDLST2_M_DynamicInsertBetween(audible,voice->audible,tmp_audible);
	}
}
*/
 
//allocate a new virtual voice; init any filed; init lInstancve field; add theelement in the list
/*
p_virtual_voice3D alloc_virtual_voice3D()
{
	p_virtual_voice3D voice,v;
	int i;
 
	voice=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(virtual_voice3D));
	memset(voice,0,sizeof(virtual_voice3D));
 
	SNDLST2_M_DynamicInitElement(voice);
	SNDLST2_M_DynamicAddTail(&g_VirtualVoices.pList,voice);

	if (g_lInstance>=INSTANCE_MAX)
	{//g_lInstance is too high->reduce every previous values of lInstance
		SNDLST2_M_DynamicForEachElementOf(&g_VirtualVoices.pList,v,i)
		{
			v->lInstance=max(0,v->lInstance-INSTANCE_MAX/2);
		}
		g_lInstance=INSTANCE_MAX/2;
	}
	voice->lInstance=g_lInstance++;

	return voice;
}
*/
/*empty a virtual voice*/
/*
void purge_virtual_voice3D(virtual_voice3D* pVoice)
{
	SNDLST2_M_DynamicIsolate(pVoice);
	if (pVoice->audible)
		del_audible_voice3D(pVoice->audible);
	pVoice->audible=NULL;
	if (pVoice->pstParam)
	{
		SND_fn_vFreeSndEx(E_ucSndBlockMain,pVoice->pstParam);
		pVoice->pstParam=NULL;
		pVoice->lSizeParam=0;
	}
}
 */
/*delete a virtual voice->delete the audible voice*/
/*
void del_virtual_voice3D(virtual_voice* pVoice)
{
	purge_virtual_voice3D(pVoice);
	SND_fn_vFreeSndEx(E_ucSndBlockMain,pVoice);
}
 */
/*predestruction de la voie virtuelle; destruction uniquement si on peut  prevenir le haut-niveau*/
/*transfert de virtual_list vers dead_list*/
/*
void kill_virtual_voice(SND_tdhVirtualModule pmodule,virtual_voice* pVoice)
{
	purge_virtual_voice(pmodule,pVoice);
	//voice has never been used->you can delete it
	if (pVoice->bUsed)
		pVoice->dead=add_dead_voice(pmodule,pVoice);
	else
		del_virtual_voice(pmodule,pVoice);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x killed\n",pVoice);
#endif
}
 */
 
/*insert a new virtual voice in the classified list*/
/*
void resort_virtual_voice3D(p_virtual_voice3D _voice)
{
	p_virtual_voice3D pVoice,tmp_voice;
 
	SNDLST2_M_DynamicIsolate(_voice);
	if (_voice->audible)
	{
		SNDLST2_M_DynamicIsolate(_voice->audible);
	}
 
	pVoice=SNDLST2_M_DynamicGetFirstElement(&g_VirtualVoices.pList);
 
	//1' classify by priority level
	while ((pVoice!=NULL) && (pVoice->lPrio>_voice->lPrio))
	{
		pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
	}
 
	if ((pVoice!=NULL) && (pVoice->lPrio==_voice->lPrio))
	{//2' classify by norm
		while ((pVoice!=NULL) && (pVoice->rNorme>_voice->rNorme))
		{
			pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
		}
 
		if ((pVoice!=NULL) && (pVoice->rNorme==_voice->rNorme))
		{//3' classify by instance number
			while ((pVoice!=NULL) && (pVoice->lInstance>_voice->lInstance))
			{
				pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
			}
		}
	}
 
	if (pVoice==NULL)
	{//this voice should be the last one/
		SNDLST2_M_DynamicAddTail(&g_VirtualVoices.pList,_voice);
	}
	else
	{//_voice before pVoice
		tmp_voice=SNDLST2_M_DynamicGetPrevElement(pVoice);
		SNDLST2_M_DynamicInsertBetween(_voice,tmp_voice,pVoice);
	}
 
	if (_voice->audible)
		reinsert_audible_voice3D(_voice->audible);
}
 
//start or stop sound associated with virtual voices
void synchro_virtual_voice3D()
{
	p_virtual_voice3D pVoice,pVoiceFirstMute,pVoiceLastAudible;
	p_audible_voice3D pAudible,pAudibleNext;
	int i;
 
	//TODO
}
*/ 

/*-------------------------------------------------
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(DSBUFFERDESC *pdsbdesc,char *pcData, LPDIRECTSOUNDBUFFER *pBuffer)
{
#ifndef DISABLE_WAVE
	DSBCAPS caps;
	DWORD   sizeptr1,sizeptr2;
    LPVOID  ptr1,ptr2;
	HRESULT res;

    // create buffer
	res=IDirectSound_CreateSoundBuffer(g_lpDirectSoundObject,pdsbdesc,pBuffer,NULL);
	
#ifdef DEBUG_TRACE_DSBUFFERALLOCATION
	_RPTF1(_CRT_WARN,"Create DSbuffer %d\n",*pBuffer);
#endif

    if (res==DS_OK){
		nb_dsbuffer++;

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

		memcpy(ptr1, pcData, sizeptr1);

		if(IDirectSoundBuffer_Unlock(*pBuffer,ptr1,sizeptr1,ptr2,sizeptr2)!=DS_OK)
		{
			return FALSE;
		}

		return TRUE;
	}

	*pBuffer=NULL;
	return FALSE;
#endif
}

SndBool fn_bDuplicate2ndBuffer(LPDIRECTSOUNDBUFFER buffer, LPDIRECTSOUNDBUFFER *pBuffer)
{
	HRESULT res;

    // duplicate buffer
	res=IDirectSound_DuplicateSoundBuffer(g_lpDirectSoundObject,buffer,pBuffer);

    if (res==DS_OK)
		return TRUE;
	else
		return FALSE;
}

void fn_vRelease2ndBuffer(LPDIRECTSOUNDBUFFER buffer)
{
#ifdef DEBUG_TRACE_DSBUFFERALLOCATION
	_RPTF1(_CRT_WARN,"Release DSbuffer %d\n",*buffer);
#endif

	IDirectSoundBuffer_Release(buffer);

	nb_dsbuffer--;

}

void fn_vGetDSInterfaces(p_allocated_resource3D pres3D)
{
	HRESULT res;

	if (pres3D->Attack && !pres3D->Attack3D)
	{
		if((res=IDirectSoundBuffer_QueryInterface(pres3D->Attack
			,&IID_IDirectSound3DBuffer
			,(void**)(&pres3D->Attack3D)))
			==S_OK ) 
		{
			IDirectSound3DBuffer_SetMode(pres3D->Attack3D,DS3DMODE_NORMAL,DS3D_IMMEDIATE);
		}
	}

	if (pres3D->Loop && !pres3D->Loop3D)
	{
		if((res=IDirectSoundBuffer_QueryInterface(pres3D->Loop
			,&IID_IDirectSound3DBuffer
			,(void**)(&pres3D->Loop3D)))
			==S_OK ) 
		{
			IDirectSound3DBuffer_SetMode(pres3D->Loop3D,DS3DMODE_NORMAL,DS3D_IMMEDIATE);
		}
	}
}

void fn_vReleaseDSInterfaces(p_allocated_resource3D pres3D)
{
	if (pres3D->Attack3D)
	{
		IDirectSound3DBuffer_Release(pres3D->Attack3D);
		pres3D->Attack3D=NULL;
	}

	if (pres3D->Loop3D)
	{
		IDirectSound3DBuffer_Release(pres3D->Loop3D);
		pres3D->Loop3D=NULL;
	}
}

//release only attack interface (called when transition occured and there is a lack of dsbuffer)
void fn_vPartiallyReleaseDSInterfaces(p_allocated_resource3D pres3D)
{
	if (pres3D->Attack3D)
	{
		IDirectSound3DBuffer_Release(pres3D->Attack3D);
		pres3D->Attack3D=NULL;
	}
}

//Play the dsbuffer
//Param: res3D=dsbuffer to play
//			par=soundparam value
//		bLoop=if TRUE, play Ataack; else play Loop
SndBool fn_bStart2ndBuffer(p_allocated_resource3D res3D,SoundParam* par,SndBool bLoop)
{
	set_param_DSBuffer(res3D,par,TRUE,TRUE);

	#ifdef USE_EAX
	if (bLoop)
		set_param_eax(res3D->LoopEAX,par->ucReverb,res3D->pstRes3D->uRes.stSample.bReverbable);
	else
		set_param_eax(res3D->AttackEAX,par->ucReverb,res3D->pstRes3D->uRes.stSample.bReverbable);
	#endif

	if (bLoop)
		IDirectSoundBuffer_SetCurrentPosition(res3D->Loop,0);					
	else
		IDirectSoundBuffer_SetCurrentPosition(res3D->Attack,0);					

	if (bLoop)
		if (IDirectSoundBuffer_Play(res3D->Loop,0,0,DSBPLAY_LOOPING) == DS_OK)
			return TRUE;

	if (!bLoop)
		if (IDirectSoundBuffer_Play(res3D->Attack,0,0,0) == DS_OK)
			return TRUE;

	return FALSE;
}
            
SndBool fn_bTestDSBufferIsActive(p_audible_voice3D voice)
{
	DWORD status;
	
	if (voice->allocated_res->Loop && !voice->voice->bWaitingLoop)
	{
		if(DSoundTry(IDirectSoundBuffer_GetStatus(voice->allocated_res->Loop,&status)) == DS_OK) 
		{
			if (status & DSBSTATUS_PLAYING) return TRUE; // voice is playing
			if (status & DSBSTATUS_BUFFERLOST) return FALSE;
			DSoundError("IDirectSoundBuffer_GetStatus");
			return FALSE;
		}
		else
			return FALSE;
	}
	else
	{
		DX_snd_assert(voice->allocated_res->Attack);

		if(DSoundTry(IDirectSoundBuffer_GetStatus(voice->allocated_res->Attack,&status)) == DS_OK) 
		{
			if (status & DSBSTATUS_PLAYING) return TRUE; // voice is playing
			if (status & DSBSTATUS_BUFFERLOST) return FALSE;
			DSoundError("IDirectSoundBuffer_GetStatus");
			return FALSE;
		}
		else
			return FALSE;
	}
}

void fn_vStopDSBuffer(p_allocated_resource3D pres3D)
{
	pres3D->pAudibleVoice->voice->bWaitingLoop=FALSE;
	if (pres3D->Attack)
		IDirectSoundBuffer_Stop(pres3D->Attack);
	if (pres3D->Loop)
		IDirectSoundBuffer_Stop(pres3D->Loop);
}


/*****************************************************************************
	Public funcitons
*****************************************************************************/
///////////////////////////////////////////////////////
// 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
	memcpy(&InitStructSaved,pInitStruct,sizeof(InitStructSaved));

	SND_fn_lInitSxdVirtual3D(10);

	init_listGUID();

	return fn_iInitExSxdWave(pInitStruct);
#else
	driver_SND_OK = FALSE;
    return C_INIT_FAILED;
#endif
}

SndBool SND_fn_bTestInitSxdWave(void)
{
    return driver_SND_OK;
}

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

		destroy_listGUID();

		desinit_DSOUND(&g_lpDirectSoundObject); //free DirectSound object

		driver_SND_OK = FALSE;
	}

#endif
}

//--------------------------------------------------
//      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)
{
	tdstSampleMemoryDX3D *pSampleMem;
	DWORD   dwSizeOneShot=0,dwSizeLoop=0;
	BOOL    bAttack,bLoop; //resource with loop/attack
    tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
    DX_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;

	DX_snd_assert(!disk->uRes.stSample.bStream) ;
	
	pSampleMem=&((tdstBlockResourceMemDX3D*)mem)->uRes.stSample.uData.stMem;

	pSampleMem->pcDataAttack=NULL;
	pSampleMem->pcDataLoop=NULL;
	if (ptrBegin!=NULL)
	{
        //--------buffer one-shot or attack...		
		if ((!disk->uRes.stSample.bLoop) || (disk->uRes.stSample.ulStartLoop>0))
		{
			pSampleMem->pcDataAttack=ptrBegin;

			if (disk->uRes.stSample.bLoop)                        
			{
				pSampleMem->lAttackToLoopPoint=disk->uRes.stSample.ulStartLoop; 
				pSampleMem->lSizeAttackBuff=min(disk->uRes.stSample.ulStartLoop+4000//4000 bytes to preserve attack2loop latency
					,disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength);
			}
			else
			{
				pSampleMem->lSizeAttackBuff=disk->ulDataSize;
				pSampleMem->lAttackToLoopPoint=disk->ulDataSize;
			}
		
			bAttack=TRUE;
		} 
		else
		{
			bAttack=FALSE;
			pSampleMem->lSizeAttackBuff=0;
			pSampleMem->lAttackToLoopPoint=0;
		}

		//------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 (bAttack)
				pSampleMem->pcDataLoop+=pSampleMem->lAttackToLoopPoint; //20/11/98

			pSampleMem->lSizeLoopBuff=dwSizeLoop;
			bLoop=TRUE;
		} 
		else
		{
			pSampleMem->lSizeLoopBuff=0;
			bLoop=FALSE;
		}
	} //ptrBegin

	//--2° part: prepare buffers
	pSampleMem->allocated_resource=NULL;
	pSampleMem->allocated_resource=fn_pAllocateResource(memDX3D);
}

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


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

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

	SND_fn_vConvertResDiskToMemSxdWave(disk,mem,data);

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

	return TRUE;
#else
	return FALSE;
#endif
}


///////////////////////////////////////////////////////
// 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
	void* pData;
    char file_name[256];

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

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

	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
    tdstBlockResourceMemDX3D* memDX3D=(tdstBlockResourceMemDX3D*)mem;
	p_allocated_resource3D pres3D;

	//delete any allocated dsbuffer for this resource
	while ((pres3D=memDX3D->uRes.stSample.uData.stMem.allocated_resource)!=NULL)
		fn_vDesAllocateResource(pres3D=memDX3D->uRes.stSample.uData.stMem.allocated_resource);

	//free binary data if necessary
	if (memDX3D->eStorage==TYPE_EXTERNAL)
	{//free binary data if necessary
		if (memDX3D->uRes.stSample.uData.stMem.pcDataAttack!=NULL)
			SND_fn_vFreeSnd(memDX3D->uRes.stSample.uData.stMem.pcDataAttack);
		else
		if (memDX3D->uRes.stSample.uData.stMem.pcDataLoop!=NULL)
			SND_fn_vFreeSnd(memDX3D->uRes.stSample.uData.stMem.pcDataLoop);
	}

	mem->eType=TYPE_INVALID;
#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 	(memDX3D->uRes.stSample.bStream)
		return TRUE;// Streaming sample is always "loaded"
	else  
		return ((memDX3D->uRes.stSample.uData.stMem.pcDataAttack!=NULL)
			|| (memDX3D->uRes.stSample.uData.stMem.pcDataLoop!=NULL));
#endif
}

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

long SND_CALL SND_fn_lPlaySxdWave(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
	if (driver_SND_OK == FALSE)
		return C_PLAY_FAILED;

	return SND_fn_lPlaySxdVirtual3D(res,par,prio,fn_callback,par_callback);
}

void SND_CALL SND_fn_vRemoveCallbackSxdWave(long voice)
{
	SND_fn_vRemoveCallbackSxdVirtual3D(voice);
}

SndBool	SND_CALL SND_fn_bSetParamSxdWave(long voice,SoundParam *par)
{
	return SND_fn_bSetParamSxdVirtual3D(voice,par);
}

SndBool	SND_CALL SND_fn_bTestIsPlayingSxdWave(long voice)
{
	return SND_fn_bTestIsPlayingSxdVirtual3D(voice);
}

void SND_CALL SND_fn_vStopSxdWave(long voice)
{
	SND_fn_vStopSxdVirtual3D(voice);
}

void SND_CALL SND_fn_vPauseSxdWave(long voice)
{
	SND_fn_vPauseSxdVirtual3D(voice);
}

void SND_CALL SND_fn_vResumeSxdWave(long voice)
{
	SND_fn_vResumeSxdVirtual3D(voice);
}
 
SndBool  SND_fn_bIsResourceLoopingSxdWave(tduRefRes res)
{
	return res.pstPtr->uRes.stSample.bLoop;
}

SndReal	SND_CALL SND_fn_rGetPosSxdWave(long voice)
{
	//todo
	return 0;
}

SndReal	SND_CALL SND_fn_rGetLengthSxdWave(long voice)
{
#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_vSetSoundVolumeSxdWave(unsigned char vol)
{
#ifndef DISABLE_WAVE
	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()
{
		return ucMainVol;
}
 
void SND_fn_vSetReverseStereoSxdWave(SndBool audible)
{
	stereo_reverse = audible;
}

SndBool	SND_fn_bGetReverseStereoSxdWave(void)
{
	return stereo_reverse;
}

void		SND_fn_vSetEffectSxdWave(long num)
{
#ifdef USE_EAX
	SND_fn_vSetPreset((unsigned long)effect);
#endif
}
 
void		SND_fn_vGetTargetLabelSxdWave(char* name,int nb_char)
{
	strncpy(name,"A1",nb_char);
}

void		SND_fn_vSetupTargetSxdWave()
{
	//Nothing: DLL isnot able to setup SNDCPA.INI; an externa lapplication is requirred
}

SndBool	SND_fn_bCanSetupTargetSxdWave()
{
	return FALSE;
}

/*--------------------------------------------------
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)
{
	int i;
	p_allocated_resource3D res,nextres;

	if (bReleasedForced) return;

	bReleasedForced=TRUE;

	while (bTimerInstance)
		Sleep(0); 

	SND_fn_vEnterCriticalSectionThreadSnd();//27/01/99

    fn_vForceReleaseClientBuffers();

	//desactive any DSBuffer
	SNDLST2_M_DynamicForEachMovingElementOf(&g_AllocatedResources.pList,res,nextres,i)
	{
		fn_vDesAllocateResource(res);
	}

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

	desinit_DSOUND(&g_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)
{
	if (!bReleasedForced) return;

	if (fn_iInitExSxdWave(&InitStructSaved)==C_INIT_FAILED) return;

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

	fn_vForceRestoreClientBuffers();

	//todo
	//reactivate dsbuffer

	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
}

void		SND_fn_vSynchroSxdWave()
{
#ifndef DISABLE_WAVE
	if (driver_SND_OK == FALSE) {
		return ;
	}
	if ((lpListener3D == NULL)||(bReleasedForced)) return;

	SND_fn_vSynchroSxdVirtual3D();

	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_b3DOneShootShouldBeConvertedSxdWave()
{
	return TRUE;
}

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

	return TRUE;
}


SndBool SND_fn_bIsResourceLoopingSxdDX3D(tduRefResDX3D tduResDX3D)
{
	tduRefRes tduRes;

	tduRes.pstPtr=(tdstBlockResourceMem*)tduResDX3D.pstPtr;

	return SND_fn_bIsResourceLoopingSxd(tduRes);
}

