You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1283 lines
35 KiB
1283 lines
35 KiB
/*==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/>. |
|
|
|
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==*/ |
|
#include "HeadSpin.h" |
|
#include <al.h> |
|
#include <alc.h> |
|
#include <efx.h> |
|
#include <MMREG.H> |
|
#ifdef EAX_SDK_AVAILABLE |
|
#include <eax.h> |
|
#endif |
|
|
|
#include "hsTimer.h" |
|
#include "hsGeometry3.h" |
|
#include "plgDispatch.h" |
|
#include "plProfile.h" |
|
#include "plStatusLog/plStatusLog.h" |
|
|
|
#include "plSound.h" |
|
#include "plAudioCaps.h" |
|
#include "plAudioSystem.h" |
|
#include "plDSoundBuffer.h" |
|
#include "plEAXEffects.h" |
|
#include "plEAXListenerMod.h" |
|
#include "plVoiceChat.h" |
|
|
|
#include "pnMessage/plAudioSysMsg.h" |
|
#include "plMessage/plRenderMsg.h" |
|
#include "pnMessage/plRefMsg.h" |
|
#include "plMessage/plAgeLoadedMsg.h" |
|
#include "pnMessage/plTimeMsg.h" |
|
|
|
#include "pnKeyedObject/plFixedKey.h" |
|
#include "pnKeyedObject/plKey.h" |
|
|
|
|
|
#define SAFE_RELEASE(p) if(p){ p->Release(); p = nil; } |
|
#define FADE_TIME 3 |
|
#define MAX_NUM_SOURCES 128 |
|
#define UPDATE_TIME_MS 100 |
|
|
|
plProfile_CreateTimer("EAX Update", "Sound", SoundEAXUpdate); |
|
plProfile_CreateTimer("Soft Update", "Sound", SoundSoftUpdate); |
|
plProfile_CreateCounter("Max Sounds", "Sound", SoundMaxNum); |
|
plProfile_CreateTimer("AudioUpdate", "RenderSetup", AudioUpdate); |
|
|
|
typedef std::vector<DeviceDescriptor>::iterator DeviceIter; |
|
|
|
//// Internal plSoftSoundNode Class Definition /////////////////////////////// |
|
class plSoftSoundNode |
|
{ |
|
public: |
|
const plKey fSoundKey; |
|
hsScalar fRank; |
|
|
|
plSoftSoundNode *fNext; |
|
plSoftSoundNode **fPrev; |
|
|
|
plSoftSoundNode *fSortNext; |
|
plSoftSoundNode **fSortPrev; |
|
|
|
plSoftSoundNode( const plKey s ) : fSoundKey( s ) { fNext = nil; fPrev = nil; } |
|
~plSoftSoundNode() { Unlink(); } |
|
|
|
void Link( plSoftSoundNode **prev ) |
|
{ |
|
fNext = *prev; |
|
fPrev = prev; |
|
if( fNext != nil ) |
|
fNext->fPrev = &fNext; |
|
*prev = this; |
|
} |
|
|
|
void Unlink( void ) |
|
{ |
|
if( fPrev != nil ) |
|
{ |
|
*fPrev = fNext; |
|
if( fNext ) |
|
fNext->fPrev = fPrev; |
|
fPrev = nil; |
|
fNext = nil; |
|
} |
|
} |
|
|
|
void SortedLink( plSoftSoundNode **prev, hsScalar rank ) |
|
{ |
|
fRank = rank; |
|
|
|
fSortNext = *prev; |
|
fSortPrev = prev; |
|
if( fSortNext != nil ) |
|
fSortNext->fSortPrev = &fSortNext; |
|
*prev = this; |
|
} |
|
|
|
// Larger values are first in the list |
|
void AddToSortedLink( plSoftSoundNode *toAdd, hsScalar rank ) |
|
{ |
|
if( fRank > rank ) |
|
{ |
|
if( fSortNext != nil ) |
|
fSortNext->AddToSortedLink( toAdd, rank ); |
|
else |
|
{ |
|
toAdd->SortedLink( &fSortNext, rank ); |
|
} |
|
} |
|
else |
|
{ |
|
// Cute trick here... |
|
toAdd->SortedLink( fSortPrev, rank ); |
|
} |
|
} |
|
|
|
void BootSourceOff( void ) |
|
{ |
|
plSound *sound = plSound::ConvertNoRef( fSoundKey->ObjectIsLoaded() ); |
|
if( sound != nil ) |
|
{ |
|
sound->ForceUnregisterFromAudioSys(); |
|
} |
|
} |
|
}; |
|
|
|
// plAudioSystem ////////////////////////////////////////////////////////////////////////// |
|
|
|
Int32 plAudioSystem::fMaxNumSounds = 16; |
|
Int32 plAudioSystem::fNumSoundsSlop = 8; |
|
|
|
plAudioSystem::plAudioSystem() : |
|
fStartTime(0), |
|
fListenerInit(false), |
|
fSoftRegionSounds(nil), |
|
fActiveSofts(nil), |
|
fCurrDebugSound(nil), |
|
fDebugActiveSoundDisplay(nil), |
|
fUsingEAX(false), |
|
fRestartOnDestruct(false), |
|
fWaitingForShutdown(false), |
|
fActive(false), |
|
fDisplayNumBuffers(false), |
|
fStartFade(0), |
|
fFadeLength(FADE_TIME), |
|
fCaptureDevice(nil), |
|
fLastUpdateTimeMs(0) |
|
{ |
|
fCurrListenerPos.Set( -1.e30, -1.e30, -1.e30 ); |
|
//fCommittedListenerPos.Set( -1.e30, -1.e30, -1.e30 ); |
|
fLastPos.Set(100, 100, 100); |
|
} |
|
|
|
plAudioSystem::~plAudioSystem() |
|
{ |
|
} |
|
|
|
void plAudioSystem::IEnumerateDevices() |
|
{ |
|
fDeviceList.clear(); |
|
plStatusLog::AddLineS("audio.log", "--Audio Devices --" ); |
|
char *devices = (char *)alcGetString(nil, ALC_DEVICE_SPECIFIER); |
|
int major, minor; |
|
|
|
while(*devices != nil) |
|
{ |
|
ALCdevice *device = alcOpenDevice(devices); |
|
if (device) |
|
{ |
|
ALCcontext *context = alcCreateContext(device, NULL); |
|
if (context) |
|
{ |
|
alcMakeContextCurrent(context); |
|
// if new actual device name isn't already in the list, then add it... |
|
bool bNewName = true; |
|
for (DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) |
|
{ |
|
if (strcmp((*i).GetDeviceName(), devices) == 0) |
|
{ |
|
bNewName = false; |
|
} |
|
} |
|
if ((bNewName)) |
|
{ |
|
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &major); |
|
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &minor); |
|
plStatusLog::AddLineS("audio.log", "%s OpenAL ver: %d.%d", devices, major, minor ); |
|
|
|
// filter out any devices that aren't openal 1.1 compliant |
|
if(major > 1 || (major == 1 && minor >= 1)) |
|
{ |
|
hsBool supportsEAX = false; |
|
#ifdef EAX_SDK_AVAILABLE |
|
if(alIsExtensionPresent((ALchar *)"EAX4.0") || alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) |
|
{ |
|
supportsEAX = true; |
|
} |
|
#endif |
|
DeviceDescriptor desc(devices, supportsEAX); |
|
fDeviceList.push_back(desc); |
|
} |
|
} |
|
alcMakeContextCurrent(nil); |
|
alcDestroyContext(context); |
|
} |
|
alcCloseDevice(device); |
|
} |
|
devices += strlen(devices) + 1; |
|
} |
|
|
|
DeviceDescriptor temp("", 0); |
|
// attempt to order devices |
|
for(unsigned i = 0; i < fDeviceList.size(); ++i) |
|
{ |
|
if(strstr(fDeviceList[i].GetDeviceName(), "Software")) |
|
{ |
|
temp = fDeviceList[i]; |
|
fDeviceList[i] = fDeviceList[0]; |
|
fDeviceList[0] = temp; |
|
} |
|
if(strstr(fDeviceList[i].GetDeviceName(), "Hardware")) |
|
{ |
|
temp = fDeviceList[i]; |
|
fDeviceList[i] = fDeviceList[1]; |
|
fDeviceList[1] = temp; |
|
} |
|
} |
|
} |
|
|
|
//// Init //////////////////////////////////////////////////////////////////// |
|
hsBool plAudioSystem::Init( hsWindowHndl hWnd ) |
|
{ |
|
plgAudioSys::fRestarting = false; |
|
static hsBool firstTimeInit = true; |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Init --" ); |
|
|
|
fMaxNumSources = 0; |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kGreen, "ASYS: Detecting caps..." ); |
|
plSoundBuffer::Init(); |
|
|
|
// Set the maximum number of sounds based on priority cutoff slider |
|
SetMaxNumberOfActiveSounds(); |
|
const char *deviceName = plgAudioSys::fDeviceName.c_str(); |
|
hsBool useDefaultDevice = true; |
|
|
|
if(firstTimeInit) |
|
{ |
|
IEnumerateDevices(); |
|
firstTimeInit = false; |
|
} |
|
|
|
if(!fDeviceList.size()) |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR Unable to query any devices, is openal installed?" ); |
|
return false; |
|
} |
|
|
|
// shouldn't ever happen, but just in case |
|
if(!deviceName) |
|
plgAudioSys::SetDeviceName(DEFAULT_AUDIO_DEVICE_NAME); |
|
|
|
for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) |
|
{ |
|
if(!strcmp((*i).GetDeviceName(), deviceName)) |
|
{ |
|
useDefaultDevice = false; |
|
break; |
|
} |
|
} |
|
|
|
if(useDefaultDevice) |
|
{ |
|
// if no device has been specified we will use the "Generic Software" device by default. If "Generic Software" is unavailable(which can happen) we select the first available device |
|
// We want to use software by default since some audio cards have major problems with hardware + eax. |
|
const char *defaultDev = fDeviceList.front().GetDeviceName(); |
|
for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) |
|
{ |
|
if(!strcmp(DEFAULT_AUDIO_DEVICE_NAME, (*i).GetDeviceName())) |
|
{ |
|
defaultDev = DEFAULT_AUDIO_DEVICE_NAME; |
|
break; |
|
} |
|
} |
|
|
|
plgAudioSys::SetDeviceName(defaultDev, false); |
|
fDevice = alcOpenDevice(defaultDev); |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: %s device selected", defaultDev ); |
|
deviceName = defaultDev; |
|
} |
|
else |
|
{ |
|
plgAudioSys::SetDeviceName(deviceName, false); |
|
fDevice = alcOpenDevice(deviceName); |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: %s device selected", deviceName ); |
|
} |
|
if(!fDevice) |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR initializing OpenAL" ); |
|
return false; |
|
} |
|
|
|
fContext = alcCreateContext(fDevice, 0); |
|
alcMakeContextCurrent(fContext); |
|
|
|
ALenum error; |
|
if(alGetError() != AL_NO_ERROR) |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR alcMakeContextCurrent failed" ); |
|
return false; |
|
} |
|
|
|
plStatusLog::AddLineS("audio.log", "OpenAL vendor: %s", alGetString(AL_VENDOR)); |
|
plStatusLog::AddLineS("audio.log", "OpenAL version: %s", alGetString(AL_VERSION)); |
|
plStatusLog::AddLineS("audio.log", "OpenAL renderer: %s", alGetString(AL_RENDERER)); |
|
plStatusLog::AddLineS("audio.log", "OpenAL extensions: %s", alGetString(AL_EXTENSIONS)); |
|
plAudioCaps caps = plAudioCapsDetector::Detect(); |
|
|
|
if(strcmp(deviceName, DEFAULT_AUDIO_DEVICE_NAME)) |
|
{ |
|
// we are using a hardware device, set priority based on number of hardware voices |
|
unsigned int numVoices = caps.GetMaxNumVoices(); |
|
|
|
if(numVoices < 16) |
|
plgAudioSys::SetPriorityCutoff(3); |
|
|
|
SetMaxNumberOfActiveSounds(); |
|
} |
|
|
|
// setup capture device |
|
ALCsizei bufferSize = FREQUENCY * 2 * BUFFER_LEN_SECONDS; // times 2 for 16-bit format |
|
//const char *dev = alcGetString(fDevice, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); |
|
fCaptureDevice = alcCaptureOpenDevice(nil, FREQUENCY, AL_FORMAT_MONO16, bufferSize); |
|
|
|
fMaxNumSources = caps.GetMaxNumVoices(); |
|
|
|
// attempt to init the EAX listener. |
|
if( plgAudioSys::fEnableEAX ) |
|
{ |
|
fUsingEAX = plEAXListener::GetInstance().Init(); |
|
if( fUsingEAX ) |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kGreen, "ASYS: EAX support detected and enabled." ); |
|
} |
|
else |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: EAX support NOT detected. EAX effects disabled." ); |
|
} |
|
} |
|
else |
|
fUsingEAX = false; |
|
|
|
plProfile_Set(SoundMaxNum, fMaxNumSounds); |
|
|
|
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); |
|
|
|
error = alGetError(); |
|
|
|
fWaitingForShutdown = false; |
|
plgDispatch::Dispatch()->RegisterForExactType( plAgeLoadedMsg::Index(), GetKey() ); |
|
return true; |
|
} |
|
|
|
//// Shutdown //////////////////////////////////////////////////////////////// |
|
|
|
void plAudioSystem::Shutdown() |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Shutdown --" ); |
|
|
|
plSoundBuffer::Shutdown(); |
|
|
|
// Delete our active sounds list |
|
delete fDebugActiveSoundDisplay; |
|
fDebugActiveSoundDisplay = nil; |
|
|
|
// Unregister soft sounds |
|
while( fSoftRegionSounds != nil ) |
|
{ |
|
fSoftRegionSounds->BootSourceOff(); |
|
delete fSoftRegionSounds; |
|
} |
|
while( fActiveSofts != nil ) |
|
{ |
|
fActiveSofts->BootSourceOff(); |
|
delete fActiveSofts; |
|
} |
|
|
|
while( fEAXRegions.GetCount() > 0 ) |
|
{ |
|
if( fEAXRegions[ 0 ] != nil && fEAXRegions[ 0 ]->GetKey() != nil ) |
|
{ |
|
GetKey()->Release( fEAXRegions[ 0 ]->GetKey() ); |
|
} |
|
fEAXRegions.Remove( 0 ); |
|
} |
|
plEAXListener::GetInstance().ClearProcessCache(); |
|
|
|
plSound::SetCurrDebugPlate( nil ); |
|
fCurrDebugSound = nil; |
|
|
|
// Reset this, just in case |
|
fPendingRegisters.Reset(); |
|
|
|
//fListenerInit = false; |
|
|
|
if( fUsingEAX ) |
|
{ |
|
plEAXListener::GetInstance().Shutdown(); |
|
} |
|
|
|
alcCaptureStop(fCaptureDevice); |
|
alcCaptureCloseDevice(fCaptureDevice); |
|
fCaptureDevice = nil; |
|
|
|
alcMakeContextCurrent(nil); |
|
alcDestroyContext(fContext); |
|
alcCloseDevice(fDevice); |
|
fContext = nil; |
|
fDevice = nil; |
|
|
|
fStartTime = 0; |
|
fUsingEAX = false; |
|
fCurrListenerPos.Set( -1.e30, -1.e30, -1.e30 ); |
|
//fCommittedListenerPos.Set( -1.e30, -1.e30, -1.e30 ); |
|
|
|
if( fRestartOnDestruct ) |
|
{ |
|
fRestartOnDestruct = false; |
|
plgAudioSys::Activate( true ); |
|
} |
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey() ); |
|
fWaitingForShutdown = false; |
|
} |
|
|
|
int plAudioSystem::GetNumAudioDevices() |
|
{ |
|
return fDeviceList.size(); |
|
} |
|
|
|
const char *plAudioSystem::GetAudioDeviceName(int index) |
|
{ |
|
if(index < 0 || index >= fDeviceList.size()) |
|
{ |
|
hsAssert(false, "Invalid index passed to GetAudioDeviceName"); |
|
return nil; |
|
} |
|
return fDeviceList[index].GetDeviceName(); |
|
} |
|
|
|
hsBool plAudioSystem::SupportsEAX(const char *deviceName) |
|
{ |
|
for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) |
|
{ |
|
if(!strcmp((*i).GetDeviceName(), deviceName)) |
|
{ |
|
return (*i).SupportsEAX(); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
void plAudioSystem::SetDistanceModel(int i) |
|
{ |
|
switch(i) |
|
{ |
|
case 0: alDistanceModel(AL_NONE); break; |
|
case 1: alDistanceModel(AL_INVERSE_DISTANCE ); break; |
|
case 2: alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); break; |
|
case 3: alDistanceModel(AL_LINEAR_DISTANCE ); break; |
|
case 4: alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED ); break; |
|
case 5: alDistanceModel(AL_EXPONENT_DISTANCE ); break; |
|
case 6: alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED); break; |
|
} |
|
} |
|
|
|
// Set the number of active sounds the audio system is allowed to play, based on the priority cutoff |
|
void plAudioSystem::SetMaxNumberOfActiveSounds() |
|
{ |
|
UInt16 priorityCutoff = plgAudioSys::GetPriorityCutoff(); |
|
int maxNumSounds = 24; |
|
|
|
// Keep this to a reasonable amount based on the users hardware, since we want the sounds to be played in hardware |
|
if(maxNumSounds > fMaxNumSources && fMaxNumSources != 0 ) |
|
maxNumSounds = fMaxNumSources / 2; |
|
|
|
// Allow a certain number of sounds based on a user specified setting. |
|
// Above that limit, we want 1/2 more sounds (8 for the max of 16) for a slop buffer, |
|
// if they fit, so that sounds that shouldn't be playing can still play in case they suddenly pop |
|
// back into priority range (so we don't incur performance hits when sounds hover on |
|
// the edge of being high enough priority to play) |
|
fMaxNumSounds = maxNumSounds; |
|
fNumSoundsSlop = fMaxNumSounds / 2; |
|
|
|
plStatusLog::AddLineS( "audio.log", "Max Number of Sounds Set to: %d", fMaxNumSounds); |
|
} |
|
|
|
void plAudioSystem::SetListenerPos(const hsPoint3 pos) |
|
{ |
|
fCurrListenerPos = pos; |
|
alListener3f(AL_POSITION, pos.fX, pos.fZ, -pos.fY); // negate z coord, since openal uses opposite handedness |
|
} |
|
|
|
void plAudioSystem::SetListenerVelocity(const hsVector3 vel) |
|
{ |
|
alListener3f(AL_VELOCITY, 0, 0, 0); // no doppler shift |
|
} |
|
|
|
void plAudioSystem::SetListenerOrientation(const hsVector3 view, const hsVector3 up) |
|
{ |
|
ALfloat orientation[] = { view.fX, view.fZ, -view.fY, up.fX, up.fZ, -up.fY }; |
|
alListenerfv(AL_ORIENTATION, orientation); |
|
} |
|
|
|
void plAudioSystem::SetActive( hsBool b ) |
|
{ |
|
fActive = b; |
|
if( fActive ) |
|
{ |
|
// Clear to send activate message (if listener not inited yet, delay until then) |
|
plgDispatch::MsgSend( TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kActivate ) ); |
|
} |
|
} |
|
|
|
//// IRegisterSoftSound ////////////////////////////////////////////////////// |
|
// Note: each sound might kick another off the top-n-ranked-sounds list, so |
|
// we call IUpdateSoftSounds() each time one is added. Ugly as sin, but at |
|
// least all the calc code is in one place. Possible optimization in the |
|
// future: when calling IUpdate(), any sounds that are already active don't |
|
// need to be recalced, just resorted. |
|
void plAudioSystem::RegisterSoftSound( const plKey soundKey ) |
|
{ |
|
plSoftSoundNode *node = TRACKED_NEW plSoftSoundNode( soundKey ); |
|
node->Link( &fSoftRegionSounds ); |
|
|
|
fCurrDebugSound = nil; |
|
plSound::SetCurrDebugPlate( nil ); |
|
} |
|
|
|
//// IUnregisterSoftSound //////////////////////////////////////////////////// |
|
|
|
void plAudioSystem::UnregisterSoftSound( const plKey soundKey ) |
|
{ |
|
plSoftSoundNode *node; |
|
for( node = fActiveSofts; node != nil; node = node->fNext ) |
|
{ |
|
if( node->fSoundKey == soundKey ) |
|
{ |
|
delete node; |
|
return; |
|
} |
|
} |
|
|
|
for( node = fSoftRegionSounds; node != nil; node = node->fNext ) |
|
{ |
|
if( node->fSoundKey == soundKey ) |
|
{ |
|
delete node; |
|
return; |
|
} |
|
} |
|
|
|
// We might have unregistered it ourselves on destruction, so don't bother |
|
|
|
fCurrDebugSound = nil; |
|
plSound::SetCurrDebugPlate( nil ); |
|
} |
|
|
|
//// IUpdateSoftSounds /////////////////////////////////////////////////////// |
|
// OK, so the listener moved. Since our sound soft volumes are all based on |
|
// the listener's position, we have to update all sounds with soft volumes |
|
// now. This involves some steps: |
|
// - Determining what volumes the listener has moved out of |
|
// - Determining what volumes the listener has changed position in |
|
// - Determining what volumes the listener has entered |
|
// - Updating the levels of all sounds associated with all of the above |
|
// volumes |
|
// The first two tests are easy, since we'll have kept track of what volumes |
|
// the listener was in last time. The last part is the tricky one and the |
|
// one that will kill us in performance if we're not careful. However, we |
|
// can first check the bounding box of the sounds in question, since outside |
|
// of them the soft volume won't have any effect. The last part is simply a |
|
// matter of querying the sound volumes based on the listener's position and |
|
// setting the soft volume attenuation on the sound to that level. |
|
// |
|
// Note: for each sound that is still in range, we call CalcSoftVolume() and |
|
// use the resulting value to rank each sound. Then, we take the top n sounds |
|
// and disable the rest. We *could* rank by distance to listener, which would |
|
// be far faster; however, we could have a sound (or background music) that |
|
// is technically closest to the listener but completely faded by a soft volume, |
|
// thus needlessly cutting off another sound that might be more important. |
|
// This way is slower, but better quality. |
|
// Also note: to differentiate between two sounds in the same soft volume at |
|
// full strength, we divide the soft volume ranks by the distSquared, thus |
|
// giving us a reasonable approximation at a good rank...<sigh> |
|
|
|
void plAudioSystem::IUpdateSoftSounds( const hsPoint3 &newPosition ) |
|
{ |
|
plSoftSoundNode *node, *myNode; |
|
hsScalar distSquared, rank; |
|
plSoftSoundNode *sortedList = nil; |
|
Int32 i; |
|
|
|
plProfile_BeginTiming(SoundSoftUpdate); |
|
|
|
// Check the sounds the listener is already inside of. If we moved out, stop sound, else |
|
// just change attenuation |
|
for( node = fActiveSofts; node != nil; ) |
|
{ |
|
plSound *sound = plSound::ConvertNoRef( node->fSoundKey->ObjectIsLoaded() ); |
|
|
|
bool notActive = false; |
|
if(sound) |
|
sound->Update(); |
|
|
|
// Quick checks for not-active |
|
if( sound == nil ) |
|
notActive = true; |
|
else if( !sound->IsWithinRange( newPosition, &distSquared ) ) |
|
notActive = true; |
|
else if(sound->GetPriority() > plgAudioSys::GetPriorityCutoff()) |
|
notActive = true; |
|
|
|
if(plgAudioSys::fMutedStateChange) |
|
{ |
|
sound->SetMuted(plgAudioSys::fMuted); |
|
} |
|
|
|
if( !notActive ) |
|
{ |
|
/// Our initial guess is that it's enabled... |
|
sound->CalcSoftVolume( true, distSquared ); |
|
rank = sound->GetVolumeRank(); |
|
if( rank <= 0.f ) |
|
notActive = true; |
|
else |
|
{ |
|
/// Queue up in our sorted list... |
|
if( sortedList == nil ) |
|
node->SortedLink( &sortedList, (10.0f - sound->GetPriority()) * rank ); |
|
else |
|
sortedList->AddToSortedLink( node, (10.0f - sound->GetPriority()) * rank ); |
|
/// Still in radius, so consider it still "active". |
|
node = node->fNext; |
|
} |
|
} |
|
|
|
if( notActive ) |
|
{ |
|
/// Moved out of range of the sound--stop the sound entirely and move it to our |
|
/// yeah-they're-registered-but-not-active list |
|
myNode = node; |
|
node = node->fNext; |
|
myNode->Unlink(); |
|
myNode->Link( &fSoftRegionSounds ); |
|
|
|
/// We know this sound won't be enabled, so skip the Calc() call |
|
if( sound != nil ) |
|
sound->UpdateSoftVolume( false ); |
|
} |
|
} |
|
|
|
// Now check remaining sounds to see if the listener moved into them |
|
for( node = fSoftRegionSounds; node != nil; ) |
|
{ |
|
if( !fListenerInit ) |
|
{ |
|
node = node->fNext; |
|
continue; |
|
} |
|
|
|
plSound *sound = plSound::ConvertNoRef( node->fSoundKey->ObjectIsLoaded() ); |
|
if( !sound || sound->GetPriority() > plgAudioSys::GetPriorityCutoff() ) |
|
{ |
|
node = node->fNext; |
|
continue; |
|
} |
|
|
|
sound->Update(); |
|
if(plgAudioSys::fMutedStateChange) |
|
{ |
|
sound->SetMuted(plgAudioSys::fMuted); |
|
} |
|
|
|
if( sound->IsWithinRange( newPosition, &distSquared ) ) |
|
{ |
|
/// Our initial guess is that it's enabled... |
|
sound->CalcSoftVolume( true, distSquared ); |
|
rank = sound->GetVolumeRank(); |
|
|
|
if( rank > 0.f ) |
|
{ |
|
/// We just moved into its range, so move it to our active list and start the sucker |
|
myNode = node; |
|
node = node->fNext; |
|
myNode->Unlink(); |
|
myNode->Link( &fActiveSofts ); |
|
|
|
/// Queue up in our sorted list... |
|
if( sortedList == nil ) |
|
myNode->SortedLink( &sortedList, (10.0f - sound->GetPriority()) * rank ); |
|
else |
|
sortedList->AddToSortedLink( myNode, (10.0f - sound->GetPriority()) * rank ); |
|
} |
|
else |
|
{ |
|
/// Do NOT notify sound, since we were outside of its range and still are |
|
// (but if we're playing, we shouldn't be, so better update) |
|
if( sound->IsPlaying() ) |
|
sound->UpdateSoftVolume( false ); |
|
|
|
node = node->fNext; |
|
} |
|
} |
|
else |
|
{ |
|
/// Do NOT notify sound, since we were outside of its range and still are |
|
node = node->fNext; |
|
sound->Disable(); // ensure that dist attenuation is set to zero so we don't accidentally play |
|
} |
|
} |
|
|
|
plgAudioSys::fMutedStateChange = false; |
|
/// Go through sorted list, enabling only the first n sounds and disabling the rest |
|
// DEBUG: Create a screen-only statusLog to display which sounds are what |
|
if( fDebugActiveSoundDisplay == nil ) |
|
fDebugActiveSoundDisplay = plStatusLogMgr::GetInstance().CreateStatusLog( 32, "Active Sounds", plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground ); |
|
fDebugActiveSoundDisplay->Clear(); |
|
|
|
if(fDisplayNumBuffers) |
|
fDebugActiveSoundDisplay->AddLineF(0xffffffff, "Num Buffers: %d", plDSoundBuffer::GetNumBuffers() ); |
|
fDebugActiveSoundDisplay->AddLine("Not streamed", plStatusLog::kGreen); |
|
fDebugActiveSoundDisplay->AddLine("Disk streamed", plStatusLog::kYellow); |
|
fDebugActiveSoundDisplay->AddLine("RAM streamed", plStatusLog::kWhite); |
|
fDebugActiveSoundDisplay->AddLine("Ogg streamed", plStatusLog::kRed); |
|
fDebugActiveSoundDisplay->AddLine("Incidentals", 0xff00ffff); |
|
fDebugActiveSoundDisplay->AddLine("--------------------"); |
|
|
|
for( i = 0; sortedList != nil && i < fMaxNumSounds; sortedList = sortedList->fSortNext ) |
|
{ |
|
plSound *sound = plSound::ConvertNoRef( sortedList->fSoundKey->GetObjectPtr() ); |
|
if(!sound) continue; |
|
|
|
/// Notify sound that it really is still enabled |
|
sound->UpdateSoftVolume( true ); |
|
|
|
UInt32 color = plStatusLog::kGreen; |
|
switch (sound->GetStreamType()) |
|
{ |
|
case plSound::kStreamFromDisk: color = plStatusLog::kYellow; break; |
|
case plSound::kStreamFromRAM: color = plStatusLog::kWhite; break; |
|
case plSound::kStreamCompressed: color = plStatusLog::kRed; break; |
|
} |
|
if(sound->GetType() == plgAudioSys::kVoice) color = 0xffff8800; |
|
if(sound->IsPropertySet(plSound::kPropIncidental)) color = 0xff00ffff; |
|
|
|
if( fUsingEAX && sound->GetEAXSettings().IsEnabled() ) |
|
{ |
|
fDebugActiveSoundDisplay->AddLineF( |
|
color, |
|
"%d %1.2f %1.2f (%d occ) %s", |
|
sound->GetPriority(), |
|
sortedList->fRank, |
|
sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, |
|
sound->GetEAXSettings().GetCurrSofts().GetOcclusion(), |
|
sound->GetKeyName() |
|
); |
|
} |
|
else |
|
{ |
|
fDebugActiveSoundDisplay->AddLineF( |
|
color, |
|
"%d %1.2f %1.2f %s", |
|
sound->GetPriority(), |
|
sortedList->fRank, |
|
sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, |
|
sound->GetKeyName() |
|
); |
|
} |
|
i++; |
|
} |
|
|
|
for( ; sortedList != nil; sortedList = sortedList->fSortNext, i++ ) |
|
{ |
|
plSound *sound = plSound::ConvertNoRef( sortedList->fSoundKey->GetObjectPtr() ); |
|
if(!sound) continue; |
|
|
|
/// These unlucky sounds don't get to play (yet). Also, be extra mean |
|
/// and pretend we're updating for "the first time", which will force them to |
|
/// stop immediately |
|
// Update: since being extra mean can incur a nasty performance hit when sounds hover back and |
|
// forth around the fMaxNumSounds mark, we have a "slop" allowance: i.e. sounds that we're going |
|
// to say shouldn't be playing but we'll let them play for a bit anyway just in case they raise |
|
// in priority. So only be mean to the sounds outside this slop range |
|
sound->UpdateSoftVolume( false, ( i < fMaxNumSounds + fNumSoundsSlop ) ? false : true ); |
|
fDebugActiveSoundDisplay->AddLineF( |
|
0xff808080, |
|
"%d %1.2f %s", |
|
sound->GetPriority(), |
|
sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, |
|
sound->GetKeyName() |
|
); |
|
} |
|
|
|
plProfile_EndTiming(SoundSoftUpdate); |
|
} |
|
|
|
void plAudioSystem::NextDebugSound( void ) |
|
{ |
|
plSoftSoundNode *node; |
|
|
|
if( fCurrDebugSound == nil ) |
|
fCurrDebugSound = ( fSoftRegionSounds == nil ) ? fActiveSofts : fSoftRegionSounds; |
|
else |
|
{ |
|
node = fCurrDebugSound; |
|
fCurrDebugSound = fCurrDebugSound->fNext; |
|
if( fCurrDebugSound == nil ) |
|
{ |
|
// Trace back to find which list we were in |
|
for( fCurrDebugSound = fSoftRegionSounds; fCurrDebugSound != nil; fCurrDebugSound = fCurrDebugSound->fNext ) |
|
{ |
|
if( fCurrDebugSound == node ) // Was in first list, move to 2nd |
|
{ |
|
fCurrDebugSound = fActiveSofts; |
|
break; |
|
} |
|
} |
|
// else Must've been in 2nd list, so keep nil |
|
} |
|
} |
|
|
|
if( fCurrDebugSound != nil ) |
|
plSound::SetCurrDebugPlate( fCurrDebugSound->fSoundKey ); |
|
else |
|
plSound::SetCurrDebugPlate( nil ); |
|
} |
|
|
|
void plAudioSystem::SetFadeLength(float lengthSec) |
|
{ |
|
fFadeLength = lengthSec; |
|
} |
|
|
|
hsBool plAudioSystem::MsgReceive(plMessage* msg) |
|
{ |
|
if(plTimeMsg *time = plTimeMsg::ConvertNoRef( msg ) ) |
|
{ |
|
if(!plgAudioSys::IsMuted()) |
|
{ |
|
double currTime = hsTimer::GetSeconds(); |
|
if(fStartFade == 0) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "Starting Fade %f", currTime); |
|
} |
|
if((currTime - fStartFade) > fFadeLength) |
|
{ |
|
fStartFade = 0; |
|
plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); |
|
plStatusLog::AddLineS("audio.log", "Stopping Fade %f", currTime); |
|
plgAudioSys::SetGlobalFadeVolume( 1.0 ); |
|
} |
|
else |
|
{ |
|
plgAudioSys::SetGlobalFadeVolume( (hsScalar)((currTime-fStartFade) / fFadeLength) ); |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
if (plAudioSysMsg* pASMsg = plAudioSysMsg::ConvertNoRef( msg )) |
|
{ |
|
if (pASMsg->GetAudFlag() == plAudioSysMsg::kPing && fListenerInit) |
|
{ |
|
plAudioSysMsg* pMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kActivate ); |
|
pMsg->AddReceiver( pASMsg->GetSender() ); |
|
pMsg->SetBCastFlag(plMessage::kBCastByExactType, false); |
|
plgDispatch::MsgSend( pMsg ); |
|
return true; |
|
} |
|
else if (pASMsg->GetAudFlag() == plAudioSysMsg::kSetVol) |
|
{ |
|
return true; |
|
} |
|
else if( pASMsg->GetAudFlag() == plAudioSysMsg::kDestroy ) |
|
{ |
|
if( fWaitingForShutdown ) |
|
Shutdown(); |
|
return true; |
|
} |
|
else if( pASMsg->GetAudFlag() == plAudioSysMsg::kUnmuteAll ) |
|
{ |
|
if( !pASMsg->HasBCastFlag( plMessage::kBCastByExactType ) ) |
|
plgAudioSys::SetMuted( false ); |
|
return true; |
|
} |
|
} |
|
|
|
if(plRenderMsg* pRMsg = plRenderMsg::ConvertNoRef( msg )) |
|
{ |
|
//if( fListener ) |
|
{ |
|
plProfile_BeginLap(AudioUpdate, this->GetKey()->GetUoid().GetObjectName()); |
|
if(hsTimer::GetMilliSeconds() - fLastUpdateTimeMs > UPDATE_TIME_MS) |
|
{ |
|
IUpdateSoftSounds( fCurrListenerPos ); |
|
|
|
if( fUsingEAX ) |
|
{ |
|
plProfile_BeginTiming(SoundEAXUpdate); |
|
plEAXListener::GetInstance().ProcessMods( fEAXRegions ); |
|
plProfile_EndTiming(SoundEAXUpdate); |
|
} |
|
//fCommittedListenerPos = fCurrListenerPos; |
|
} |
|
plProfile_EndLap(AudioUpdate, this->GetKey()->GetUoid().GetObjectName()); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
if(plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg )) |
|
{ |
|
if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) |
|
{ |
|
fEAXRegions.Append( plEAXListenerMod::ConvertNoRef( refMsg->GetRef() ) ); |
|
plEAXListener::GetInstance().ClearProcessCache(); |
|
} |
|
else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) |
|
{ |
|
int idx = fEAXRegions.Find( plEAXListenerMod::ConvertNoRef( refMsg->GetRef() ) ); |
|
if( idx != fEAXRegions.kMissingIndex ) |
|
fEAXRegions.Remove( idx ); |
|
plEAXListener::GetInstance().ClearProcessCache(); |
|
} |
|
return true; |
|
} |
|
|
|
if(plAgeLoadedMsg *pALMsg = plAgeLoadedMsg::ConvertNoRef(msg)) |
|
{ |
|
if(!pALMsg->fLoaded) |
|
{ |
|
fLastPos = fCurrListenerPos; |
|
fListenerInit = false; |
|
} |
|
else |
|
{ |
|
fListenerInit = true; |
|
} |
|
} |
|
|
|
return hsKeyedObject::MsgReceive(msg); |
|
} |
|
|
|
// plgAudioSystem ////////////////////////////////////////////////////////////////////// |
|
|
|
plAudioSystem* plgAudioSys::fSys = nil; |
|
hsBool plgAudioSys::fInit = false; |
|
hsBool plgAudioSys::fActive = false; |
|
hsBool plgAudioSys::fUseHardware = false; |
|
hsBool plgAudioSys::fMuted = true; |
|
hsBool plgAudioSys::fDelayedActivate = false; |
|
hsBool plgAudioSys::fEnableEAX = false; |
|
hsWindowHndl plgAudioSys::fWnd = nil; |
|
hsScalar plgAudioSys::fChannelVolumes[ kNumChannels ] = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f }; |
|
hsScalar plgAudioSys::f2D3DBias = 0.75f; |
|
UInt32 plgAudioSys::fDebugFlags = 0; |
|
hsScalar plgAudioSys::fStreamingBufferSize = 2.f; |
|
hsScalar plgAudioSys::fStreamFromRAMCutoff = 10.f; |
|
UInt8 plgAudioSys::fPriorityCutoff = 9; // We cut off sounds above this priority |
|
hsBool plgAudioSys::fEnableExtendedLogs = false; |
|
hsScalar plgAudioSys::fGlobalFadeVolume = 1.f; |
|
hsBool plgAudioSys::fLogStreamingUpdates = false; |
|
std::string plgAudioSys::fDeviceName; |
|
hsBool plgAudioSys::fRestarting = false; |
|
hsBool plgAudioSys::fMutedStateChange = false; |
|
|
|
void plgAudioSys::Init(hsWindowHndl hWnd) |
|
{ |
|
fSys = TRACKED_NEW plAudioSystem; |
|
fSys->RegisterAs( kAudioSystem_KEY ); |
|
plgDispatch::Dispatch()->RegisterForExactType( plAudioSysMsg::Index(), fSys->GetKey() ); |
|
plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), fSys->GetKey() ); |
|
|
|
if(hsPhysicalMemory() <= 380) |
|
{ |
|
plStatusLog::AddLineS("audio.log", "StreamFromRam Disabled"); |
|
fStreamFromRAMCutoff = 4; |
|
} |
|
fWnd = hWnd; |
|
|
|
if(fMuted) |
|
SetGlobalFadeVolume(0.0f); |
|
|
|
if( fDelayedActivate ) |
|
Activate( true ); |
|
} |
|
|
|
void plgAudioSys::SetActive(hsBool b) |
|
{ |
|
fActive = b; |
|
} |
|
|
|
void plgAudioSys::SetMuted( hsBool b ) |
|
{ |
|
fMuted = b; |
|
fMutedStateChange = true; |
|
|
|
if(fMuted) |
|
SetGlobalFadeVolume(0.0f); |
|
else |
|
SetGlobalFadeVolume(1.0); |
|
} |
|
|
|
void plgAudioSys::SetUseHardware(hsBool b) |
|
{ |
|
fUseHardware = b; |
|
if( fActive ) |
|
Restart(); |
|
} |
|
|
|
void plgAudioSys::EnableEAX( hsBool b ) |
|
{ |
|
fEnableEAX = b; |
|
if( fActive ) |
|
Restart(); |
|
} |
|
|
|
void plgAudioSys::SetAudioMode(AudioMode mode) |
|
{ |
|
if(mode == kDisabled) |
|
{ |
|
Activate(false); |
|
return; |
|
} |
|
else if(mode == kSoftware) |
|
{ |
|
fActive = true; |
|
fUseHardware = false; |
|
fEnableEAX = false; |
|
} |
|
else if(mode == kHardware) |
|
{ |
|
fActive = true; |
|
fUseHardware = true; |
|
fEnableEAX = false; |
|
} |
|
else if(mode == kHardwarePlusEAX) |
|
{ |
|
fActive = true; |
|
fUseHardware = true; |
|
fEnableEAX = true; |
|
} |
|
Restart(); |
|
} |
|
|
|
int plgAudioSys::GetAudioMode() |
|
{ |
|
if (fActive) |
|
{ |
|
if (fUseHardware) |
|
{ |
|
if (fEnableEAX) |
|
{ |
|
return kHardwarePlusEAX; |
|
} |
|
else |
|
{ |
|
return kHardware; |
|
} |
|
} |
|
else |
|
{ |
|
return kSoftware; |
|
} |
|
} |
|
else |
|
{ |
|
return kDisabled; |
|
} |
|
} |
|
|
|
void plgAudioSys::Restart( void ) |
|
{ |
|
if( fSys ) |
|
{ |
|
fSys->fRestartOnDestruct = true; |
|
Activate( false ); |
|
fRestarting = true; |
|
} |
|
} |
|
|
|
void plgAudioSys::Shutdown() |
|
{ |
|
Activate( false ); |
|
if( fSys ) |
|
{ |
|
fSys->UnRegisterAs( kAudioSystem_KEY ); |
|
} |
|
} |
|
|
|
void plgAudioSys::Activate(hsBool b) |
|
{ |
|
if( fSys == nil ) |
|
{ |
|
fDelayedActivate = true; |
|
return; |
|
} |
|
|
|
if (b == fInit) |
|
return; |
|
if (!fActive) |
|
return; |
|
if( b ) |
|
{ |
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Attempting audio system init --" ); |
|
if( !fSys->Init( fWnd ) ) |
|
{ |
|
// Cannot init audio system. Don't activate |
|
return; |
|
} |
|
fInit = true; |
|
fSys->SetActive( true ); |
|
|
|
if( !IsMuted() ) |
|
{ |
|
SetMuted( true ); |
|
plAudioSysMsg *msg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kUnmuteAll ); |
|
msg->SetTimeStamp( hsTimer::GetSysSeconds() ); |
|
msg->AddReceiver( fSys->GetKey() ); |
|
msg->SetBCastFlag( plMessage::kBCastByExactType, false ); |
|
msg->Send(); |
|
} |
|
return; |
|
} |
|
|
|
fSys->SetActive( false ); |
|
|
|
plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Sending deactivate/destroy messages --" ); |
|
plgDispatch::MsgSend( TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kDeActivate ) ); |
|
|
|
// Send ourselves a shutdown message, so that the deactivates get processed first |
|
fSys->fWaitingForShutdown = true; |
|
plAudioSysMsg *msg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kDestroy ); |
|
msg->SetBCastFlag( plMessage::kBCastByExactType, false ); |
|
msg->Send( fSys->GetKey() ); |
|
// fSys->Shutdown(); |
|
|
|
fInit = false; |
|
} |
|
|
|
void plgAudioSys::SetChannelVolume( ASChannel chan, hsScalar vol ) |
|
{ |
|
fChannelVolumes[ chan ] = vol; |
|
} |
|
|
|
void plgAudioSys::SetGlobalFadeVolume( hsScalar vol ) |
|
{ |
|
if(!fMuted) |
|
fGlobalFadeVolume = vol; |
|
else |
|
fGlobalFadeVolume = 0; |
|
} |
|
|
|
hsScalar plgAudioSys::GetChannelVolume( ASChannel chan ) |
|
{ |
|
return fChannelVolumes[ chan ]; |
|
} |
|
|
|
void plgAudioSys::NextDebugSound( void ) |
|
{ |
|
fSys->NextDebugSound(); |
|
} |
|
|
|
void plgAudioSys::Set2D3DBias( hsScalar bias ) |
|
{ |
|
f2D3DBias = bias; |
|
} |
|
|
|
hsScalar plgAudioSys::Get2D3Dbias() |
|
{ |
|
return f2D3DBias; |
|
} |
|
|
|
void plgAudioSys::SetDeviceName(const char *device, hsBool restart /* = false */) |
|
{ |
|
fDeviceName = device; |
|
if(restart) |
|
Restart(); |
|
} |
|
|
|
int plgAudioSys::GetNumAudioDevices() |
|
{ |
|
if(fSys) |
|
return fSys->GetNumAudioDevices(); |
|
return 0; |
|
} |
|
|
|
const char *plgAudioSys::GetAudioDeviceName(int index) |
|
{ |
|
if(fSys) |
|
{ |
|
return fSys->GetAudioDeviceName(index); |
|
} |
|
return nil; |
|
} |
|
|
|
ALCdevice *plgAudioSys::GetCaptureDevice() |
|
{ |
|
if(fSys) |
|
{ |
|
return fSys->fCaptureDevice; |
|
} |
|
return nil; |
|
} |
|
|
|
hsBool plgAudioSys::SupportsEAX(const char *deviceName) |
|
{ |
|
if(fSys) |
|
{ |
|
return fSys->SupportsEAX(deviceName); |
|
} |
|
return nil; |
|
} |
|
|
|
void plgAudioSys::RegisterSoftSound( const plKey soundKey ) |
|
{ |
|
if(fSys) |
|
{ |
|
fSys->RegisterSoftSound(soundKey); |
|
} |
|
} |
|
|
|
void plgAudioSys::UnregisterSoftSound( const plKey soundKey ) |
|
{ |
|
if(fSys) |
|
{ |
|
fSys->UnregisterSoftSound(soundKey); |
|
} |
|
} |
|
|
|
void plgAudioSys::SetListenerPos(const hsPoint3 pos) |
|
{ |
|
if(fSys) |
|
{ |
|
fSys->SetListenerPos(pos); |
|
} |
|
} |
|
|
|
void plgAudioSys::SetListenerVelocity(const hsVector3 vel) |
|
{ |
|
if(fSys) |
|
{ |
|
fSys->SetListenerVelocity(vel); |
|
} |
|
} |
|
|
|
void plgAudioSys::SetListenerOrientation(const hsVector3 view, const hsVector3 up) |
|
{ |
|
if(fSys) |
|
{ |
|
fSys->SetListenerOrientation(view, up); |
|
} |
|
} |