/************************************************************************
*		Module de gestion des voies virtuelles pour DirectSound3D		*
*		généré à partir de SNDVIRT.C									*
*************************************************************************/

#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>

#include <crtdbg.h>

// 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"
 
#include "sndvrt3D.h"
#include "sndxd.h"
 
//#define DEBUG_OUTPUT_SNDVIRT

#ifdef DEBUG_OUTPUT_SNDVIRT
#include <crtdbg.h>
#endif


allocated_resource3D_list	g_AllocatedResources;
virtual_module3D g_VirtualModule3D;
 
 
/*liste des modules de voies virtuelles*/
//static virtual_module_list virtual_modules;
//static SndBool virtual_module_list_init=FALSE;
 
/*static virtual_module vmodule;*/
 
/*liste des voies virtuelles = id retournes par PlayVitual*/
/*static virtual_voice_list virtual_voices;*/
/*liste des voies physique = id retournes par PlaySnd*/
/*static audible_voice_list audible_voices;*/
/*liste des voies desallouees*/
/*static dead_voice_list dead_voices;*/
 
/*à chaque element de audible_voices correspond un element dans virtual_voices*/
/*certains elements de virtual_voices n'ont pas de pendant dans audible_voices (sons virtuels)*/
/*l'ordonnancement est le meme dans les 2 listes*/
 
 
#define INSTANCE_MAX 0x10000000
 
 
//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


void check_integrity3D()
{
	p_audible_voice3D	audible;
	p_virtual_voice3D voice;
	p_dead_voice3D dead;
	p_allocated_resource3D pres3D,pres3Dbis;
	int i,j;
	SndBool bCheck;
 
	//check resource list
	SNDLST2_M_DynamicForEachElementOf(&g_AllocatedResources.pList,pres3D,i)
	{
		pres3Dbis=pres3D;
		while (pres3Dbis->original_resource)
			pres3Dbis=pres3Dbis->original_resource;
		if (pres3Dbis!=pres3Dbis->pstRes3D->uRes.stSample.uData.stMem.allocated_resource)
			DebugBreak();
		bCheck=FALSE;
		do {
			if (pres3Dbis->pstRes3D!=pres3D->pstRes3D)
				DebugBreak();
			if (pres3Dbis==pres3D)
				bCheck=TRUE;
			pres3Dbis=pres3Dbis->duplicate_resource;
		} while (pres3Dbis);
		if (!bCheck)
			DebugBreak();

		if (pres3D->pAudibleVoice)
			if (pres3D->pAudibleVoice->allocated_res!=pres3D)
				DebugBreak();
	}

	//check virtual list
	SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.virtual_voices.pList,voice,i)
	{
		/*coherence de la liste*/
		if (i>=SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.virtual_voices.pList))
			DX_snd_assert(0);
 
		/*coherence des references liste*/
		if ((voice->audible) && (voice->audible->voice!=voice))
			DX_snd_assert(0);
 
		SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.dead_voices.pList,dead,j)
		{
			/*unr voie ne peut etre virtual et dead*/
			if (dead->voice==voice)
				DX_snd_assert(0);
		}
	}
	if (i!=SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.virtual_voices.pList))
			DX_snd_assert(0);
 
	SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.audible_voices.pList,audible,i)
	{
		if ((i>=SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.audible_voices.pList))
			|| (audible->voice->audible!=audible))
			DX_snd_assert(0);
	}
	if (i!=SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.audible_voices.pList))
			DX_snd_assert(0);
}
 
/*raj du champ soundparam*/
void raj_soundparam3D(virtual_voice3D* pVoice,SoundParam* par)
{
	int size;
 
	if (par)
		size=SND_M_lGetSizeOfSoundParam(par);
	else
		size=0;
 
	if (size)
	{
		if (size!=pVoice->lSizeParam)
		{
			if (pVoice->pstParam)
				pVoice->pstParam=SND_fn_pvReAllocSndEx(E_ucSndBlockMain,pVoice->pstParam,size);
			else
				pVoice->pstParam=SND_fn_pvMallocSndEx(E_ucSndBlockMain,size);
		}
		pVoice->lSizeParam=size;
		memcpy(pVoice->pstParam,par,size);
	}
	else
	{
		if (pVoice->pstParam)
		{
			SND_fn_vFreeSndEx(E_ucSndBlockMain,pVoice->pstParam);
			pVoice->pstParam=NULL;
			pVoice->lSizeParam=0;
		}
	}
}
 
/*transfert d'une voie morte de pmodule->virtual_voices vers pmodule->dead_voices*/
dead_voice3D* add_dead_voice3D(virtual_voice3D* pVoice)
{
	p_dead_voice3D dead;

	dead=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(dead_voice3D));
	SNDLST2_M_DynamicInitElement(dead);
	dead->voice=pVoice;
	SNDLST2_M_DynamicAddTail(&g_VirtualModule3D.dead_voices.pList,dead);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VD %x new (VV=%x)\n",dead,pVoice);
#endif
	return dead;
}
 
/*destruction d'une voie morte = le haut-niveau a pris note de la mort du son*/
void erase_dead_voice_from_list3D(dead_voice3D* pDead)
{
	SNDLST2_M_DynamicIsolate(pDead);
	SND_fn_vFreeSndEx(E_ucSndBlockMain,pDead);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VD %x destroy (VV=%x)\n",pDead,pDead->voice);
#endif
}
 
/*replace une voie audible à se place*/
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_VirtualModule3D.audible_voices.pList,audible);
	}
	else
	{/*audible est inserer apres voice->audible*/
		tmp_audible=SNDLST2_M_DynamicGetNextElement(voice->audible);
		SNDLST2_M_DynamicInsertBetween(audible,voice->audible,tmp_audible);
	}
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VA %x reinserted (VV=%x)\n",audible,audible->voice);
#endif
}
 
/*creation d'une voie audible -> lancement du son*/
/*l'ordonnacement des 2 listes doit etre le meme*/
/*si audible1<audible2 alors audible1->voice<audible2->voice*/
p_audible_voice3D add_audible_voice3D(p_virtual_voice3D _voice,p_allocated_resource3D pres3D)
{
	p_audible_voice3D audible;
	SndBool bOk=FALSE;
 
	DX_snd_assert(pres3D->pAudibleVoice==NULL);
	DX_snd_assert(pres3D);
	DX_snd_assert(_voice);
	DX_snd_assert(pres3D->bValid);

	audible=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(audible_voice3D));
	SNDLST2_M_DynamicInitElement(audible);
	audible->voice=_voice;
	audible->allocated_res=pres3D;
	pres3D->pAudibleVoice=audible;
 
	/*insertion de audible*/
	reinsert_audible_voice3D(audible);
 
	fn_vGetDSInterfaces(pres3D);
 
	//lauch the dsbuffer
	if (_voice->bWaitingLoop && audible->allocated_res->Attack)
		bOk=fn_bStart2ndBuffer(audible->allocated_res,_voice->pstParam,FALSE);

	if (!bOk)
	{
		_voice->bWaitingLoop=FALSE;
		if (audible->allocated_res->Loop)
			bOk=fn_bStart2ndBuffer(audible->allocated_res,_voice->pstParam,TRUE);
		else
			if (audible->allocated_res->Attack)
				bOk=fn_bStart2ndBuffer(audible->allocated_res,_voice->pstParam,FALSE);
	
	}

	
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VV %x began audible (VA=%x)\n",_voice,audible);
#endif
	return audible;
}
 
/*desctruction d'une voie audible -> arret du son*/
void del_audible_voice3D(p_audible_voice3D audible)
{
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VA %x died (VV=%x)\n",audible,audible->voice);
#endif
 
	fn_vStopDSBuffer(audible->allocated_res);
 
	fn_vReleaseDSInterfaces(audible->allocated_res);

	audible->allocated_res->pAudibleVoice=NULL;
	audible->voice->audible=NULL;
 
	SNDLST2_M_DynamicIsolate(audible);

	SND_fn_vFreeSndEx(E_ucSndBlockMain,audible);
}
 
/*retourne une voie virtuelle  (par recyclage d'une voie dead, ou malloc)*/
/*	la creation de la voie audible sera faite plus tard*/
p_virtual_voice3D get_virtual_voice3D()
{
	p_virtual_voice3D voice;
	p_dead_voice3D dead;
 
	if ((dead=SNDLST2_M_DynamicGetFirstElement(&g_VirtualModule3D.dead_voices.pList))!=NULL)
	{/*recyclage*/
		voice=dead->voice;
		erase_dead_voice_from_list3D(dead);
	}
	else
	{/*new voice*/
		voice=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(virtual_voice3D));
		voice->bUsed=FALSE;
	}
 
	SNDLST2_M_DynamicInitElement(voice);
	SNDLST2_M_DynamicAddTail(&g_VirtualModule3D.virtual_voices.pList,voice);
	voice->audible=NULL;
	voice->pstParam=NULL;
	voice->lSizeParam=0;
	voice->dead=NULL;
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x new\n",voice);
#endif
	return voice;
}
 
/*destruction du contenu de pVoice*/
void purge_virtual_voice3D(virtual_voice3D* pVoice)
{
	if (pVoice->pfnCallback)
	{
		fn_vStartCallBack(pVoice);
		pVoice->pfnCallback=NULL;
	}

	SNDLST2_M_DynamicIsolate(pVoice);
	if (pVoice->audible)
		del_audible_voice3D(pVoice->audible);
	if (pVoice->pstParam)
	{
		SND_fn_vFreeSndEx(E_ucSndBlockMain,pVoice->pstParam);
		pVoice->pstParam=NULL;
		pVoice->lSizeParam=0;
	}
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x purged\n",pVoice);
#endif
}
 
/*desctruction de la voie virtuelle->destrcution dela voie audible*/
void del_virtual_voice3D(virtual_voice3D* pVoice)
{
	purge_virtual_voice3D(pVoice);
	SND_fn_vFreeSndEx(E_ucSndBlockMain,pVoice);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x deleted\n",pVoice);
#endif
}
 
/*predestruction de la voie virtuelle; destruction uniquement si on peut  prevenir le haut-niveau*/
/*transfert de virtual_list vers dead_list*/
void kill_virtual_voice3D(virtual_voice3D* pVoice)
{
	purge_virtual_voice3D(pVoice);
	/*voice has never been used->you can delete it*/
	if (pVoice->bUsed)
		pVoice->dead=add_dead_voice3D(pVoice);
	else
		del_virtual_voice3D(pVoice);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x killed\n",pVoice);
#endif
}
 
//--------------------------------------------------------
//Attempt to allocate a DSbuffer for this resource
//if a DSBuffer was previously allocated, just duplicate it
//Return NULL fi allocation faile (ie no more DSBuffer availlable)
p_allocated_resource3D fn_pAllocateResource(tdstBlockResourceMemDX3D *mem)
{
	//TODO
	WAVEFORMATEX pcmwf;
	DSBUFFERDESC dsbdesc;
	SndBool bOk=TRUE;
	LPDIRECTSOUNDBUFFER Attack=NULL,Loop=NULL;
	p_allocated_resource3D pres3D,pres3Dprev,pres3Dduplicatable=NULL;
	tdstSampleMemoryDX3D *pSampleMem=&mem->uRes.stSample.uData.stMem;

	//find the original resource
	if (pSampleMem->allocated_resource)
	{//duplicate original DSbuffer
		pres3Dprev=pSampleMem->allocated_resource;
		while (pres3Dprev->duplicate_resource)
			pres3Dprev=pres3Dprev->duplicate_resource;
	}

	//Create Loopbuffer instance
	//check if a DSBuffer has previously been created
	if (pSampleMem->allocated_resource)
	{//duplicate original DSbuffer
		if (pSampleMem->allocated_resource->Loop)
		{
			if (fn_bDuplicate2ndBuffer(pSampleMem->allocated_resource->Loop,&Loop)==FALSE)
			{
				bOk=FALSE;
			} 
		}
	}
	else
	{//create new DSbuffer
		// set up wave format structure.
		memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
		pcmwf.wFormatTag=WAVE_FORMAT_PCM;
		pcmwf.nChannels=mem->uRes.stSample.uwNbChannels;
		pcmwf.nSamplesPerSec=mem->uRes.stSample.ulFreq;
		pcmwf.wBitsPerSample=mem->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_CTRLDEFAULT | DSBCAPS_LOCHARDWARE ;
		if (mem->uRes.stSample.bSpacable) 
			dsbdesc.dwFlags|= DSBCAPS_CTRL3D ; 
		
		dsbdesc.lpwfxFormat=&pcmwf;

		if (mem->uRes.stSample.uData.stMem.pcDataLoop)
		{
			dsbdesc.dwBufferBytes=mem->uRes.stSample.uData.stMem.lSizeLoopBuff;
			if (fn_bCreate2ndBuffer(&dsbdesc,
				mem->uRes.stSample.uData.stMem.pcDataLoop,
				&Loop
				)==FALSE)
			{
				bOk=FALSE;
			} 
		}
	}

	//create attack buffer instance
	//check if a DSBuffer has previously been created
	if (bOk)
	{
		//search for a "valid" dsbuffer to be duplicate
		pres3Dduplicatable=pres3Dprev;

		while (pres3Dduplicatable!=NULL) 
		{
			if (pres3Dduplicatable->bValid)
				break;
			else
				pres3Dduplicatable=pres3Dduplicatable->original_resource;
		}


		if (pres3Dduplicatable)
		{//duplicate original DSbuffer
			if (pres3Dduplicatable->Attack)
				if (fn_bDuplicate2ndBuffer(pres3Dduplicatable->Attack,&Attack)==FALSE)
				{
					bOk=FALSE;
				} 
		}
		else
		{//create new DSbuffer
			// set up wave format structure.
			memset(&pcmwf, 0, sizeof(WAVEFORMATEX));
			pcmwf.wFormatTag=WAVE_FORMAT_PCM;
			pcmwf.nChannels=mem->uRes.stSample.uwNbChannels;
			pcmwf.nSamplesPerSec=mem->uRes.stSample.ulFreq;
			pcmwf.wBitsPerSample=mem->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_CTRLDEFAULT | DSBCAPS_LOCHARDWARE ;
			if (mem->uRes.stSample.bSpacable) 
				dsbdesc.dwFlags|= DSBCAPS_CTRL3D ; 
			
			dsbdesc.lpwfxFormat=&pcmwf;

			if (mem->uRes.stSample.uData.stMem.pcDataAttack)
			{			
				dsbdesc.dwBufferBytes=mem->uRes.stSample.uData.stMem.lSizeAttackBuff;
				if (fn_bCreate2ndBuffer(&dsbdesc,
					mem->uRes.stSample.uData.stMem.pcDataAttack,
					&Attack		
					)==FALSE)
				{
					bOk=FALSE;
				} 
			}
		}
	}

	if (bOk)
	{//DSBufffer(s) was (were) succesfully created add element in list of allocated resource
		pres3D=SND_fn_pvMallocSnd(sizeof(allocated_resource3D));
		memset(pres3D,0,sizeof(allocated_resource3D));
		SNDLST2_M_DynamicInitElement(pres3D);
		SNDLST2_M_DynamicAddTail(&g_AllocatedResources.pList,pres3D);

#ifdef DEBUG_TRACE_DSBUFFERALLOCATION
		_RPTF1(_CRT_WARN,"Add res %d\n",pres3D);
#endif

		pres3D->Attack=Attack;pres3D->Loop=Loop;
		pres3D->pstRes3D=mem;
		pres3D->pAudibleVoice=NULL;
		pres3D->bValid=TRUE;

		if (pres3Dprev)
		{//DSbuffer is a duplicate ->update chained list of duplicates
			pres3D->original_resource=pres3Dprev;
			pres3Dprev->duplicate_resource=pres3D;
		}
		else
			pSampleMem->allocated_resource=pres3D;

		return pres3D;
	}

	return NULL;
}

//release a allocated_resource3D
void fn_vDesAllocateResource(p_allocated_resource3D pres3D)
{
	//first stop sound (if necessary)
	if (pres3D->pAudibleVoice)
	{
		if (!SND_fn_bIsResourceLoopingSxdDX3D(pres3D->pAudibleVoice->voice->tduRes))
			//voice become a pure virtual (may be hearable agin later)
			kill_virtual_voice3D(pres3D->pAudibleVoice->voice);
		else
			//voice is definitively destroy  (will never be hearable again)
			del_audible_voice3D(pres3D->pAudibleVoice);
	}


	//then release DSBuffer(s)
	if (pres3D->Attack)
		fn_vRelease2ndBuffer(pres3D->Attack);
	if (pres3D->Loop)
		fn_vRelease2ndBuffer(pres3D->Loop);

	//at least, destory elelment and update list
	SNDLST2_M_DynamicIsolate(pres3D);

#ifdef DEBUG_TRACE_DSBUFFERALLOCATION
	_RPTF1(_CRT_WARN,"Retire res %d\n",pres3D);
#endif

	if (!pres3D->original_resource)
	{//this resource is an original DSBuffer
		if (pres3D->duplicate_resource)
		{//this resource have been duplicated
			pres3D->pstRes3D->uRes.stSample.uData.stMem.allocated_resource=pres3D->duplicate_resource;
			pres3D->duplicate_resource->original_resource=NULL;
		}
		else
		{//there is no duplicate for this resource
			pres3D->pstRes3D->uRes.stSample.uData.stMem.allocated_resource=NULL;
		}
	}
	else
	{//this resource is a duplicate
		pres3D->original_resource->duplicate_resource=pres3D->duplicate_resource;
		if (pres3D->duplicate_resource)
			pres3D->duplicate_resource->original_resource=pres3D->original_resource;
	}

	SND_fn_vFreeSnd(pres3D);
}

//release the attack buffer for a allocated_resource3D
void fn_vInvalidateAllocatedResource(p_allocated_resource3D pres3D)
{
	//then release DSBuffer(s)
	fn_vPartiallyReleaseDSInterfaces(pres3D);
	if (pres3D->Attack)
		fn_vRelease2ndBuffer(pres3D->Attack);
	pres3D->Attack=NULL;

	pres3D->bValid=TRUE;
}

/*search for an availlable allocated resource; 
if no availlable allocated ressource is found, attempt to create a new one
if there is no more availlable hrardware channel, return NULL (an other hardware cvhannel have to be released)*/
p_allocated_resource3D get_allocated_resource(tdstBlockResourceMemDX3D *mem)
{
	tdstSampleMemoryDX3D *pSampleMem=&mem->uRes.stSample.uData.stMem;
	p_allocated_resource3D pres3D;

	pres3D=pSampleMem->allocated_resource;

	if (pres3D==NULL)
		//no dsbuffer have been allocated for this resource
		return NULL;

	//search for any dsbuffer (original or duplicate, not audible)
	while (pres3D!=NULL && pres3D->bValid && pres3D->pAudibleVoice)
		pres3D=pres3D->duplicate_resource;
	
	return pres3D;
}

/* return any non-audible allocated voice
used when allocation failed due to dsbuffer lack*/
p_allocated_resource3D fn_pGetNonAudibleAllocatedResource()
{
	p_allocated_resource3D pres3D;
	int i;

	SNDLST2_M_DynamicForEachElementOf(&g_AllocatedResources.pList,pres3D,i)
	{
		if (!pres3D->pAudibleVoice)
			//this one have been allocated but not currently playing
			return pres3D;
	}

	//all allocated dsbuffer are currently playing
	return NULL;
}

/* return any allocated looping allocated resource whose attack has been played
 (and so is no more necessary)*/
p_allocated_resource3D fn_pGetInvalidableAllocatedResource()
{
	p_allocated_resource3D pres3D;
	int i;

	SNDLST2_M_DynamicForEachElementOf(&g_AllocatedResources.pList,pres3D,i)
	{
		if (pres3D->Attack && pres3D->Loop && pres3D->pAudibleVoice)
			if (!pres3D->pAudibleVoice->voice->bWaitingLoop)
				//this one do mot need its attack anymore
				return pres3D;
	}

	return NULL;
}

/*remet une virtual voices à sa place quand on sait qu'elle est mal placee*/
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_VirtualModule3D.virtual_voices.pList);
 
	/*classement selon la priorite*/
	while ((pVoice!=NULL) && (pVoice->lPrio>_voice->lPrio))
	{
		pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
	}
 
	if ((pVoice!=NULL) && (pVoice->lPrio==_voice->lPrio))
	{/*classement selon la norme*/
		while ((pVoice!=NULL) && (pVoice->rNorme>_voice->rNorme))
		{
			pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
		}
 
		if ((pVoice!=NULL) && (pVoice->rNorme==_voice->rNorme))
		{/*classement selon l'instance*/
			while ((pVoice!=NULL) && (pVoice->lInstance>_voice->lInstance))
			{
				pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
			}
		}
	}
 
	if (pVoice==NULL)
	{/*la derniere place ira tres bien*/
		SNDLST2_M_DynamicAddTail(&g_VirtualModule3D.virtual_voices.pList,_voice);
	}
	else
	{/*_voice avant pVoice*/
		tmp_voice=SNDLST2_M_DynamicGetPrevElement(pVoice);
		SNDLST2_M_DynamicInsertBetween(_voice,tmp_voice,pVoice);
	}
 
	if (_voice->audible)
		reinsert_audible_voice3D(_voice->audible);
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x resorted\n",_voice);
#endif
}
 
 
/*relance-arret des sons associes aux voies virtuelles*/
/*we assume the list have been ordered by "to be hearable" */
void synchro_virtual_voice3D()
{
	p_virtual_voice3D pVoice;
	p_audible_voice3D pAudible;
	p_allocated_resource3D pres3D;
	int i;
 
	//reset bcheck flag
	//once an audible voice has bCheck to TRUE, it cannot be killed
	SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.audible_voices.pList,pAudible,i)
		pAudible->bCheck=FALSE;

	//1- find the first non-curently hearable voice to become hearable
	pVoice=SNDLST2_M_DynamicGetFirstElement(&g_VirtualModule3D.virtual_voices.pList);
	i=0;
	while ((pVoice) && (pVoice->audible) && (i<g_VirtualModule3D.g_lNbSxdAudible))
	{
		pVoice->audible->bCheck=TRUE;
		pVoice=SNDLST2_M_DynamicGetNextElement(pVoice);
		i++;
	}

	if ((!pVoice) || pVoice->audible)
	{//there are less audible voice than availlable or whished hardware-channels ->nothing to do
		return;
	}

	//pVoice should become audible (if possible)
	pres3D=get_allocated_resource(pVoice->tduRes.pstPtr);

	if (!pres3D)
		//there is no non-audible availlable dsbuffer->we have to create a new one
		pres3D=fn_pAllocateResource(pVoice->tduRes.pstPtr);

	if (!pres3D)
	{//there is no more availlable dsbuffer->we have to free one previously allocated
		do {//free any non-audible dsbuffer until we can allocated ours
			pres3D=fn_pGetNonAudibleAllocatedResource();
			if (pres3D)
			{
				fn_vDesAllocateResource(pres3D);
				pres3D=fn_pAllocateResource(pVoice->tduRes.pstPtr);	
			}
			else
				break;
		} while(!pres3D);
	}

	if (!pres3D)
	{//there is no more availlable dsbuffer->we have to free one attack
		do {//free any non-audible dsbuffer until we can allocated ours
			pres3D=fn_pGetInvalidableAllocatedResource();
			if (pres3D)
			{
				fn_vInvalidateAllocatedResource(pres3D);
				pres3D=fn_pAllocateResource(pVoice->tduRes.pstPtr);	
			}
			else
				break;
		} while(!pres3D);
	}

	if (!pres3D)
	{//we have to kill an audible voice to free hardware voice
		pAudible=SNDLST2_M_DynamicGetLastElement(&g_VirtualModule3D.audible_voices.pList);

		while (pAudible)
		{//stop dsbuffer
			if (pAudible->bCheck)
				//ressource cannot be stopped (priority is higher than current one
				break;

			pres3D=pAudible->allocated_res;

			if (!SND_fn_bIsResourceLoopingSxdDX3D(pAudible->voice->tduRes))
				//voice become a pure virtual (may be hearable agin later)
				kill_virtual_voice3D(pAudible->voice);
			else
				//voice is definitively destroy  (will never be hearable again)
				del_audible_voice3D(pAudible);
			//from here, pAudible is invalid (donot use it anymore)

			//free associated allocated resource
			if (pres3D->pstRes3D!=pVoice->tduRes.pstPtr)
			{//desallocate dsbuffer to allow a new allocation
				fn_vDesAllocateResource(pres3D);
				pres3D=fn_pAllocateResource(pVoice->tduRes.pstPtr);
			}
			//else recycle this resource

			if (pres3D)
				//we got it !!
				break;

			//try with next audible
			pAudible=SNDLST2_M_DynamicGetLastElement(&g_VirtualModule3D.audible_voices.pList);
		}
	}

	if (!pres3D)
	{//abandon ->nothing can be done to hear this voice
		
	}
	else
	{
		pVoice->audible=add_audible_voice3D(pVoice,pres3D);
	}
}
 
/*destruction du contenu de voice_good et transfert de voice_bad dans voice_good, puis destruction de voice_bad*/
/*ne pas toucher au audible de voice_bad*/
/*utilise dans le cas ou PlayVirt renvoie 2 fois la meme valeur pour signifier que */
/*	la 1° instance du son est finie*/
/*retourne la voie virtuelle raj*/
p_virtual_voice3D replace_virtual_voice3D(p_virtual_voice3D voice_good,p_virtual_voice3D voice_bad)
{
	purge_virtual_voice3D(voice_good);
 
	SNDLST2_M_DynamicIsolate(voice_bad);
	memcpy(voice_good,voice_bad,sizeof(virtual_voice3D));
 
	voice_bad->audible=NULL;
	voice_bad->pstParam=NULL;
	voice_bad->lSizeParam=0;
	kill_virtual_voice3D(voice_bad);
 
	if (voice_good->audible)
		voice_good->audible->voice=voice_good;
 
	resort_virtual_voice3D(voice_good);
 
	synchro_virtual_voice3D();
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VV %x replaced %x\n",voice_good,voice_bad);
#endif
	return voice_good;
}
 
/************************************************
	Fonctions publiques
************************************************/
long SND_fn_lPlaySxdVirtual3D(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
	p_virtual_voice3D pVoice,voice;
	p_dead_voice3D	pDead;
	int i;
	SndBool	bReplaced=FALSE;
 
	/*pre-attribution d'une valeur pour pVoice (pourra changer jusqu'au return)*/
	pVoice=get_virtual_voice3D();
 
	pVoice->rNorme=SND_fn_rGetNormeSxd(par);
	if (g_VirtualModule3D.g_lInstance>=INSTANCE_MAX)
	{/*g_lInstance is too high->reduce every previous values of lInstance*/
		SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.virtual_voices.pList,voice,i)
		{
			voice->lInstance=max(0,voice->lInstance-INSTANCE_MAX/2);
		}
		g_VirtualModule3D.g_lInstance=INSTANCE_MAX/2;
	}
	pVoice->lInstance=g_VirtualModule3D.g_lInstance++;
	pVoice->lPrio=prio;
	pVoice->pfnCallback=fn_callback;
	pVoice->lParamCallback=par_callback;
	pVoice->bHardCallback=FALSE;
	pVoice->tduRes.pstPtr=(tdstBlockResourceMemDX3D*)(res.pstPtr);
	pVoice->lSizeParam=SND_M_lGetSizeOfSoundParam(par);
	pVoice->pstParam=SND_fn_pvMallocSndEx(E_ucSndBlockMain,pVoice->lSizeParam);
	memcpy(pVoice->pstParam,par,pVoice->lSizeParam);
	pVoice->bPaused=FALSE;

	if (SND_fn_bIsResourceLoopingSxdDX3D(pVoice->tduRes))
		pVoice->bWaitingLoop=TRUE;
	else
		pVoice->bWaitingLoop=FALSE;
 
	resort_virtual_voice3D(pVoice);
 
	synchro_virtual_voice3D();
 
	/*PlaySxdWave à echoue OU son non bouclant et non audible -> PlaySxdVirtual echouera*/
	if (!pVoice->audible && !SND_fn_bIsResourceLoopingSxdDX3D(pVoice->tduRes))
	{/*on ne lancera jamais le son*/
		kill_virtual_voice3D(pVoice);
 
		check_integrity3D();
		return C_PLAY_FAILED;
	}
 
	if (!bReplaced)
	{
		/*recyclage d'une (eventuelle) dead_voice3D*/
		if ((pDead=SNDLST2_M_DynamicGetFirstElement(&g_VirtualModule3D.dead_voices.pList))!=NULL)
		{
			pVoice=replace_virtual_voice3D(pDead->voice,pVoice);
			erase_dead_voice_from_list3D(pDead);
			bReplaced=TRUE;
		}
	}
 
	pVoice->bUsed=TRUE;
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"Play VV %x done**********\n",pVoice);
#endif
	check_integrity3D();
 
	return (long)pVoice;
}
 
void SND_fn_vRemoveCallbackSxdVirtual3D(long voice)
{
	p_virtual_voice3D pVoice=(p_virtual_voice3D)voice;
 
	pVoice->pfnCallback=NULL;
}
 
SndBool	SND_fn_bSetParamSxdVirtual3D(long voice,SoundParam *par)
{
	p_virtual_voice3D pVoice=(p_virtual_voice3D)voice;
	SndBool	bRet;
 
	if (pVoice->dead)
	{
		bRet=FALSE;
	}
	else
	{
		if (pVoice->audible)
		{
			if (fn_bTestDSBufferIsActive(pVoice->audible))
			{
				set_param_DSBuffer(pVoice->audible->allocated_res,pVoice->pstParam,TRUE,FALSE);

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

				bRet=TRUE;
			}
			else
				bRet=FALSE;
		}
		else
			bRet=TRUE;
	}
 
	if (bRet==FALSE)
	{/*destruction de la voie virtuelle*/
		if ((!pVoice->audible) && (pVoice->pfnCallback) && (!pVoice->bHardCallback))
			(*pVoice->pfnCallback)(pVoice->lParamCallback);
		if (pVoice->dead)
			erase_dead_voice_from_list3D(pVoice->dead);
		del_virtual_voice3D(pVoice);
	}
	else
	{/*raj du soundParam*/
		raj_soundparam3D(pVoice,par);
		pVoice->rNorme=SND_fn_rGetNormeSxd(par);
		resort_virtual_voice3D(pVoice);
	}
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VV %x Tested(SetParam) and return %d**********\n",pVoice,bRet);
#endif
	return bRet;
}
 
SndBool	SND_fn_bTestIsPlayingSxdVirtual3D(long voice)
{
	virtual_voice3D* pVoice=(p_virtual_voice3D)voice;
	SndBool	bRet;
 
	if (pVoice->dead)
	{
		bRet=FALSE;
	}
	else
	{
		if (pVoice->audible)
			//TODO TestIsPlaying
			bRet=FALSE;
		else
			bRet=TRUE;
	}
 
	if (bRet==FALSE)
	{/*destruction de la voie virtuelle*/
		if ((!pVoice->audible) && (pVoice->pfnCallback) && (!pVoice->bHardCallback))
			(*pVoice->pfnCallback)(pVoice->lParamCallback);
		if (pVoice->dead)
			erase_dead_voice_from_list3D(pVoice->dead);
		del_virtual_voice3D(pVoice);
	}
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT2(_CRT_WARN,"VV %x Tested and return %d**********\n",pVoice,bRet);
#endif
 
	return bRet;
}
 
void SND_fn_vStopSxdVirtual3D(long voice)
{
	virtual_voice3D* pVoice=(p_virtual_voice3D)voice;
 
	if (pVoice->dead)
		erase_dead_voice_from_list3D(pVoice->dead);
	if (pVoice->audible)
		del_audible_voice3D(pVoice->audible);
 
	if ((pVoice->pfnCallback) && (!pVoice->bHardCallback))
		(*pVoice->pfnCallback)(pVoice->lParamCallback);
 
 
	del_virtual_voice3D(pVoice);
 
	synchro_virtual_voice3D();
 
	check_integrity3D();
 
#ifdef DEBUG_OUTPUT_SNDVIRT
	_RPT1(_CRT_WARN,"VV %x Stoped done **********\n",voice);
#endif
}
 
void SND_fn_vPauseSxdVirtual3D(long voice)
{
	virtual_voice3D* pVoice=(p_virtual_voice3D)voice;
 
	pVoice->bPaused=TRUE;
 
	//if (pVoice->audible)
	//	(*(pmodule->stPtrFunctions.pfn_vPauseSxd))(pVoice->lVoice);
	//TODO
}
 
void SND_fn_vResumeSxdVirtual3D(long voice)
{
	p_virtual_voice3D pVoice=(p_virtual_voice3D)voice;
 
	pVoice->bPaused=FALSE;
 
	if (pVoice->audible)
		//(*(pmodule->stPtrFunctions.pfn_vResumeSxd))(pVoice->lVoice);
		//TODO
		pVoice;
	else /*maybe, the virtual voice may start again*/
		synchro_virtual_voice3D();
}
 
 
SndReal	SND_fn_rGetPosSxdVirtual3D(long voice)
{
	p_virtual_voice3D pVoice=(p_virtual_voice3D)voice;
 
	if (pVoice->audible)
		//return (*(pmodule->stPtrFunctions.pfn_rGetPosSxd))(pVoice->lVoice);
		//TODO
		pVoice;
	else
		return SND_C_POS_UNKNOWN;
}
 
SndReal	SND_fn_rGetLengthSxdVirtual3D(long voice)
{
	p_virtual_voice3D pVoice=(p_virtual_voice3D)voice;
 
	if (pVoice->audible)
		//return (*(pmodule->stPtrFunctions.pfn_rGetLengthSxd))(pVoice->lVoice);
		//TODO
		pVoice;
	else
		return SND_C_LENGTH_UNKNOWN;
}
 
 
long SND_fn_lInitSxdVirtual3D(long lMaxNumberVoicesHearable)
{
	SNDLST2_M_DynamicInitAnchor(&g_VirtualModule3D.dead_voices.pList);
	SNDLST2_M_DynamicInitAnchor(&g_VirtualModule3D.audible_voices.pList);
	SNDLST2_M_DynamicInitAnchor(&g_VirtualModule3D.virtual_voices.pList);
	g_VirtualModule3D.g_lNbSxdAudible=lMaxNumberVoicesHearable;
 
	g_VirtualModule3D.g_lInstance=0;
 
	return C_INIT_OK;
}
 
SndBool	SND_fn_bTestInitSxdVirtual3D()
{
	return TRUE;
}
 
void SND_fn_vDesInitSxdVirtual3D()
{
}
 
void SND_fn_vSynchroSxdVirtual3D()
{
#ifdef DEBUG_TRACE_VOICES
	int nb_ds3D=0;
	int nb_loop=0;
	p_allocated_resource3D pres3D;
	p_audible_voice3D pv;
	int i;
#endif
	synchro_virtual_voice3D();
 
	check_integrity3D();

#ifdef DEBUG_TRACE_VOICES
	SNDLST2_M_DynamicForEachElementOf(&g_AllocatedResources.pList,pres3D,i)
		nb_ds3D+=((pres3D->Attack==NULL)?0:1)+((pres3D->Loop==NULL)?0:1);
	SNDLST2_M_DynamicForEachElementOf(&g_VirtualModule3D.audible_voices.pList,pv,i)
		nb_loop+=((pv->allocated_res->Attack3D!=NULL) && (pv->allocated_res->Loop3D!=NULL))? 1 : 0;
	_CrtDbgReport(_CRT_WARN,NULL,0,NULL,"Res %d (DX %d);Virtual Voices %d ;Audible Voices %d (Looping %d)\n"
		,SNDLST2_M_DynamicGetNumberOfElements(&g_AllocatedResources.pList)
		,nb_ds3D
		,SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.virtual_voices.pList)
		,SNDLST2_M_DynamicGetNumberOfElements(&g_VirtualModule3D.audible_voices.pList)
		,nb_loop);
#endif
}
 
 
