|
|
|
/*==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. //
|
|
|
|
// //
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "hsThread.h"
|
|
|
|
#ifndef EAX_SDK_AVAILABLE
|
|
|
|
#include "plEAXStructures.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>
|
|
|
|
|
|
|
|
#if HS_BUILD_FOR_WIN32
|
|
|
|
# if defined(DX_OLD_SDK) || defined(__MINGW32__)
|
|
|
|
# include <dxerr9.h>
|
|
|
|
# else
|
|
|
|
# include <dxerr.h>
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#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, float 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
|
|
|
|
|
|
|
|
float 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
|
|
|
|
{
|
|
|
|
float 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->ReadLE16();
|
|
|
|
fRoomHF = s->ReadLE16();
|
|
|
|
fRoomAuto = s->ReadBool();
|
|
|
|
fRoomHFAuto = s->ReadBool();
|
|
|
|
|
|
|
|
fOutsideVolHF = s->ReadLE16();
|
|
|
|
|
|
|
|
fAirAbsorptionFactor = s->ReadLEFloat();
|
|
|
|
fRoomRolloffFactor = s->ReadLEFloat();
|
|
|
|
fDopplerFactor = s->ReadLEFloat();
|
|
|
|
fRolloffFactor = s->ReadLEFloat();
|
|
|
|
|
|
|
|
fSoftStarts.Read( s );
|
|
|
|
fSoftEnds.Read( s );
|
|
|
|
|
|
|
|
fOcclusionSoftValue = -1.f;
|
|
|
|
SetOcclusionSoftValue( s->ReadLEFloat() );
|
|
|
|
|
|
|
|
fDirtyParams = kAll;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Enable( false ); // Force init of params
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSettings::Write( hsStream *s )
|
|
|
|
{
|
|
|
|
s->WriteBool( fEnabled );
|
|
|
|
if( fEnabled )
|
|
|
|
{
|
|
|
|
s->WriteLE16( fRoom );
|
|
|
|
s->WriteLE16( fRoomHF );
|
|
|
|
s->WriteBool( fRoomAuto );
|
|
|
|
s->WriteBool( fRoomHFAuto );
|
|
|
|
|
|
|
|
s->WriteLE16( fOutsideVolHF );
|
|
|
|
|
|
|
|
s->WriteLEFloat( fAirAbsorptionFactor );
|
|
|
|
s->WriteLEFloat( fRoomRolloffFactor );
|
|
|
|
s->WriteLEFloat( fDopplerFactor );
|
|
|
|
s->WriteLEFloat( fRolloffFactor );
|
|
|
|
|
|
|
|
fSoftStarts.Write( s );
|
|
|
|
fSoftEnds.Write( s );
|
|
|
|
|
|
|
|
s->WriteLEFloat( fOcclusionSoftValue );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSettings::SetRoomParams( int16_t room, int16_t 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_t vol )
|
|
|
|
{
|
|
|
|
fOutsideVolHF = vol;
|
|
|
|
fDirtyParams |= kOutsideVolHF;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSettings::SetFactors( float airAbsorption, float roomRolloff, float doppler, float rolloff )
|
|
|
|
{
|
|
|
|
fAirAbsorptionFactor = airAbsorption;
|
|
|
|
fRoomRolloffFactor = roomRolloff;
|
|
|
|
fDopplerFactor = doppler;
|
|
|
|
fRolloffFactor = rolloff;
|
|
|
|
fDirtyParams |= kFactors;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSettings::SetOcclusionSoftValue( float value )
|
|
|
|
{
|
|
|
|
if( fOcclusionSoftValue != value )
|
|
|
|
{
|
|
|
|
fOcclusionSoftValue = value;
|
|
|
|
IRecalcSofts( kOcclusion );
|
|
|
|
fDirtyParams |= kOcclusion;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSettings::IRecalcSofts( uint8_t whichOnes )
|
|
|
|
{
|
|
|
|
float percent, invPercent;
|
|
|
|
|
|
|
|
if( whichOnes & kOcclusion )
|
|
|
|
{
|
|
|
|
percent = fOcclusionSoftValue;
|
|
|
|
invPercent = 1.f - percent;
|
|
|
|
|
|
|
|
int16_t occ = (int16_t)( ( (float)fSoftStarts.GetOcclusion() * invPercent ) + ( (float)fSoftEnds.GetOcclusion() * percent ) );
|
|
|
|
float lfRatio = (float)( ( fSoftStarts.GetOcclusionLFRatio() * invPercent ) + ( fSoftEnds.GetOcclusionLFRatio() * percent ) );
|
|
|
|
float roomRatio = (float)( ( fSoftStarts.GetOcclusionRoomRatio() * invPercent ) + ( fSoftEnds.GetOcclusionRoomRatio() * percent ) );
|
|
|
|
float directRatio = (float)( ( 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->ReadLE( &fOcclusion );
|
|
|
|
s->ReadLE( &fOcclusionLFRatio );
|
|
|
|
s->ReadLE( &fOcclusionRoomRatio );
|
|
|
|
s->ReadLE( &fOcclusionDirectRatio );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSoftSettings::Write( hsStream *s )
|
|
|
|
{
|
|
|
|
s->WriteLE( fOcclusion );
|
|
|
|
s->WriteLE( fOcclusionLFRatio );
|
|
|
|
s->WriteLE( fOcclusionRoomRatio );
|
|
|
|
s->WriteLE( fOcclusionDirectRatio );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEAXSourceSoftSettings::SetOcclusion( int16_t occ, float lfRatio, float roomRatio, float 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_t 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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|