/*==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 3 ds 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 , 3 ds 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 = = */
# include "hsTypes.h"
# include "hsUtils.h"
# include "hsResMgr.h"
# include "hsTimer.h"
# include "hsGeometry3.h"
# include "hsColorRGBA.h"
# include "plProfile.h"
# include "plgDispatch.h"
# include "plAudioSystem.h"
# include "plSound.h"
# include "plWin32Sound.h"
# include "../plAudioCore/plSoundBuffer.h"
# include "../plDrawable/plDrawableGenerator.h"
# include "../pnMessage/plRefMsg.h"
# include "../pnMessage/plTimeMsg.h"
# include "../pnMessage/plAudioSysMsg.h"
# include "../pnMessage/plSoundMsg.h"
# include "../plMessage/plListenerMsg.h"
# include "../plIntersect/plSoftVolume.h"
# include "../plStatusLog/plStatusLog.h"
# include "../plPipeline/plPlates.h"
# include "../pnKeyedObject/plKey.h"
# include "../pnNetCommon/plSDLTypes.h"
# include "../plAvatar/plScalarChannel.h"
# include "../plAvatar/plAGModifier.h"
# include "../pnSceneObject/plSceneObject.h"
# include "../pnSceneObject/plAudioInterface.h"
plProfile_CreateCounterNoReset ( " Loaded " , " Sound " , SoundNumLoaded ) ;
plProfile_CreateCounterNoReset ( " Waiting to Die " , " Sound " , WaitingToDie ) ;
plProfile_CreateAsynchTimer ( " Sound Load Time " , " Sound " , SoundLoadTime ) ;
plGraphPlate * plSound : : fDebugPlate = nil ;
plSound * plSound : : fCurrDebugPlateSound = nil ;
hsBool plSound : : fLoadOnDemandFlag = true ;
hsBool plSound : : fLoadFromDiskOnDemand = true ;
unsigned plSound : : fIncidentalsPlaying = 0 ;
plSound : : plSound ( ) :
fPlaying ( false ) ,
fActive ( false ) ,
fTime ( 0 ) ,
fMaxFalloff ( 0 ) ,
fMinFalloff ( 0 ) ,
fCurrVolume ( 0.f ) ,
fOuterVol ( 0 ) ,
fOuterCone ( 360 ) ,
fInnerCone ( 360 ) ,
fLength ( 0.0f ) ,
fDesiredVol ( 0.f ) ,
fFading ( false ) ,
fRegisteredForTime ( false ) ,
fMuted ( true ) ,
fFadedVolume ( 0.f ) ,
fSoftRegion ( nil ) ,
fSoftOcclusionRegion ( nil ) ,
fSoftVolume ( 0.f ) ,
fCurrFadeParams ( nil ) ,
fRegistered ( false ) ,
fDistAttenuation ( 0.f ) ,
fProperties ( 0 ) ,
fNotHighEnoughPriority ( false ) ,
fVirtualStartTime ( 0 ) ,
fOwningSceneObject ( nil ) ,
fPriority ( 0 ) ,
fType ( plSound : : kSoundFX ) ,
fQueued ( false ) ,
fLoading ( false ) ,
fSynchedStartTimeSec ( 0 ) ,
fMaxVolume ( 0 ) ,
fFreeData ( false )
{
plProfile_Inc ( SoundNumLoaded ) ;
f3DPosition . Set ( 0.f , 0.f , 0.f ) ;
f3DVelocity . Set ( 0.f , 0.f , 0.f ) ;
fDataBuffer = nil ;
fDataBufferKey = nil ;
fPlayOnReactivate = false ;
fDataBufferLoaded = false ;
}
plSound : : ~ plSound ( )
{
IStopFade ( true ) ;
plProfile_Dec ( SoundNumLoaded ) ;
}
void plSound : : IPrintDbgMessage ( const char * msg , hsBool isError )
{
static plStatusLog * ourLog = nil ;
// Print to our log file (create it if necessary)
if ( ourLog = = nil )
{
// ourLog = plStatusLogMgr::GetInstance().CreateStatusLog( 15,
// "audio.log", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kAlignToTop );
}
if ( isError )
// ourLog->AddLineF( plStatusLog::kRed, "ERROR: %s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" );
ourLog - > AddLineS ( " audio.log " , plStatusLog : : kRed , " ERROR: %s (%s) " , msg , GetKey ( ) ? GetKeyName ( ) : " unkeyed " ) ;
else
// ourLog->AddLineF( "%s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" );
ourLog - > AddLineS ( " audio.log " , " %s (%s) " , msg , GetKey ( ) ? GetKeyName ( ) : " unkeyed " ) ;
}
///////////////////////////////////////////////////////////
// Called to send more values to the debug plate, assuming this is the right
// sound. Should be called every time any of the values change, which means
// the best place is inside ISetActualVolume(). Since that's a pure virtual,
// it makes the placement of the call a bit annoying, but oh well.
void plSound : : IUpdateDebugPlate ( void )
{
if ( this = = fCurrDebugPlateSound )
{
if ( fDebugPlate = = nil )
{
plPlateManager : : Instance ( ) . CreateGraphPlate ( & fDebugPlate ) ;
fDebugPlate - > SetSize ( 0.50 , 0.25 ) ;
fDebugPlate - > SetPosition ( - 0.5 , 0 ) ;
fDebugPlate - > SetDataRange ( 0 , 100 , 100 ) ;
fDebugPlate - > SetColors ( 0x80202000 ) ;
fDebugPlate - > SetTitle ( ( char * ) GetKeyName ( ) ) ; // Bleah
fDebugPlate - > SetLabelText ( " Desired " , " Curr " , " Soft " , " Dist " ) ;
}
fDebugPlate - > SetVisible ( true ) ;
fDebugPlate - > AddData ( ( Int32 ) ( fDesiredVol * 100.f ) ,
( Int32 ) ( fCurrVolume * 100.f ) ,
( Int32 ) ( fSoftVolume * 100.f ) ,
( Int32 ) ( fDistAttenuation * 100.f ) ) ;
}
}
void plSound : : SetCurrDebugPlate ( const plKey soundKey )
{
if ( soundKey = = nil )
{
fCurrDebugPlateSound = nil ;
if ( fDebugPlate ! = nil )
fDebugPlate - > SetVisible ( false ) ;
}
else
{
fCurrDebugPlateSound = plSound : : ConvertNoRef ( soundKey - > GetObjectPtr ( ) ) ;
if ( fDebugPlate ! = nil )
{
fDebugPlate - > ClearData ( ) ;
fDebugPlate - > SetVisible ( true ) ;
fDebugPlate - > SetTitle ( ( char * ) fCurrDebugPlateSound - > GetKeyName ( ) ) ; // Bleah
}
}
}
/////////////////////////////////////////////////////////////////////
// We don't keep track of the current time we should be at, but rather the
// time we started at. Since we're calling SetTime(), we should adjust the
// start time to be accurate for the time we want to be at now. Note: we
// don't actually move the buffer position unless it's loaded, since we
// don't want to force a load on a buffer just from a SetTime() call.
void plSound : : SetTime ( double t )
{
fVirtualStartTime = hsTimer : : GetSysSeconds ( ) - t ;
if ( IActuallyLoaded ( ) )
ISetActualTime ( t ) ;
}
// Support for Fast forward responder
void plSound : : FastForwardPlay ( )
{
if ( fProperties & kPropLooping )
{
Play ( ) ;
}
}
void plSound : : FastForwardToggle ( )
{
if ( fPlaying = = true )
{
Stop ( ) ;
return ;
}
FastForwardPlay ( ) ;
}
////////////////////////////////////////////////////////////////////////
// Our basic play function. Marks the sound as playing, and if we're actually
// allowed to play, will actually start the sound playing as well.
void plSound : : Play ( )
{
if ( fLoading ) // if we are loading there is no reason to do this. Play will be called, by Update(), once the data is loaded and this floag is set to false
return ;
if ( ! fActive )
{
// We're not active, so we can't play, but mark to make sure we'll play once we do get activated
fPlayOnReactivate = true ;
return ;
}
fPlaying = true ;
if ( IPreLoadBuffer ( true ) = = plSoundBuffer : : kPending )
{
return ;
}
fVirtualStartTime = hsTimer : : GetSysSeconds ( ) ; // When we "started", even if we really don't start
// if the sound system is not active do a fake play so callbacks get sent
if ( ! plgAudioSys : : Active ( ) )
{
// Do the (fake) actual play
IActuallyPlay ( ) ;
}
if ( IWillBeAbleToPlay ( ) )
{
IRefreshParams ( ) ;
if ( fFadeInParams . fLengthInSecs > 0 )
{
IStartFade ( & fFadeInParams ) ;
}
else
{
// we're NOT fading!!!!
if ( fFading )
IStopFade ( ) ;
SetVolume ( fDesiredVol ) ;
}
// Do the actual play
IActuallyPlay ( ) ;
}
}
void plSound : : SynchedPlay ( unsigned bytes )
{
if ( fFading )
IStopFade ( ) ;
if ( fLoading ) // the sound is loading, it will be played when loading is finished
return ;
if ( ! fActive )
{
// We're not active, so we can't play, but mark to make sure we'll play once we do get activated
fPlayOnReactivate = true ;
return ;
}
// Mark as playing, since we'll be calling ITryPlay() directly
fPlaying = true ;
fPlayOnReactivate = false ;
if ( IWillBeAbleToPlay ( ) )
{
SetStartPos ( bytes ) ;
Play ( ) ;
}
}
/////////////////////////////////////////////////////////////////
// Used for synching play state. The state only knows that we're playing
// and what time we started at, so we use that to compute what time we should
// be at and update. Note that we also set our virtual start time to what
// we're given, NOT the current time, 'cause, well, duh, that should be our
// start time!
// So think of it as "Play() but act as if you started at *this* time"...
void plSound : : SynchedPlay ( hsScalar virtualStartTime )
{
if ( fFading )
IStopFade ( ) ;
ISynchedPlay ( virtualStartTime ) ;
}
////////////////////////////////////////////////////////////////
// Only want to do the fade hack when somebody outside synch()s us.
void plSound : : ISynchedPlay ( double virtualStartTime )
{
if ( fLoading ) // the sound is loading, it will be played when loading is finished
return ;
// Store our start time
fVirtualStartTime = virtualStartTime ;
if ( ! fActive )
{
// We're not active, so we can't play, but mark to make sure we'll play once we do get activated
fPlayOnReactivate = true ;
return ;
}
// Mark as playing, since we'll be calling ITryPlay() directly
fPlaying = true ;
fPlayOnReactivate = false ;
// Do da synch, which will start us playing
if ( IWillBeAbleToPlay ( ) )
{
ISynchToStartTime ( ) ;
}
}
///////////////////////////////////////////////////////////
// Takes the virtual start time and sets us to the real time we should be at,
// then starts us playing via ITryPlay().
void plSound : : ISynchToStartTime ( void )
{
if ( ! plgAudioSys : : Active ( ) )
return ;
// We don't want to do this until we're actually loaded, since that's when we'll know our
// REAL length (thanks to the inaccuracies of WMA compression)
// LoadSound( IsPropertySet( kPropIs3DSound ) );
// Haha, the GetLength() call will do this for us
// Calculate what time we should be at
double deltaTime = hsTimer : : GetSysSeconds ( ) - fVirtualStartTime ;
double length = GetLength ( ) ;
if ( deltaTime > length | | deltaTime < 0 )
{
// Hmm, our time went past the length of sound, so handle that
if ( IsPropertySet ( kPropLooping ) )
{
if ( length < = 0 )
deltaTime = 0 ; // Error, attempt to recover
else if ( deltaTime < 0 )
{
int numWholeParts = ( int ) ( - deltaTime / length ) ;
deltaTime + = length * ( numWholeParts + 1 ) ;
}
else
{
int numWholeParts = ( int ) ( deltaTime / length ) ;
deltaTime - = length * ( double ) numWholeParts ;
}
//ISetActualTime( deltaTime );
Play ( ) ;
}
else
// We already played and stopped virtually, so really mark us as stopped
Stop ( ) ;
}
else
{
// Easy 'nuf...
//ISetActualTime( deltaTime );
Play ( ) ;
}
}
void plSound : : SetPosition ( const hsPoint3 pos )
{
f3DPosition = pos ;
}
void plSound : : SetVelocity ( const hsVector3 vel )
{
f3DVelocity = vel ;
}
hsPoint3 plSound : : GetPosition ( void ) const
{
return f3DPosition ;
}
hsVector3 plSound : : GetVelocity ( void ) const
{
return f3DVelocity ;
}
void plSound : : SetMin ( const int m )
{
fMinFalloff = m ;
}
void plSound : : SetMax ( const int m )
{
fMaxFalloff = m ;
}
void plSound : : SetOuterVolume ( const int v )
{
fOuterVol = v ;
}
void plSound : : SetConeOrientation ( hsScalar x , hsScalar y , hsScalar z )
{
fConeOrientation . Set ( x , y , z ) ;
}
void plSound : : SetConeAngles ( int inner , int outer )
{
fOuterCone = outer ;
fInnerCone = inner ;
}
int plSound : : GetMin ( ) const
{
return fMinFalloff ;
}
int plSound : : GetMax ( ) const
{
return fMaxFalloff ;
}
void plSound : : SetVolume ( const float v )
{
fDesiredVol = v ;
if ( ! fMuted & & ! fFading )
fCurrVolume = fDesiredVol ;
RefreshVolume ( ) ;
}
void plSound : : RefreshVolume ( void )
{
this - > ISetActualVolume ( fCurrVolume ) ;
}
void plSound : : SetMuted ( hsBool muted )
{
if ( muted ! = fMuted )
{
fMuted = muted ;
if ( fMuted )
fCurrVolume = 0.f ;
else if ( ! fFading )
fCurrVolume = fDesiredVol ;
RefreshVolume ( ) ;
}
}
void plSound : : IRefreshParams ( void )
{
SetMax ( fMaxFalloff ) ;
SetMin ( fMinFalloff ) ;
SetOuterVolume ( fOuterVol ) ;
SetConeAngles ( fInnerCone , fOuterCone ) ;
SetConeOrientation ( fConeOrientation . fX , fConeOrientation . fY , fConeOrientation . fZ ) ;
SetPosition ( f3DPosition ) ;
SetVelocity ( f3DVelocity ) ;
}
////////////////////////////////////////////////////////////////////////
// The public interface to stopping, which also synchs the state with the
// server.
void plSound : : Stop ( void )
{
fPlaying = false ;
// if the audio data is loading while stop is called we need to make sure the sounds doesn't play, and the data is unloaded.
fPlayOnReactivate = false ;
fFreeData = true ;
// Do we have an ending fade?
if ( fFadeOutParams . fLengthInSecs > 0 & & ! plgAudioSys : : IsRestarting ( ) )
{
IStartFade ( & fFadeOutParams ) ;
}
else
{
if ( fFading )
IStopFade ( ) ;
fCurrVolume = 0.f ;
ISetActualVolume ( fCurrVolume ) ;
IActuallyStop ( ) ;
}
if ( fPlayWhenLoaded )
{
fPlayWhenLoaded = false ;
}
}
void plSound : : IActuallyStop ( void )
{
if ( fLoadOnDemandFlag & & ! IsPropertySet ( kPropDisableLOD ) & & ! IsPropertySet ( kPropLoadOnlyOnCall ) )
{
// If we're loading on demand, we want to unload on stop
IFreeBuffers ( ) ;
}
}
void plSound : : Update ( )
{
if ( fLoading )
{
plSoundBuffer : : ELoadReturnVal retVal = IPreLoadBuffer ( fPlayWhenLoaded ) ;
if ( retVal = = plSoundBuffer : : kError )
{
fLoading = false ;
fPlayWhenLoaded = false ;
}
if ( retVal = = plSoundBuffer : : kSuccess )
{
fLoading = false ;
if ( fPlayWhenLoaded )
Play ( ) ;
fPlayWhenLoaded = false ;
// ensure the sound data is released if the sound object was stopped while the audio data was being loaded.
if ( fFreeData )
{
fFreeData = false ;
FreeSoundData ( ) ;
}
}
}
}
hsScalar plSound : : IGetChannelVolume ( void ) const
{
hsScalar channelVol = plgAudioSys : : GetChannelVolume ( ( plgAudioSys : : ASChannel ) fType ) ;
// if not using hardware acceleration then apply 2D/3D bias to non 3D sounds
if ( ! plgAudioSys : : Hardware ( ) & & ! IsPropertySet ( kPropIs3DSound ) )
channelVol * = plgAudioSys : : Get2D3Dbias ( ) ;
if ( IsPropertySet ( kPropDontFade ) )
return channelVol ;
return channelVol * plgAudioSys : : GetGlobalFadeVolume ( ) ;
}
void plSound : : IStartFade ( plFadeParams * params , hsScalar offsetIntoFade )
{
fFading = true ;
if ( params = = & fFadeOutParams )
{
fFadeOutParams . fVolStart = fCurrVolume ;
fFadeOutParams . fVolEnd = fFadedVolume ;
fCurrFadeParams = & fFadeOutParams ;
}
else if ( params = = & fFadeInParams )
{
fFadeInParams . fVolStart = fCurrVolume ; // Hopefully, we got to fFadedVolume, but maybe not
fFadeInParams . fVolEnd = fDesiredVol ;
fCurrFadeParams = & fFadeInParams ;
plStatusLog : : AddLineS ( " audio.log " , " Fading in %s " , GetKeyName ( ) ) ;
}
else
fCurrFadeParams = params ;
fCurrFadeParams - > fCurrTime = offsetIntoFade ;
ISetActualVolume ( fCurrFadeParams - > InterpValue ( ) ) ;
if ( ! fRegisteredForTime )
{
plgDispatch : : Dispatch ( ) - > RegisterForExactType ( plTimeMsg : : Index ( ) , GetKey ( ) ) ;
fRegisteredForTime = true ;
}
}
void plSound : : IStopFade ( hsBool shuttingDown , hsBool SetVolEnd )
{
if ( fCurrFadeParams ! = nil )
{
if ( fCurrFadeParams = = & fCoolSoftVolumeTrickParams )
{
plProfile_Dec ( WaitingToDie ) ;
}
// This can cause problems if we've exited a soft region and are doing a soft volume fade.
// If the camera pops back into the region of this particular sound this will cause the soft volume to be zero,
// therefore not allowing the sound to play until the listener moves again(triggering another softsound update).
// So if this function is called from UpdateSoftSounds this will not be performed
if ( SetVolEnd )
{
if ( fCurrFadeParams - > fFadeSoftVol )
fSoftVolume = fCurrFadeParams - > fVolEnd ;
else
fCurrVolume = fCurrFadeParams - > fVolEnd ;
}
if ( ! shuttingDown )
ISetActualVolume ( fCurrVolume ) ;
fCurrFadeParams - > fCurrTime = - 1.f ;
}
fFading = false ;
fCurrFadeParams = nil ;
// Fade done, unregister for time message
if ( fRegisteredForTime )
{
plgDispatch : : Dispatch ( ) - > UnRegisterForExactType ( plTimeMsg : : Index ( ) , GetKey ( ) ) ;
fRegisteredForTime = false ;
}
}
hsBool plSound : : MsgReceive ( plMessage * pMsg )
{
plTimeMsg * time = plTimeMsg : : ConvertNoRef ( pMsg ) ;
if ( time ! = nil )
{
/// Time message for handling fade ins/outs
if ( fCurrFadeParams = = nil )
return true ;
fCurrFadeParams - > fCurrTime + = time - > DelSeconds ( ) ;
if ( fCurrFadeParams - > fCurrTime > = fCurrFadeParams - > fLengthInSecs )
{
if ( fCurrFadeParams - > fFadeSoftVol )
fSoftVolume = fCurrFadeParams - > fVolEnd ;
else
fCurrVolume = fCurrFadeParams - > fVolEnd ;
ISetActualVolume ( fCurrVolume ) ;
fCurrFadeParams - > fCurrTime = - 1.f ;
// Fade done, unregister for time message
if ( fRegisteredForTime )
{
plgDispatch : : Dispatch ( ) - > UnRegisterForExactType ( plTimeMsg : : Index ( ) , GetKey ( ) ) ;
fRegisteredForTime = false ;
}
// Note: if we're done, and we were fading out, we need to STOP
if ( fCurrFadeParams - > fStopWhenDone )
{
// REALLY STOP
IActuallyStop ( ) ;
}
if ( fCurrFadeParams = = & fCoolSoftVolumeTrickParams )
{
plProfile_Dec ( WaitingToDie ) ;
}
// Done with this one!
fCurrFadeParams = nil ;
fFading = false ;
}
else
{
// Gotta interp
if ( fCurrFadeParams - > fFadeSoftVol )
fSoftVolume = fCurrFadeParams - > InterpValue ( ) ;
else
fCurrVolume = fCurrFadeParams - > InterpValue ( ) ;
ISetActualVolume ( fCurrVolume ) ;
}
return true ;
}
plGenRefMsg * refMsg = plGenRefMsg : : ConvertNoRef ( pMsg ) ;
if ( refMsg )
{
if ( refMsg - > fType = = kRefSoftVolume )
{
if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
{
ISetSoftRegion ( plSoftVolume : : ConvertNoRef ( refMsg - > GetRef ( ) ) ) ;
return true ;
}
else if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnRemove | plRefMsg : : kOnDestroy ) )
{
ISetSoftRegion ( nil ) ;
return true ;
}
}
else if ( refMsg - > fType = = kRefSoftOcclusionRegion )
{
if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
{
ISetSoftOcclusionRegion ( plSoftVolume : : ConvertNoRef ( refMsg - > GetRef ( ) ) ) ;
return true ;
}
else if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnRemove | plRefMsg : : kOnDestroy ) )
{
ISetSoftOcclusionRegion ( nil ) ;
return true ;
}
}
else if ( refMsg - > fType = = kRefDataBuffer )
{
if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
{
fDataBuffer = plSoundBuffer : : ConvertNoRef ( refMsg - > GetRef ( ) ) ;
SetLength ( fDataBuffer - > GetDataLengthInSecs ( ) ) ;
}
else
fDataBuffer = nil ;
return true ;
}
else if ( refMsg - > fType = = kRefParentSceneObject )
{
if ( refMsg - > GetContext ( ) & ( plRefMsg : : kOnCreate | plRefMsg : : kOnRequest | plRefMsg : : kOnReplace ) )
fOwningSceneObject = plSceneObject : : ConvertNoRef ( refMsg - > GetRef ( ) ) ;
else
fOwningSceneObject = nil ;
return true ;
}
}
plSoundMsg * pSoundMsg = plSoundMsg : : ConvertNoRef ( pMsg ) ;
if ( pSoundMsg ! = nil )
{
if ( pSoundMsg - > Cmd ( plSoundMsg : : kAddCallbacks ) )
{
AddCallbacks ( pSoundMsg ) ;
return true ;
}
else if ( pSoundMsg - > Cmd ( plSoundMsg : : kRemoveCallbacks ) )
{
RemoveCallbacks ( pSoundMsg ) ;
return true ;
}
return false ;
}
plListenerMsg * listenMsg = plListenerMsg : : ConvertNoRef ( pMsg ) ;
if ( listenMsg ! = nil )
{
if ( fSoftOcclusionRegion ! = nil )
{
// The EAX settings have 0 as the start value and 1 as the end, and since start
// translates to "inside the soft region", it's reversed of what the region gives us
fEAXSettings . SetOcclusionSoftValue ( 1.f - fSoftOcclusionRegion - > GetListenerStrength ( ) ) ;
IRefreshEAXSettings ( ) ;
}
return true ;
}
return plSynchedObject : : MsgReceive ( pMsg ) ;
}
void plSound : : ForceLoad ( )
{
if ( ! IsPropertySet ( kPropLoadOnlyOnCall ) )
return ;
LoadSound ( IsPropertySet ( kPropIs3DSound ) ) ;
}
void plSound : : ForceUnload ( void )
{
if ( ! IsPropertySet ( kPropLoadOnlyOnCall ) )
return ;
Stop ( ) ;
IFreeBuffers ( ) ;
}
bool plSound : : ILoadDataBuffer ( void )
{
if ( ! fDataBufferLoaded )
{
plSoundBuffer * buffer = ( plSoundBuffer * ) fDataBufferKey - > RefObject ( ) ;
if ( ! buffer )
{
hsAssert ( false , " unable to load sound buffer " ) ;
plStatusLog : : AddLineS ( " audio.log " , " Unable to load sound buffer: %s " , GetKeyName ( ) ) ;
return false ;
}
SetLength ( buffer - > GetDataLengthInSecs ( ) ) ;
fDataBufferLoaded = true ;
}
return true ;
}
void plSound : : FreeSoundData ( )
{
if ( ! fDataBufferKey ) return ; // for plugins
plSoundBuffer * buffer = ( plSoundBuffer * ) fDataBufferKey - > ObjectIsLoaded ( ) ;
if ( buffer )
{
buffer - > UnLoad ( ) ;
}
}
void plSound : : IUnloadDataBuffer ( void )
{
if ( fDataBufferLoaded )
{
fDataBufferLoaded = false ;
fDataBufferKey - > UnRefObject ( ) ;
}
}
/////////////////////////////////////////////////////////////////////////
// calling preload will cause the sound to play once loaded
plSoundBuffer : : ELoadReturnVal plSound : : IPreLoadBuffer ( hsBool playWhenLoaded , hsBool isIncidental /* = false */ )
{
if ( ! ILoadDataBuffer ( ) )
{
return plSoundBuffer : : kError ;
}
plSoundBuffer * buffer = ( plSoundBuffer * ) fDataBufferKey - > ObjectIsLoaded ( ) ;
if ( buffer & & buffer - > IsValid ( ) )
{
plProfile_BeginTiming ( SoundLoadTime ) ;
plSoundBuffer : : ELoadReturnVal retVal = buffer - > AsyncLoad ( buffer - > HasFlag ( plSoundBuffer : : kStreamCompressed ) ? plAudioFileReader : : kStreamNative : plAudioFileReader : : kStreamWAV ) ;
if ( retVal = = plSoundBuffer : : kPending )
{
fPlayWhenLoaded = playWhenLoaded ;
fLoading = true ;
}
plProfile_EndTiming ( SoundLoadTime ) ;
return retVal ;
}
else
{
return plSoundBuffer : : kError ;
}
}
const char * plSound : : GetFileName ( void ) const
{
if ( fDataBufferKey - > ObjectIsLoaded ( ) )
{
return ( ( plSoundBuffer * ) fDataBufferKey - > ObjectIsLoaded ( ) ) - > GetFileName ( ) ;
}
return nil ;
}
/////////////////////////////////////////////////////////////////////////
// WARNING: to do this right, we have to load the data buffer in, which could
// force an early load of the sound if you're just calling this on a whim. So
// it's best to call this right before you're going to load it anyway (or
// when it's already loaded).
// Note that if we already set the length (like at export time), we never need
// to load the sound, so the optimization at export time is all ready to plug-
// and-play...
double plSound : : GetLength ( void )
{
if ( ( ( double ) fLength = = 0.f ) )
ILoadDataBuffer ( ) ;
return fLength ;
}
void plSound : : ISetSoftRegion ( plSoftVolume * region )
{
/// We're either registering or unregistering
if ( fSoftRegion = = nil & & region ! = nil )
RegisterOnAudioSys ( ) ;
else if ( fSoftRegion ! = nil & & region = = nil )
UnregisterOnAudioSys ( ) ;
fSoftRegion = region ;
fSoftVolume = 0.f ; // Set to zero until we can get our processing call
}
void plSound : : ISetSoftOcclusionRegion ( plSoftVolume * region )
{
/// We're either registering or unregistering for listener messages
if ( fSoftOcclusionRegion = = nil & & region ! = nil )
{
plgDispatch : : Dispatch ( ) - > RegisterForExactType ( plListenerMsg : : Index ( ) , GetKey ( ) ) ;
}
else if ( fSoftOcclusionRegion ! = nil & & region = = nil )
{
plgDispatch : : Dispatch ( ) - > UnRegisterForExactType ( plListenerMsg : : Index ( ) , GetKey ( ) ) ;
}
fSoftOcclusionRegion = region ;
}
/////////////////////////////////////////////////////////////////////////
// This function calculates our new softVolume value. Used both to update the
// said value and so the audio system can rank us in importance.
hsScalar plSound : : CalcSoftVolume ( hsBool enable , hsScalar distToListenerSquared )
{
// Do distance-based attenuation ourselves
# if MCN_HACK_OUR_ATTEN
if ( IsPropertySet ( kPropIs3DSound ) )
{
hsScalar minDist = ( hsScalar ) GetMin ( ) ;
if ( distToListenerSquared < = minDist * minDist )
{
fDistAttenuation = 1.f ;
}
else
{
hsScalar maxDist = ( hsScalar ) GetMax ( ) ;
if ( distToListenerSquared > = maxDist * maxDist )
{
fDistAttenuation = 0.f ;
}
else
{
hsScalar d = ( hsScalar ) sqrt ( distToListenerSquared ) ;
fDistAttenuation = minDist / d ;
// The following line ramps it to 0 at the maxDistance. Kinda klunky, but good for now I guess...
// fDistAttenuation *= 1.f - ( 1.f / ( maxDist - minDist ) ) * ( d - minDist );
}
}
}
else
fDistAttenuation = 1.f ;
# endif
// At the last 50% of our distance attenuation (squared, so it really is farther than that),
// ramp down to 0 so we don't get annoying popping when we stop stuff
if ( IsPropertySet ( kPropIs3DSound ) )
{
hsScalar maxDistSquared = ( hsScalar ) ( GetMax ( ) * GetMax ( ) ) ;
hsScalar distToStartSquared = ( hsScalar ) ( maxDistSquared * 0.50 ) ;
if ( maxDistSquared < 0.f ) // Happens when the max distance is REALLY big
{
maxDistSquared = distToListenerSquared + 1.f ; // :)
distToStartSquared = maxDistSquared ;
}
if ( distToListenerSquared > maxDistSquared )
fDistAttenuation = 0.f ;
else if ( distToListenerSquared > distToStartSquared )
fDistAttenuation = ( maxDistSquared - distToListenerSquared ) / ( maxDistSquared - distToStartSquared ) ;
else
fDistAttenuation = 1.f ;
fDistToListenerSquared = distToListenerSquared ;
}
else
{
fDistAttenuation = 1.f ;
fDistToListenerSquared = 0.f ;
}
// Attenuate based on our soft region, if we have one
if ( ! enable )
// We apparently don't know jack. Let the audioSystem's decision rule
fSoftVolume = 0.f ;
else if ( fSoftRegion ! = nil )
fSoftVolume = fSoftRegion - > GetListenerStrength ( ) ;
else
fSoftVolume = 1.f ;
return fSoftVolume ;
}
/////////////////////////////////////////////////////////////////////////
// Wee function for the audio system. This basically returns the effective
// current volume of this sound. Useful for doing things like ranking all
// sounds based on volume.
hsScalar plSound : : GetVolumeRank ( void )
{
if ( ! IsPlaying ( ) & & ! this - > IActuallyPlaying ( ) )
return 0.f ;
hsScalar rank = fSoftVolume * fDesiredVol ;
if ( IsPropertySet ( kPropIs3DSound ) )
{
hsScalar minDistSquared = ( hsScalar ) ( GetMin ( ) * GetMin ( ) ) ;
hsScalar maxDistSquared = ( hsScalar ) ( GetMax ( ) * GetMax ( ) ) ;
hsPoint3 listenerPos = plgAudioSys : : Sys ( ) - > GetCurrListenerPos ( ) ;
if ( fDistToListenerSquared > minDistSquared )
{
hsScalar diff = maxDistSquared - minDistSquared ;
rank * = fabs ( ( fDistToListenerSquared - maxDistSquared ) ) / diff ;
}
}
return rank ;
}
/////////////////////////////////////////////////////////////////////////
// Tests to see whether, if we try to play this sound now, it'll actually
// be able to play. Takes into account whether the sound is within range
// of the listener and the current soft region value.
hsBool plSound : : IWillBeAbleToPlay ( void )
{
if ( fSoftVolume = = 0.f )
return false ;
return IsWithinRange ( plgAudioSys : : Sys ( ) - > GetCurrListenerPos ( ) , nil ) ;
}
/////////////////////////////////////////////////////////////////////////
// Tests to see whether this sound is within range of the position given,
// ignoring soft volumes.
hsBool plSound : : IsWithinRange ( const hsPoint3 & listenerPos , hsScalar * distSquared )
{
if ( ! IsPropertySet ( plSound : : kPropIs3DSound ) )
{
if ( distSquared ! = nil )
* distSquared = 1.f ;
return true ;
}
hsVector3 distance ;
hsPoint3 soundPos = GetPosition ( ) ;
distance . Set ( & listenerPos , & soundPos ) ;
if ( distSquared ! = nil )
* distSquared = distance . MagnitudeSquared ( ) ;
if ( GetMax ( ) = = 1000000000 )
return true ;
hsScalar soundRadius = ( hsScalar ) ( GetMax ( ) * GetMax ( ) ) ;
return ( distance . MagnitudeSquared ( ) < = soundRadius ) ? true : false ;
}
//// ////////////////////////////////////////////////////////
// Once the soft volume is calculated and our rank is computed, we can
// decide whether to actually enable or not.
// Note: we might have been "enabled" by our Calc call, but the ranking
// still could have disabled us, so we have to specify again whether
// we're enabled.
// Note: if we KNOW we're disabling this sound (out of range), Calc() doesn't
// have to be called at all, and we can simply call this function with
// enable = false.
void plSound : : UpdateSoftVolume ( hsBool enable , hsBool firstTime )
{
fNotHighEnoughPriority = ! enable ;
// Don't do any of this special stuff that follows if we're not supposed to be playing
if ( IsPlaying ( ) )
{
if ( fSoftVolume * fDistAttenuation > 0.f & & ! fNotHighEnoughPriority )
{
if ( fCurrFadeParams = = & fCoolSoftVolumeTrickParams )
{
// Stop the fade, but since we are updating the softvolume with the intention of being audible
// tell StopFade not to set the soft volume to zero.
IStopFade ( false , false ) ;
}
if ( ! IActuallyPlaying ( ) )
{
// Must've been stopped from being out of range. Start up again...
// Synch up to our start time.
// If this sound is auto starting and is background music, get the current time so we don't start
// with the play cursor already into the piece.
if ( IsPropertySet ( kPropAutoStart ) & & fType = = kBackgroundMusic ) fVirtualStartTime = hsTimer : : GetSysSeconds ( ) ;
ISynchedPlay ( fVirtualStartTime ) ;
}
}
else if ( fCurrFadeParams ! = & fCoolSoftVolumeTrickParams & & IActuallyPlaying ( ) )
{
// Start our special trick, courtesy of Brice. Basically, we don't
// stop the sound immediately, but rather let it get to the end of
// the sound and loop once more. This way, if we go away and come back soon
// enough, it will be continuing as we expect it to, but if we wait long enough,
// it'll stop, saving processing time.
// Note: we just do it as a fade because it makes it easier on us that way!
fCoolSoftVolumeTrickParams . fCurrTime = 0.f ;
fCoolSoftVolumeTrickParams . fLengthInSecs = firstTime ? 0.f : ( hsScalar ) fLength + ( ( hsScalar ) fLength - ( hsScalar ) GetTime ( ) ) ;
fCoolSoftVolumeTrickParams . fStopWhenDone = true ;
fCoolSoftVolumeTrickParams . fFadeSoftVol = true ;
fCoolSoftVolumeTrickParams . fType = plFadeParams : : kLinear ;
fCoolSoftVolumeTrickParams . fVolStart = fSoftVolume ; // Don't actually change the volume this way
fCoolSoftVolumeTrickParams . fVolEnd = 0.f ;
IStartFade ( & fCoolSoftVolumeTrickParams ) ;
plProfile_Inc ( WaitingToDie ) ;
}
}
RefreshVolume ( ) ;
}
/////////////////////////////////////////////////////////////////////////
// Returns the current volume, attenuated
hsScalar plSound : : QueryCurrVolume ( void ) const
{
return IAttenuateActualVolume ( fCurrVolume ) * IGetChannelVolume ( ) ;
}
/////////////////////////////////////////////////////////////////////////
// Used by ISetActualVolume(). Does the final attenuation on a volume before
// sending it to the sound processing. Only does soft regions for now.
hsScalar plSound : : IAttenuateActualVolume ( hsScalar volume ) const
{
if ( fNotHighEnoughPriority )
return 0.f ;
volume * = fSoftVolume ;
if ( IsPropertySet ( kPropIs3DSound ) )
volume * = fDistAttenuation ;
return volume ;
}
void plSound : : Activate ( hsBool forcePlay )
{
// Our actual state...
fActive = true ;
// re-create the sound state here:
if ( forcePlay | | fPlayOnReactivate )
{
ISynchedPlay ( hsTimer : : GetSysSeconds ( ) ) ;
fPlayOnReactivate = false ;
}
RegisterOnAudioSys ( ) ;
SetMuted ( plgAudioSys : : IsMuted ( ) ) ;
}
void plSound : : DeActivate ( void )
{
UnregisterOnAudioSys ( ) ;
if ( fActive )
{
if ( IsPlaying ( ) )
{
Stop ( ) ;
fPlayOnReactivate = true ;
}
else
fPlayOnReactivate = false ;
}
fActive = false ;
}
/////////////////////////////////////////////////////////////////////////
// Tell the audio system about ourselves.
void plSound : : RegisterOnAudioSys ( void )
{
if ( ! fRegistered )
{
plgAudioSys : : RegisterSoftSound ( GetKey ( ) ) ;
fRegistered = true ;
}
}
/////////////////////////////////////////////////////////////////////////
// Tell the audio system to stop caring about us
void plSound : : UnregisterOnAudioSys ( void )
{
if ( fRegistered )
{
plgAudioSys : : UnregisterSoftSound ( GetKey ( ) ) ;
fRegistered = false ;
}
}
/////////////////////////////////////////////////////////////////////////
// Called by the audio system when we've been booted off (audio system is
// shutting down). Normally, we should already be shut down, but in case
// we're not, this function makes sure everything is cleaned up before
// the audio system itself shuts down.
void plSound : : ForceUnregisterFromAudioSys ( void )
{
DeActivate ( ) ;
fRegistered = false ;
}
void plSound : : Read ( hsStream * s , hsResMgr * mgr )
{
plSynchedObject : : Read ( s , mgr ) ;
IRead ( s , mgr ) ;
// If we're autostart, start playing
if ( IsPropertySet ( kPropAutoStart ) )
Play ( ) ;
// Make sure we synch or don't synch
if ( IsPropertySet ( kPropLocalOnly ) )
SetLocalOnly ( true ) ;
// If we're not playing, but we're going to, and we're going to fade in,
// then our current state is faded out, so set fFading
if ( fFadeInParams . fLengthInSecs > 0 & & ! fPlaying )
fFading = true ;
else if ( fFadeInParams . fLengthInSecs < = 0 & & ! fPlaying )
fFading = false ;
ILoadDataBuffer ( ) ; // make sure our sound buffer is loaded
// Force load on read
if ( ! fLoadOnDemandFlag | | IsPropertySet ( kPropDisableLOD ) )
{
LoadSound ( IsPropertySet ( kPropIs3DSound ) ) ;
}
else
{
// Loading on demand, but we still need the length. But that's ok, we'll get it when we get the fDataBuffer ref.
// But we want to preload the data, so go ahead and do that
if ( ! fLoadFromDiskOnDemand & & ! IsPropertySet ( kPropLoadOnlyOnCall ) & & fPriority < = plgAudioSys : : GetPriorityCutoff ( ) )
{
IPreLoadBuffer ( false ) ;
}
}
}
void plSound : : Write ( hsStream * s , hsResMgr * mgr )
{
plSynchedObject : : Write ( s , mgr ) ;
IWrite ( s , mgr ) ;
}
void plSound : : IRead ( hsStream * s , hsResMgr * mgr )
{
fPlaying = s - > ReadBool ( ) ;
fVirtualStartTime = hsTimer : : GetSysSeconds ( ) ; // Need if we're autostart
fTime = s - > ReadSwapDouble ( ) ;
fMaxFalloff = s - > ReadSwap32 ( ) ;
fMinFalloff = s - > ReadSwap32 ( ) ;
s - > ReadSwap ( & fCurrVolume ) ;
s - > ReadSwap ( & fDesiredVol ) ;
/// mcn debugging - Thanks to some of my older sound code, it's possible that a few volumes
/// will come in too large. This will only happen with scenes that were exported with that intermediate
/// code, which should be limited to my test scenes locally. Otherwise, things should be fine. This
/// is to compensate for those bogus files. (The fix is to reset the volume in MAX, since the bogusness
/// is in the range of the volume slider itself).
if ( fDesiredVol > 1.f )
fDesiredVol = 1.f ;
if ( fCurrVolume > 1.f )
fCurrVolume = 1.f ;
fMaxVolume = fDesiredVol ;
fOuterVol = s - > ReadSwap32 ( ) ;
fInnerCone = s - > ReadSwap32 ( ) ;
fOuterCone = s - > ReadSwap32 ( ) ;
s - > ReadSwap ( & fFadedVolume ) ;
s - > ReadSwap ( & fProperties ) ;
fType = s - > ReadByte ( ) ;
fPriority = s - > ReadByte ( ) ;
// HACK FOR OLDER EXPORTERS that thought Auto-start meant set fPlaying true
if ( fPlaying )
SetProperty ( kPropAutoStart , true ) ;
// Read in fade params
fFadeInParams . Read ( s ) ;
fFadeOutParams . Read ( s ) ;
// Read in soft volume key
mgr - > ReadKeyNotifyMe ( s , TRACKED_NEW plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , 0 , kRefSoftVolume ) , plRefFlags : : kActiveRef ) ;
// Read in the data buffer key
fDataBufferKey = mgr - > ReadKey ( s ) ;
// EAX params
fEAXSettings . Read ( s ) ;
// EAX soft keys
mgr - > ReadKeyNotifyMe ( s , TRACKED_NEW plGenRefMsg ( GetKey ( ) , plRefMsg : : kOnCreate , 0 , kRefSoftOcclusionRegion ) , plRefFlags : : kActiveRef ) ;
}
void plSound : : IWrite ( hsStream * s , hsResMgr * mgr )
{
s - > WriteBool ( fPlaying ) ;
s - > WriteSwapDouble ( fTime ) ;
s - > WriteSwap32 ( fMaxFalloff ) ;
s - > WriteSwap32 ( fMinFalloff ) ;
s - > WriteSwap ( fCurrVolume ) ;
s - > WriteSwap ( fDesiredVol ) ;
s - > WriteSwap32 ( fOuterVol ) ;
s - > WriteSwap32 ( fInnerCone ) ;
s - > WriteSwap32 ( fOuterCone ) ;
s - > WriteSwap ( fFadedVolume ) ;
s - > WriteSwap ( fProperties ) ;
s - > WriteByte ( fType ) ;
s - > WriteByte ( fPriority ) ;
// Write out fade params
fFadeInParams . Write ( s ) ;
fFadeOutParams . Write ( s ) ;
// Write out soft volume key
mgr - > WriteKey ( s , fSoftRegion ) ;
// Write out data buffer key
if ( fDataBuffer )
mgr - > WriteKey ( s , fDataBuffer - > GetKey ( ) ) ;
else
mgr - > WriteKey ( s , fDataBufferKey ) ;
// EAX params
fEAXSettings . Write ( s ) ;
// EAX Soft keys
mgr - > WriteKey ( s , fSoftOcclusionRegion ) ;
}
void plSound : : plFadeParams : : Read ( hsStream * s )
{
s - > ReadSwap ( & fLengthInSecs ) ;
s - > ReadSwap ( & fVolStart ) ;
s - > ReadSwap ( & fVolEnd ) ;
s - > ReadSwap ( & fType ) ;
s - > ReadSwap ( & fCurrTime ) ;
s - > ReadSwap ( & fStopWhenDone ) ;
s - > ReadSwap ( & fFadeSoftVol ) ;
}
void plSound : : plFadeParams : : Write ( hsStream * s )
{
s - > WriteSwap ( fLengthInSecs ) ;
s - > WriteSwap ( fVolStart ) ;
s - > WriteSwap ( fVolEnd ) ;
s - > WriteSwap ( fType ) ;
s - > WriteSwap ( fCurrTime ) ;
s - > WriteSwap ( fStopWhenDone ) ;
s - > WriteSwap ( fFadeSoftVol ) ;
}
hsScalar plSound : : plFadeParams : : InterpValue ( void )
{
hsScalar val ;
switch ( fType )
{
case kLinear :
val = ( ( fCurrTime / fLengthInSecs ) * ( fVolEnd - fVolStart ) ) + fVolStart ;
break ;
case kLogarithmic :
val = fCurrTime / fLengthInSecs ;
val = ( ( val * val ) * ( fVolEnd - fVolStart ) ) + fVolStart ;
break ;
case kExponential :
val = fCurrTime / fLengthInSecs ;
val = ( ( hsScalar ) sqrt ( val ) * ( fVolEnd - fVolStart ) ) + fVolStart ;
break ;
default :
val = 0.f ;
}
return val ;
}
void plSound : : SetFadeInEffect ( plSound : : plFadeParams : : Type type , hsScalar length )
{
fFadeInParams . fLengthInSecs = length ;
fFadeInParams . fType = type ;
fFadeInParams . fVolStart = 0.f ; // Will be set when activated
fFadeInParams . fVolEnd = 1.f ; // Will be set when activated
fFadeInParams . fCurrTime = - 1.f ;
// If we're not playing, but we're going to, and we're going to fade in,
// then our current state is faded out, so set fFading
if ( fFadeInParams . fLengthInSecs > 0 & & ! fPlaying )
fFading = true ;
else if ( fFadeInParams . fLengthInSecs < = 0 & & ! fPlaying )
fFading = false ;
}
void plSound : : SetFadeOutEffect ( plSound : : plFadeParams : : Type type , hsScalar length )
{
fFadeOutParams . fLengthInSecs = length ;
fFadeOutParams . fType = type ;
fFadeOutParams . fVolStart = 1.f ; // Will be set when activated
fFadeOutParams . fVolEnd = 0.f ; // Will be set when activated
fFadeOutParams . fCurrTime = - 1.f ;
fFadeOutParams . fStopWhenDone = true ;
}
plDrawableSpans * plSound : : CreateProxy ( const hsMatrix44 & l2w , hsGMaterial * mat , hsTArray < UInt32 > & idx , plDrawableSpans * addTo )
{
plDrawableSpans * myDraw = addTo ;
if ( fOuterCone < 360 )
{
hsScalar len = ( hsScalar ) GetMax ( ) ;
hsScalar halfAng = hsScalarDegToRad ( hsScalar ( fInnerCone ) * 0.5f ) ;
hsScalar radius = len * tanf ( halfAng ) ;
if ( fInnerCone < 180 )
len = - len ;
myDraw = plDrawableGenerator : : GenerateConicalDrawable (
radius ,
len ,
mat ,
l2w ,
true ,
& hsColorRGBA ( ) . Set ( 1.f , 0.5f , 0.5f , 1.f ) ,
& idx ,
myDraw ) ;
len = ( hsScalar ) GetMin ( ) ;
halfAng = hsScalarDegToRad ( hsScalar ( fOuterCone ) * 0.5f ) ;
radius = len * tanf ( halfAng ) ;
if ( fOuterCone < 180 )
len = - len ;
myDraw = plDrawableGenerator : : GenerateConicalDrawable (
radius ,
len ,
mat ,
l2w ,
true ,
& hsColorRGBA ( ) . Set ( 0.25f , 0.25f , 0.5f , 1.f ) ,
& idx ,
myDraw ) ;
}
else
{
myDraw = plDrawableGenerator : : GenerateSphericalDrawable (
hsPoint3 ( 0 , 0 , 0 ) ,
( hsScalar ) GetMin ( ) ,
mat ,
l2w ,
true ,
& hsColorRGBA ( ) . Set ( 1.f , 0.5f , 0.5f , 1.f ) ,
& idx ,
myDraw ) ;
myDraw = plDrawableGenerator : : GenerateSphericalDrawable (
hsPoint3 ( 0 , 0 , 0 ) ,
( hsScalar ) GetMax ( ) ,
mat ,
l2w ,
true ,
& hsColorRGBA ( ) . Set ( 0.25f , 0.25f , 0.5f , 1.f ) ,
& idx ,
myDraw ) ;
}
return myDraw ;
}
// call when state has changed
hsBool plSound : : DirtySynchState ( const char * sdlName /* kSDLSound */ , UInt32 sendFlags )
{
/*
if ( sdlName = = nil )
sdlName = kSDLSound ;
if ( fOwningSceneObject ! = nil )
return fOwningSceneObject - > DirtySynchState ( sdlName , sendFlags ) ;
*/
return false ;
}
//////////////////////////////////////////////////////////////////////////////
//// plSoundVolumeApplicator /////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void plSoundVolumeApplicator : : IApply ( const plAGModifier * mod , double time )
{
plScalarChannel * chan = plScalarChannel : : ConvertNoRef ( fChannel ) ;
if ( chan )
{
hsScalar volume = chan - > Value ( time ) ;
hsScalar digitalVolume = ( float ) pow ( 10.f , volume / 20.f ) ;
// Find the audio interface and thus the plSound from it
plSceneObject * so = mod - > GetTarget ( 0 ) ;
if ( so ! = nil )
{
const plAudioInterface * ai = so - > GetAudioInterface ( ) ;
if ( ai ! = nil & & fIndex < ai - > GetNumSounds ( ) )
{
plSound * sound = ai - > GetSound ( fIndex ) ;
if ( sound ! = nil )
{
sound - > SetVolume ( digitalVolume ) ;
return ;
}
}
}
}
}
plAGApplicator * plSoundVolumeApplicator : : CloneWithChannel ( plAGChannel * channel )
{
plSoundVolumeApplicator * clone = ( plSoundVolumeApplicator * ) plAGApplicator : : CloneWithChannel ( channel ) ;
clone - > fIndex = fIndex ;
return clone ;
}
void plSoundVolumeApplicator : : Write ( hsStream * stream , hsResMgr * mgr )
{
plAGApplicator : : Write ( stream , mgr ) ;
stream - > WriteSwap32 ( fIndex ) ;
}
void plSoundVolumeApplicator : : Read ( hsStream * s , hsResMgr * mgr )
{
plAGApplicator : : Read ( s , mgr ) ;
fIndex = s - > ReadSwap32 ( ) ;
}