/*-----------------------------------------------------
FILE:   Sndeax.c
AUTHOR: Dominique Dumont
DATE:   November 16th 1998

ROLE:   Management of EAX effects WITH DirectSound3D
-----------------------------------------------------*/


#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
#include "SNDinc.h" //for USE_EAX
#include "eax.h"
#include "sndeax.h"

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


#ifdef USE_EAX


/*-----------------------------------------------------------
	TYPEDEFS
-----------------------------------------------------------*/
typedef struct
	{
	EAX_REVERBPROPERTIES props;
	int					 iProgNumber;
	} tdstPreset;

/*-----------------------------------------------------------
	VARIABLES
-----------------------------------------------------------*/
LPDIRECTSOUNDBUFFER lpPermanent2ndBufferToAttackEax = NULL;
LPKSPROPERTYSET		lpPropertySet = NULL;
float				fReverbMix = EAX_REVERBMIX_USEDISTANCE;
BOOL				bIsEaxOk = FALSE;

EAX_REVERBPROPERTIES myPresets[30] = {
									EAX_PRESET_GENERIC, //0
									EAX_PRESET_PADDEDCELL, //1
									EAX_PRESET_ROOM,//2
									EAX_PRESET_BATHROOM,//3
									EAX_PRESET_LIVINGROOM,//4
									EAX_PRESET_STONEROOM,//5
									EAX_PRESET_AUDITORIUM,//6
									EAX_PRESET_CONCERTHALL,//7
									EAX_PRESET_CAVE,//8
									EAX_PRESET_ARENA,//9
									EAX_PRESET_HANGAR,//10
									EAX_PRESET_CARPETEDHALLWAY, //1
									EAX_PRESET_HALLWAY,//12
									EAX_PRESET_STONECORRIDOR, //13
									EAX_PRESET_ALLEY,//14
									EAX_PRESET_FOREST,//15
									EAX_PRESET_CITY,//16
									EAX_PRESET_MOUNTAINS,//17
									EAX_PRESET_QUARRY,//18
									EAX_PRESET_PLAIN,//19
									EAX_PRESET_PARKINGLOT,//20
									EAX_PRESET_SEWERPIPE,//21
									EAX_PRESET_UNDERWATER,//22
									EAX_PRESET_DRUGGED,//23
									EAX_PRESET_DIZZY,//24
									EAX_PRESET_PSYCHOTIC};//25



tdstPreset			 *pUserPresets = NULL;
int 				 iNbUserPresets;
BOOL				 bUserPresetsAreGood = FALSE;
#endif

//---------------------------------------------------------
// int fniReadEaxPrograms()
//---------------------------------------------------------
// 
// 
//---------------------------------------------------------
int fniReadEaxPrograms()
{
#ifdef USE_EAX
	FILE *	fEAX;
	char	line[300];
	char	fileLocation[310];
	int		iRet;
	float	myFloat;
	char	commande[100];
	BOOL	bOk;
	int		iPhase;

// mini init
if (pUserPresets != NULL)
	{
	SND_fn_vFreeSnd(pUserPresets);
	}
iNbUserPresets = 0;
bUserPresetsAreGood = FALSE;

/////////////////////////////////////////////////////
// Computes the name of the program file and opens it

strncpy(fileLocation,SND_fn_szGetSoftDirectory(),300);
strcat(fileLocation,"\\eax.prg");
fEAX = fopen(fileLocation,"rt");
if (fEAX== NULL) 
	{
	SND_fn_vDisplayError(E_uwSndErrorReadingFile,"EAX PROGRAM FILE NOT PRESENT");
	return 1;
	}


bOk = TRUE;
iPhase = 4;

do
	{
	if(fgets(line,300,fEAX) != NULL)
		{
		if ((line[0] == ' ')||(line[0] == ';')||(line[0] == 10)||(line[0] == 13)||(line[0] == '\0'))
			{
			// Commentaire
			}
		else
			{
			// no comment :-)
			iRet = sscanf(line,"%s : %f",commande,&myFloat);

			if (strcmp(commande,"PRG")==0)
				{
				if (iPhase == 4)
					{
					// We must create a new program entry
					if (iNbUserPresets)
							{
							pUserPresets=SND_fn_pvReAllocSnd(pUserPresets,sizeof(tdstPreset)*(iNbUserPresets+1));
							}
						else
							{
							pUserPresets=SND_fn_pvMallocSnd(sizeof(tdstPreset));		
							}
					pUserPresets[iNbUserPresets].iProgNumber = (long)myFloat;
					iPhase = 0;
					}
				else
					{
					SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
					return 2;
					}
				}
			else
			if (strcmp(commande,"ENV")==0)
				{
				if (iPhase == 0)
					{
					pUserPresets[iNbUserPresets].props.environment = (long)myFloat;
					iPhase = 1;
					}
				else
					{
					SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
					return 3;
					}
				}
			else
			if (strcmp(commande,"VOL")==0)
				{
				if (iPhase == 1)
					{
					pUserPresets[iNbUserPresets].props.fVolume = myFloat;
					iPhase = 2;
					}
				else
					{
					SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
					return 4;
					}
				}
			else
			if (strcmp(commande,"DEC")==0)
				{
				if (iPhase == 2)
					{
					pUserPresets[iNbUserPresets].props.fDecayTime_sec = myFloat;
					iPhase = 3;
					}
				else
					{
					SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
					return 5;
					}
				}
			else
			if (strcmp(commande,"DAM")==0)
				{
				if (iPhase == 3)
					{
					pUserPresets[iNbUserPresets].props.fDamping = myFloat;
					iNbUserPresets++;
					iPhase = 4;
					}
				else
					{
					SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
					return 6;
					}
				}
				

			}
		}
	else
		{
		bOk = FALSE;
		}
	
	}
while (bOk);
if (iPhase != 4)
	{
	bUserPresetsAreGood = FALSE;
	SND_fn_vDisplayError(E_uwSndErrorReadingFile,"BAD EAX PROGRAM FILE");
	}
else
	{
	bUserPresetsAreGood = TRUE;
	}
fclose(fEAX);
#endif
return 0;
}
//---------------------------------------------------------
// BOOL fn_bIsEaxAvailable()
//---------------------------------------------------------
// 
// 
//---------------------------------------------------------
BOOL fn_bIsEaxAvailable()
{
#ifdef USE_EAX
	return bIsEaxOk;
#else
	return FALSE;
#endif
}
//---------------------------------------------------------
// void SND_fn_vInitEaxModule(LPDIRECTSOUND	lpDSO)
//---------------------------------------------------------
// Initialize the eax module	
// set the bIsEaxOk flag 	
//---------------------------------------------------------
void SND_fn_vInitEaxModule(LPDIRECTSOUND	lpDSO)
{
#ifdef USE_EAX
	WAVEFORMATEX	pcmOut;				// Format of the wave for secondary buffer if we need to make one.
	DSBUFFERDESC    dsbdSecondary;		// description for creating secondary buffer if we need to make one.
    HRESULT			hr;
	ULONG			ulSupport = 0;

bIsEaxOk = FALSE;
if(lpPermanent2ndBufferToAttackEax == NULL)
	{
	// we don't have a secondary to work with so we will create one.
	ZeroMemory( &dsbdSecondary, sizeof(DSBUFFERDESC));
	ZeroMemory( &pcmOut, sizeof(WAVEFORMATEX));
	// any format should do I just say 11kHz 16 bit mono
	pcmOut.wFormatTag = WAVE_FORMAT_PCM;
	pcmOut.nChannels = 1;
	pcmOut.nSamplesPerSec = 11025;
	pcmOut.nAvgBytesPerSec = 22050;
	pcmOut.nBlockAlign = 2;
	pcmOut.wBitsPerSample = 16;
	pcmOut.cbSize = 0;
	// size is just arbitary but not too small as I have seen problems with single sample buffers...
	dsbdSecondary.dwSize = sizeof(DSBUFFERDESC);
	dsbdSecondary.dwBufferBytes = 1024;
	dsbdSecondary.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_STATIC ;
	dsbdSecondary.lpwfxFormat = &pcmOut;
		
	if( FAILED(hr=IDirectSound_CreateSoundBuffer(lpDSO,&dsbdSecondary,&lpPermanent2ndBufferToAttackEax,NULL)))
		{
	//	MessageBox( hWndDemo, "Create secondary for getting property set extention failed",
	//			"Create secondary failed", MB_OK | MB_ICONSTOP );
	// if we can't make a secondary we can't do anything else so just exit out.
		bIsEaxOk = FALSE;
		return;
		}
	fReverbMix = EAX_REVERBMIX_USEDISTANCE;
	}

// if there is already a property set interface free it up before making a new one for the curent buffer
if(lpPropertySet != NULL)
	{
	IKsPropertySet_Release(lpPropertySet);
	lpPropertySet = NULL;
	}

// QI for the property set interface

if( FAILED( hr = IDirectSoundBuffer_QueryInterface(lpPermanent2ndBufferToAttackEax,&IID_IKsPropertySet,(void**)&lpPropertySet)))
	{
	//MessageBox( hWndDemo, "Couldn't QI for Property set interface",
	//		"QI PropertySet failed", MB_OK | MB_ICONSTOP );
	bIsEaxOk = FALSE;
	return;
	}
else
	{	
	// check if our listener related property set is avalible and supports everything.
	
	if( FAILED( hr = IKsPropertySet_QuerySupport(lpPropertySet,&DSPROPSETID_EAX_ReverbProperties,
		DSPROPERTY_EAX_ALL, &ulSupport)))
		{
		//MessageBox( hWndDemo, "Query support call failure",
		//		"Query support Error", MB_OK | MB_ICONSTOP );
		bIsEaxOk = FALSE;
		return;	
		}
	else
		{
		// check if we can get and set them.
		if ((ulSupport &(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) != (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))
			{
			//MessageBox( hWndDemo, "can't get and set all properties in Property set interface",
			//		"No EAX Support", MB_OK | MB_ICONSTOP );
			bIsEaxOk = FALSE;
			return;

			}	
		else
			{
			// check if our Buffer related property set is avalible and supports everything.
			if( FAILED( hr = IKsPropertySet_QuerySupport(lpPropertySet,&DSPROPSETID_EAXBUFFER_ReverbProperties,DSPROPERTY_EAXBUFFER_ALL, &ulSupport)))
				{
				//MessageBox( hWndDemo, "Query support call failure",
				//		"Query support Error", MB_OK | MB_ICONSTOP );
				bIsEaxOk = FALSE;
				return;

				}
			else
				{
				// check if we can get and set them.
				if ((ulSupport &(KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET)) != (KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET))
					{
					//MessageBox( hWndDemo, "can't get and set all properties in Property set interface",
					//		"No EAX Support", MB_OK | MB_ICONSTOP );
					bIsEaxOk = FALSE;
					return;

					}
				else
					{
					// everything is avalible.
					bIsEaxOk = TRUE;
					fniReadEaxPrograms();
					}
				}
			}
		}
} //Queryinterface OK
#endif

}



//---------------------------------------------------------
// EAX_REVERBPROPERTIES *fnpGetUserProgram(WORD w)
//---------------------------------------------------------
// Search for the good preset and return his address
// 	
//---------------------------------------------------------
EAX_REVERBPROPERTIES *fnpGetUserProgram(WORD w)
{
#ifdef USE_EAX
	int i;
if (bUserPresetsAreGood == TRUE)
	{
	for(i=0;i<iNbUserPresets;i++)
		{
		if (pUserPresets[i].iProgNumber == w)
			{
			return &(pUserPresets[i].props);
			}
		}
	}
#endif
return NULL;
}


//---------------------------------------------------------
// void SND_fn_vSetPreset(unsigned long ulNewPreset)
//---------------------------------------------------------
// Set a new preset for the primary buffer	
// 	
//---------------------------------------------------------
void SND_fn_vSetPreset(unsigned long ulNewPreset)
{
#ifdef USE_EAX
	    HRESULT			hr;


if (bIsEaxOk == FALSE) return;
if(lpPropertySet != NULL)
	{
	if ((ulNewPreset & 0xFFFF) == 0)
		{
		if (FAILED(hr = IKsPropertySet_Set(lpPropertySet,&DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ALL,
			NULL,0,&myPresets[(ulNewPreset>>16)],sizeof(EAX_REVERBPROPERTIES))))
			{
			//ERROR "Could not Set Environment",
			return;
			}
		}
	else
		{
		EAX_REVERBPROPERTIES *pTemp;

		pTemp = fnpGetUserProgram((unsigned short)(ulNewPreset & 0xFFFF));
		if (pTemp != NULL)
			{
			if (FAILED(hr = IKsPropertySet_Set(lpPropertySet,&DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ALL,
				NULL,0,pTemp,sizeof(EAX_REVERBPROPERTIES))))
				{
				//ERROR "Could not Set Environment",
				return;
				}
			}
		else
			{
			//ERROR "Preset doesn't exist",
			// but we apply the factory preset
			if (FAILED(hr = IKsPropertySet_Set(lpPropertySet,&DSPROPSETID_EAX_ReverbProperties, DSPROPERTY_EAX_ALL,
				NULL,0,&myPresets[(ulNewPreset>>16)],sizeof(EAX_REVERBPROPERTIES))))
				{
				//ERROR "Could not Set Environment",
				return;
				}
			}
		}
	}
#endif
}



//---------------------------------------------------------
// void SND_fn_vDesInitEaxModule()
//---------------------------------------------------------
// Close the eax module	
// 
//---------------------------------------------------------
void SND_fn_vDesInitEaxModule()
{
#ifdef USE_EAX
if(lpPropertySet != NULL)
	{
		IKsPropertySet_Release(lpPropertySet);
		lpPropertySet = NULL;
	}


if ( NULL != lpPermanent2ndBufferToAttackEax)
	{
	IDirectSoundBuffer_Release(lpPermanent2ndBufferToAttackEax);
	lpPermanent2ndBufferToAttackEax = NULL;
	}

if (pUserPresets != NULL)
	{
	SND_fn_vFreeSnd(pUserPresets);
	}
iNbUserPresets = 0;

#endif
}



/*-------------------------------------------------
fn_bGetInterfEAX
ROLE:     Gets the EAX interface
last modification: 24/03/98
by: Dom Dum
---------------------------------------------------*/
BOOL fn_bGetInterfEAX(LPDIRECTSOUNDBUFFER Buffer2D,LPKSPROPERTYSET *pBufferEAX)
{
#ifdef USE_EAX
if (fn_bIsEaxAvailable())
	{
	if( FAILED(IDirectSoundBuffer_QueryInterface(Buffer2D,&IID_IKsPropertySet,(void**)pBufferEAX)))
		{																
		return FALSE;
		}
	else
		{																
		return TRUE;
		}
	}
else
#endif
	return TRUE;
}


/*-------------------------------------------------
BOOL set_param_eax(LPKSPROPERTYSET *pBufferEAX,unsigned char ucReverb,BOOL bReverbable)
---------------------------------------------------*/

BOOL set_param_eax(LPKSPROPERTYSET pBufferEAX,unsigned char ucReverb,BOOL bReverbable)
{
#ifdef USE_EAX
	float tempReverb;

if (bIsEaxOk == FALSE) return TRUE;
if(pBufferEAX != NULL)
	{
	if (bReverbable== FALSE)
		{
		tempReverb = 0; // no reverb 1.0 = FULL REVERB
		}
	else
		{
		tempReverb = ((float)ucReverb/127.0f);
		}

	if (FAILED(IKsPropertySet_Set(pBufferEAX,&DSPROPSETID_EAXBUFFER_ReverbProperties, DSPROPERTY_EAXBUFFER_REVERBMIX,
		NULL,0,&tempReverb,sizeof(float))))
		{
		//ERROR "Could not Set Environment",
		return FALSE;
		}
	}
#endif
return TRUE;
}