/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
//																			//
//	plEAXEffects - Various classes and wrappers to support EAX				//
//					acceleration.											//
//																			//
//////////////////////////////////////////////////////////////////////////////


#ifdef EAX_SDK_AVAILABLE
# pragma message("==>Compiling with EAX_SDK")
#if defined(_MSC_VER) && _MSC_VER < 1400
// name EAX libraries here rather than in the projects so that we don't try to
// link them unless EAX_SDK_AVAILABLE (avoids the need for separate projects
// or configurations for the cases with/without EAX)
#pragma comment(lib, "eax.lib")
#pragma comment(lib, "eaxguid.lib")
#endif // _MSC_VER < 1400
#endif // EAX_SDK_AVAILABLE

#include "hsTypes.h"
#include "hsThread.h"
#ifndef EAX_SDK_AVAILABLE
#include <EFX-Util.h>
#endif
#include "plEAXEffects.h"
#include "../plAudioCore/plAudioCore.h"
#include "plDSoundBuffer.h"
#include "hsTemplates.h"
#include "plEAXListenerMod.h"
#include "hsStream.h"
#include "plAudioSystem.h"
#include <al.h>

#include <dxerr.h>
#ifdef EAX_SDK_AVAILABLE
#include <eax.h>
#include <eax-util.h>
#include <eaxlegacy.h>
#endif
#include "../plStatusLog/plStatusLog.h"

#define kDebugLog	if( myLog != nil ) myLog->AddLineF(

#ifdef EAX_SDK_AVAILABLE
static EAXGet			s_EAXGet;
static EAXSet			s_EAXSet;
#endif


//// GetInstance /////////////////////////////////////////////////////////////

plEAXListener	&plEAXListener::GetInstance( void )
{
	static plEAXListener	instance;
	return instance;
}

//// Constructor/Destructor //////////////////////////////////////////////////

plEAXListener::plEAXListener()
{
	fInited = false;
	ClearProcessCache();
}

plEAXListener::~plEAXListener()
{
	Shutdown();
}

//// Init ////////////////////////////////////////////////////////////////////

hsBool	plEAXListener::Init( void )
{
#ifdef EAX_SDK_AVAILABLE
	if( fInited )
		return true;

	if(!alIsExtensionPresent((ALchar *)"EAX4.0"))		// is eax 4 supported
	{
		if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated"))		// is an earlier version of eax supported
		{
			plStatusLog::AddLineS("audio.log", "EAX not supported");
			return false;
		}
		else
		{
			plStatusLog::AddLineS("audio.log", "EAX 4 Emulated supported");
		}
	}
	else
	{
		plStatusLog::AddLineS("audio.log", "EAX 4 available");
	}
	
	// EAX is supported 
	s_EAXGet = (EAXGet)alGetProcAddress((ALchar *)"EAXGet");
	s_EAXSet = (EAXSet)alGetProcAddress((ALchar *)"EAXSet");
	if(!s_EAXGet || ! s_EAXSet)
	{
		IFail( "EAX initialization failed", true );
		return false;
	}
	fInited = true;

#if 1
	try
	{
		// Make an EAX call here to prevent problems on WDM driver
		unsigned int lRoom = -10000;

		SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ROOM, &lRoom, sizeof( unsigned int ));
	}
	catch ( ... )
	{
		plStatusLog::AddLineS("audio.log", "Unable to set EAX Property Set, disabling EAX...");
		plgAudioSys::EnableEAX(false);
		return false;
	}
#endif

	ClearProcessCache();

	return true;
#else /* !EAX_SDK_AVAILABLE */
	plStatusLog::AddLineS("audio.log", "EAX disabled in this build");
	return false;
#endif
}

//// Shutdown ////////////////////////////////////////////////////////////////

void	plEAXListener::Shutdown( void )
{
	if( !fInited )
		return;

#ifdef EAX_SDK_AVAILABLE
	s_EAXSet = nil;
	s_EAXGet = nil;
#endif
	IRelease();
}


bool plEAXListener::SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize )
{
	if(fInited)
	{
#ifdef EAX_SDK_AVAILABLE
		return s_EAXSet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
#endif
	}
	return false;
}

bool plEAXListener::GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
	if(fInited)
	{
#ifdef EAX_SDK_AVAILABLE
		return s_EAXGet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
#endif
	}
	return false;
}

bool plEAXSource::SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
#ifdef EAX_SDK_AVAILABLE
	return s_EAXSet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
#else
	return false;
#endif
}

bool plEAXSource::GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
#ifdef EAX_SDK_AVAILABLE
	return s_EAXGet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
#else
	return false;
#endif
}


//// IRelease ////////////////////////////////////////////////////////////////

void	plEAXListener::IRelease( void )
{
	fInited = false;
}

//// IFail ///////////////////////////////////////////////////////////////////

void	plEAXListener::IFail(  hsBool major )
{
	plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
							"ERROR in plEAXListener: Could not set global eax params");

	if( major )
		IRelease();
}

void	plEAXListener::IFail( const char *msg, hsBool major )
{
	plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
							"ERROR in plEAXListener: %s", msg );

	if( major )
		IRelease();
}

//// IMuteProperties /////////////////////////////////////////////////////////
//	Mutes the given properties, so if you have some props that you want
//	half strength, this function will do it for ya.

void	plEAXListener::IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent )
{
	// We only mute the room, roomHF and roomLF, since those control the overall effect
	// application. All three are a direct linear blend as defined by eax-util.cpp, so
	// this should be rather easy

	hsScalar invPercent = 1.f - percent;

	// The old way, as dictated by EAX sample code...
#ifdef EAX_SDK_AVAILABLE
	props->lRoom   = (int)( ( (float)EAXLISTENER_MINROOM   * invPercent ) + ( (float)props->lRoom   * percent ) );
#endif
	// The new way, as suggested by EAX guys...
//	props->lRoom = (int)( 2000.f * log( invPercent ) ) + props->lRoom;

//	props->lRoomLF = (int)( ( (float)EAXLISTENER_MINROOMLF * invPercent ) + ( (float)props->lRoomLF * percent ) );
//	props->lRoomHF = (int)( ( (float)EAXLISTENER_MINROOMHF * invPercent ) + ( (float)props->lRoomHF * percent ) );
}

//// ClearProcessCache ///////////////////////////////////////////////////////
//	Clears the cache settings used to speed up ProcessMods(). Call whenever
//	the list of mods changed.

void	plEAXListener::ClearProcessCache( void )
{
	fLastBigRegion = nil;
	fLastModCount = -1;
	fLastWasEmpty = false;
	fLastSingleStrength = -1.f;
}

//// ProcessMods /////////////////////////////////////////////////////////////
//	9.13.02 mcn - Updated the caching method. Now fLastBigRegion will point
//	to a region iff it's the only region from the last pass that had a
//	strength > 0. The reason we can't do our trick before is because even if
//	we have a region w/ strength 1, another region might power up from 1 and
//	thus suddenly alter the total reverb settings. Thus, the only time we 
//	can wisely skip is if our current big region == fLastBigRegion *and*
//	the total strength is the same.

void	plEAXListener::ProcessMods( hsTArray<plEAXListenerMod *> &modArray )
{
#ifdef EAX_SDK_AVAILABLE
	int		i;
	float	totalStrength;
	hsBool	firstOne;

	plEAXListenerMod		*thisBigRegion = nil;
	EAXLISTENERPROPERTIES	finalProps;
	static int oldTime = timeGetTime();		// Get starting time
	int newTime;
	hsBool bMorphing = false;

	static plStatusLog	*myLog = nil;

	if( myLog == nil && plgAudioSys::AreExtendedLogsEnabled() )
		myLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "EAX Reverbs", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile );
	else if( myLog != nil && !plgAudioSys::AreExtendedLogsEnabled() )
	{
		delete myLog;
		myLog = nil;
	}

	if( myLog != nil )
		myLog->Clear();

	if( modArray.GetCount() != fLastModCount )
	{
		kDebugLog "Clearing cache..." );
		ClearProcessCache();	// Code path changed, clear the entire cache
		fLastModCount = modArray.GetCount();
	}
	else
	{
		kDebugLog "" );
	}

	if( modArray.GetCount() > 0 )
	{
		kDebugLog "%d regions to calc", modArray.GetCount() );

		// Reset and find a new one if applicable
		thisBigRegion = nil;

		// Accumulate settings from all the active listener regions (shouldn't be too many, we hope)
		totalStrength = 0.f;
		firstOne = true;
		for( i = 0; i < modArray.GetCount(); i++ )
		{
			float strength = modArray[ i ]->GetStrength();
			kDebugLog "%4.2f - %s", strength, modArray[ i ]->GetKey()->GetUoid().GetObjectName() );
			if( strength > 0.f )
			{
				// fLastBigRegion will point to a region iff it's the only region w/ strength > 0
				if( totalStrength == 0.f )
					thisBigRegion = modArray[ i ];
				else
					thisBigRegion = nil;

				if( firstOne )
				{
					// First one, just init to it
					memcpy( &finalProps, modArray[ i ]->GetListenerProps(), sizeof( finalProps ) );
					totalStrength = strength;
					firstOne = false;
				}
				else
				{
					hsScalar scale = strength / ( totalStrength + strength );
					EAX3ListenerInterpolate( &finalProps, modArray[ i ]->GetListenerProps(), scale, &finalProps, false );
					totalStrength += strength;
					bMorphing = true;
				}
				
				if( totalStrength >= 1.f )
					break;
			}
		}

		if( firstOne )
		{
			// No regions of strength > 0, so just make it quiet
			kDebugLog "Reverb should be quiet" );
			if( fLastWasEmpty )
				return;

			memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
			finalProps.lRoom = EAXLISTENER_MINROOM;
//			finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
//			finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
			fLastWasEmpty = true;
			fLastBigRegion = nil;
			fLastSingleStrength = -1.f;
		}
		else 
		{
			fLastWasEmpty = false;

			if( thisBigRegion == fLastBigRegion && totalStrength == fLastSingleStrength )
				// Cached values should be the same, so we can bail at this point
				return;

			fLastBigRegion = thisBigRegion;
			fLastSingleStrength = ( thisBigRegion != nil ) ? totalStrength : -1.f;

			if( totalStrength < 1.f )
			{
				kDebugLog "Total strength < 1; muting result" );
				// All of them together is less than full strength, so mute our result
				IMuteProperties( &finalProps, totalStrength );
			}
		}
	}
	else
	{
		kDebugLog "No regions at all; disabling reverb" );
		// No regions whatsoever, so disable listener props entirely
		if( fLastWasEmpty )
			return;

		memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
		finalProps.lRoom = EAXLISTENER_MINROOM;
//		finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
//		finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
		fLastWasEmpty = true;
	}

	// if were morphing between regions, do 10th of a second check, otherwise just let it 
	// change due to opt out(caching) feature.
	if(bMorphing)
	{
		newTime = timeGetTime();

		// Update, at most, ten times per second
		if((newTime - oldTime) < 100) return;
			
		oldTime = newTime;		// update time
	}
//finalProps.flAirAbsorptionHF *= 0.3048f; // Convert to feet
	//kDebugLog "** Updating property set **" );


	if(!SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &finalProps, sizeof( finalProps )))
	{
		IFail(  false );
	}
#endif /* EAX_SDK_AVAILABLE */
}


//////////////////////////////////////////////////////////////////////////////
//// Source Settings /////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

//// Constructor/Destructor //////////////////////////////////////////////////

plEAXSourceSettings::plEAXSourceSettings()
{
	fDirtyParams = kAll;
	Enable( false );
}

plEAXSourceSettings::~plEAXSourceSettings()
{
}

//// Read/Write/Set //////////////////////////////////////////////////////////

void	plEAXSourceSettings::Read( hsStream *s )
{
	fEnabled = s->ReadBool();
	if( fEnabled )
	{
		fRoom = s->ReadSwap16();
		fRoomHF = s->ReadSwap16();
		fRoomAuto = s->ReadBool();
		fRoomHFAuto = s->ReadBool();

		fOutsideVolHF = s->ReadSwap16();
		
		fAirAbsorptionFactor = s->ReadSwapFloat();
		fRoomRolloffFactor = s->ReadSwapFloat();
		fDopplerFactor = s->ReadSwapFloat();
		fRolloffFactor = s->ReadSwapFloat();

		fSoftStarts.Read( s );
		fSoftEnds.Read( s );

		fOcclusionSoftValue = -1.f;
		SetOcclusionSoftValue( s->ReadSwapFloat() );

		fDirtyParams = kAll;
	}
	else
		Enable( false );	// Force init of params
}

void	plEAXSourceSettings::Write( hsStream *s )
{
	s->WriteBool( fEnabled );
	if( fEnabled )
	{
		s->WriteSwap16( fRoom );
		s->WriteSwap16( fRoomHF );
		s->WriteBool( fRoomAuto );
		s->WriteBool( fRoomHFAuto );

		s->WriteSwap16( fOutsideVolHF );
		
		s->WriteSwapFloat( fAirAbsorptionFactor );
		s->WriteSwapFloat( fRoomRolloffFactor );
		s->WriteSwapFloat( fDopplerFactor );
		s->WriteSwapFloat( fRolloffFactor );

		fSoftStarts.Write( s );
		fSoftEnds.Write( s );

		s->WriteSwapFloat( fOcclusionSoftValue );
	}
}

void	plEAXSourceSettings::SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto )
{
	fRoom = room;
	fRoomHF = roomHF;
	fRoomAuto = roomAuto;
	fRoomHFAuto = roomHFAuto;
	fDirtyParams |= kRoom;
}

void	plEAXSourceSettings::Enable( hsBool e )
{
	fEnabled = e;
	if( !e )
	{
#ifdef EAX_SDK_AVAILABLE
		fRoom = EAXBUFFER_MINROOM;
		fRoomHF = EAXBUFFER_MINROOMHF;
#else
		fRoom = 0;
		fRoomHF = 0;
#endif
		fRoomAuto = true;
		fRoomHFAuto = true;

		fOutsideVolHF = 0;

		fAirAbsorptionFactor = 1.f;
		fRoomRolloffFactor = 0.f;
		fDopplerFactor = 0.f;
		fRolloffFactor = 0.f;

		fOcclusionSoftValue = 0.f;
		fSoftStarts.Reset();
		fSoftEnds.Reset();
		fCurrSoftValues.Reset();
		fDirtyParams = kAll;
	}
}

void	plEAXSourceSettings::SetOutsideVolHF( Int16 vol )
{
	fOutsideVolHF = vol;
	fDirtyParams |= kOutsideVolHF;
}

void	plEAXSourceSettings::SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff )
{
	fAirAbsorptionFactor = airAbsorption;
	fRoomRolloffFactor = roomRolloff;
	fDopplerFactor = doppler;
	fRolloffFactor = rolloff;
	fDirtyParams |= kFactors;
}

void	plEAXSourceSettings::SetOcclusionSoftValue( hsScalar value )
{
	if( fOcclusionSoftValue != value )
	{
		fOcclusionSoftValue = value;
		IRecalcSofts( kOcclusion );
		fDirtyParams |= kOcclusion;
	}
}

void	plEAXSourceSettings::IRecalcSofts( UInt8 whichOnes )
{
	hsScalar	percent, invPercent;

	if( whichOnes & kOcclusion )
	{
		percent = fOcclusionSoftValue;
		invPercent = 1.f - percent;

		Int16		occ	= (Int16)( ( (float)fSoftStarts.GetOcclusion() * invPercent ) + ( (float)fSoftEnds.GetOcclusion() * percent ) );
		hsScalar	lfRatio = (hsScalar)( ( fSoftStarts.GetOcclusionLFRatio() * invPercent ) + ( fSoftEnds.GetOcclusionLFRatio() * percent ) );
		hsScalar	roomRatio = (hsScalar)( ( fSoftStarts.GetOcclusionRoomRatio() * invPercent ) + ( fSoftEnds.GetOcclusionRoomRatio() * percent ) );
		hsScalar	directRatio = (hsScalar)( ( fSoftStarts.GetOcclusionDirectRatio() * invPercent ) + ( fSoftEnds.GetOcclusionDirectRatio() * percent ) );

		fCurrSoftValues.SetOcclusion( occ, lfRatio, roomRatio, directRatio );
	}
}


//////////////////////////////////////////////////////////////////////////////
//// Source Soft Settings ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

void	plEAXSourceSoftSettings::Reset( void )
{
	fOcclusion = 0;
	fOcclusionLFRatio = 0.25f;
	fOcclusionRoomRatio = 1.5f;
	fOcclusionDirectRatio = 1.f;
}

void	plEAXSourceSoftSettings::Read( hsStream *s )
{
	s->ReadSwap( &fOcclusion );
	s->ReadSwap( &fOcclusionLFRatio );
	s->ReadSwap( &fOcclusionRoomRatio );
	s->ReadSwap( &fOcclusionDirectRatio );
}

void	plEAXSourceSoftSettings::Write( hsStream *s )
{
	s->WriteSwap( fOcclusion );
	s->WriteSwap( fOcclusionLFRatio );
	s->WriteSwap( fOcclusionRoomRatio );
	s->WriteSwap( fOcclusionDirectRatio );
}

void	plEAXSourceSoftSettings::SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio )
{
	fOcclusion = occ;
	fOcclusionLFRatio = lfRatio;
	fOcclusionRoomRatio = roomRatio;
	fOcclusionDirectRatio = directRatio;
}

//// Constructor/Destructor //////////////////////////////////////////////////

plEAXSource::plEAXSource()
{	
	fInit = false;
	
}

plEAXSource::~plEAXSource()
{
	Release();
}

//// Init/Release ////////////////////////////////////////////////////////////

void	plEAXSource::Init( plDSoundBuffer *parent )
{
	fInit = true;
	// Init some default params
	plEAXSourceSettings	defaultParams;
	SetFrom( &defaultParams, parent->GetSource() );
}

void	plEAXSource::Release( void )
{
	fInit = false;
}

hsBool	plEAXSource::IsValid( void ) const
{
	return true;
}

//// SetFrom /////////////////////////////////////////////////////////////////

void	plEAXSource::SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force )
{
	UInt32 dirtyParams;
	if(source == 0 || !fInit) 
		return;

	if( force )
		dirtyParams = plEAXSourceSettings::kAll;
	else
		dirtyParams = settings->fDirtyParams;
	
	// Do the params
#ifdef EAX_SDK_AVAILABLE
	if( dirtyParams & plEAXSourceSettings::kRoom )
	{
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOM, &settings->fRoom, sizeof(settings->fRoom));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMHF, &settings->fRoomHF, sizeof(settings->fRoomHF));
	}

	if( dirtyParams & plEAXSourceSettings::kOutsideVolHF )
	{
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, &settings->fOutsideVolHF, sizeof(settings->fOutsideVolHF));
	}
	
	if( dirtyParams & plEAXSourceSettings::kFactors )
	{
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, &settings->fDopplerFactor, sizeof(settings->fDopplerFactor));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, &settings->fRolloffFactor, sizeof(settings->fRolloffFactor));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, &settings->fRoomRolloffFactor, sizeof(settings->fRoomRolloffFactor));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, &settings->fAirAbsorptionFactor, sizeof(settings->fAirAbsorptionFactor));
	}

	if( dirtyParams & plEAXSourceSettings::kOcclusion )
	{
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSION, &settings->GetCurrSofts().fOcclusion, sizeof(settings->GetCurrSofts().fOcclusion));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, &settings->GetCurrSofts().fOcclusionLFRatio, sizeof(settings->GetCurrSofts().fOcclusionLFRatio));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, &settings->GetCurrSofts().fOcclusionRoomRatio, sizeof(settings->GetCurrSofts().fOcclusionRoomRatio));
		SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, &settings->GetCurrSofts().fOcclusionDirectRatio, sizeof(settings->GetCurrSofts().fOcclusionDirectRatio));
	}
#endif /* EAX_SDK_AVAILABLE */

	settings->ClearDirtyParams();

	// Do all the flags in one pass
#ifdef EAX_SDK_AVAILABLE
	DWORD	flags;
	

	if( GetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD )) ) 
	{
		if( settings->GetRoomAuto() )
			flags |= EAXBUFFERFLAGS_ROOMAUTO;
		else
			flags &= ~EAXBUFFERFLAGS_ROOMAUTO;

		if( settings->GetRoomHFAuto() )
			flags |= EAXBUFFERFLAGS_ROOMHFAUTO;
		else
			flags &= ~EAXBUFFERFLAGS_ROOMHFAUTO;

		if( SetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD ) ) ) 
		{
			return;	// All worked, return here
		}	
		
		// Flag setting failed somehow
		hsAssert( false, "Unable to set EAX buffer flags" );
	}
#endif /* EAX_SDK_AVAILABLE */
}