diff options
Diffstat (limited to 'game/code/sound')
148 files changed, 42369 insertions, 0 deletions
diff --git a/game/code/sound/allsound.cpp b/game/code/sound/allsound.cpp new file mode 100644 index 0000000..9d10b15 --- /dev/null +++ b/game/code/sound/allsound.cpp @@ -0,0 +1,7 @@ +#include <sound/listener.cpp> +#include <sound/simpsonssoundplayer.cpp> +#include <sound/soundcluster.cpp> +#include <sound/soundloader.cpp> +#include <sound/soundmanager.cpp> +#include <sound/soundrenderercallback.cpp> +#include <sound/positionalsoundplayer.cpp> diff --git a/game/code/sound/avatar/allsoundavatar.cpp b/game/code/sound/avatar/allsoundavatar.cpp new file mode 100644 index 0000000..ff0df8f --- /dev/null +++ b/game/code/sound/avatar/allsoundavatar.cpp @@ -0,0 +1,5 @@ +#include <sound/avatar/avatarsoundplayer.cpp> +#include <sound/avatar/carsoundparameters.cpp> +#include <sound/avatar/soundavatar.cpp> +#include <sound/avatar/vehiclesoundplayer.cpp> +#include <sound/avatar/vehiclesounddebugpage.cpp>
\ No newline at end of file diff --git a/game/code/sound/avatar/avatarsoundplayer.cpp b/game/code/sound/avatar/avatarsoundplayer.cpp new file mode 100644 index 0000000..4f6df3a --- /dev/null +++ b/game/code/sound/avatar/avatarsoundplayer.cpp @@ -0,0 +1,187 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: avatarsoundplayer.cpp +// +// Description: Implementation of AvatarSoundPlayer, which directs the +// playing of avatar-related sounds +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +#include <radfactory.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/avatar/avatarsoundplayer.h> + +#include <mission/gameplaymanager.h> +#include <worldsim/avatarmanager.h> + +#include <sound/avatar/soundavatar.h> +#include <sound/avatar/carsoundparameters.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// AvatarSoundPlayer::AvatarSoundPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +AvatarSoundPlayer::AvatarSoundPlayer() +{ + int i; + + for( i = 0; i < MAX_PLAYERS; i++ ) + { + m_avatars[i] = NULL; + } +} + +//============================================================================== +// AvatarSoundPlayer::~AvatarSoundPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +AvatarSoundPlayer::~AvatarSoundPlayer() +{ + int i; + + for( i = 0; i < MAX_PLAYERS; i++ ) + { + if( m_avatars[i] != NULL ) + { + delete m_avatars[i]; + } + } +} + +//============================================================================= +// AvatarSoundPlayer::Initialize +//============================================================================= +// Description: Do the initialization stuff for the avatar sound subsystem +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void AvatarSoundPlayer::Initialize() +{ + // + // Must register the carSoundParameters factory method with RadScript before + // the script creating these objects gets run + // + ::radFactoryRegister( "carSoundParameters", (radFactoryOutParamProc*) ::CarSoundParameterObjCreate ); +} + +//============================================================================= +// AvatarSoundPlayer::UpdateOncePerFrame +//============================================================================= +// Description: Update function. Used for stuff that either doesn't need to +// be called often and/or uses expensive math. +// +// Parameters: elapsedTime - time since last frame in msecs +// +// Return: void +// +//============================================================================= +void AvatarSoundPlayer::UpdateOncePerFrame( unsigned int elapsedTime ) +{ + int i; + + for( i = 0; i < MAX_PLAYERS; i++ ) + { + if( m_avatars[i] != NULL ) + { + m_avatars[i]->UpdateOncePerFrame( elapsedTime ); + } + } +} + +//============================================================================= +// AvatarSoundPlayer::OnBeginGameplay +//============================================================================= +// Description: Create SoundAvatar objects when gameplay begins +// +// Parameters: None +// +// Return: true if player 0 in car, false otherwise +// +//============================================================================= +bool AvatarSoundPlayer::OnBeginGameplay() +{ + unsigned int i; + unsigned int numPlayers = GetGameplayManager()->GetNumPlayers(); + + for( i = 0; i < numPlayers; i++ ) + { + rAssert( m_avatars[i] == NULL ); + + m_avatars[i] = new(GMA_LEVEL_AUDIO) SoundAvatar( GetAvatarManager()->GetAvatarForPlayer( i ) ); + } + + return( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() ); +} + +//============================================================================= +// AvatarSoundPlayer::OnEndGameplay +//============================================================================= +// Description: Destroy SoundAvatar objects when gameplay ends +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void AvatarSoundPlayer::OnEndGameplay() +{ + int i; + + // + // Destroy the avatar trackers, since we won't have avatars anymore until + // gameplay restarts + // + for( i = 0; i < MAX_PLAYERS; i++ ) + { + if( m_avatars[i] != NULL ) + { + delete m_avatars[i]; + m_avatars[i] = NULL; + } + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/avatar/avatarsoundplayer.h b/game/code/sound/avatar/avatarsoundplayer.h new file mode 100644 index 0000000..ce8bacd --- /dev/null +++ b/game/code/sound/avatar/avatarsoundplayer.h @@ -0,0 +1,63 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: avatarsoundplayer.h +// +// Description: Declaration of AvatarSoundPlayer class, which directs the +// playing of avatar-related sounds +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef AVATARSOUNDPLAYER_H +#define AVATARSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <constants/maxplayers.h> + +//======================================== +// Forward References +//======================================== + +class SoundAvatar; + +//============================================================================= +// +// Synopsis: AvatarSoundPlayer +// +//============================================================================= + +class AvatarSoundPlayer +{ + public: + AvatarSoundPlayer(); + virtual ~AvatarSoundPlayer(); + + void Initialize(); + + void UpdateOncePerFrame( unsigned int elapsedTime ); + + // + // Returns true if first player in car, false otherwise + // + bool OnBeginGameplay(); + + void OnEndGameplay(); + + private: + //Prevent wasteful constructor creation. + AvatarSoundPlayer( const AvatarSoundPlayer& original ); + AvatarSoundPlayer& operator=( const AvatarSoundPlayer& rhs ); + + // + // One SoundAvatar to track activity of each Avatar object + // + SoundAvatar* m_avatars[MAX_PLAYERS]; +}; + + +#endif // AVATARSOUNDPLAYER_H + diff --git a/game/code/sound/avatar/carsoundparameters.cpp b/game/code/sound/avatar/carsoundparameters.cpp new file mode 100644 index 0000000..870c13f --- /dev/null +++ b/game/code/sound/avatar/carsoundparameters.cpp @@ -0,0 +1,1184 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: carsoundparameters.cpp +// +// Description: Implement carSoundParameters +// +// History: 01/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <string.h> + +#include <raddebugwatch.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/avatar/carsoundparameters.h> + +#include <memory/srrmemory.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Initialially the list is empty +// +carSoundParameters* radLinkedClass< carSoundParameters >::s_pLinkedClassHead = NULL; +carSoundParameters* radLinkedClass< carSoundParameters >::s_pLinkedClassTail = NULL; + +static float s_maxPitchDefault = 5.0f; +static float s_inAirIdleDefault = 0.2f; +static float s_inAirThrottleDefault = 2.0f; +static float s_powerslideMinDefault = 0.2f; +static float s_powerslideMaxDefault = 2.0f; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// carSoundParameters::carSoundParameters +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +carSoundParameters::carSoundParameters() : + radRefCount( 0 ), + m_clipRPM( 3000 ), + m_engineClipName( NULL ), + m_idleClipName( NULL ), + m_damagedClipName( NULL ), + m_hornClipName( NULL ), + m_carOpenClipName( NULL ), + m_carCloseClipName( NULL ), + m_overlayClipName( NULL ), + m_roadSkidClipName( NULL ), + m_dirtSkidClipName( NULL ), + m_backupClipName( NULL ), + m_downshiftDamper( 0.05f ), + m_attackTime( 100 ), + m_delayTime( 50 ), + m_decayTime( 200 ), + m_decayFinishTrim( 0.75f ), + m_maxReverseKmh( 50.0f ), + m_minReversePitch( 1.5f ), + m_maxReversePitch( 3.0f ), + m_damageStartPcnt( 0.4f ), + m_damageVolumeRange( m_damageStartPcnt ), + m_damageStartTrim( 0.0f ), + m_damageMaxTrim( 1.0f ), + m_idleEnginePitch( 1.0f ), + m_inAirThrottlePitch( s_inAirThrottleDefault ), + m_inAirIdlePitch( s_inAirIdleDefault ), + m_inAirResponseMsecs( 500 ), + m_burnoutMinPitch( 0.5f ), + m_burnoutMaxPitch( 1.3f ), + m_powerslideMinPitch( s_powerslideMinDefault ), + m_powerslideMaxPitch( s_powerslideMaxDefault ), + m_msecsPerOctave( 500 ) +{ + unsigned int i; + + // + // Shift point defaults + // + m_shiftPoints[0] = 0.01f; + m_shiftPoints[1] = 0.30f; + m_shiftPoints[2] = 0.45f; + m_shiftPoints[3] = 0.70f; + m_shiftPoints[4] = 0.85f; + m_shiftPoints[5] = 0.95f; + + m_shiftPoints[MAX_GEARS] = 1.0f; + + // + // Pitch range and gear shift defaults + // + for( i = 0; i < MAX_GEARS; i++ ) + { + m_minPitch[i] = 0.5f; + m_maxPitch[i] = s_maxPitchDefault; + + m_gearShiftPitchDrop[i] = 0.2f; + } +} + +//============================================================================== +// carSoundParameters::~carSoundParameters +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +carSoundParameters::~carSoundParameters() +{ + if( m_engineClipName != NULL ) + { + delete [] m_engineClipName; + } + if( m_idleClipName != NULL ) + { + delete [] m_idleClipName; + } + if( m_damagedClipName != NULL ) + { + delete [] m_damagedClipName; + } + if( m_hornClipName != NULL ) + { + delete [] m_hornClipName; + } + if( m_carOpenClipName != NULL ) + { + delete [] m_carOpenClipName; + } + if( m_carCloseClipName != NULL ) + { + delete [] m_carCloseClipName; + } + if( m_overlayClipName != NULL ) + { + delete [] m_overlayClipName; + } + if( m_roadSkidClipName != NULL ) + { + delete [] m_roadSkidClipName; + } + if( m_dirtSkidClipName != NULL ) + { + delete [] m_dirtSkidClipName; + } + if( m_backupClipName != NULL ) + { + delete [] m_backupClipName; + } +} + +//============================================================================= +// carSoundParameters::SetWatcherName +//============================================================================= +// Description: Set the name of this object, since it's usually stored +// in radScript's data structures. FOR DEBUGGING ONLY. We use +// this to give the car a name for Watcher tuning. +// +// Parameters: name - name of car +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetWatcherName( const char* name ) +{ +#ifndef RAD_RELEASE + unsigned int i; + char label[50]; + char watcherGroup[50]; + + sprintf( watcherGroup, "Sound::%s", name ); + + // + // Register members with Watcher for tweakage + // + for( i = 1; i <= MAX_GEARS; i++ ) + { + sprintf( label, "Min pitch: gear %d", i ); + radDbgWatchAddFloat( &m_minPitch[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f ); + + sprintf( label, "Max pitch: gear %d", i ); + radDbgWatchAddFloat( &m_maxPitch[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f ); + + sprintf( label, "Pitch drop: gear %d", i ); + radDbgWatchAddFloat( &m_gearShiftPitchDrop[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f ); + } + + for( i = 1; i <= MAX_GEARS + 1; i++ ) + { + sprintf( label, "Shift point: gear %d", i ); + radDbgWatchAddFloat( &m_shiftPoints[i-1], label, watcherGroup ); + } + + radDbgWatchAddFloat( &m_downshiftDamper, "Downshift damper", watcherGroup ); + radDbgWatchAddFloat( &m_attackTime, "Attack time (msecs)", watcherGroup, NULL, NULL, 0, 2000.0f ); + radDbgWatchAddUnsignedInt( &m_delayTime, "Delay time (msecs)", watcherGroup, NULL, NULL, 0, 2000 ); + radDbgWatchAddFloat( &m_decayTime, "Decay time (msecs)", watcherGroup, NULL, NULL, 0, 2000.0f ); + radDbgWatchAddFloat( &m_decayFinishTrim, "Decay finish trim", watcherGroup ); + radDbgWatchAddFloat( &m_maxReverseKmh, "Max reverse kmh", watcherGroup, NULL, NULL, 0.0f, 200.0f ); + radDbgWatchAddFloat( &m_minReversePitch, "Min reverse pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f ); + radDbgWatchAddFloat( &m_maxReversePitch, "Max reverse pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f ); + radDbgWatchAddFloat( &m_damageStartPcnt, "Damage start", watcherGroup ); + radDbgWatchAddFloat( &m_damageVolumeRange, "Damage max volume life %", watcherGroup ); + radDbgWatchAddFloat( &m_damageStartTrim, "Damage start trim", watcherGroup ); + radDbgWatchAddFloat( &m_damageMaxTrim, "Damage max trim", watcherGroup ); + radDbgWatchAddFloat( &m_idleEnginePitch, "Idle engine pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f ); + radDbgWatchAddFloat( &m_inAirThrottlePitch, "In air throttle pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f ); + radDbgWatchAddFloat( &m_inAirIdlePitch, "In air idle pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f ); + radDbgWatchAddUnsignedInt( &m_inAirResponseMsecs, "In air response time (msecs)", watcherGroup, NULL, NULL, 0, 2000 ); +#endif +} + +//============================================================================= +// carSoundParameters::SetShiftPoint +//============================================================================= +// Description: Set the percentage of top speed at which we shift to a +// given gear +// +// Parameters: gear - gear to be shifted to +// lowPercent - percentage of top speed at which we downshift +// highPercent - percentage of top speed at which we upshift +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetShiftPoint( unsigned int gear, float percent ) +{ + rAssert( gear > 0 ); + rAssert( gear <= MAX_GEARS ); + rAssert( percent > 0.0f ); + rAssert( percent <= 1.0f ); + + m_shiftPoints[gear-1] = percent; +} + +//============================================================================= +// carSoundParameters::GetShiftPoint +//============================================================================= +// Description: Get the percentage of top speed at which we shift up from a +// given gear +// +// Parameters: gear - gear we're currently in +// +// Return: shift point as percentage of top speed +// +//============================================================================= +float carSoundParameters::GetShiftPoint( int gear ) +{ + if( gear > 0 ) + { + return( m_shiftPoints[gear-1] ); + } + else + { + return( 0.0f ); + } +} + +//============================================================================= +// carSoundParameters::SetAttackTimeMsecs +//============================================================================= +// Description: Comment +// +// Parameters: ( float msecs ) +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetAttackTimeMsecs( float msecs ) +{ + m_attackTime = msecs; +} + +//============================================================================= +// carSoundParameters::SetDelayTimeMsecs +//============================================================================= +// Description: Comment +// +// Parameters: ( unsigned int msecs ) +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDelayTimeMsecs( unsigned int msecs ) +{ + m_delayTime = msecs; +} + +//============================================================================= +// carSoundParameters::SetDecayTimeMsecs +//============================================================================= +// Description: Comment +// +// Parameters: ( float msecs ) +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDecayTimeMsecs( float msecs ) +{ + m_decayTime = msecs; +} + +//============================================================================= +// carSoundParameters::SetDecayFinishTrim +//============================================================================= +// Description: Comment +// +// Parameters: ( float trim ) +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDecayFinishTrim( float trim ) +{ + m_decayFinishTrim = trim; +} + +//============================================================================= +// carSoundParameters::SetDownshiftDamperSize +//============================================================================= +// Description: Amount that we can drop below the shift point to avoid +// downshifting +// +// Parameters: percent - percentage of top speed that acts as the buffer +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDownshiftDamperSize( float percent ) +{ + rAssert( percent >= 0.0f ); + rAssert( percent <= 1.0f ); + + m_downshiftDamper = percent; +} + +//============================================================================= +// carSoundParameters::SetEngineClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for engine +// sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetEngineClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_engineClipName = new char[strlen(clipName)+1]; + strcpy( m_engineClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetEngineIdleClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for engine +// idle sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetEngineIdleClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_idleClipName = new char[strlen(clipName)+1]; + strcpy( m_idleClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetDamagedEngineClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for damaged +// engine sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDamagedEngineClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_damagedClipName = new char[strlen(clipName)+1]; + strcpy( m_damagedClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetHornClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for horn +// sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetHornClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_hornClipName = new char[strlen(clipName)+1]; + strcpy( m_hornClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetCarDoorOpenClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for car +// door opening sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetCarDoorOpenClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_carOpenClipName = new char[strlen(clipName)+1]; + strcpy( m_carOpenClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetCarDoorCloseClipName +//============================================================================= +// Description: Store the name of the sound resource we're using for car +// door closing sounds +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetCarDoorCloseClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_carCloseClipName = new char[strlen(clipName)+1]; + strcpy( m_carCloseClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetOverlayClipName +//============================================================================= +// Description: Store the name of the sound resource we want to play along +// with the specified engine sound. Is optional. +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetOverlayClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_overlayClipName = new char[strlen(clipName)+1]; + strcpy( m_overlayClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetRoadSkidClipName +//============================================================================= +// Description: Store the name of the sound resource we want to play when +// the car skids on the road. Is optional. +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetRoadSkidClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_roadSkidClipName = new char[strlen(clipName)+1]; + strcpy( m_roadSkidClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetDirtSkidClipName +//============================================================================= +// Description: Store the name of the sound resource we want to play when +// the car skids. Is optional. +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDirtSkidClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_dirtSkidClipName = new char[strlen(clipName)+1]; + strcpy( m_dirtSkidClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetBackupClipName +//============================================================================= +// Description: Store the name of the sound resource we want to play when +// the car goes into reverse. Is optional. +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetBackupClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_PERSISTENT ); + + m_backupClipName = new char[strlen(clipName)+1]; + strcpy( m_backupClipName, clipName ); + + HeapMgr()->PopHeap(GMA_PERSISTENT); +} + +//============================================================================= +// carSoundParameters::SetGearPitchRange +//============================================================================= +// Description: Set pitch range for engine clip for given gear +// +// Parameters: gear - gear to apply range to +// min - pitch at lowest gear RPMs +// max - pitch at highest gear RPMs +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetGearPitchRange( unsigned int gear, float min, float max ) +{ + rAssert( gear > 0 ); + rAssert( gear <= MAX_GEARS ); + rAssert( max >= min ); + + m_minPitch[gear-1] = min; + m_maxPitch[gear-1] = max; +} + +//============================================================================= +// carSoundParameters::SetNumberOfGears +//============================================================================= +// Description: Like the name says +// +// Parameters: gear - number of gears we want, must be <= MAX_GEARS +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetNumberOfGears( unsigned int gear ) +{ + rAssert( gear <= MAX_GEARS ); + + // + // If we set the shift point for the given gear to 1.0f, we're + // effectively cutting the number of gears + // + m_shiftPoints[gear] = 1.0f; +} + +//============================================================================= +// carSoundParameters::SetReversePitchCapKmh +//============================================================================= +// Description: Set maximum reverse speed at which we'll increase engine +// pitch +// +// Parameters: speed - max reverse speed +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetReversePitchCapKmh( float speed ) +{ + rAssert( speed > 0.0f ); + + m_maxReverseKmh = speed; +} + +//============================================================================= +// carSoundParameters::SetReversePitchRange +//============================================================================= +// Description: Set range of engine pitch for reverse gear +// +// Parameters: min - pitch at rest +// max - pitch at m_maxReverseKmh +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetReversePitchRange( float min, float max ) +{ + rAssert( min < max ); + + m_minReversePitch = min; + m_maxReversePitch = max; +} + +//============================================================================= +// carSoundParameters::SetGearShiftPitchDrop +//============================================================================= +// Description: Set amount of engine clip pitch drop we'll do on gear shifts +// +// Parameters: gear - gear that we want to set drop for +// drop - amount of pitch drop +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetGearShiftPitchDrop( unsigned int gear, float drop ) +{ + rAssert( gear > 0 ); + rAssert( gear <= MAX_GEARS ); + rAssert( drop >= 0.0f ); + + m_gearShiftPitchDrop[gear-1] = drop; +} + +//============================================================================= +// carSoundParameters::GetGearShiftPitchDrop +//============================================================================= +// Description: Get amount of engine clip pitch drop we'll do on gear shifts +// +// Parameters: gear - gear that we want drop for +// +// Return: value of pitch drop +// +//============================================================================= +float carSoundParameters::GetGearShiftPitchDrop( unsigned int gear ) +{ + rAssert( gear > 0 ); + rAssert( gear <= MAX_GEARS ); + + return( m_gearShiftPitchDrop[gear-1] ); +} + +//============================================================================= +// carSoundParameters::SetDamageStartPcnt +//============================================================================= +// Description: Set vehicle life percentage at which we'll start playing +// damage sounds +// +// Parameters: damagePercent - vehicle life percentage for damage sound +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDamageStartPcnt( float damagePercent ) +{ + rAssert( damagePercent >= 0.0f ); + rAssert( damagePercent <= 1.0f ); + + // Also need to adjust damage volume range when start pcnt changes + m_damageVolumeRange += damagePercent - m_damageStartPcnt; + + m_damageStartPcnt = damagePercent; +} + +//============================================================================= +// carSoundParameters::SetDamageMaxVolPcnt +//============================================================================= +// Description: Set vehicle life percentage at which the damage clip will +// be played at maximum volume (linear interpolation between +// m_damageStartPcnt and m_damageMaxVolPcnt). +// +// Parameters: percent - vehicle life percentage for maximum volume +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDamageMaxVolPcnt( float percent ) +{ + rAssert( percent >= 0.0f ); + rAssert( percent <= 1.0f ); + + // + // We don't actually store the percentage, since the trim setting code + // only cares about the min/max range; might as well precalculate it + m_damageVolumeRange = m_damageStartPcnt - percent; +} + +//============================================================================= +// carSoundParameters::SetDamageStartTrim +//============================================================================= +// Description: Set volume for damage clip when vehicle life percentage +// reaches damage start percentage +// +// Parameters: trim - initial volume +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDamageStartTrim( float trim ) +{ + rAssert( trim >= 0.0f ); + rAssert( trim <= 1.0f ); + + m_damageStartTrim = trim; +} + +//============================================================================= +// carSoundParameters::SetDamageMaxTrim +//============================================================================= +// Description: Set volume for damage clip when vehicle life percentage +// reaches damage max percentage +// +// Parameters: trim - initial volume +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetDamageMaxTrim( float trim ) +{ + rAssert( trim >= 0.0f ); + rAssert( trim <= 1.0f ); + + m_damageMaxTrim = trim; +} + +//============================================================================= +// carSoundParameters::SetIdleEnginePitch +//============================================================================= +// Description: Set pitch value for idle engine clip +// +// Parameters: pitch - obvious +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetIdleEnginePitch( float pitch ) +{ + m_idleEnginePitch = pitch; +} + +//============================================================================= +// carSoundParameters::SetInAirThrottlePitch +//============================================================================= +// Description: Set pitch that engine goes to when gas applied in air +// +// Parameters: pitch - engine pitch +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetInAirThrottlePitch( float pitch ) +{ + m_inAirThrottlePitch = pitch; +} + +//============================================================================= +// carSoundParameters::GetInAirThrottlePitch +//============================================================================= +// Description: Get pitch that engine goes to when gas applied in air +// +// Parameters: None +// +// Return: full-throttle pitch +// +//============================================================================= +float carSoundParameters::GetInAirThrottlePitch() +{ + // + // POST-BETA HACK!! + // + if( m_inAirIdlePitch == s_inAirIdleDefault ) + { + // + // Untuned, d'oh. Make something up. + // + m_inAirThrottlePitch = GetRevLimit(); + } + + return( m_inAirThrottlePitch ); +} + +//============================================================================= +// carSoundParameters::SetInAirIdlePitch +//============================================================================= +// Description: Set pitch that engine goes to when engine idling in air +// +// Parameters: pitch - engine pitch +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetInAirIdlePitch( float pitch ) +{ + m_inAirIdlePitch = pitch; +} + +//============================================================================= +// carSoundParameters::GetInAirIdlePitch +//============================================================================= +// Description: Get pitch that engine goes to when engine idling in air +// +// Parameters: None +// +// Return: idle pitch +// +//============================================================================= +float carSoundParameters::GetInAirIdlePitch() +{ + // + // POST-BETA HACK!! + // + if( m_inAirIdlePitch == s_inAirIdleDefault ) + { + // + // Untuned, d'oh. Make something up as percentage of rev limit + // + m_inAirIdlePitch = 0.5f * GetRevLimit(); + } + + return( m_inAirIdlePitch ); +} + +//============================================================================= +// carSoundParameters::SetInAirThrottleResponseTimeMsecs +//============================================================================= +// Description: Set amount of time to go from throttle pitch to idle pitch +// in midair and vice versa +// +// Parameters: msecs - time in milliseconds +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetInAirThrottleResponseTimeMsecs( unsigned int msecs ) +{ + m_inAirResponseMsecs = msecs; +} + +//============================================================================= +// carSoundParameters::SetBurnoutMinPitch +//============================================================================= +// Description: Set pitch for engine when burnout factor is at minimum +// value +// +// Parameters: pitch - pitch to apply to sound clip +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetBurnoutMinPitch( float pitch ) +{ + m_burnoutMinPitch = pitch; +} + +//============================================================================= +// carSoundParameters::SetBurnoutMaxPitch +//============================================================================= +// Description: Set pitch for engine when burnout factor is at maximum +// value +// +// Parameters: pitch - pitch to apply to sound clip +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetBurnoutMaxPitch( float pitch ) +{ + m_burnoutMaxPitch = pitch; +} + +//============================================================================= +// carSoundParameters::SetPowerslideMinPitch +//============================================================================= +// Description: Set pitch for engine when powerslide factor is at minimum +// value +// +// Parameters: pitch - pitch to apply to sound clip +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetPowerslideMinPitch( float pitch ) +{ + m_powerslideMinPitch = pitch; +} + +//============================================================================= +// carSoundParameters::GetPowerslideMinPitch +//============================================================================= +// Description: Get pitch for engine when powerslide factor is at minimum +// value +// +// Parameters: None +// +// Return: min pitch +// +//============================================================================= +float carSoundParameters::GetPowerslideMinPitch() +{ + if( m_powerslideMinPitch == s_powerslideMinDefault ) + { + m_powerslideMinPitch = 0.5f * GetRevLimit(); + } + + return( m_powerslideMinPitch ); +} + +//============================================================================= +// carSoundParameters::SetPowerslideMaxPitch +//============================================================================= +// Description: Set pitch for engine when powerslide factor is at maximum +// value +// +// Parameters: pitch - pitch to apply to sound clip +// +// Return: void +// +//============================================================================= +void carSoundParameters::SetPowerslideMaxPitch( float pitch ) +{ + m_powerslideMaxPitch = pitch; +} + +//============================================================================= +// carSoundParameters::GetPowerslideMaxPitch +//============================================================================= +// Description: Get pitch for engine when powerslide factor is at maximum +// value +// +// Parameters: None +// +// Return: max pitch +// +//============================================================================= +float carSoundParameters::GetPowerslideMaxPitch() +{ + if( m_powerslideMaxPitch == s_powerslideMaxDefault ) + { + m_powerslideMaxPitch = 0.9f * GetRevLimit(); + } + + return( m_powerslideMaxPitch ); +} + +//============================================================================= +// carSoundParameters::CalculateEnginePitch +//============================================================================= +// Description: Given some car data, figure out the pitch we should be playing +// the clip at +// +// Parameters: gear - gear we're currently in +// currentSpeed - how fast the car is travelling +// topSpeed - top end for this car +// +// Return: pitch as a value from 0.0f to 1.0f +// +//============================================================================= +float carSoundParameters::CalculateEnginePitch( int gear, float currentSpeed, float topSpeed ) +{ + float enginePitch; + float percentageOfGear; + float bottomEndSpeed; + + //rAssert( currentSpeed <= topSpeed ); + rAssert( ( gear > 0 ) || ( gear == REVERSE_GEAR ) ); + rAssert( gear <= static_cast<int>(MAX_GEARS) ); + + if( gear == REVERSE_GEAR ) + { + percentageOfGear = currentSpeed / m_maxReverseKmh; + if( percentageOfGear > 1.0f ) + { + percentageOfGear = 1.0f; + } + + enginePitch = m_minReversePitch + ( percentageOfGear * ( m_maxReversePitch - m_minReversePitch ) ); + } + else + { + bottomEndSpeed = m_shiftPoints[gear-1] * topSpeed; + + // + // m_shiftPoints[gear+1] is allowed because we set m_shiftPoints[MAX_GEARS] to 1.0f + // + percentageOfGear = ( ( currentSpeed - bottomEndSpeed ) + / ( ( m_shiftPoints[gear] * topSpeed ) - bottomEndSpeed ) ); + + // + // Based on our speed position within the range of speed used for this gear, apply that + // same percentage to the pitch range for our final pitch calculation + // + enginePitch = m_minPitch[gear-1] + ( ( m_maxPitch[gear-1] - m_minPitch[gear-1] ) * percentageOfGear ); + } + + return( enginePitch ); +} + +//============================================================================= +// carSoundParameters::CalculateCurrentGear +//============================================================================= +// Description: Given our speed, figure out what gear we're in +// +// Parameters: currentSpeed - how fast the car is travelling +// previousSpeed - how fast the car was going in the previous frame +// (to see if we're upshifting or downshifting) +// topSpeed - top end for this car +// previousGear - which gear we were in before (used to cut down +// on spurious gearshifts). If unknown, use -1. +// +// Return: 0 for idle, 1 to MAX_GEARS for forward gears +// +//============================================================================= +int carSoundParameters::CalculateCurrentGear( float currentSpeed, + float previousSpeed, + float topSpeed, + int previousGear ) +{ + unsigned int currentGear; + unsigned int uiPreviousGear; + + // + // Find our current gear + // + for( currentGear = 0; currentGear < MAX_GEARS; currentGear++ ) + { + if( currentSpeed <= ( topSpeed * m_shiftPoints[currentGear] ) ) + { + break; + } + } + + // + // Don't downshift if we're accelerating, and vice versa. If + // previousGear is -1, we'll accept the gearshift anyway + // + if( previousGear >= 0 ) + { + uiPreviousGear = static_cast<unsigned int>(previousGear); + + if( ( currentSpeed > previousSpeed ) + && ( currentGear < uiPreviousGear ) ) + { + currentGear = uiPreviousGear; + } + else if( ( currentSpeed <= previousSpeed ) + && ( currentGear > uiPreviousGear ) ) + { + currentGear = uiPreviousGear; + } + } + + return( static_cast<int>(currentGear) ); +} + +//============================================================================= +// carSoundParameters::GetRevLimit +//============================================================================= +// Description: Try to calculate a sensible maximum pitch +// +// Parameters: None +// +// Return: Rev limit as pitch value +// +//============================================================================= +float carSoundParameters::GetRevLimit() +{ + unsigned int i; + float revLimit = 0.0f; + + for( i = 0; i < MAX_GEARS; i++ ) + { + // + // Ignore the default values of 2.0f, probably too high + // + if( ( m_maxPitch[i] > revLimit ) + && ( m_maxPitch[i] < s_maxPitchDefault ) ) + { + revLimit = m_maxPitch[i]; + } + } + + return( revLimit ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + + +//****************************************************************************** +// Factory functions +//****************************************************************************** + +//============================================================================== +// CarSoundParameterObjCreate +//============================================================================== +// Description: Factory function for creating carSoundParameters objects. +// Called by RadScript. +// +// Parameters: ppParametersObj - Address of ptr to new object +// allocator - FTT pool to allocate object within +// +// Return: N/A. +// +//============================================================================== +void CarSoundParameterObjCreate +( + ICarSoundParameters** ppParametersObj, + radMemoryAllocator allocator +) +{ + rAssert( ppParametersObj != NULL ); + (*ppParametersObj) = new ( allocator ) carSoundParameters( ); + (*ppParametersObj)->AddRef( ); +} + diff --git a/game/code/sound/avatar/carsoundparameters.h b/game/code/sound/avatar/carsoundparameters.h new file mode 100644 index 0000000..d5c4ef1 --- /dev/null +++ b/game/code/sound/avatar/carsoundparameters.h @@ -0,0 +1,302 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: carsoundparameters.h +// +// Description: Declaration for RadScript-created class for tunable car sound +// parameters +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef CARSOUNDPARAMETERS_H +#define CARSOUNDPARAMETERS_H + +//======================================== +// Nested Includes +//======================================== + +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <sound/avatar/icarsoundparameters.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: carSoundParameters +// +//============================================================================= + +class carSoundParameters: public ICarSoundParameters, + public radLinkedClass< carSoundParameters >, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "carSoundParameters" ); + + static const int REVERSE_GEAR = -1; + + carSoundParameters(); + virtual ~carSoundParameters(); + + void SetWatcherName( const char* name ); + + void SetClipRPM( float rpm ) { m_clipRPM = rpm; } + float GetClipRPM() { return( m_clipRPM ); } + + void SetEngineClipName( const char* clipName ); + const char* GetEngineClipName() { return( m_engineClipName ); } + + void SetEngineIdleClipName( const char* clipName ); + const char* GetEngineIdleClipName() { return( m_idleClipName ); } + + void SetDamagedEngineClipName( const char* clipName ); + const char* GetDamagedEngineClipName() { return( m_damagedClipName ); } + + void SetHornClipName( const char* clipName ); + const char* GetHornClipName() { return( m_hornClipName ); } + + void SetCarDoorOpenClipName( const char* clipName ); + const char* GetCarDoorOpenClipName() { return( m_carOpenClipName ); } + + void SetCarDoorCloseClipName( const char* clipName ); + const char* GetCarDoorCloseClipName() { return( m_carCloseClipName ); } + + void SetOverlayClipName( const char* clipName ); + const char* GetOverlayClipName() { return( m_overlayClipName ); } + + void SetRoadSkidClipName( const char* clipName ); + const char* GetRoadSkidClipName() { return( m_roadSkidClipName ); } + + void SetDirtSkidClipName( const char* clipName ); + const char* GetDirtSkidClipName() { return( m_dirtSkidClipName ); } + + void SetBackupClipName( const char* clipName ); + const char* GetBackupClipName() { return( m_backupClipName ); } + + void SetShiftPoint( unsigned int gear, float percent ); + float GetShiftPoint( int gear ); + + void SetDownshiftDamperSize( float percent ); + float GetDownshiftDamperSize() { return( m_downshiftDamper ); } + + void SetGearPitchRange( unsigned int gear, float min, float max ); + + void SetNumberOfGears( unsigned int gear ); + + float GetMsecsPerOctaveCap() { return( m_msecsPerOctave ); } + + // + // Attack/delay/decay + // + void SetAttackTimeMsecs( float msecs ); + float GetAttackTimeMsecs() { return( m_attackTime ); } + + void SetDelayTimeMsecs( unsigned int msecs ); + unsigned int GetDelayTimeMsecs() { return( m_delayTime ); } + + void SetDecayTimeMsecs( float msecs ); + float GetDecayTimeMsecs() { return( m_decayTime ); } + + void SetDecayFinishTrim( float trim ); + float GetDecayFinishTrim() { return( m_decayFinishTrim ); } + + // + // Reverse + // + void SetReversePitchCapKmh( float speed ); + float GetReversePitchCapKmh() { return( m_maxReverseKmh ); } + + void SetReversePitchRange( float min, float max ); + void GetReversePitchRange( float& min, float& max ) + { min = m_minReversePitch; max = m_maxReversePitch; } + + // + // Gear shifts + // + void SetGearShiftPitchDrop( unsigned int gear, float drop ); + float GetGearShiftPitchDrop( unsigned int gear ); + + // + // Damage + // + void SetDamageStartPcnt( float damagePercent ); + float GetDamageStartPcnt() { return( m_damageStartPcnt ); } + + void SetDamageMaxVolPcnt( float percent ); + float GetDamageVolumeRange() { return( m_damageVolumeRange ); } + + void SetDamageStartTrim( float trim ); + float GetDamageStartTrim() { return( m_damageStartTrim ); } + void SetDamageMaxTrim( float trim ); + float GetDamageMaxTrim() { return( m_damageMaxTrim ); } + + // + // Idle + // + void SetIdleEnginePitch( float pitch ); + float GetIdleEnginePitch() { return( m_idleEnginePitch ); } + + // + // In-air sound characteristics + // + void SetInAirThrottlePitch( float pitch ); + float GetInAirThrottlePitch(); + + void SetInAirIdlePitch( float pitch ); + float GetInAirIdlePitch(); + + void SetInAirThrottleResponseTimeMsecs( unsigned int msecs ); + unsigned int GetInAirThrottleResponseTimeMsecs() { return( m_inAirResponseMsecs ); } + + // + // Burnout sound characteristics + // + void SetBurnoutMinPitch( float pitch ); + float GetBurnoutMinPitch() { return( m_burnoutMinPitch ); } + + void SetBurnoutMaxPitch( float pitch ); + float GetBurnoutMaxPitch() { return( m_burnoutMaxPitch ); } + + // + // Powerslide sound characteristics + // + void SetPowerslideMinPitch( float pitch ); + float GetPowerslideMinPitch(); + + void SetPowerslideMaxPitch( float pitch ); + float GetPowerslideMaxPitch(); + + // + // Given a vehicle's current speed and max speed, figure out the + // gear it's in + // + int CalculateCurrentGear( float currentSpeed, float previousSpeed, + float topSpeed, int previousGear ); + + // + // Given a vehicle's current gear, speed, and max speed, figure out the + // pitch for the engine clip + // + float CalculateEnginePitch( int gear, float currentSpeed, float topSpeed ); + + float GetRevLimit(); + + private: + //Prevent wasteful constructor creation. + carSoundParameters( const carSoundParameters& original ); + carSoundParameters& operator=( const carSoundParameters& rhs ); + + static const unsigned int MAX_GEARS = 6; + + float m_clipRPM; + + float m_minPitch[MAX_GEARS]; + float m_maxPitch[MAX_GEARS]; + + char* m_engineClipName; + char* m_idleClipName; + char* m_damagedClipName; + char* m_hornClipName; + char* m_carOpenClipName; + char* m_carCloseClipName; + char* m_overlayClipName; + char* m_roadSkidClipName; + char* m_dirtSkidClipName; + char* m_backupClipName; + + // + // Percentages of top speed at which we shift gears. + // E.g. m_shiftPoints[0] == 0.01f means that we shift from idle + // to first at 1% of top speed. m_shiftPoints[MAX_GEARS+1] is + // 1.0f to simplify math. + // + + // Used for shifting + float m_shiftPoints[MAX_GEARS+1]; + float m_downshiftDamper; + + // + // Attack/delay/decay + // + float m_attackTime; + unsigned int m_delayTime; + float m_decayTime; + + float m_decayFinishTrim; + + // + // Reverse + // + float m_maxReverseKmh; + float m_minReversePitch; + float m_maxReversePitch; + + // + // Gear shifts + // + float m_gearShiftPitchDrop[MAX_GEARS]; + unsigned int m_gearShiftTimeMsecs[MAX_GEARS]; + + // + // Damage + // + float m_damageStartPcnt; + float m_damageVolumeRange; + + float m_damageStartTrim; + float m_damageMaxTrim; + + // + // Idle + // + float m_idleEnginePitch; + + // + // Airborne + // + float m_inAirThrottlePitch; + float m_inAirIdlePitch; + unsigned int m_inAirResponseMsecs; + + // + // Burnout + // + float m_burnoutMinPitch; + float m_burnoutMaxPitch; + + // + // Powerslide + // + float m_powerslideMinPitch; + float m_powerslideMaxPitch; + + // + // Makes transitions a little less abrupt + // + float m_msecsPerOctave; +}; + +//============================================================================= +// Factory Functions +//============================================================================= + +// +// Create a CarSoundParameters object +// +void CarSoundParameterObjCreate +( + ICarSoundParameters** ppSoundResource, + radMemoryAllocator allocator +); + + + + +#endif // CARSOUNDPARAMETERS_H + diff --git a/game/code/sound/avatar/icarsoundparameters.h b/game/code/sound/avatar/icarsoundparameters.h new file mode 100644 index 0000000..6fb8f9a --- /dev/null +++ b/game/code/sound/avatar/icarsoundparameters.h @@ -0,0 +1,168 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: icarsoundparameters.h +// +// Description: Interface declaration for RadScript-created class for +// tunable car sound parameters +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef ICARSOUNDPARAMETERS_H +#define ICARSOUNDPARAMETERS_H + +//======================================== +// Nested Includes +//======================================== + +#include <radobject.hpp> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: ICarSoundParameters +// +//============================================================================= + +struct ICarSoundParameters : public IRefCount +{ + // + // Vehicle RPM at which the engine sound clip should be played unchanged + // + virtual void SetClipRPM( float rpm ) = 0; + + // + // Name of clip to use for engine sound + // + virtual void SetEngineClipName( const char* clipName ) = 0; + + // + // Name of clip to use for engine idle sound + // + virtual void SetEngineIdleClipName( const char* clipName ) = 0; + + // + // Name of clip to use for damaged engine sound + // + virtual void SetDamagedEngineClipName( const char* clipName ) = 0; + + // + // Horn sound + // + virtual void SetHornClipName( const char* clipName ) = 0; + + // + // Skid sounds + // + virtual void SetRoadSkidClipName( const char* clipName ) = 0; + virtual void SetDirtSkidClipName( const char* clipName ) = 0; + + // + // Backup sound + // + virtual void SetBackupClipName( const char* clipName ) = 0; + + // + // Car door entry/exit sounds + // + virtual void SetCarDoorOpenClipName( const char* clipName ) = 0; + virtual void SetCarDoorCloseClipName( const char* clipName ) = 0; + + // + // Percentages of top speed for shift points + // + virtual void SetShiftPoint( unsigned int gear, float percent ) = 0; + + // + // Percentage of top speed that we'll use as a range for dropping + // below the shift point to avoid downshifting + // + virtual void SetDownshiftDamperSize( float percent ) = 0; + + // + // Pitch range for a particular gear + // + virtual void SetGearPitchRange( unsigned int gear, float min, float max ) = 0; + + // + // Number of gears + // + virtual void SetNumberOfGears( unsigned int gear ) = 0; + + // + // Attack/delay/decay + // + virtual void SetAttackTimeMsecs( float msecs ) = 0; + virtual void SetDelayTimeMsecs( unsigned int msecs ) = 0; + virtual void SetDecayTimeMsecs( float msecs ) = 0; + + virtual void SetDecayFinishTrim( float trim ) = 0; + + // + // Top speed for which we'll increase pitch in reverse + // + virtual void SetReversePitchCapKmh( float speed ) = 0; + + // + // Reverse gear pitch range + // + virtual void SetReversePitchRange( float min, float max ) = 0; + + // + // Amount of pitch drop and time to apply in gear shifts + // + virtual void SetGearShiftPitchDrop( unsigned int gear, float drop ) = 0; + + // + // Percentage damage when we play damage sound + // + virtual void SetDamageStartPcnt( float damagePercent ) = 0; + virtual void SetDamageMaxVolPcnt( float percent ) = 0; + + // + // Volume control on damage sounds + // + virtual void SetDamageStartTrim( float trim ) = 0; + virtual void SetDamageMaxTrim( float trim ) = 0; + + // + // Pitch for idle engine clip (separate from sound resource in case + // we want to share resource between cars. Strictly a convenience, + // since multiple sound resources with the same sound file could be + // created instead). + // + virtual void SetIdleEnginePitch( float pitch ) = 0; + + // + // In-air sound characteristics + // + virtual void SetInAirThrottlePitch( float pitch ) = 0; + virtual void SetInAirIdlePitch( float pitch ) = 0; + virtual void SetInAirThrottleResponseTimeMsecs( unsigned int msecs ) = 0; + + // + // Burnout sound characteristics + // + virtual void SetBurnoutMinPitch( float pitch ) = 0; + virtual void SetBurnoutMaxPitch( float pitch ) = 0; + + // + // Powerslide sound characteristics + // + virtual void SetPowerslideMinPitch( float pitch ) = 0; + virtual void SetPowerslideMaxPitch( float pitch ) = 0; + + // + // Playing an overlay clip + // + virtual void SetOverlayClipName( const char* clipName ) = 0; +}; + + +#endif // ICARSOUNDPARAMETERS_H + diff --git a/game/code/sound/avatar/soundavatar.cpp b/game/code/sound/avatar/soundavatar.cpp new file mode 100644 index 0000000..b1e827f --- /dev/null +++ b/game/code/sound/avatar/soundavatar.cpp @@ -0,0 +1,245 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundavatar.cpp +// +// Description: Implements SoundAvatar, which maintains a reference +// to an avatar for which sounds are to be created, and directs +// the playing of sound as either vehicle- or character-based, +// whichever is appropriate at the time. +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/avatar/soundavatar.h> + +#include <sound/avatar/vehiclesoundplayer.h> +#include <worldsim/avatar.h> +#include <worldsim/character/character.h> +#include <events/eventmanager.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundAvatar::SoundAvatar +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundAvatar::SoundAvatar( Avatar* avatarObj ) : + m_avatar( avatarObj ), + m_isInCar( false ), + m_turboTimer( 0 ) +{ + rAssert( avatarObj != NULL ); + + // + // Register as an event listener + // + GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_MISSION_RESET ); + GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_SYNC_SOUND ); + GetEventManager()->AddListener( this, EVENT_CHARACTER_POS_RESET ); + GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED ); + GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_DESTROYED ); + GetEventManager()->AddListener( this, EVENT_TURBO_START ); + + syncCarSoundState(); +} + +//============================================================================== +// SoundAvatar::~SoundAvatar +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundAvatar::~SoundAvatar() +{ + if( m_isInCar ) + { + m_vehicleSoundPlayer.StopCarSounds(); + } + + // + // Deregister from EventManager + // + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// SoundAvatar::HandleEvent +//============================================================================= +// Description: Listen for events that tell us if we're swapping between +// walking and driving +// +// Parameters: id - indicates which event has occurred +// pEventData - ptr to Character class, must match to our avatar +// +// Return: void +// +//============================================================================= +void SoundAvatar::HandleEvent( EventEnum id, void* pEventData ) +{ + switch( id ) + { + case EVENT_GETINTOVEHICLE_END: + if( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() ) + { + m_isInCar = true; + + rAssert( m_avatar->GetVehicle() ); + m_vehicleSoundPlayer.StartCarSounds( m_avatar->GetVehicle() ); + } + break; + + case EVENT_GETOUTOFVEHICLE_END: + if( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() ) + { + m_isInCar = false; + + m_vehicleSoundPlayer.StopCarSounds(); + } + break; + + case EVENT_MISSION_RESET: + case EVENT_CHARACTER_POS_RESET: + case EVENT_VEHICLE_DESTROYED_SYNC_SOUND: + syncCarSoundState(); + break; + + case EVENT_CHASE_VEHICLE_SPAWNED: + m_vehicleSoundPlayer.AddAIVehicleProximityTest( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_CHASE_VEHICLE_DESTROYED: + m_vehicleSoundPlayer.DeleteAIVehicleProximityTest( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_TURBO_START: + // + // If event applies to this character, start a timer. When it expires, + // say something funny + // + if( ( m_turboTimer == 0 ) && + ( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() ) ) + { + m_turboTimer = 5000; + } + + default: + break; + } +} + +//============================================================================= +// SoundAvatar::UpdateOncePerFrame +//============================================================================= +// Description: Update function. Used for stuff that either doesn't need to +// be called often and/or uses expensive math. +// +// Parameters: elapsedTime - time since last frame in msecs +// +// Return: void +// +//============================================================================= +void SoundAvatar::UpdateOncePerFrame( unsigned int elapsedTime ) +{ + if( m_isInCar ) + { + m_vehicleSoundPlayer.UpdateOncePerFrame( elapsedTime ); + } + + if( m_turboTimer > 0 ) + { + if( !(m_avatar->GetCharacter()->IsTurbo()) ) + { + // + // Player no longer sprinting, stop the countdown + // + m_turboTimer = 0; + } + else if( elapsedTime >= m_turboTimer ) + { + // + // Timer expired. Time to make with the funny. + // + GetEventManager()->TriggerEvent( EVENT_CHARACTER_TIRED_NOW ); + + // + // Set up for another line, but if it's a repeat, let's make + // it take a little longer + //m_turboTimer = 10000; + + // + // Okay, the repeating line is unpopular. Just play it once + // and be done. + // + m_turboTimer = 0; + } + else + { + m_turboTimer -= elapsedTime; + } + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// SoundAvatar::syncCarSoundState +//============================================================================= +// Description: Determine whether the avatar is in the car, and start or +// stop engine sounds accordingly. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundAvatar::syncCarSoundState() +{ + m_isInCar = m_avatar->IsInCar(); + + if( m_isInCar ) + { + rAssert( m_avatar->GetVehicle() ); + + m_vehicleSoundPlayer.StartCarSounds( m_avatar->GetVehicle() ); + } + else + { + m_vehicleSoundPlayer.StopCarSounds(); + } +}
\ No newline at end of file diff --git a/game/code/sound/avatar/soundavatar.h b/game/code/sound/avatar/soundavatar.h new file mode 100644 index 0000000..3ea676a --- /dev/null +++ b/game/code/sound/avatar/soundavatar.h @@ -0,0 +1,78 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundavatar.h +// +// Description: Declaration of SoundAvatar class, which maintains a reference +// to an avatar for which sounds are to be created, and directs +// the playing of sound as either vehicle- or character-based, +// whichever is appropriate at the time. +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDAVATAR_H +#define SOUNDAVATAR_H + +//======================================== +// Nested Includes +//======================================== + +#include <sound/avatar/vehiclesoundplayer.h> + +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +class Avatar; + +//============================================================================= +// +// Synopsis: SoundAvatar +// +//============================================================================= + +class SoundAvatar : public EventListener +{ + public: + SoundAvatar( Avatar* avatarObj ); + virtual ~SoundAvatar(); + + void HandleEvent( EventEnum id, void* pEventData ); + + void UpdateOncePerFrame( unsigned int elapsedTime ); + + private: + //Prevent wasteful constructor creation. + SoundAvatar(); + SoundAvatar( const SoundAvatar& original ); + SoundAvatar& operator=( const SoundAvatar& rhs ); + + void syncCarSoundState(); + + // + // Avatar object that we're going to keep an eye on + // + Avatar* m_avatar; + + // + // Is the player in the car? + // + bool m_isInCar; + + // + // Object for playing vehicle sounds + VehicleSoundPlayer m_vehicleSoundPlayer; + + // + // Timer for character sprinting + // + unsigned int m_turboTimer; +}; + + +#endif // SOUNDAVATAR_H + diff --git a/game/code/sound/avatar/vehiclesounddebugpage.cpp b/game/code/sound/avatar/vehiclesounddebugpage.cpp new file mode 100644 index 0000000..20102b3 --- /dev/null +++ b/game/code/sound/avatar/vehiclesounddebugpage.cpp @@ -0,0 +1,138 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclesounddebugpage.cpp +// +// Description: Definition for VehicleSoundDebugPage class, which displays +// vehicle sound info through Watcher +// +// History: 11/22/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/avatar/vehiclesounddebugpage.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// VehicleSoundDebugPage::VehicleSoundDebugPage +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +VehicleSoundDebugPage::VehicleSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) : + SoundDebugPage( pageNum, master ), + m_shiftInProgress( false ), + m_currentGear( 0 ), + m_currentSpeed( 0.0f ), + m_shiftTime( 0 ), + m_downshiftSpeed( 0.0f ), + m_upshiftSpeed( 0.0f ), + m_currentPitch( 0.0f ), + m_isDamaged( false ) +{ +} + +//============================================================================== +// VehicleSoundDebugPage::~VehicleSoundDebugPage +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +VehicleSoundDebugPage::~VehicleSoundDebugPage() +{ +} + +//****************************************************************************** +// +// Protected Member Functions +// +//****************************************************************************** + +void VehicleSoundDebugPage::fillLineBuffer( int lineNum, char* buffer ) +{ + switch( lineNum ) + { + case 0: + sprintf( buffer, "Gear: %d Speed: %f", m_currentGear, m_currentSpeed ); + break; + + case 2: + sprintf( buffer, "Shift speeds: %f %f", m_downshiftSpeed, m_upshiftSpeed ); + break; + + case 4: + if( m_shiftInProgress ) + { + sprintf( buffer, "SHIFTING - Time in msecs: %d", m_shiftTime ); + } + else + { + buffer[0] = '\0'; + } + break; + + case 6: + sprintf( buffer, "Pitch: %f", m_currentPitch ); + break; + + case 8: + if( m_isDamaged ) + { + sprintf( buffer, "Life: %f Threshold: %f CAR DAMAGE SOUND", + m_vehicleLife, m_damageThreshold ); + } + else + { + sprintf( buffer, "Life: %f Threshold: %f", + m_vehicleLife, m_damageThreshold ); + } + break; + + default: + buffer[0] = '\0'; + break; + } +} + +//============================================================================= +// VehicleSoundDebugPage::getNumLines +//============================================================================= +// Description: Returns number of lines that we'll display on screen +// +// Parameters: None +// +// Return: Line count +// +//============================================================================= +int VehicleSoundDebugPage::getNumLines() +{ + return( 9 ); +} diff --git a/game/code/sound/avatar/vehiclesounddebugpage.h b/game/code/sound/avatar/vehiclesounddebugpage.h new file mode 100644 index 0000000..3259558 --- /dev/null +++ b/game/code/sound/avatar/vehiclesounddebugpage.h @@ -0,0 +1,75 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclesounddebugpage.h +// +// Description: Declaration for VehicleSoundDebugPage class, which displays +// vehicle sound info through Watcher +// +// History: 11/22/2002 + Created -- Darren +// +//============================================================================= + +#ifndef VEHICLESOUNDDEBUGPAGE_H +#define VEHICLESOUNDDEBUGPAGE_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/sounddebug/sounddebugpage.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: VehicleSoundDebugPage +// +//============================================================================= + +class VehicleSoundDebugPage : public SoundDebugPage +{ + public: + VehicleSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ); + virtual ~VehicleSoundDebugPage(); + + void SetShiftInProgress( bool inProgress ) { m_shiftInProgress = inProgress; } + void SetCurrentGear( int gear ) { m_currentGear = gear; } + void SetCurrentSpeed( float speed ) { m_currentSpeed = speed; } + void SetShiftTime( unsigned int time ) { m_shiftTime = time; } + void SetDownshiftSpeed( float speed ) { m_downshiftSpeed = speed; } + void SetUpshiftSpeed( float speed ) { m_upshiftSpeed = speed; } + void SetCurrentPitch( float pitch ) { m_currentPitch = pitch; } + void SetDamageEnabled( bool isDamaged ) { m_isDamaged = isDamaged; } + void SetVehicleLife( float life ) { m_vehicleLife = life; } + void SetDamageThreshold( float threshold ) { m_damageThreshold = threshold; } + + protected: + // + // Pure virtual functions from SoundDebugPage + // + void fillLineBuffer( int lineNum, char* buffer ); + int getNumLines(); + + private: + //Prevent wasteful constructor creation. + VehicleSoundDebugPage(); + VehicleSoundDebugPage( const VehicleSoundDebugPage& original ); + VehicleSoundDebugPage& operator=( const VehicleSoundDebugPage& rhs ); + + bool m_shiftInProgress; + int m_currentGear; + float m_currentSpeed; + unsigned int m_shiftTime; + float m_downshiftSpeed; + float m_upshiftSpeed; + float m_currentPitch; + bool m_isDamaged; + float m_vehicleLife; + float m_damageThreshold; +}; + + +#endif // VEHICLESOUNDDEBUGPAGE_H + diff --git a/game/code/sound/avatar/vehiclesoundplayer.cpp b/game/code/sound/avatar/vehiclesoundplayer.cpp new file mode 100644 index 0000000..257d342 --- /dev/null +++ b/game/code/sound/avatar/vehiclesoundplayer.cpp @@ -0,0 +1,2499 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclesoundplayer.cpp +// +// Description: Implement VehicleSoundPlayer +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <float.h> + +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/avatar/vehiclesoundplayer.h> + +#include <sound/avatar/carsoundparameters.h> +#include <sound/tuning/globalsettings.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundmanager.h> + +#include <worldsim/redbrick/vehicle.h> +#include <worldsim/vehiclecentral.h> +#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h> + +#include <input/button.h> +#include <events/eventmanager.h> +#include <mission/gameplaymanager.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// For tuning +// +static bool s_resetEngineOnHonk = false; +static bool s_resetEngineInitialized = false; + +static const char* s_skidResource = "skid"; +static const char* s_gearResource = "gearshift"; + +static const float GAME_POWERSLIDE_TRIM_MIN = 0.6f; +static const float GAME_POWERSLIDE_TRIM_MAX = 0.9f; + +static const float SUPERSPRINT_POWERSLIDE_TRIM_MIN = 0.4f; +static const float SUPERSPRINT_POWERSLIDE_TRIM_MAX = 0.7f; + +// +// MACROS FOR DEBUG INFO +// +#ifdef SOUND_DEBUG_INFO_ENABLED + +#define SET_SHIFT_IN_PROGRESS(inProgress) m_debugInfo->SetShiftInProgress(inProgress) +#define SET_CURRENT_GEAR(gear) m_debugInfo->SetCurrentGear(gear) +#define SET_CURRENT_SPEED(speed) m_debugInfo->SetCurrentSpeed(speed) +#define SET_SHIFT_TIME_REMAINING(time) m_debugInfo->SetShiftTime(time) +#define SET_DOWNSHIFT_SPEED(speed) m_debugInfo->SetDownshiftSpeed(speed) +#define SET_UPSHIFT_SPEED(speed) m_debugInfo->SetUpshiftSpeed(speed) +#define SET_CURRENT_PITCH(pitch) m_debugInfo->SetCurrentPitch(pitch) +#define SET_IS_DAMAGED(isDamaged) m_debugInfo->SetDamageEnabled(isDamaged) +#define SET_VEHICLE_LIFE(life) m_debugInfo->SetVehicleLife(life) +#define SET_DAMAGE_THRESHOLD(threshold) m_debugInfo->SetDamageThreshold(threshold) + +// +// This is annoying. Stinky bad design. +// +#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress) m_debugInfo.SetShiftInProgress(inProgress) +#define SET_LOCAL_CURRENT_GEAR(gear) m_debugInfo.SetCurrentGear(gear) +#define SET_LOCAL_CURRENT_SPEED(speed) m_debugInfo.SetCurrentSpeed(speed) +#define SET_LOCAL_SHIFT_TIME_REMAINING(time) m_debugInfo.SetShiftTime(time) +#define SET_LOCAL_DOWNSHIFT_SPEED(speed) m_debugInfo.SetDownshiftSpeed(speed) +#define SET_LOCAL_UPSHIFT_SPEED(speed) m_debugInfo.SetUpshiftSpeed(speed) +#define SET_LOCAL_CURRENT_PITCH(pitch) m_debugInfo.SetCurrentPitch(pitch) +#define SET_LOCAL_IS_DAMAGED(isDamaged) m_debugInfo.SetDamageEnabled(isDamaged) +#define SET_LOCAL_VEHICLE_LIFE(life) m_debugInfo.SetVehicleLife(life) +#define SET_LOCAL_DAMAGE_THRESHOLD(threshold) m_debugInfo.SetDamageThreshold(threshold) + +#else + +#define SET_SHIFT_IN_PROGRESS(inProgress) +#define SET_CURRENT_GEAR(gear) +#define SET_CURRENT_SPEED(speed) +#define SET_SHIFT_TIME_REMAINING(time) +#define SET_DOWNSHIFT_SPEED(speed) +#define SET_UPSHIFT_SPEED(speed) +#define SET_CURRENT_PITCH(pitch) +#define SET_IS_DAMAGED(isDamaged) +#define SET_VEHICLE_LIFE(life) +#define SET_DAMAGE_THRESHOLD(threshold) + +#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress) +#define SET_LOCAL_CURRENT_GEAR(gear) +#define SET_LOCAL_CURRENT_SPEED(speed) +#define SET_LOCAL_SHIFT_TIME_REMAINING(time) +#define SET_LOCAL_DOWNSHIFT_SPEED(speed) +#define SET_LOCAL_UPSHIFT_SPEED(speed) +#define SET_LOCAL_CURRENT_PITCH(pitch) +#define SET_LOCAL_IS_DAMAGED(isDamaged) +#define SET_LOCAL_VEHICLE_LIFE(life) +#define SET_LOCAL_DAMAGE_THRESHOLD(threshold) + +#endif + +enum GearShiftResult +{ + GEARSHIFT_NONE, + GEARSHIFT_DAMPED, + GEARSHIFT_UP, + GEARSHIFT_DOWN +}; + + +// +// Classes for engine states. Each one represents a state +// in which the engine will have different sound-speed characteristics +// (e.g. car in air, gearshift in progress). +// + +// +// Base class for engine states +// +class EngineState +{ + public: + EngineState(); + virtual ~EngineState(); + + void Initialize( Vehicle* vehicle, carSoundParameters* parameters, + SimpsonsSoundPlayer* player, radKey32 resourceKey, + VehicleSoundDebugPage* debugPtr ); + bool StartNewState() { return( m_nextState != ENGINE_STATE_INVALID ); } + EngineStateEnum GetNewState() { return( m_nextState ); } + void Reset() { m_nextState = ENGINE_STATE_INVALID; } + + virtual void SetDebugInfo(); + + virtual void Service( unsigned int elapsedTime ) = 0; + + virtual float GetCurrentPitch(); + + bool IsActive() { return( m_isActive ); } + void SetActive( bool isActive, float prevPitch = 0.0f ); + + void StartFade( float initialTrim, bool stopAfterFade = true ); + bool IsFading(); + void ServiceFade( unsigned int elapsedTime ); + + void SetTrim( float trim ); + + protected: + + virtual void startup( float prevPitch ) = 0; + bool isAtIdleSpeed(); + bool isSkidding(); + + // + // True if this state is running and producing sound + // + bool m_isActive; + + // + // When we want to kick off a new state, set this to something + // other than ENGINE_STATE_INVALID + // + EngineStateEnum m_nextState; + + // + // Stuff for getting vehicle and sound parameter info + // + Vehicle* m_vehicle; + carSoundParameters* m_parameters; + + // + // Sound player stuff + // + SimpsonsSoundPlayer* m_player; + radKey32 m_resourceKey; + + // + // Rev limiter, calculated from pitch ranges + // + float m_revLimit; + + // + // Debug stuff + // +#ifdef SOUND_DEBUG_INFO_ENABLED + VehicleSoundDebugPage* m_debugInfo; +#endif + + private: + //Prevent wasteful constructor creation. + EngineState( const EngineState& original ); + EngineState& operator=( const EngineState& rhs ); + + float m_fadeTrim; + bool m_stopAfterFade; +}; + +// +// State for normal, on-ground, no-shift engine sound +// +class NormalEngineState : public EngineState +{ + public: + NormalEngineState(); + virtual ~NormalEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + float GetCurrentPitch(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + NormalEngineState( const NormalEngineState& original ); + NormalEngineState& operator=( const NormalEngineState& rhs ); + + GearShiftResult updateCurrentGearFromVehicle( bool dampenShifts = false ); + + int m_currentGear; + float m_currentSpeed; + float m_currentPitch; + bool m_isAttacking; + float m_currentTrim; + unsigned int m_interpolateTime; +}; + +// +// State for upshifts (e.g. 2nd to 3rd gear) +// +class UpshiftEngineState : public EngineState +{ + public: + UpshiftEngineState(); + virtual ~UpshiftEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + UpshiftEngineState( const UpshiftEngineState& original ); + UpshiftEngineState& operator=( const UpshiftEngineState& rhs ); + + float m_startPitch; + float m_pitchDrop; + float m_shiftTimeMsecs; + unsigned int m_remainingTime; +}; + +// +// State for downshifts (e.g. 3rd to 2nd gear) +// +class DownshiftEngineState : public EngineState +{ + public: + DownshiftEngineState(); + virtual ~DownshiftEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + float GetCurrentPitch(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + DownshiftEngineState( const DownshiftEngineState& original ); + DownshiftEngineState& operator=( const DownshiftEngineState& rhs ); + + float m_startSpeed; + float m_startPitch; + float m_lastSpeed; + float m_currentPitch; +}; + +// +// State for car in mid-air +// +class InAirEngineState : public EngineState +{ + public: + InAirEngineState(); + virtual ~InAirEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + InAirEngineState( const InAirEngineState& original ); + InAirEngineState& operator=( const InAirEngineState& rhs ); + + float m_currentPitch; +}; + +// +// State for car in reverse +// +class ReverseEngineState : public EngineState +{ + public: + ReverseEngineState( SimpsonsSoundPlayer* beepPlayer ); + virtual ~ReverseEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + ReverseEngineState( const ReverseEngineState& original ); + ReverseEngineState& operator=( const ReverseEngineState& rhs ); + + SimpsonsSoundPlayer* m_beepPlayer; +}; + +// +// State for car in idle +// +class IdleEngineState : public EngineState +{ + public: + IdleEngineState(); + virtual ~IdleEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + float GetCurrentPitch(); + + protected: + void startup( float prevPitch ); + private: + //Prevent wasteful constructor creation. + IdleEngineState( const IdleEngineState& original ); + IdleEngineState& operator=( const IdleEngineState& rhs ); + + float m_currentPitch; +}; + +// +// State for car when skidding +// +class SkidEngineState : public EngineState +{ + public: + SkidEngineState(); + virtual ~SkidEngineState(); + + void Service( unsigned int elapsedTime ); + + void SetDebugInfo(); + + float GetCurrentPitch(); + + protected: + void startup( float prevPitch ); + + private: + //Prevent wasteful constructor creation. + SkidEngineState( const SkidEngineState& original ); + SkidEngineState& operator=( const SkidEngineState& rhs ); + + float m_currentPitch; +}; + +//****************************************************************************** +// +// EngineState +// +//****************************************************************************** + +EngineState::EngineState() : + m_isActive( false ), + m_nextState( ENGINE_STATE_INVALID ), + m_vehicle( NULL ), + m_parameters( NULL ), + m_player( NULL ), + m_resourceKey( 0 ), + m_fadeTrim( 0.0f ), + m_stopAfterFade( true ) +{ +} + +EngineState::~EngineState() +{ +} + +void EngineState::Initialize( Vehicle* vehicle, carSoundParameters* parameters, + SimpsonsSoundPlayer* player, radKey32 resourceKey, + VehicleSoundDebugPage* debugPtr ) +{ + rAssert( vehicle != NULL ); + rAssert( parameters != NULL ); + rAssert( player != NULL ); + + m_vehicle = vehicle; + m_parameters = parameters; + m_player = player; + m_resourceKey = resourceKey; + + // + // Figure out a sensible rev limit + // + m_revLimit = parameters->GetRevLimit(); + +#ifdef SOUND_DEBUG_INFO_ENABLED + m_debugInfo = debugPtr; +#endif +} + +void EngineState::SetActive( bool isActive, float prevPitch ) +{ + bool wasActive = m_isActive; + + m_isActive = isActive; + if( m_isActive && !wasActive ) + { + startup( prevPitch ); + } +} + +void EngineState::SetDebugInfo() +{ + // Do nothing by default +} + +bool EngineState::isAtIdleSpeed() +{ + return( m_vehicle->GetSpeedKmh() + <= ( m_parameters->GetShiftPoint( 1 ) + * m_vehicle->mDesignerParams.mDpTopSpeedKmh ) ); +} + +bool EngineState::isSkidding() +{ + return( m_vehicle->GetSkidLevel() > 0.0f ); +} + +float EngineState::GetCurrentPitch() +{ + // + // Not needed by all states, so return zero by default. + // + return( 0.0f ); +} + +bool EngineState::IsFading() +{ + return( m_fadeTrim > 0.0f ); +} + +void EngineState::StartFade( float initialTrim, bool stopAfterFade ) +{ + m_fadeTrim = initialTrim; + m_stopAfterFade = stopAfterFade; + + // + // Check whether we're already faded + // + if( ( m_fadeTrim <= 0.0f ) + && stopAfterFade ) + { + m_player->Stop(); + } + else + { + // + // Set the trim, in case the parent hasn't done it already + // + m_player->SetTrim( initialTrim ); + } +} + +void EngineState::ServiceFade( unsigned int elapsedTime ) +{ + // + // Fade from 1.0f to 0.0f in 1/10th of a second, time picked arbitrarily + // + m_fadeTrim -= static_cast<float>( elapsedTime ) / 100.0f; + if( m_fadeTrim < 0.0f ) + { + m_fadeTrim = 0.0f; + } + + m_player->SetTrim( m_fadeTrim ); + + if( ( m_fadeTrim == 0.0f ) + && m_stopAfterFade ) + { + m_player->Stop(); + } +} + +void EngineState::SetTrim( float trim ) +{ + // + // Cancel fades in effect + // + m_fadeTrim = 0.0f; + + m_player->SetTrim( trim ); +} + +//****************************************************************************** +// +// NormalEngineState +// +//****************************************************************************** + +NormalEngineState::NormalEngineState() : + m_currentGear( 0 ), + m_currentSpeed( 0.0f ), + m_isAttacking( false ), + m_currentTrim( 1.0f ), + m_interpolateTime( 0 ) +{ +} + +NormalEngineState::~NormalEngineState() +{ +} + +void NormalEngineState::Service( unsigned int elapsedTime ) +{ + GearShiftResult startedShift; + float newPitch; + float trimDiff; + float pitchDiff; + float maxDiff; + float speedPcnt; + float shiftPoint; + float shiftSpeed; + float topSpeed; + + if( ( m_interpolateTime > 0 ) && !m_isAttacking ) + { + // + // Delaying + // + if( elapsedTime > m_interpolateTime ) + { + // + // Delay done, start attack + // + m_interpolateTime = static_cast<unsigned int>(m_parameters->GetAttackTimeMsecs()); + m_isAttacking = true; + } + else + { + // + // Delay not done + // + m_interpolateTime -= elapsedTime; + + if( m_interpolateTime <= m_parameters->GetDelayTimeMsecs() ) + { + m_currentTrim = m_parameters->GetDecayFinishTrim(); + m_player->SetPitch( 1.0f ); + } + + // + // Since we're delaying, don't do anything else + // + return; + } + } + else if( m_interpolateTime > 0 ) + { + // + // Attacking + // + if( elapsedTime > m_interpolateTime ) + { + m_currentTrim = 1.0f; + m_interpolateTime = 0; + m_isAttacking = false; + } + else + { + m_interpolateTime -= elapsedTime; + trimDiff = static_cast<float>(elapsedTime) / m_parameters->GetAttackTimeMsecs(); + m_currentTrim += trimDiff * ( 1.0f - m_parameters->GetDecayFinishTrim() ); // Increase at half-speed because we start at 0.5 above + if( m_currentTrim > 1.0f ) + { + // + // Interpolation error + // + m_currentTrim = 1.0f; + } + } + } + else + { + // + // Regular engine playback, make sure the attack is off and the trim is maxed. + // + m_isAttacking = false; + + if( m_currentTrim != 1.0f ) + { + m_currentTrim = 1.0f; + } + } + + m_currentSpeed = m_vehicle->GetSpeedKmh(); + + if( m_vehicle->IsMovingBackward() ) + { + m_nextState = ENGINE_STATE_REVERSE; + } + else if( m_vehicle->IsAirborn() ) + { + m_nextState = ENGINE_STATE_IN_AIR; + } + else if( isSkidding() ) + { + m_nextState = ENGINE_STATE_SKIDDING; + } + else + { + startedShift = updateCurrentGearFromVehicle(); + if( ( startedShift == GEARSHIFT_UP ) + || ( startedShift == GEARSHIFT_DOWN ) ) + { + if( startedShift == GEARSHIFT_UP ) + { + m_nextState = ENGINE_STATE_UPSHIFTING; + // + // Don't stop the player, start delay + // + m_interpolateTime = m_parameters->GetDelayTimeMsecs() + + static_cast<unsigned int>(m_parameters->GetDecayTimeMsecs()); + if( m_interpolateTime > 0 ) + { + m_currentTrim = 0.0f; + } + } + else if( m_currentGear > 0 ) + { + m_nextState = ENGINE_STATE_DOWNSHIFTING; + } + else + { + m_nextState = ENGINE_STATE_IDLING; + } + } + else + { + if( startedShift == GEARSHIFT_DAMPED ) + { + // + // If we're going slower than the speed appropriate for the gear, + // don't let the pitch drop too low + // + shiftPoint = m_parameters->GetShiftPoint( m_currentGear ); + topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh; + shiftSpeed = shiftPoint * topSpeed; + speedPcnt = m_currentSpeed / shiftSpeed; + newPitch = m_parameters->CalculateEnginePitch( m_currentGear, shiftSpeed, topSpeed ); + newPitch *= speedPcnt; + } + else + { + // + // No shift, just set the pitch to value appropriate to the vehicle speed + // + newPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed, + m_vehicle->mDesignerParams.mDpTopSpeedKmh ); + } + + pitchDiff = newPitch - m_currentPitch; + maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap(); + + // + // Ignore the calculated pitch if it means we're going to jump around + // too suddenly + // + if( pitchDiff >= 0.0f ) + { + // Pitch rising + m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff; + } + else + { + // Pitch dropping + m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff; + } + + if( m_currentPitch > m_revLimit ) + { + m_currentPitch = m_revLimit; + } + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); + + if( !m_isAttacking ) + { + m_currentTrim = 1.0f; + } + } + } + + if( m_nextState != ENGINE_STATE_INVALID ) + { + if( ( m_nextState == ENGINE_STATE_DOWNSHIFTING ) + || ( m_nextState == ENGINE_STATE_UPSHIFTING ) ) + { + StartFade( m_currentTrim, false ); + } + else + { + SetTrim( m_currentTrim ); + } + + if( m_nextState != ENGINE_STATE_UPSHIFTING ) + { + SetActive( false ); + } + } + else + { + SetTrim( m_currentTrim ); + } + + rAssertMsg( m_currentPitch >= 0.0f, "If you're running a debugger, tell Esan about this" ); +} + +void NormalEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_CURRENT_GEAR( m_currentGear ); + SET_CURRENT_SPEED( m_currentSpeed ); + } +} + +float NormalEngineState::GetCurrentPitch() +{ + return( m_currentPitch ); +} + +void NormalEngineState::startup( float prevPitch ) +{ + m_currentSpeed = m_vehicle->GetSpeedKmh(); + m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed, + m_vehicle->mDesignerParams.mDpTopSpeedKmh, + -1 ); + + // + // Start the sound + // + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + + m_currentTrim = 1.0f; + m_interpolateTime = 0; + if( prevPitch > 0.0f ) + { + m_currentPitch = prevPitch; + } + else + { + m_currentPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed, + m_vehicle->mDesignerParams.mDpTopSpeedKmh ); + } + SetTrim( m_currentTrim ); + + if( m_currentPitch > m_revLimit ) + { + m_currentPitch = m_revLimit; + } + m_player->SetPitch( m_currentPitch ); +} + +//============================================================================= +// NormalEngineState::updateCurrentGearFromVehicle +//============================================================================= +// Description: Update m_currentGear from the current state of m_vehicle +// +// Parameters: dampenShifts - indicates whether we want to eliminate unnecessary +// gear shifts. Not needed when we get in the car. +// +// Return: enumeration indicating if we shifted and if so, which direction +// +//============================================================================= +GearShiftResult NormalEngineState::updateCurrentGearFromVehicle( bool dampenShifts /* = false */ ) +{ + int currentGear; + GearShiftResult shiftResult; + float damperSize; + float upperShiftSpeed; + int oldGear = m_currentGear; + float topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh; + + if( dampenShifts ) + { + currentGear = m_currentGear; + } + else + { + currentGear = -1; + } + + m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed, + topSpeed, -1 ); + + if( m_currentGear == oldGear ) + { + shiftResult = GEARSHIFT_NONE; + } + else + { + if( m_currentGear > oldGear ) + { + if( m_vehicle->IsSafeToUpShift() ) + { + shiftResult = GEARSHIFT_UP; + } + else + { + // + // Don't upshift when we're climbing hills, because the engine + // tends to stay bogged down at low revs + // + m_currentGear = oldGear; + shiftResult = GEARSHIFT_NONE; + } + } + else if( m_currentGear == 0 ) + { + // + // Don't damp going into idle + // + shiftResult = GEARSHIFT_DOWN; + } + else + { + // + // Apply some gearshift damping + // + damperSize = m_parameters->GetDownshiftDamperSize() * topSpeed; + upperShiftSpeed = m_parameters->GetShiftPoint( m_currentGear + 1 ) * topSpeed; + if( ( upperShiftSpeed - m_currentSpeed ) <= damperSize ) + { + shiftResult = GEARSHIFT_DAMPED; + + // + // Wah. I think there's a problem where having the top gear too narrow can + // cause us to bump up into a non-existent gear. Don't do that. + // + if( upperShiftSpeed < topSpeed ) + { + ++m_currentGear; + } + //else + //{ + // rDebugString("Gotcha!"); + //} + } + else + { + shiftResult = GEARSHIFT_DOWN; + } + } + } + + return( shiftResult ); +} + +//****************************************************************************** +// +// UpshiftEngineState +// +//****************************************************************************** + +UpshiftEngineState::UpshiftEngineState() +{ +} + +UpshiftEngineState::~UpshiftEngineState() +{ +} + +void UpshiftEngineState::Service( unsigned int elapsedTime ) +{ + float newPitch; + float timePercent; + float newTrim; + float targetTrim; + + if( elapsedTime > m_remainingTime ) + { + // + // Upshift is done, return to normal engine sound + // + + // + // No need for state change, normal doesn't shut off on + // upshifts anymore + // + //m_nextState = ENGINE_STATE_NORMAL; + + SetTrim( 1.0f ); + m_player->Stop(); + SetActive( false ); + } + else + { + // + // Calculate the amount we need to drop the pitch in this frame + // + m_remainingTime -= elapsedTime; + timePercent = static_cast<float>(m_remainingTime) / static_cast<float>(m_shiftTimeMsecs); + newPitch = m_startPitch - ( ( 1.0f - timePercent ) * m_pitchDrop ); + targetTrim = m_parameters->GetDecayFinishTrim(); + newTrim = targetTrim + ( ( 1.0f - targetTrim ) * timePercent ); + + m_player->SetPitch( newPitch ); + SET_CURRENT_PITCH( newPitch ); + SetTrim( newTrim ); + } +} + +void UpshiftEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_SHIFT_IN_PROGRESS( true ); + SET_SHIFT_TIME_REMAINING( m_remainingTime ); + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + } +} + +void UpshiftEngineState::startup( float prevPitch ) +{ + int currentGear; + float currentSpeed; + + // + // Initialize the current pitch, the amount we're going to drop it by, + // and the length of time we'll take to drop it + // + currentSpeed = m_vehicle->GetSpeedKmh(); + currentGear = m_parameters->CalculateCurrentGear( currentSpeed, currentSpeed, + m_vehicle->mDesignerParams.mDpTopSpeedKmh, + -1 ); + + // + // Don't forget to subtract one from the gear because the parameter calculation + // will already have signalled the upshift, and we still want the old gear's pitch + // + m_startPitch = m_parameters->CalculateEnginePitch( currentGear - 1, currentSpeed, + m_vehicle->mDesignerParams.mDpTopSpeedKmh ); + m_pitchDrop = m_parameters->GetGearShiftPitchDrop( currentGear ); + m_shiftTimeMsecs = m_parameters->GetDecayTimeMsecs(); + m_remainingTime = static_cast<unsigned int>(m_shiftTimeMsecs); + + // + // Start the player + // + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + m_player->SetPitch( m_startPitch ); + SetTrim( 1.0f ); +} + +//****************************************************************************** +// +// DownshiftEngineState +// +//****************************************************************************** + +DownshiftEngineState::DownshiftEngineState() +{ +} + +DownshiftEngineState::~DownshiftEngineState() +{ +} + +void DownshiftEngineState::Service( unsigned int elapsedTime ) +{ + float speedPercent; + float idlePitch; + float currentSpeed = m_vehicle->GetSpeedKmh(); + + if( isAtIdleSpeed() ) + { + // + // Switch to idle sound + // + m_nextState = ENGINE_STATE_IDLING; + + m_player->Stop(); + SetActive( false ); + } + else if( currentSpeed > m_lastSpeed ) + { + // + // We've started accelerating again, switch to normal engine sound + // + m_nextState = ENGINE_STATE_NORMAL; + + StartFade( 1.0f ); + SetActive( false ); + } + else + { + // + // Calculate new engine pitch + // + speedPercent = m_vehicle->GetSpeedKmh() / m_startSpeed; + // For now, use idle engine pitch for bottom of pitch range + idlePitch = m_parameters->GetIdleEnginePitch(); + m_currentPitch = idlePitch + ( speedPercent * ( m_startPitch - idlePitch ) ); + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); + + m_lastSpeed = currentSpeed; + } +} + +void DownshiftEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_SHIFT_IN_PROGRESS( true ); + SET_SHIFT_TIME_REMAINING( 0 ); + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + } +} + +float DownshiftEngineState::GetCurrentPitch() +{ + return( m_currentPitch ); +} + +void DownshiftEngineState::startup( float prevPitch ) +{ + //int currentGear; + + m_startSpeed = m_vehicle->GetSpeedKmh(); + m_lastSpeed = m_startSpeed; + + // + // Find the speed that we need to interpolate over + // + + // "+ 1" is because we're already in the new gear, and we want to interpolate + // from the last one + //currentGear = m_parameters->CalculateCurrentGear( m_startSpeed, m_startSpeed, + // m_vehicle->mDesignerParams.mDpTopSpeedKmh, + // -1 ) + 1; + + //m_startPitch = m_parameters->CalculateEnginePitch( currentGear, m_startSpeed, + // m_vehicle->mDesignerParams.mDpTopSpeedKmh ); + m_startPitch = prevPitch; + m_currentPitch = m_startPitch; + + // + // Start the sound + // + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + m_player->SetPitch( m_startPitch ); +} + +//****************************************************************************** +// +// InAirEngineState +// +//****************************************************************************** + +InAirEngineState::InAirEngineState() +{ +} + +InAirEngineState::~InAirEngineState() +{ +} + +void InAirEngineState::Service( unsigned int elapsedTime ) +{ + float maxPitchChange; + float idlePitch; + float throttlePitch; + unsigned int responseTime; + float targetPitch; + float newPitch; + + if( !(m_vehicle->IsAirborn()) ) + { + if( isAtIdleSpeed() ) + { + m_nextState = ENGINE_STATE_IDLING; + } + else if( isSkidding() ) + { + m_nextState = ENGINE_STATE_SKIDDING; + } + else + { + m_nextState = ENGINE_STATE_NORMAL; + } + + m_player->Stop(); + SetActive( false ); + } + else + { + // + // Calculate the maximum pitch change given the car's responsiveness + idlePitch = m_parameters->GetInAirIdlePitch(); + throttlePitch = m_parameters->GetInAirThrottlePitch(); + responseTime = m_parameters->GetInAirThrottleResponseTimeMsecs(); + maxPitchChange = ( throttlePitch - idlePitch ) + * ( static_cast<float>(elapsedTime) / static_cast<float>(responseTime) ); + + // + // Get target pitch + // + targetPitch = idlePitch + ( m_vehicle->mGas * ( throttlePitch - idlePitch ) ); + if( targetPitch > m_currentPitch ) + { + if( m_currentPitch + maxPitchChange > targetPitch ) + { + newPitch = targetPitch; + } + else + { + newPitch = m_currentPitch + maxPitchChange; + } + } + else + { + if( m_currentPitch - maxPitchChange < targetPitch ) + { + newPitch = targetPitch; + } + else + { + newPitch = m_currentPitch - maxPitchChange; + } + } + + m_player->SetPitch( newPitch ); + m_currentPitch = newPitch; + } +} + +void InAirEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_CURRENT_PITCH( m_currentPitch ); + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + } +} + +void InAirEngineState::startup( float prevPitch ) +{ + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + + m_currentPitch = prevPitch; + + m_player->SetPitch( m_currentPitch ); +} + +//****************************************************************************** +// +// ReverseEngineState +// +//****************************************************************************** + +ReverseEngineState::ReverseEngineState( SimpsonsSoundPlayer* beepPlayer ) : + m_beepPlayer( beepPlayer ) +{ +} + +ReverseEngineState::~ReverseEngineState() +{ +} + +void ReverseEngineState::Service( unsigned int elapsedTime ) +{ + float speed; + float maxReverseSpeed; + float minPitch; + float maxPitch; + float newPitch; + + speed = m_vehicle->GetSpeedKmh(); + + if( !(m_vehicle->IsMovingBackward()) ) + { + if( m_vehicle->IsAirborn() ) + { + m_nextState = ENGINE_STATE_IN_AIR; + } + else if( isAtIdleSpeed() ) + { + m_nextState = ENGINE_STATE_IDLING; + } + else if( isSkidding() ) + { + m_nextState = ENGINE_STATE_SKIDDING; + } + else + { + m_nextState = ENGINE_STATE_NORMAL; + } + + m_beepPlayer->Stop(); + SetActive( false ); + } + else + { + // + // Set pitch based on speed + // + maxReverseSpeed = m_parameters->GetReversePitchCapKmh(); + m_parameters->GetReversePitchRange( minPitch, maxPitch ); + if( speed > maxReverseSpeed ) + { + newPitch = maxPitch; + } + else + { + newPitch = minPitch + ( ( speed / maxReverseSpeed ) * ( maxPitch - minPitch ) ); + } + + m_player->SetPitch( newPitch ); + SET_CURRENT_PITCH( newPitch ); + } +} + +void ReverseEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + SET_CURRENT_GEAR( -1 ); + } +} + +void ReverseEngineState::startup( float prevPitch ) +{ + const char* backupClipName; + + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + + // + // Call Service() to set the pitch + // + Service( 0 ); + + // + // If we have a backup beep sound, play it + // + if( ( backupClipName = m_parameters->GetBackupClipName() ) != NULL ) + { + m_beepPlayer->PlaySound( backupClipName ); + } +} + +//****************************************************************************** +// +// IdleEngineState +// +//****************************************************************************** + +IdleEngineState::IdleEngineState() : + m_currentPitch( 0.0f ) +{ +} + +IdleEngineState::~IdleEngineState() +{ +} + +void IdleEngineState::Service( unsigned int elapsedTime ) +{ + float idlePitch; + float pitchDiff; + float maxDiff; + + // + // Check for car motion, indicating we're leaving the idle state + // + if( m_vehicle->IsMovingBackward() ) + { + m_nextState = ENGINE_STATE_REVERSE; + } + else if( isSkidding() ) + { + m_nextState = ENGINE_STATE_SKIDDING; + } + else if( !isAtIdleSpeed() ) + { + m_nextState = ENGINE_STATE_NORMAL; + } + + if( m_nextState != ENGINE_STATE_INVALID ) + { + // + // Deactivate self + // + m_player->Stop(); + SetActive( false ); + } + else + { + // + // Keep playing idle. Interpolate to it if we're not playing + // the idle pitch already + // + idlePitch = m_parameters->GetIdleEnginePitch(); + if( m_currentPitch != idlePitch ) + { + pitchDiff = idlePitch - m_currentPitch; + maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap(); + + // + // Ignore the calculated pitch if it means we're going to jump around + // too suddenly + // + if( pitchDiff >= 0.0f ) + { + // Pitch rising + m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff; + } + else + { + // Pitch dropping + m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff; + } + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); + } + } +} + +void IdleEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + SET_CURRENT_GEAR( 0 ); + } +} + +float IdleEngineState::GetCurrentPitch() +{ + return( m_currentPitch ); +} + +void IdleEngineState::startup( float prevPitch ) +{ + rAssert( m_parameters != NULL ); + + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + + m_currentPitch = m_parameters->GetIdleEnginePitch(); + if( prevPitch != m_currentPitch ) + { + m_currentPitch = prevPitch; + } + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); +} + +//****************************************************************************** +// +// SkidEngineState +// +//****************************************************************************** + +SkidEngineState::SkidEngineState() : + m_currentPitch( 0.0f ) +{ +} + +SkidEngineState::~SkidEngineState() +{ +} + +void SkidEngineState::Service( unsigned int elapsedTime ) +{ + float burnoutLevel; + float desiredPitch; + float minBurnoutPitch; + float minPowerslidePitch; + float pitchDiff; + float maxDiff; + + // + // Get a desired pitch based on the burnout level that the + // vehicle object gives us + // + if( isSkidding() ) + { + // + // Sometimes we're skidding, sometimes we're burning out. Try to figure out + // which value makes more sense. + // + if( m_vehicle->mBurnoutLevel > 0.0f ) + { + burnoutLevel = m_vehicle->mBurnoutLevel; + + minBurnoutPitch = m_parameters->GetBurnoutMinPitch(); + desiredPitch = minBurnoutPitch + + ( ( m_parameters->GetBurnoutMaxPitch() - minBurnoutPitch ) + * burnoutLevel ); + } + else + { + //burnoutLevel = m_vehicle->GetSkidLevel(); + + // + // The skid level appears to be a constant value of 0.5. Try using the + // vehicle gas value instead, since the tires would be spinning kinda + // freely in the real world anyway. + // + // HACK: Since sliding skids seem to be quite different from burnouts, + // bump the burnout level beyond 1.0f at will to make this sound right. + // This should be a separate set of tuning parameters next time. + // + burnoutLevel = m_vehicle->mGas; + + minPowerslidePitch = m_parameters->GetPowerslideMinPitch(); + desiredPitch = minPowerslidePitch + + ( ( m_parameters->GetPowerslideMaxPitch() - minPowerslidePitch ) + * burnoutLevel ); + } + + // + // Ease toward desired pitch + // + pitchDiff = desiredPitch - m_currentPitch; + maxDiff = static_cast<float>(elapsedTime) / ( m_parameters->GetMsecsPerOctaveCap() * 4 ); + + // + // Ignore the calculated pitch if it means we're going to jump around + // too suddenly + // + if( pitchDiff >= 0.0f ) + { + // Pitch rising + m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff; + } + else + { + // Pitch dropping + m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff; + } + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); + } + else + { + if( isAtIdleSpeed() ) + { + m_nextState = ENGINE_STATE_IDLING; + } + else if( m_vehicle->IsMovingBackward() ) + { + m_nextState = ENGINE_STATE_REVERSE; + } + else + { + m_nextState = ENGINE_STATE_NORMAL; + } + + SetActive( false ); + } +} + +void SkidEngineState::SetDebugInfo() +{ + if( IsActive() ) + { + SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() ); + } +} + +float SkidEngineState::GetCurrentPitch() +{ + return( m_currentPitch ); +} + +void SkidEngineState::startup( float prevPitch ) +{ + rAssert( m_parameters != NULL ); + + if( !(m_player->IsInUse()) ) + { + m_player->PlaySound( m_resourceKey ); + } + + m_currentPitch = prevPitch; + + m_player->SetPitch( m_currentPitch ); + SET_CURRENT_PITCH( m_currentPitch ); +} + +//****************************************************************************** +// +// VehicleSoundPlayer +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// VehicleSoundPlayer::VehicleSoundPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +VehicleSoundPlayer::VehicleSoundPlayer() : +#ifdef SOUND_DEBUG_INFO_ENABLED + m_debugInfo( 2, GetSoundManager()->GetDebugDisplay() ), +#endif + m_vehicle( NULL ), + m_parameters( NULL ), + m_peeloutSettings( NULL ), + m_isSkidding( false ), + m_hornPlaying( false ), + m_oneTimeHorn( false ), + m_powerslideTrim( 0.0f ), + m_playingDamage( false ), + m_proximityAIVehicle( NULL ), + m_terrainType( TT_Road ) +{ + m_engineStates[ENGINE_STATE_NORMAL] = new NormalEngineState(); + m_engineStates[ENGINE_STATE_UPSHIFTING] = new UpshiftEngineState(); + m_engineStates[ENGINE_STATE_DOWNSHIFTING] = new DownshiftEngineState(); + m_engineStates[ENGINE_STATE_IN_AIR] = new InAirEngineState(); + m_engineStates[ENGINE_STATE_REVERSE] = new ReverseEngineState( &(m_soundPlayers[CARPLAYER_BACKUP_BEEP]) ); + m_engineStates[ENGINE_STATE_IDLING] = new IdleEngineState(); + m_engineStates[ENGINE_STATE_SKIDDING] = new SkidEngineState(); + + if( !s_resetEngineInitialized ) + { + radDbgWatchAddBoolean( &s_resetEngineOnHonk, "Reset engine on honk", "Sound Info" ); + } +} + +//============================================================================== +// VehicleSoundPlayer::~VehicleSoundPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +VehicleSoundPlayer::~VehicleSoundPlayer() +{ + int i; + + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + delete m_engineStates[i]; + } +} + +//============================================================================= +// VehicleSoundPlayer::StartCarSounds +//============================================================================= +// Description: Begin playing car-related sounds +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::StartCarSounds( Vehicle* newVehicle ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + VehicleSoundDebugPage* debugPtr; + const char* overlayName; + + rAssert( newVehicle != NULL ); + + GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, newVehicle ); + + // + // Don't bother switching sounds if we're already playing this car + // + if( ( newVehicle == m_vehicle ) && carSoundIsActive() ) + { + return; + } + + // + // Remember which vehicle we're playing + // + m_vehicle = newVehicle; + + // + // Get the car sound parameter object for this vehicle. + // + // IMPORTANT: We assume that the object in the namespace has the same + // name as the one in the vehicle object, which comes from the loading + // script for that car, I think. + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + nameSpaceObj = nameSpace->GetInstance( m_vehicle->GetName() ); + if( nameSpaceObj != NULL ) + { + m_parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj ); + + // + // Set up the Watcher tuning + // + m_parameters->SetWatcherName( m_vehicle->GetName() ); + + // + // Get global car values + // + nameSpaceObj = nameSpace->GetInstance( "tuner" ); + rAssert( nameSpaceObj != NULL ); + m_peeloutSettings = reinterpret_cast<globalSettings*>( nameSpaceObj ); + + const char* clipName = m_parameters->GetEngineClipName(); + + const char* idleClipName = m_parameters->GetEngineIdleClipName(); + if( idleClipName == NULL ) + { + // + // No idle clip, use the engine clip by default + // + idleClipName = clipName; + } + + if( ( clipName != NULL ) && ( idleClipName != NULL ) ) + { +#ifdef SOUND_DEBUG_INFO_ENABLED + debugPtr = &m_debugInfo; +#else + debugPtr = NULL; +#endif + + // + // Initialize the engine sound states + // + m_engineStates[ENGINE_STATE_NORMAL]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_ENGINE], + ::radMakeKey32( clipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_UPSHIFTING]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_SHIFT], + ::radMakeKey32( clipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_DOWNSHIFTING]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_SHIFT], + ::radMakeKey32( clipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_IN_AIR]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_ENGINE], + ::radMakeKey32( clipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_REVERSE]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_ENGINE], + ::radMakeKey32( clipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_IDLING]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_ENGINE], + ::radMakeKey32( idleClipName ), + debugPtr ); + m_engineStates[ENGINE_STATE_SKIDDING]->Initialize( newVehicle, m_parameters, + &m_soundPlayers[CARPLAYER_ENGINE], + ::radMakeKey32( clipName ), + debugPtr ); + + // + // Assume that we're getting into an idle car + // + m_engineStates[ENGINE_STATE_IDLING]->SetActive( true ); + } + else + { + // + // What the...? No engine clip? That's not right. + // + rTuneString( "WARNING: No engine clip found. No engine sounds will be played.\n" ); + m_vehicle = NULL; + } + + // + // If we have an overlay clip, start it + // + overlayName = m_parameters->GetOverlayClipName(); + if( overlayName != NULL ) + { + m_soundPlayers[CARPLAYER_OVERLAY].PlaySound( overlayName ); + } + } + else + { + rTunePrintf( "Couldn't find carSoundParameters object named %s, no sound played\n", + m_vehicle->GetName() ); + m_vehicle = NULL; + } +} + +//============================================================================= +// VehicleSoundPlayer::StopCarSounds +//============================================================================= +// Description: Stop playing car-related sounds +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::StopCarSounds() +{ + unsigned int i; + + for( i = 0; i < CARPLAYER_NUMPLAYERS; i++ ) + { + m_soundPlayers[i].Stop(); + } + + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + m_engineStates[i]->SetActive( false ); + m_engineStates[i]->Reset(); + } + + m_proximityAIVehicle = NULL; + + GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, m_vehicle ); +} + +//============================================================================= +// VehicleSoundPlayer::UpdateSoundParameters +//============================================================================= +// Description: Look at the car sound parameter object, and make sure that +// the engine sound is being played at a pitch appropriate for +// the vehicle RPMs. +// +// Parameters: elapsedTime - time since last frame in msecs +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::UpdateSoundParameters( unsigned int elapsedTime ) +{ + //float newPitch; + //unsigned int shiftTime; + int i; + static bool warningPrinted = false; + bool activateState[NUM_ENGINE_STATES]; + float prevStatePitch[NUM_ENGINE_STATES]; + EngineStateEnum newState; + + // + // Before updating, check to make sure we have a vehicle. If we don't, + // then we got into a car that didn't have a carSoundParameters object + // associated with it. Handle it gracefully, but we really should fix it + // + if( m_vehicle != NULL ) + { + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + activateState[i] = false; + prevStatePitch[i] = 0.0f; + } + + // + // Service the active engine states + // + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + if( m_engineStates[i]->IsActive() ) + { + m_engineStates[i]->Service( elapsedTime ); + if( m_engineStates[i]->StartNewState() ) + { + newState = m_engineStates[i]->GetNewState(); + activateState[newState] = true; + prevStatePitch[newState] = m_engineStates[i]->GetCurrentPitch(); + } + } + } + + // + // Service fades + // + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + if( m_engineStates[i]->IsFading() ) + { + m_engineStates[i]->ServiceFade( elapsedTime ); + } + } + + // + // Initialize debug info + // + SET_LOCAL_SHIFT_IN_PROGRESS( false ); + + // + // Start up any states that were marked for activation during servicing + // + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + if( activateState[i] ) + { + m_engineStates[i]->SetActive( true, prevStatePitch[i] ); + } + + m_engineStates[i]->Reset(); + + m_engineStates[i]->SetDebugInfo(); + } + } + else + { + if( !warningPrinted ) + { + rTuneString("Warning: Vehicle doesn't have a sound associated with it (CarSoundParameters)\n"); + warningPrinted = true; + } + } +} + +//============================================================================= +// VehicleSoundPlayer::UpdateOncePerFrame +//============================================================================= +// Description: Update routine, used to set the engine RPMs correctly and +// check for skids, gear changes, and horns +// +// Parameters: elapsedTime - time since last frame in msecs +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::UpdateOncePerFrame( unsigned int elapsedTime ) +{ + UpdateSoundParameters( elapsedTime ); + checkDamage(); + CheckForSkid( elapsedTime ); + CheckHorn(); + checkProximity(); + + // + // Debug stuff + // + if( ( m_parameters != NULL ) && ( m_vehicle != NULL ) ) + { + /*SET_SHIFT_IN_PROGRESS( m_gearChangeInProgress ); + SET_CURRENT_GEAR( m_currentGear ); + SET_CURRENT_SPEED( m_currentSpeed ); + SET_SHIFT_TIME_REMAINING( m_gearChangeTimeRemaining ); + SET_DOWNSHIFT_SPEED( m_parameters->GetLowShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh ); + SET_UPSHIFT_SPEED( m_parameters->GetHighShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh );*/ + SET_LOCAL_IS_DAMAGED( m_playingDamage ); + SET_LOCAL_VEHICLE_LIFE( m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints) ); + SET_LOCAL_DAMAGE_THRESHOLD( m_parameters->GetDamageStartPcnt() ); + } +} + +//============================================================================= +// VehicleSoundPlayer::CheckForSkid +//============================================================================= +// Description: Check the vehicle object to see if we're skidding, and make +// the appropriate noise +// +// Parameters: elapsedTime - time elapsed in the last frame +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::CheckForSkid( unsigned int elapsedTime ) +{ + bool skidPlaySuccessful; + float trim; + float skidLevel; + float peeloutMin; + float peeloutMax; + const char* skidName; + bool isSupersprint; + float trimMax; + + // + // If we don't have a car, don't do anything + // + if( m_vehicle == NULL ) + { + return; + } + + // + // Find out if the car is skidding + // + skidLevel = m_vehicle->GetSkidLevel(); + peeloutMin = m_peeloutSettings->GetPeeloutMin(); + peeloutMax = m_peeloutSettings->GetPeeloutMax(); + if( skidLevel > peeloutMin ) + { + // + // Vehicle skidding. Start making tire squeal if we're not doing it yet + // + if( !m_isSkidding ) + { + // + // Get the terrain type underneath the car so we know which sound + // resource to play + // + m_terrainType = m_vehicle->mTerrainType; + skidName = getSkidResourceForTerrain( m_terrainType ); + + // + // Squeal like a pig, boy + // + skidPlaySuccessful = + m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName ); + rAssert( skidPlaySuccessful ); + + m_isSkidding = true; + m_powerslideTrim = 0.0f; + + // + // If we're starting from a slow speed, assume this is a burnout + // + + // move the triggering, and triggering of the END of this event, to + // Vehicle since Cary wants it also, to play with the camera + + //if( m_vehicle->GetSpeedKmh() < 10.0f ) + //{ + // GetEventManager()->TriggerEvent( EVENT_BURNOUT ); + //} + } + else + { + // + // Check for whether we've changed terrain types + // + if( m_vehicle->mTerrainType != m_terrainType ) + { + m_terrainType = m_vehicle->mTerrainType; + skidName = getSkidResourceForTerrain( m_terrainType ); + + m_soundPlayers[CARPLAYER_SKID].Stop(); + + skidPlaySuccessful = + m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName ); + rAssert( skidPlaySuccessful ); + } + } + + if( m_vehicle->mBurnoutLevel > 0.0f ) + { + // + // Burnout + // + if( skidLevel >= peeloutMax ) + { + trim = m_peeloutSettings->GetPeeloutMaxTrim(); + } + else + { + trim = ( ( skidLevel - peeloutMin ) / ( peeloutMax - peeloutMin ) ) + * m_peeloutSettings->GetPeeloutMaxTrim(); + } + } + else + { + // + // Powerslide + // + if( ( GetGameplayManager() != NULL ) + && ( GetGameplayManager()->IsSuperSprint() ) ) + { + isSupersprint = true; + trimMax = SUPERSPRINT_POWERSLIDE_TRIM_MAX; + } + else + { + isSupersprint = false; + trimMax = GAME_POWERSLIDE_TRIM_MAX; + } + + if( m_powerslideTrim <= 0.0f ) + { + if( isSupersprint ) + { + m_powerslideTrim = SUPERSPRINT_POWERSLIDE_TRIM_MIN; + } + else + { + m_powerslideTrim = GAME_POWERSLIDE_TRIM_MIN; + } + } + else + { + m_powerslideTrim += static_cast<float>(elapsedTime) * 0.0003f; + } + + if( m_powerslideTrim > trimMax ) + { + m_powerslideTrim = trimMax; + } + + trim = m_powerslideTrim; + } + m_soundPlayers[CARPLAYER_SKID].SetTrim( trim ); + } + else + { + // + // Not skidding. Stop the noise if it's playing + // + if( m_isSkidding ) + { + m_soundPlayers[CARPLAYER_SKID].Stop(); + m_isSkidding = false; + } + } +} + +//============================================================================= +// VehicleSoundPlayer::CheckHorn +//============================================================================= +// Description: Find the vehicle controller, and check to see whether the horn +// button is being pressed and play/stop/do nothing with the +// horn clip appropriately +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::CheckHorn() +{ + int vehicleId; + VehicleController* controller; + bool hornPlaySuccessful; + float buttonVal = 0.0f; + const char* hornClipName; + IDaSoundResource* resource; + + if( m_vehicle == NULL ) + { + return; + } + + // + // Find the horn button for this vehicle and get its state + // + vehicleId = GetVehicleCentral()->GetVehicleId( m_vehicle, false ); + controller = GetVehicleCentral()->GetVehicleController( vehicleId ); + + // + // Controller might already be gone, if we're playing car sound while + // the player is getting out of the car. + // + if( controller != NULL ) + { + buttonVal = controller->GetHorn(); + } + + // + // Look for whether we need to start/stop a sound + // + if( buttonVal > 0.05f ) + { + if( !m_hornPlaying ) + { + // + // Horn button has just been pressed, find the correct clip and play it + // + rAssert( m_parameters != NULL ); + hornClipName = m_parameters->GetHornClipName(); + + if( hornClipName == NULL ) + { + hornClipName = "horn"; + } + + // + // Just in case we were still playing a non-looping horn sound + // + m_soundPlayers[CARPLAYER_HORNPLAYER].Stop(); + + hornPlaySuccessful = + m_soundPlayers[CARPLAYER_HORNPLAYER].PlaySound( hornClipName ); + rAssert( hornPlaySuccessful ); + + m_hornPlaying = true; + + // + // Find out if this is looping horn sound + // + resource = Sound::daSoundRenderingManagerGet()->GetResourceManager()->FindResource( hornClipName ); + rAssert( resource != NULL ); + + // Sanity check + if( resource == NULL ) + { + rTunePrintf( "No horn named %s found\n", hornClipName ); + m_oneTimeHorn = true; + } + else + { + m_oneTimeHorn = !( resource->GetLooping() ); + } + +#ifndef RAD_RELEASE + // + // Tuning hack. Stop and restart the engine to pick up any trim changes + // + if( s_resetEngineOnHonk ) + { + Vehicle* theCar = m_vehicle; + StopCarSounds(); + StartCarSounds( theCar ); + } +#endif + } + } + else + { + if( m_hornPlaying ) + { + // + // Horn button has just been released, stop the clip + // + if( !m_oneTimeHorn ) + { + m_soundPlayers[CARPLAYER_HORNPLAYER].Stop(); + } + m_hornPlaying = false; + } + } +} + +//============================================================================= +// VehicleSoundPlayer::OnPlaybackComplete +//============================================================================= +// Description: SimpsonsSoundPlayer callback, used when gearshift is done +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::OnPlaybackComplete() +{ +} + +//============================================================================= +// VehicleSoundPlayer::OnSoundReady +//============================================================================= +// Description: Unused, required for SimpsonsSoundPlayerCallback interface +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::OnSoundReady() +{ +} + +//============================================================================= +// VehicleSoundPlayer::AddAIVehicleProximityTest +//============================================================================= +// Description: Add check for proximity to newly spawned AI vehicle +// +// Parameters: aiVehicle - vehicle to test +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::AddAIVehicleProximityTest( Vehicle* aiVehicle ) +{ + if( m_proximityAIVehicle == NULL ) + { + m_proximityAIVehicle = aiVehicle; + } +} + +//============================================================================= +// VehicleSoundPlayer::DeleteAIVehicleProximityTest +//============================================================================= +// Description: Stop proximity testing for given vehicle, if that's the one +// we're currently testing. +// +// Parameters: aiVehicle - vehicle to stop testing for +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::DeleteAIVehicleProximityTest( Vehicle* aiVehicle ) +{ + if( m_proximityAIVehicle == aiVehicle ) + { + m_proximityAIVehicle = NULL; + } +} + +//============================================================================= +// VehicleSoundPlayer::PlayDoorOpen +//============================================================================= +// Description: Play a door opening sound +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::PlayDoorOpen() +{ +} + +//============================================================================= +// VehicleSoundPlayer::PlayDoorClose +//============================================================================= +// Description: Play a door closing sound +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::PlayDoorClose() +{ +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// VehicleSoundPlayer::checkDamage +//============================================================================= +// Description: Start or stop the damage sound for this vehicle as appropriate +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::checkDamage() +{ + float damagePercentage; + float lifePercentage; + float damageTrimPercentage; + float damageTrim; + float trimRange; + float minTrim; + + if( m_vehicle == NULL ) + { + return; + } + + damagePercentage = m_parameters->GetDamageStartPcnt(); + lifePercentage = m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints); + minTrim = m_parameters->GetDamageStartTrim(); + trimRange = m_parameters->GetDamageMaxTrim() - m_parameters->GetDamageStartTrim(); + + if( ( lifePercentage <= damagePercentage ) && ( !m_playingDamage ) ) + { + m_playingDamage = true; + m_soundPlayers[CARPLAYER_DAMAGE].PlaySound( m_parameters->GetDamagedEngineClipName() ); + + // + // Scale volume by amount of damage + // + damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange(); + if( damageTrimPercentage > 1.0f ) + { + damageTrimPercentage = 1.0f; + } + damageTrim = minTrim + ( damageTrimPercentage * trimRange ); + m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim ); + } + else if( m_playingDamage ) + { + if( lifePercentage > damagePercentage ) + { + // + // Must've reset or something + // + m_playingDamage = false; + m_soundPlayers[CARPLAYER_DAMAGE].Stop(); + } + else + { + // + // Adjust volume + // + damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange(); + if( damageTrimPercentage > 1.0f ) + { + damageTrimPercentage = 1.0f; + } + damageTrim = minTrim + ( damageTrimPercentage * trimRange ); + m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim ); + } + } +} + +//============================================================================= +// VehicleSoundPlayer::carSoundIsActive +//============================================================================= +// Description: Indicate whether any of the engine sound states are going +// +// Parameters: None +// +// Return: true if a state is active, false otherwise +// +//============================================================================= +bool VehicleSoundPlayer::carSoundIsActive() +{ + int i; + bool isActive = false; + + for( i = 0; i < NUM_ENGINE_STATES; i++ ) + { + if( m_engineStates[i]->IsActive() ) + { + isActive = true; + break; + } + } + + return( isActive ); +} + +//============================================================================= +// VehicleSoundPlayer::checkProximity +//============================================================================= +// Description: Determine whether AI vehicle has gotten close enough to +// trigger an event +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehicleSoundPlayer::checkProximity() +{ + rmt::Vector position1; + rmt::Vector position2; + + //rAssert( m_vehicle != NULL ); + + if( m_proximityAIVehicle != NULL && m_vehicle != NULL ) + { + m_vehicle->GetPosition( &position1 ); + m_proximityAIVehicle->GetPosition( &position2 ); + + // + // Trigger event at 20 (use 20^2 for efficiency) metres + // + position1.Sub( position2 ); + if( position1.MagnitudeSqr() < 400.0f ) + { + GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_PROXIMITY ); + m_proximityAIVehicle = NULL; + } + } +} + +//============================================================================= +// VehicleSoundPlayer::getSkidResourceForTerrain +//============================================================================= +// Description: Find the name of the sound resource for a skid appropriate +// for the kind of ground we're driving over +// +// Parameters: terrain - type of terrain the car is over right now +// +// Return: name of sound resource to use +// +//============================================================================= +const char* VehicleSoundPlayer::getSkidResourceForTerrain( eTerrainType terrain ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + globalSettings* clipNameObj; + const char* name; + bool terrainIsDirt = ( terrain == TT_Dirt ) + || ( terrain == TT_Sand ) + || ( terrain == TT_Gravel ) + || ( terrain == TT_Grass ); + + // + // First, see if we have something specific for this car + // + rAssert( m_parameters != NULL ); + if( terrainIsDirt ) + { + name = m_parameters->GetDirtSkidClipName(); + } + else + { + name = m_parameters->GetRoadSkidClipName(); + } + + if( name == NULL ) + { + // + // No car-specific skid, get the global one from the globalSettings + // object + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( "tuner" ); + rAssert( nameSpaceObj != NULL ); + + clipNameObj = static_cast<globalSettings*>( nameSpaceObj ); + + if( terrainIsDirt ) + { + name = clipNameObj->GetSkidDirtClipName(); + } + else + { + name = clipNameObj->GetSkidRoadClipName(); + } + } + + rAssert( name != NULL ); + + return( name ); +} diff --git a/game/code/sound/avatar/vehiclesoundplayer.h b/game/code/sound/avatar/vehiclesoundplayer.h new file mode 100644 index 0000000..45e9665 --- /dev/null +++ b/game/code/sound/avatar/vehiclesoundplayer.h @@ -0,0 +1,162 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclesoundplayer.h +// +// Description: Declaration of the VehicleSoundPlayer class, which plays sounds +// for user-controlled vehicles in game. +// +// History: 30/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef VEHICLESOUNDPLAYER_H +#define VEHICLESOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== + +#include <sound/simpsonssoundplayer.h> +#include <sound/avatar/vehiclesounddebugpage.h> +#include <render/IntersectManager/IntersectManager.h> + +//======================================== +// Forward References +//======================================== + +class Vehicle; +class carSoundParameters; +class globalSettings; +class VehicleSoundPlayer; +class EngineState; + +// +// Enumerated list of temporary engine states +// +enum EngineStateEnum +{ + ENGINE_STATE_NORMAL, + ENGINE_STATE_UPSHIFTING, + ENGINE_STATE_DOWNSHIFTING, + ENGINE_STATE_IN_AIR, + ENGINE_STATE_REVERSE, + ENGINE_STATE_IDLING, + ENGINE_STATE_SKIDDING, + + NUM_ENGINE_STATES, + + ENGINE_STATE_INVALID +}; + +//============================================================================= +// +// Synopsis: VehicleSoundPlayer +// +//============================================================================= + +class VehicleSoundPlayer : public SimpsonsSoundPlayerCallback +{ + public: + VehicleSoundPlayer(); + virtual ~VehicleSoundPlayer(); + + // + // Update routines + // + void UpdateOncePerFrame( unsigned int elapsedTime ); + + void UpdateSoundParameters( unsigned int elapsedTime ); + + // + // Check the vehicle to see if we should be playing a skid noise + // + void CheckForSkid( unsigned int elapsedTime ); + + // + // Check the vehicle controller to see if we should be playing + // a horn noise + // + void CheckHorn(); + + // + // Start and stop sound when the player gets in and out of the car + // + void StartCarSounds( Vehicle* newVehicle ); + void StopCarSounds(); + + // + // Play door sounds + // + void PlayDoorOpen(); + void PlayDoorClose(); + + // + // Called when gearshift is done playing + // + void OnPlaybackComplete(); + + // + // Needed to complete SimpsonsSoundPlayerCallback interface + // + void OnSoundReady(); + + // + // Proximity testing for AI vehicle dialog (better here than + // anywhere else) + // + void AddAIVehicleProximityTest( Vehicle* aiVehicle ); + void DeleteAIVehicleProximityTest( Vehicle* aiVehicle ); + + private: + //Prevent wasteful constructor creation. + VehicleSoundPlayer( const VehicleSoundPlayer& original ); + VehicleSoundPlayer& operator=( const VehicleSoundPlayer& rhs ); + + bool carSoundIsActive(); + + void checkDamage(); + void checkProximity(); + + const char* getSkidResourceForTerrain( eTerrainType terrain ); + + enum CarSoundPlayers + { + CARPLAYER_ENGINE, + CARPLAYER_SHIFT, + CARPLAYER_SKID, + CARPLAYER_HORNPLAYER, + CARPLAYER_DAMAGE, + CARPLAYER_OVERLAY, + CARPLAYER_BACKUP_BEEP, + CARPLAYER_DOOR, + + CARPLAYER_NUMPLAYERS + }; + +#ifdef SOUND_DEBUG_INFO_ENABLED + VehicleSoundDebugPage m_debugInfo; +#endif + + SimpsonsSoundPlayer m_soundPlayers[CARPLAYER_NUMPLAYERS]; + EngineState* m_engineStates[NUM_ENGINE_STATES]; + + Vehicle* m_vehicle; + carSoundParameters* m_parameters; + globalSettings* m_peeloutSettings; + + bool m_isSkidding; + bool m_hornPlaying; + bool m_oneTimeHorn; + float m_powerslideTrim; + + bool m_playingDamage; + + Vehicle* m_proximityAIVehicle; + + eTerrainType m_terrainType; +}; + + +#endif // VEHICLESOUNDPLAYER_H + diff --git a/game/code/sound/dialog/alldialog.cpp b/game/code/sound/dialog/alldialog.cpp new file mode 100644 index 0000000..b4681d5 --- /dev/null +++ b/game/code/sound/dialog/alldialog.cpp @@ -0,0 +1,11 @@ +#include <sound/dialog/conversation.cpp> +#include <sound/dialog/conversationmatcher.cpp> +#include <sound/dialog/dialogcoordinator.cpp> +#include <sound/dialog/dialogline.cpp> +#include <sound/dialog/dialoglist.cpp> +#include <sound/dialog/dialogpriorityqueue.cpp> +#include <sound/dialog/dialogqueueelement.cpp> +#include <sound/dialog/dialogselectiongroup.cpp> +#include <sound/dialog/playabledialog.cpp> +#include <sound/dialog/selectabledialog.cpp> +#include <sound/dialog/dialogsounddebugpage.cpp> diff --git a/game/code/sound/dialog/conversation.cpp b/game/code/sound/dialog/conversation.cpp new file mode 100644 index 0000000..4aac61b --- /dev/null +++ b/game/code/sound/dialog/conversation.cpp @@ -0,0 +1,408 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: conversation.cpp +// +// Description: Conversation objects aggregate groups of DialogLine objects +// which are intended to be played without interruption, like +// a conversation. Hence the name. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <stdio.h> +#include <raddebug.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/conversation.h> + +#include <sound/dialog/dialogline.h> +#include <sound/dialog/dialogselectiongroup.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// Conversation::Conversation +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Conversation::Conversation( DialogLine& line ) : + PlayableDialog( line.GetLevel(), line.GetMission(), line.GetEvent() ), + m_maxOrderNumber( line.GetConversationPosition() ), + m_dialogList( NULL ), + m_currentLine( NULL ) +{ + line.AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_dialogList) ); +} + +//============================================================================== +// Conversation::~Conversation +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Conversation::~Conversation() +{ +} + +//============================================================================= +// Conversation::LineFits +//============================================================================= +// Description: Determine whether the given line matches this conversation +// +// Parameters: line - dialog line to test +// +// Return: true if it fits in this conversation, false otherwise +// +//============================================================================= +bool Conversation::LineFits( DialogLine& line ) +{ + bool matches; + DialogLine* existingLine = m_dialogList; + + rAssert( existingLine != NULL ); + + // + // If the new line matches one dialog line in this conversation, it should + // match them all + // + matches = ( line.GetLevel() == existingLine->GetLevel() ) + && ( line.GetMission() == existingLine->GetMission() ) + && ( line.GetEvent() == existingLine->GetEvent() ) + && ( line.GetConversationName() == existingLine->GetConversationName() ); + +#ifdef RAD_DEBUG + if( matches ) + { + rAssert( line.GetConversationPosition() != existingLine->GetConversationPosition() ); + } +#endif + + return( matches ); +} + +//============================================================================= +// Conversation::AddToConversation +//============================================================================= +// Description: Add the given dialog line to this conversation +// +// Parameters: line - line to be added +// +// Return: void +// +//============================================================================= +void Conversation::AddToConversation( DialogLine& line ) +{ + int position = line.GetConversationPosition(); + DialogLine* listObj; + DialogLine* nextListObj; + + if( ( m_dialogList == NULL ) + || ( position < m_dialogList->GetConversationPosition() ) ) + { + line.AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_dialogList) ); + } + else + { + listObj = m_dialogList; + // ARGH! Stinky downcast! I hate this! + nextListObj = reinterpret_cast<DialogLine*>(listObj->GetNextInList()); + while( ( nextListObj != NULL ) + && ( nextListObj->GetConversationPosition() < position ) ) + { + listObj = nextListObj; + nextListObj = reinterpret_cast<DialogLine*>(listObj->GetNextInList()); + } + + line.AddToDialogList( listObj ); + } +} + +//============================================================================= +// Conversation::IsComplete +//============================================================================= +// Description: Returns true if we have a set of consecutively-numbered +// dialog lines, starting from 1. +// +// Parameters: None +// +// Return: true if conversation valid and complete, false otherwise +// +//============================================================================= +bool Conversation::IsComplete() +{ + bool complete = true; + int nextPosition = 1; + DialogLine* nextDialogLine = m_dialogList; + + while( nextDialogLine != NULL ) + { + if( nextDialogLine->GetConversationPosition() != nextPosition ) + { + complete = false; + break; + } + + // Stinky downcast! + nextDialogLine = reinterpret_cast<DialogLine*>(nextDialogLine->GetNextInList()); + ++nextPosition; + } + + return( complete ); +} + +//============================================================================= +// Conversation::PlayLine +//============================================================================= +// Description: Comment +// +// Parameters: lineIndex - index for the DialogLine we want to play +// player - player to play it with +// callback - callback to call when we're done +// +// Return: void +// +//============================================================================= +void Conversation::PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + DialogLine* currentDialog = findDialogLineByIndex( lineIndex ); + + currentDialog->PlayLine( 0, player, callback ); +} + +void Conversation::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player ) +{ + m_currentLine = findDialogLineByIndex( lineIndex ); + + m_currentLine->QueueLine( 0, player ); +} + +void Conversation::PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + m_currentLine->PlayQueuedLine( player, callback ); +} + +//============================================================================= +// Conversation::GetNumDialogLines +//============================================================================= +// Description: Returns the number of DialogLine objects associated with +// this conversation. +// +// Parameters: None +// +// Return: number of dialog lines +// +//============================================================================= +unsigned int Conversation::GetNumDialogLines() const +{ + unsigned int lineCount = 0; + DialogLine* lineObj = m_dialogList; + + while( lineObj != NULL ) + { + ++lineCount; + lineObj = static_cast<DialogLine*>(lineObj->GetNextInList()); + } + + return( lineCount ); +} + +//============================================================================= +// Conversation::UsesCharacter +//============================================================================= +// Description: Indicate if any of our dialog lines are from the given +// character +// +// Parameters: characterObj - character to match +// +// Return: true if >=1 dialog line comes from the given character, +// false otherwise +// +//============================================================================= +bool Conversation::UsesCharacter( tUID characterUID ) +{ + bool match = false; + DialogLine* lineObj = m_dialogList; + + while( lineObj != NULL ) + { + if( lineObj->UsesCharacter( characterUID ) ) + { + match = true; + break; + } + lineObj = static_cast<DialogLine*>(lineObj->GetNextInList()); + } + + return( match ); +} + +//============================================================================= +// Conversation::IsVillainLine +//============================================================================= +// Description: Indicate if this is a villain one-liner (answer: no) +// +// Parameters: None +// +// Return: false +// +//============================================================================= +bool Conversation::IsVillainLine() +{ + // + // No conversations are villain one-liners + // + return( false ); +} + +//============================================================================= +// Conversation::GetDialogLineCharacterUID +//============================================================================= +// Description: Indicate which character is speaking with the given line +// +// Parameters: lineNum - indicates which dialog line we want the UID for +// +// Return: tUID of character associated with dialog line +// +//============================================================================= +tUID Conversation::GetDialogLineCharacterUID( unsigned int lineNum ) +{ + unsigned int lineCount = lineNum; + DialogLine* lineObj = m_dialogList; + + rAssert( lineNum > 0 ); + + while( ( --lineCount > 0 ) && ( lineObj != NULL ) ) + { + lineObj = static_cast<DialogLine*>(lineObj->GetNextInList()); + } + + if( lineObj != NULL ) + { + return( lineObj->GetCharacterUID() ); + } + else + { + return( 0 ); + } +} + +//============================================================================= +// Conversation::GetConversationName +//============================================================================= +// Description: I don't really expect this to be used, but that's no reason +// to return garbage here. Find the name stored with one of the +// individual lines. +// +// Parameters: None +// +// Return: radKey32 representing conversation name field +// +//============================================================================= +radKey32 Conversation::GetConversationName() +{ + rAssert( m_dialogList != NULL ); + + return( m_dialogList->GetConversationName() ); +} + +//============================================================================= +// Conversation::AddMatchingDialog +//============================================================================= +// Description: Remove self from the given list and add ourself and the new +// dialog back in within a DialogSelectionGroup object +// +// Parameters: newDialog - new dialog with same characteristics as self +// list - list to add to +// +// Return: void +// +//============================================================================= +void Conversation::AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ) +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + DialogSelectionGroup* group = new DialogSelectionGroup( *this, newDialog ); + + rAssert( group != NULL ); + + list.remove( this ); + list.push_back( group ); + + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); +} + +//============================================================================= +// Conversation::PrintDialogLineNames +//============================================================================= +// Description: For debugging. Print the names of the dialog lines. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Conversation::PrintDialogLineNames() +{ +#ifndef RAD_RELEASE + DialogLine* currentDialog = m_dialogList; + + rDebugString( "Conversation contents:\n" ); + while( currentDialog != NULL ) + { + currentDialog->PrintResourceName(); + currentDialog = static_cast<DialogLine*>(currentDialog->GetNextInList()); + } +#endif +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +DialogLine* Conversation::findDialogLineByIndex( unsigned int lineIndex ) +{ + unsigned int lineCount = lineIndex; + DialogLine* currentDialog = m_dialogList; + + rAssert( currentDialog != NULL ); + + while( lineCount > 0 ) + { + currentDialog = static_cast<DialogLine*>(currentDialog->GetNextInList()); + rAssert( currentDialog != NULL ); + --lineCount; + } + + return( currentDialog ); +}
\ No newline at end of file diff --git a/game/code/sound/dialog/conversation.h b/game/code/sound/dialog/conversation.h new file mode 100644 index 0000000..29459f0 --- /dev/null +++ b/game/code/sound/dialog/conversation.h @@ -0,0 +1,79 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: conversation.h +// +// Description: Conversation objects aggregate groups of DialogLine objects +// which are intended to be played without interruption, like +// a conversation. Hence the name. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef CONVERSATION_H +#define CONVERSATION_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/dialog/playabledialog.h> + +//======================================== +// Forward References +//======================================== +class DialogLine; + +//============================================================================= +// +// Synopsis: Conversation +// +//============================================================================= + +class Conversation : public PlayableDialog +{ + public: + Conversation( DialogLine& line ); + virtual ~Conversation(); + + bool LineFits( DialogLine& line ); + void AddToConversation( DialogLine& line ); + bool IsComplete(); + + // + // Pure virtual functions from SelectableDialog + // + void PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + void QueueLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player ); + void PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + + unsigned int GetNumDialogLines() const; + bool UsesCharacter( tUID characterUID ); + tUID GetDialogLineCharacterUID( unsigned int lineNum ); + radKey32 GetConversationName(); + bool IsVillainLine(); + + void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ); + + void PrintDialogLineNames(); + + private: + //Prevent wasteful constructor creation. + Conversation(); + Conversation( const Conversation& original ); + Conversation& operator=( const Conversation& rhs ); + + DialogLine* findDialogLineByIndex( unsigned int lineIndex ); + + unsigned int m_maxOrderNumber; + DialogLine* m_dialogList; + DialogLine* m_currentLine; +}; + + +#endif // CONVERSATION_H + diff --git a/game/code/sound/dialog/conversationmatcher.cpp b/game/code/sound/dialog/conversationmatcher.cpp new file mode 100644 index 0000000..db49580 --- /dev/null +++ b/game/code/sound/dialog/conversationmatcher.cpp @@ -0,0 +1,237 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: conversationmatcher.cpp +// +// Description: Takes sound resource names which form individual dialog lines +// and groups them into the intended conversations. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/conversationmatcher.h> + +#include <sound/dialog/conversation.h> +#include <sound/dialog/dialogline.h> +#include <sound/dialog/dialoglist.h> + +#include <memory/srrmemory.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// ConversationMatcher::ConversationMatcher +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +ConversationMatcher::ConversationMatcher() : + m_conversationList( NULL ) +{ +} + +//============================================================================== +// ConversationMatcher::~ConversationMatcher +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +ConversationMatcher::~ConversationMatcher() +{ +} + +//============================================================================= +// ConversationMatcher::AddNewLine +//============================================================================= +// Description: Make a new DialogLine object for the resource and add it to +// the list of conversations to be grouped together. +// +// Parameters: resource - conversation sound resource +// +// Return: void +// +//============================================================================= +void ConversationMatcher::AddNewLine( IDaSoundResource* resource ) +{ + DialogLine* line; + Conversation* conversationObj; + Conversation* newConversation; + + // + // Create a DialogLine object + // +#ifdef RAD_GAMECUBE + line = new( GMA_GC_VMM ) DialogLine( resource ); +#else + line = new( GMA_PERSISTENT ) DialogLine( resource ); +#endif + rAssert( line != NULL ); + + // + // Work through the conversation list, trying to find an existing + // conversation that matches this line + // + conversationObj = m_conversationList; + + while( conversationObj != NULL ) + { + if( conversationObj->LineFits( *line ) ) + { + conversationObj->AddToConversation( *line ); + break; + } + + // + // Argh! This must go. Stinky downcast. + // + conversationObj = reinterpret_cast<Conversation*>(conversationObj->GetNextInList()); + } + + if( conversationObj == NULL ) + { + // + // No conversation matched, create a new conversation + // +#ifdef RAD_GAMECUBE + newConversation = new( GMA_GC_VMM ) Conversation( *line ); +#else + newConversation = new( GMA_PERSISTENT ) Conversation( *line ); +#endif + newConversation->AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_conversationList) ); + } +} + +//============================================================================= +// ConversationMatcher::AreAllConversationsComplete +//============================================================================= +// Description: Determine whether all the conversations have been filled out. +// Sanity check function. +// +// Parameters: None +// +// Return: true if all conversations complete, false otherwise +// +//============================================================================= +bool ConversationMatcher::AreAllConversationsComplete() +{ + Conversation* current; + bool allComplete = true; + + current = m_conversationList; + while( current != NULL ) + { + if( !(current->IsComplete()) ) + { + allComplete = false; + current->PrintDialogLineNames(); + + // + // We know we're incomplete at this point, but keep going so that + // we get everything printed out in one go. + // + } + + current = reinterpret_cast<Conversation*>(current->GetNextInList()); + } + + return( allComplete ); +} + +//============================================================================= +// ConversationMatcher::AddConversationsToList +//============================================================================= +// Description: Transfer all of our conversations from our own list to the +// list given that match the specification +// +// Parameters: level - level of conversations for list (NO_LEVEL if N/A) +// mission - mission of conversations for list (NO_MISSION if N/A) +// list - list to add to +// +// Return: void +// +//============================================================================= +void ConversationMatcher::AddConversationsToList( unsigned int level, + unsigned int mission, + SelectableDialogList& list ) +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + Conversation* conversationObj; + Conversation* nextConversation = NULL; + Conversation* temp; + + // + // First, deal with the head of the list as a special case. This can + // probably be cleaned up + // + while( ( m_conversationList != NULL ) + && ( m_conversationList->GetLevel() == level ) + && ( m_conversationList->GetMission() == mission ) ) + { + temp = m_conversationList; + m_conversationList = reinterpret_cast<Conversation*>( m_conversationList->GetNextInList() ); + list.push_back( temp ); + } + + // + // Now, the first one in the list, if it exists, doesn't match the spec + // + conversationObj = m_conversationList; + if( conversationObj != NULL ) + { + nextConversation = reinterpret_cast<Conversation*>( conversationObj->GetNextInList() ); + } + + while( ( conversationObj != NULL ) + && ( nextConversation != NULL ) ) + { + if( ( nextConversation->GetLevel() == level ) + && ( nextConversation->GetMission() == mission ) ) + { + // + // Remove conversation from our list and add it to the one supplied + // + conversationObj->RemoveNextFromList(); + list.push_back( nextConversation ); + } + else + { + conversationObj = nextConversation; + } + + nextConversation = reinterpret_cast<Conversation*>( conversationObj->GetNextInList() ); + } + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/dialog/conversationmatcher.h b/game/code/sound/dialog/conversationmatcher.h new file mode 100644 index 0000000..18e9b15 --- /dev/null +++ b/game/code/sound/dialog/conversationmatcher.h @@ -0,0 +1,57 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: conversationmatcher.h +// +// Description: Takes sound resource names which form individual dialog lines +// and groups them into the intended conversations. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef CONVERSATIONMATCHER_H +#define CONVERSATIONMATCHER_H + +//======================================== +// Nested Includes +//======================================== +// +// I'd like a forward declaration, but it's a little complicated with +// STL lists +// +#include <sound/dialog/selectabledialoglist.h> + +//======================================== +// Forward References +//======================================== +struct IDaSoundResource; +class Conversation; + +//============================================================================= +// +// Synopsis: ConversationMatcher +// +//============================================================================= + +class ConversationMatcher +{ + public: + ConversationMatcher(); + virtual ~ConversationMatcher(); + + void AddNewLine( IDaSoundResource* resource ); + bool AreAllConversationsComplete(); + void AddConversationsToList( unsigned int level, unsigned int mission, SelectableDialogList& list ); + + private: + //Prevent wasteful constructor creation. + ConversationMatcher( const ConversationMatcher& original ); + ConversationMatcher& operator=( const ConversationMatcher& rhs ); + + Conversation* m_conversationList; +}; + + +#endif // CONVERSATIONMATCHER_H + diff --git a/game/code/sound/dialog/dialogcoordinator.cpp b/game/code/sound/dialog/dialogcoordinator.cpp new file mode 100644 index 0000000..ba6e269 --- /dev/null +++ b/game/code/sound/dialog/dialogcoordinator.cpp @@ -0,0 +1,971 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogcoordinator.cpp +// +// Description: The main interface class for the dialog system. It listens +// for game events and cues the appropriate line of dialog. +// Objects representing lines or sets of lines are obtained +// from the SpeechCoordinator, and are passed to the +// DialogPriorityQueue, which determines when playback is +// ready to begin. +// +// History: 30/08/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogcoordinator.h> + +#include <sound/dialog/dialoglist.h> +#include <sound/dialog/dialogpriorityqueue.h> +#include <sound/dialog/dialogline.h> + +#include <memory/srrmemory.h> +#include <events/eventmanager.h> +#include <events/eventdata.h> +#include <worldsim/avatarmanager.h> +#include <worldsim/vehiclecentral.h> +#include <worldsim/character/charactermanager.h> +#include <mission/objectives/missionobjective.h> +#include <mission/conditions/missioncondition.h> +#include <gameflow/gameflow.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +struct TutorialConversationData +{ + const char* convName; + radKey32 convNameKey; + bool platformSpecificLine; +}; + +// +// IMPORTANT: needs to line up with TutorialMode enumeration in tutorialmode.h. +// Not ideal, but we're three days from alpha. +// +TutorialConversationData tutorialConvNames[] = +{ + { "camera", 0, false }, + { "bonus", 0, false }, + { "start", 0, true }, + { "drive", 0, true }, + { "traffic", 0, false }, + { "gag", 0, false }, + { "race", 0, false }, + { "card", 0, false }, + { "coin", 0, false }, + { "reward", 0, true }, + { "outcar", 0, true }, + { "break", 0, true }, + { "broken", 0, false }, + { "interior", 0, true }, + { "unknown", 0, false }, + { "damaged", 0, false }, + { "Gil", 0, false }, + { "wrench", 0, false }, + { "incar", 0, true }, + { "unknown", 0, false }, + { "timetrial", 0, false } +}; + +static unsigned int tutorialTableLength = sizeof( tutorialConvNames ) / sizeof( TutorialConversationData ); + +static tUID genericTrafficUIDs[] = +{ + tEntity::MakeUID( "traffic1" ), + tEntity::MakeUID( "traffic2" ), + tEntity::MakeUID( "traffic3" ), + tEntity::MakeUID( "traffic4" ) +}; + +static radKey32 s_failDialog1 = ::radMakeKey32( "fail1" ); +static radKey32 s_failDialog2 = ::radMakeKey32( "fail2" ); + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogCoordinator::DialogCoordinator +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogCoordinator::DialogCoordinator( IRadNameSpace* namespaceObj ) : + m_dialogNamespace( namespaceObj ), + m_dialogList( new( GMA_PERSISTENT ) DialogList() ), + m_playbackQueue( new( GMA_PERSISTENT ) DialogPriorityQueue() ), + m_dialogOn( true ), + m_phoneBoothRequestMade( false ) +{ + unsigned int i; + char buffer[50]; + + rAssert( m_dialogList != NULL ); + rAssert( m_dialogNamespace != NULL ); + rAssert( m_playbackQueue != NULL ); + + // + // Initialize the tutorial dialog table + // + for( i = 0; i < tutorialTableLength; i++ ) + { + if( tutorialConvNames[i].platformSpecificLine ) + { +#ifdef RAD_PS2 + sprintf( buffer, "%sps2", tutorialConvNames[i].convName ); +#elif RAD_GAMECUBE + sprintf( buffer, "%sngc", tutorialConvNames[i].convName ); +#else + sprintf( buffer, "%sxbx", tutorialConvNames[i].convName ); +#endif + + tutorialConvNames[i].convNameKey = ::radMakeKey32( buffer ); + } + else + { + tutorialConvNames[i].convNameKey = ::radMakeKey32( tutorialConvNames[i].convName ); + } + } +} + +//============================================================================== +// DialogCoordinator::~DialogCoordinator +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogCoordinator::~DialogCoordinator() +{ + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// DialogCoordinator::HandleEvent +//============================================================================= +// Description: Pass the event to the DialogList, which will find a suitable +// SelectableDialog for us, and give it to the priority queue +// for playback. +// +// Parameters: id - event ID +// pEventData - user data, unused here +// +// Return: void +// +//============================================================================= +void DialogCoordinator::HandleEvent( EventEnum id, void* pEventData ) +{ + unsigned int tutorialIndex; + SelectableDialog* dialog; + DialogEventData* eventData; + DialogEventData raceEventData; + bool dialogWasPlaying; + AvatarManager* avatarMgr; + Vehicle* vehicleEventData = NULL; + Vehicle* minigameVehicle; + Vehicle* avatarVehicle; + tUID charUID1 = 0; + tUID charUID2 = 0; + int i; + int numPlayers; + const char* charName; + rmt::Vector dlgPosition; + rmt::Vector* dlgPositionPtr = NULL; + radKey32 convName = 0; + Character* character1 = NULL; + Character* character2 = NULL; + Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 ); + bool overridePositional = false; + int coinToss; + + if( GetGameplayManager() != NULL ) + { + // + // Filter out the crap we don't want in supersprint + // + if( GetGameplayManager()->IsSuperSprint() + && ( id != EVENT_BIG_VEHICLE_CRASH ) + && ( id != EVENT_BIG_BOOM_SOUND ) + && ( id != EVENT_DEATH_VOLUME_SOUND ) ) + { + return; + } + + // + // Filter out the crap we don't want in race missions + // + if( ( GetGameplayManager()->GetCurrentMission() != NULL ) + && ( GetGameplayManager()->GetCurrentMission()->IsRaceMission() ) ) + { + if( id == EVENT_MISSION_FAILURE ) + { + const char* modelName; + + // + // Hack! + // Turn this into a Patty/walker failure conversation. + // Randomly choose between fail1 and fail2. + // + rAssert( playerAvatar != NULL ); + modelName = GetCharacterManager()->GetModelName( playerAvatar->GetCharacter() ); + raceEventData.charUID1 = tEntity::MakeUID( modelName ); + raceEventData.charUID2 = tEntity::MakeUID( "patty" ); + if( rand() % 2 == 0 ) + { + raceEventData.dialogName = s_failDialog1; + } + else + { + raceEventData.dialogName = s_failDialog2; + } + + pEventData = &raceEventData; + id = EVENT_IN_GAMEPLAY_CONVERSATION; + } + else if( id == EVENT_MISSION_BRIEFING_ACCEPTED ) + { + return; + } + } + } + + //********************************************************** + // + // TODO: this is becoming a bit of an if-statement mess. We're getting close + // to alpha, so I'm not changing it now. In the sequel, pEventData should + // point to a parameter object which can be created in a variety of ways with + // a variety of data, and you should be able to ask it for a tUID or + // something when it gets here. + // + //********************************************************** + + // + // Hack!! + // + if( id == EVENT_PHONE_BOOTH_RIDE_REQUEST ) + { + m_phoneBoothRequestMade = true; + } + else if( ( id == EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED ) + || ( id == EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED ) ) + { + if( !m_phoneBoothRequestMade ) + { + // + // This isn't a phone booth car request, it's just a regular + // vehicle load. Throw the event out. + // + return; + } + else + { + m_phoneBoothRequestMade = false; + } + } + else if( id == EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE ) + { + m_phoneBoothRequestMade = false; + return; + } + else + { + // + // We expect those requests to be made back-to-back. Anything else, + // and we're done with the phone booth. + // + m_phoneBoothRequestMade = false; + } + + // + // Filter out the reverse burnouts we don't want + // + if( id == EVENT_BURNOUT ) + { + // + // Need to check the mBrake value, calling IsInReverse() or + // IsMovingBackward() filters out the small velocities we're + // getting at the start of the burnout + // + if( playerAvatar->IsInCar() + && ( playerAvatar->GetVehicle()->mBrake > 0.0f ) ) + { + return; + } + } + + // + // Turn wasp zaps into hit by car to play HitByC dialog lines + // + if( id == EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS ) + { + id = EVENT_PLAYER_CAR_HIT_NPC; + overridePositional = true; + } + + if( id == EVENT_DIALOG_SHUTUP ) + { + m_playbackQueue->StopAllDialog(); + } + else if( id == EVENT_CONVERSATION_SKIP ) + { + // + // Stop anything that might be playing + // + dialogWasPlaying = m_playbackQueue->StopAllDialog(); + if( dialogWasPlaying ) + { + GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE ); + } + } + else if( m_dialogOn ) + { + if( id == EVENT_TUTORIAL_DIALOG_PLAY ) + { + tutorialIndex = *(reinterpret_cast<unsigned int*>( pEventData )); + if( tutorialIndex < tutorialTableLength ) + { + // + // It's always Bart. Specify him by UID (his Character + // object isn't loaded for these) + // + character1 = NULL; + character2 = NULL; + charUID1 = tEntity::MakeUID( "bart" ); + charUID2 = tEntity::MakeUID( "homer" ); + convName = tutorialConvNames[tutorialIndex].convNameKey; + } + else + { + // + // No line exists here, play nothing + // + rDebugString( "No tutorial dialog exists for event data\n" ); + return; + } + } + else if( ( id == EVENT_CONVERSATION_INIT_DIALOG ) + || ( id == EVENT_IN_GAMEPLAY_CONVERSATION ) ) + { + // + // Get characters to match + // + eventData = static_cast<DialogEventData*>( pEventData ); + character1 = eventData->char1; + character2 = eventData->char2; + charUID1 = eventData->charUID1; + charUID2 = eventData->charUID2; + + // + // Assumption: if eventData->dialogNameis zero, + // that's an indication that no name is to be matched + // + convName = eventData->dialogName; + } + else if( id == EVENT_TRAFFIC_IMPEDED ) + { + // + // Almost-final hack! + // + // Special case, of course. For this one, toss a coin between + // playing the avatar line and the traffic line. For the + // traffic, play one of the four generic vehicles. + // + if( playerAvatar->IsInCar() ) + { + coinToss = rand() % 2; + } + else + { + coinToss = 1; + } + + if( coinToss == 0 ) + { + character1 = playerAvatar->GetCharacter(); + overridePositional = true; + } + else + { + coinToss = rand() % 4; + charUID1 = genericTrafficUIDs[coinToss]; + vehicleEventData = static_cast<Vehicle*>( pEventData ); + } + } + else if( ( pEventData != NULL ) + && ( id != EVENT_LOCATOR + LocatorEvent::BOUNCEPAD ) ) + { + if( GetGameplayManager()->IsSuperSprint() ) + { + minigameVehicle = static_cast<Vehicle*>(pEventData); + + avatarMgr = GetAvatarManager(); + numPlayers = GetGameplayManager()->GetNumPlayers(); + for( i = 0; i < numPlayers; i++ ) + { + playerAvatar = avatarMgr->GetAvatarForPlayer( i ); + if( playerAvatar->GetVehicle() == minigameVehicle ) + { + character1 = playerAvatar->GetCharacter(); + break; + } + } + + if( i >= numPlayers ) + { + // + // Not the player, so use the driver name + // + charUID1 = tEntity::MakeUID( minigameVehicle->mDriverName ); + } + } + else if( id == EVENT_WRONG_SIDE_DUMBASS ) + { + // + // Stinky special case. pEventData is actually a Vehicle in this case, since + // we can't use the character, since drivers have different names + // + vehicleEventData = static_cast<Vehicle*>(pEventData); + dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, + tEntity::MakeUID( vehicleEventData->mDriverName ), 0, convName, false ); + if( dialog == NULL ) + { + rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) ); + } + else + { + m_playbackQueue->AddDialogToQueue( *dialog ); + } + + return; + } + else if( id == EVENT_DING_DONG ) + { + // + // Yet Another Special Case. Doorbells play dialog for unloaded characters. + // We're getting a string with the character name + // + charName = static_cast<char*>(pEventData); + dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, tEntity::MakeUID( charName ), 0, 0, false ); + + if( dialog == NULL ) + { + rDebugPrintf( "No doorbell dialog found for character %s\n", charName ); + } + else + { + m_playbackQueue->AddDialogToQueue( *dialog ); + } + + return; + } + else if( id == EVENT_BIG_BOOM_SOUND ) + { + Vehicle* blowedUpCar = static_cast<Vehicle*>(pEventData); + + // + // Can't handle below because we can get thrown out of the car before the + // explosion. + // + if( playerAvatar->IsInCar() && ( blowedUpCar != playerAvatar->GetVehicle() ) ) + { + return; + } + + character1 = playerAvatar->GetCharacter(); + rAssert( character1 != NULL ); + + // + // Play funny driver line + // + if( blowedUpCar->GetDriver() != NULL ) + { + dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, + tEntity::MakeUID(blowedUpCar->mDriverName), 0, 0, false ); + if( dialog == NULL ) + { + rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) ); + } + else + { + m_playbackQueue->AddDialogToQueue( *dialog ); + } + } + } + else if( eventHasVehicleData( id ) + || ( id == EVENT_MISSION_FAILURE ) + || ( id == EVENT_HIT_BREAKABLE ) + || ( id == EVENT_DEATH_VOLUME_SOUND ) + || ( id == EVENT_CARD_COLLECTED ) ) + { + if( eventHasVehicleData( id ) + && ( !(playerAvatar->IsInCar()) + || ( static_cast<Vehicle*>(pEventData) != playerAvatar->GetVehicle() ) ) ) + { + // + // We're only interested in our own vehicle + // + return; + } + + // + // Another stinky special case. Event data is used in the call + // to queueVillainDialog below. + // + character1 = playerAvatar->GetCharacter(); + rAssert( character1 != NULL ); + } + else + { + // + // We expect a Character* as the event data, + // + character1 = static_cast<Character*>( pEventData ); + } + } + else + { + // + // If nothing is supplied as event data, we assume that the character + // referred to is avatar 0 + // + character1 = playerAvatar->GetCharacter(); + rAssert( character1 != NULL ); + } + + // + // Check for events where we want to queue up a villain line before the walker/driver + // + queueVillainDialog( id, pEventData ); + + // + // Driver lines + // + if( ( playerAvatar != NULL ) + && playerAvatar->IsInCar() ) + { + avatarVehicle = playerAvatar->GetVehicle(); + if( ( avatarVehicle->mpDriver != NULL ) && ( avatarVehicle->mpDriver != character1 ) ) + { + tUID driverUID; + + // + // Character is in car and not driving. Trigger driver dialog + // as well as character dialog below + // + driverUID = tEntity::MakeUID(avatarVehicle->mDriverName); + + dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, driverUID, charUID2, convName, false ); + if( dialog == NULL ) + { + rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) ); + } + else + { + m_playbackQueue->AddDialogToQueue( *dialog ); + } + } + } + + dialog = m_dialogList->FindDialogForEvent( id, character1, character2, charUID1, charUID2, convName, false ); + + if( dialog == NULL ) + { + rDebugPrintf( "No dialog found for event %d\n", static_cast<int>( id ) ); + } + else + { + if( playLinePositionally( id ) && !overridePositional ) + { + if( character1 != NULL ) + { + getCharacterPosition( character1, dlgPosition ); + } + else + { + vehicleEventData->GetPosition( &dlgPosition ); + } + dlgPositionPtr = &dlgPosition; + } + m_playbackQueue->AddDialogToQueue( *dialog, dlgPositionPtr ); + } + } +} + +//============================================================================= +// DialogCoordinator::Initialize +//============================================================================= +// Description: Parse the namespace for dialog resources and register for +// dialog-related events +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogCoordinator::Initialize() +{ + m_dialogList->OrganizeDialog( m_dialogNamespace ); + + registerDialogEvents(); +} + + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogCoordinator::registerDialogEvents +//============================================================================= +// Description: Register as a listener for the dialog-related events with +// the event manager. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogCoordinator::registerDialogEvents() +{ + unsigned int tableSize = DialogLine::GetEventTableSize(); + unsigned int i; + + for( i = 0; i < tableSize; i++ ) + { + GetEventManager()->AddListener( this, DialogLine::GetEventTableEntry( i )->event ); + } + + GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP ); + GetEventManager()->AddListener( this, EVENT_TUTORIAL_DIALOG_PLAY ); + GetEventManager()->AddListener( this, EVENT_DIALOG_SHUTUP ); + GetEventManager()->AddListener( this, EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS ); + GetEventManager()->AddListener( this, EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE ); +} + +//============================================================================= +// DialogCoordinator::queueVillainDialog +//============================================================================= +// Description: If this is a villain event, queue up some villain dialog +// +// Parameters: id - walker/driver event to be mapped to villain event +// eventData - user data passed in with event +// +// Return: void +// +//============================================================================= +void DialogCoordinator::queueVillainDialog( EventEnum id, void* eventData ) +{ + EventEnum villainID = NUM_EVENTS; // Invalid placeholder value, shouldn't be used + Mission* currentMission; + MissionStage* currentStage; + MissionObjective* currentObjective; + MissionCondition* failureCondition; + Vehicle* eventCar; + Vehicle* AICar; + VehicleAI* AICarAI; + const char* villainName; + SelectableDialog* dialog; + GameplayManager* gameplayMgr; + rmt::Vector villainPosn; + rmt::Vector avatarPosn; + rmt::Vector diff; + bool noVillainLine = false; + + if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ) + { + // + // No villains in the FE + // + return; + } + + gameplayMgr = GetGameplayManager(); + if( ( gameplayMgr == NULL ) || ( gameplayMgr->IsSuperSprint() ) ) + { + return; + } + + // + // See if the given event ID is one that has corresponding villain dialog + // + switch( id ) + { + case EVENT_TAIL_LOST_DIALOG: + villainID = EVENT_VILLAIN_TAIL_EVADE; + break; + + case EVENT_MINOR_VEHICLE_CRASH: + case EVENT_BIG_VEHICLE_CRASH: + eventCar = static_cast<Vehicle*>(eventData); + if( ( eventCar != NULL ) + && ( gameplayMgr->GetCurrentMission() != NULL ) + && ( gameplayMgr->GetCurrentMission()->GetCurrentStage() != NULL ) + && ( gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage() != NULL ) ) + { + // + // Hack! + // + // Ideally, we should only play villain car crash dialogue if it's involved in the + // collision, apparently. Since we're trying to go final, just check to see if + // it's close by. Good enough. + // + AICar = gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage(); + AICar->GetPosition( &villainPosn ); + GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( avatarPosn ); + diff.Sub( villainPosn, avatarPosn ); + if( diff.MagnitudeSqr() > 75.0f ) + { + noVillainLine = true; + } + else + { + // + // Dig through the GameplayManager stuff to find out who is + // attacking who + // + currentMission = gameplayMgr->GetCurrentMission(); + rAssert( currentMission != NULL ); + currentStage = currentMission->GetCurrentStage(); + if (currentStage != NULL) + { + currentObjective = currentStage->GetObjective(); + rAssert( currentObjective != NULL ); + + if( currentObjective->GetObjectiveType() == MissionObjective::OBJ_DESTROY ) + { + villainID = EVENT_BIG_CRASH; + } + else + { + villainID = EVENT_VILLAIN_CAR_HIT_PLAYER; + } + } + else + { + noVillainLine = true; + } + } + } + else + { + noVillainLine = true; + } + break; + + case EVENT_MISSION_FAILURE: + failureCondition = static_cast<MissionCondition*>(eventData); + + // + // We only play villain dialog on mission failure + // when the player blew a tail mission + // + if( failureCondition->GetType() == MissionCondition::COND_FOLLOW_DISTANCE ) + { + villainID = EVENT_TAIL_LOST_DIALOG; + } + else if( failureCondition->IsChaseCondition() ) + { + villainID = EVENT_MISSION_FAILURE; + } + else + { + // + // No villain involved + // + noVillainLine = true; + } + break; + + case EVENT_MISSION_SUCCESS_DIALOG: + currentMission = gameplayMgr->GetCurrentMission(); + rAssert( currentMission != NULL ); + currentStage = currentMission->GetCurrentStage(); + if( currentStage != NULL ) + { + currentObjective = currentStage->GetObjective(); + rAssert( currentObjective != NULL ); + + if( currentObjective->GetObjectiveType() == MissionObjective::OBJ_DESTROY ) + { + villainID = EVENT_MISSION_SUCCESS_DIALOG; + } + else + { + noVillainLine = true; + } + } + else + { + noVillainLine = true; + } + break; + + default: + // + // No villain line needed for this type of event + // + noVillainLine = true; + break; + } + + if( noVillainLine ) + { + return; + } + + // + // Find out who the driver of the AI vehicle is and play the line + // if the dialog exists + // + // AICar = gameplayMgr->GetVehicleInSlot( GameplayManager::eAICar ); + + //Chuck: Esan use this method for getting the mainAI for a stage. It should work + + AICar = gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage(); + + if( AICar != NULL ) + { + if( ( id != EVENT_MISSION_FAILURE ) + && ( id != EVENT_MISSION_SUCCESS_DIALOG ) ) + { + // HACK: + // [Dusit Matthew Eakkachaichanvet: July 3rd, 2003] + // When a vehicle is destroyed and we ask for the AI, well, + // it wont' find that vehicle in the database... So.. we have some + // possible REAL fixes: + // - When a car is destroyed, I should send you an event and you should remember the new husk (or lack thereof). + // For this particular dialogue it doesn't matter. But y'know... maybe there are other bugs associated with this. + // - We check it here. I would prefer we STILL play the sound, but it's really your call. + if( !AICar->mVehicleDestroyed ) + { + AICarAI = GetVehicleCentral()->GetVehicleAI( AICar ); + + // At the end of the mission, we don't seem to have AI anymore... + if( ( AICarAI == NULL ) + || ( AICarAI->GetState() == VehicleAI::STATE_WAITING ) ) + { + // + // Don't play dialogue for inactive AI vehicles + // + return; + } + } + } + villainName = AICar->GetDriverName(); + rAssert( villainName != NULL ); + + // + // If the villain isn't there, then we're probably in a race mission or something + // where it isn't loaded + // + if( villainName != NULL ) + { + dialog = m_dialogList->FindDialogForEvent( villainID, NULL, NULL, tEntity::MakeUID( villainName ), 0, 0, true ); + + if( dialog == NULL ) + { + rDebugPrintf( "No dialog found for villain event %d\n", static_cast<int>( villainID ) ); + } + else + { + m_playbackQueue->AddDialogToQueue( *dialog ); + } + } + } +} + +//============================================================================= +// DialogCoordinator::playLinePositionally +//============================================================================= +// Description: Comment +// +// Parameters: ( EventEnum id ) +// +// Return: bool +// +//============================================================================= +bool DialogCoordinator::playLinePositionally( EventEnum id ) +{ + return( ( id == EVENT_KICK_NPC_SOUND ) + || ( id == EVENT_PEDESTRIAN_DODGE ) + || ( id == EVENT_PLAYER_CAR_HIT_NPC ) + || ( id == EVENT_TRAFFIC_IMPEDED ) ); +} + +//============================================================================= +// DialogCoordinator::getCharacterPosition +//============================================================================= +// Description: Comment +// +// Parameters: thePed - character to get position for. +// +// Return: position +// +//============================================================================= +void DialogCoordinator::getCharacterPosition( Character* thePed, rmt::Vector& posn ) +{ + rAssert( thePed != NULL ); + + // + // Get position from the character + // + thePed->GetPosition( posn ); +} + +//============================================================================= +// DialogCoordinator::eventHasVehicleData +//============================================================================= +// Description: Indicate whether the given event has a vehicle as a data +// parameter (stinky void*'s) +// +// Parameters: id - event ID to check +// +// Return: true if vehicle parameter expected, false otherwise +// +//============================================================================= +bool DialogCoordinator::eventHasVehicleData( EventEnum id ) +{ + bool retVal = false; + + switch( id ) + { + case EVENT_MINOR_VEHICLE_CRASH: + case EVENT_BIG_VEHICLE_CRASH: + case EVENT_WRONG_SIDE_DUMBASS: + case EVENT_TRAFFIC_IMPEDED: + case EVENT_BIG_BOOM_SOUND: + case EVENT_DEATH_VOLUME_SOUND: + retVal = true; + break; + + default: + break; + } + + return( retVal ); +} diff --git a/game/code/sound/dialog/dialogcoordinator.h b/game/code/sound/dialog/dialogcoordinator.h new file mode 100644 index 0000000..e162de4 --- /dev/null +++ b/game/code/sound/dialog/dialogcoordinator.h @@ -0,0 +1,84 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogcoordinator.h +// +// Description: The main interface class for the dialog system. It listens +// for game events and cues the appropriate line of dialog. +// Objects representing lines or sets of lines are obtained +// from the SpeechCoordinator, and are passed to the +// DialogPriorityQueue, which determines when playback is +// ready to begin. +// +// History: 30/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGCOORDINATOR_H +#define DIALOGCOORDINATOR_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/dialog/dialogpriorityqueue.h> +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +struct IRadNameSpace; +class DialogList; +class Character; +class Vehicle; + +//============================================================================= +// +// Synopsis: DialogCoordinator +// +//============================================================================= + +class DialogCoordinator : public EventListener +{ + public: + DialogCoordinator( IRadNameSpace* namespaceObj ); + virtual ~DialogCoordinator(); + + void Initialize(); + void OnGameplayEnd() { m_playbackQueue->StopAllDialog(); } + + void OnPauseStart() { m_playbackQueue->PauseDialog(); } + void OnPauseEnd() { m_playbackQueue->UnpauseDialog(); } + + void HandleEvent( EventEnum id, void* pEventData ); + + void ServiceOncePerFrame() { m_playbackQueue->ServiceOncePerFrame(); } + + private: + //Prevent wasteful constructor creation. + DialogCoordinator(); + DialogCoordinator( const DialogCoordinator& original ); + DialogCoordinator& operator=( const DialogCoordinator& rhs ); + + void registerDialogEvents(); + void queueVillainDialog( EventEnum id, void* eventData ); + bool playLinePositionally( EventEnum id ); + void getCharacterPosition( Character* thePed, rmt::Vector& posn ); + bool eventHasVehicleData( EventEnum id ); + + IRadNameSpace* m_dialogNamespace; + + DialogList* m_dialogList; + DialogPriorityQueue* m_playbackQueue; + + bool m_dialogOn; + + // + // Hack! + // + bool m_phoneBoothRequestMade; +}; + + +#endif // DIALOGCOORDINATOR_H + diff --git a/game/code/sound/dialog/dialogline.cpp b/game/code/sound/dialog/dialogline.cpp new file mode 100644 index 0000000..d23c3f2 --- /dev/null +++ b/game/code/sound/dialog/dialogline.cpp @@ -0,0 +1,1018 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogline.cpp +// +// Description: Atomic unit of dialog. A DialogLine object represents a +// complete line of dialog spoken by a single character. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radkey.hpp> +#include <radnamespace.hpp> +#include <p3d/anim/skeleton.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogline.h> + +#include <sound/dialog/dialogselectiongroup.h> + +#include <sound/simpsonssoundplayer.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundrenderingmanager.h> + +#include <mission/gameplaymanager.h> +#include <worldsim/character/character.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +EventTableEntry eventTable[] = +{ + { "GIC", 0, EVENT_GETINTOVEHICLE_START, 2500 }, + { "GOC", 0, EVENT_GETOUTOFVEHICLE_START, 2500 }, + { "convinit", 0, EVENT_CONVERSATION_INIT_DIALOG, 2500 }, + { "noboxconv", 0, EVENT_IN_GAMEPLAY_CONVERSATION, 2500 }, + { "tutorial", 0, EVENT_TUTORIAL_DIALOG_PLAY, 2500 }, + { "Mcrash", 0, EVENT_MINOR_VEHICLE_CRASH, 2500 }, + { "Bcrash", 0, EVENT_BIG_VEHICLE_CRASH, 2500 }, + { "Arrive", 0, EVENT_DESTINATION_REACHED, 2500 }, + { "Air", 0, EVENT_BIG_AIR, 2500 }, + { "Burn", 0, EVENT_BURNOUT, 2500 }, + { "ObjectW", 0, EVENT_COLLECT_OBJECT, 2500 }, + { "Break", 0, EVENT_HIT_BREAKABLE, 2500 }, + { "Mfail", 0, EVENT_MISSION_FAILURE, 4000 }, + { "Mvic", 0, EVENT_MISSION_SUCCESS_DIALOG, 4000 }, + { "Tail", 0, EVENT_TAIL_LOST_DIALOG, 2500 }, + { "NewAI", 0, EVENT_CHASE_VEHICLE_PROXIMITY, 2500 }, + { "Time", 0, EVENT_TIME_RUNNING_OUT, 2500 }, + { "Pass", 0, EVENT_RACE_PASSED_AI, 2500 }, + { "Passed", 0, EVENT_RACE_GOT_PASSED_BY_AI, 2500 }, + { "Activate", 0, EVENT_BIG_RED_SWITCH_PRESSED, 250 }, // Make this quite short + { "Fall", 0, EVENT_DEATH_VOLUME_SOUND, 2500 }, + { "Char", 0, EVENT_PEDESTRIAN_SMACKDOWN, 2500 }, + { "HitByW", 0, EVENT_KICK_NPC_SOUND, 2500 }, + { "BreakCa", 0, EVENT_BREAK_CAMERA_OR_BOX, 2500 }, + { "Turbo", 0, EVENT_CHARACTER_TIRED_NOW, 2500 }, + { "Springboard", 0, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::BOUNCEPAD), 2500 }, + { "NHitByC", 0, EVENT_PEDESTRIAN_DODGE, 2500 }, + { "HitByC", 0, EVENT_PLAYER_CAR_HIT_NPC, 2500 }, + { "HitP", 0, EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC, 2500 }, + { "Damage", 0, EVENT_BIG_CRASH, 2500 }, + { "Door", 0, EVENT_WRONG_SIDE_DUMBASS, 2500 }, + { "CarWay", 0, EVENT_TRAFFIC_IMPEDED, 2500 }, + { "Doorbell", 0, EVENT_DING_DONG, 2500 }, + { "Askride", 0, EVENT_PHONE_BOOTH_RIDE_REQUEST, 0 }, + { "Ridereply", 0, EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, 0 }, + { "Answer", 0, EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED, 0 }, + { "HitCar", 0, EVENT_VILLAIN_CAR_HIT_PLAYER, 2500 }, + { "Greeting", 0, EVENT_AMBIENT_GREETING, 2500 }, + { "Idlereply", 0, EVENT_AMBIENT_RESPONSE, 0 }, + { "Askfood", 0, EVENT_AMBIENT_ASKFOOD, 2500 }, + { "Foodreply", 0, EVENT_AMBIENT_FOODREPLY, 0 }, + { "CarBuy", 0, EVENT_HAGGLING_WITH_GIL, 2500 }, + { "Dcar", 0, EVENT_BIG_BOOM_SOUND, 5000 }, + { "Card", 0, EVENT_CARD_COLLECTED, 2500 }, + { "Mstart", 0, EVENT_MISSION_BRIEFING_ACCEPTED, 0 } +}; + +static unsigned int eventTableLength = sizeof( eventTable ) / sizeof( EventTableEntry ); + +CharacterTableEntry characterTable[] = +{ + { 0, "Hom", 0, "homer" }, + { 0, "Mrg", 0, "marge" }, + { 0, "Brt", 0, "bart" }, + { 0, "Lis", 0, "lisa" }, + { 0, "Apu", 0, "apu" }, + { 0, "Agn", 0, "askinner" }, + { 0, "Brn", 0, "barney" }, + { 0, "Bee", 0, "beeman" }, + { 0, "Crl", 0, "carl" }, + { 0, "Cbg", 0, "cbg" }, + { 0, "Clt", 0, "cletus" }, + { 0, "Dol", 0, "dolph" }, + { 0, "Nic", 0, "nriviera" }, + { 0, "Frk", 0, "frink" }, + { 0, "Fla", 0, "ned" }, + { 0, "By1", 0, "boy1" }, + { 0, "By2", 0, "boy2" }, + { 0, "Fm1", 0, "fem1" }, + { 0, "Fm2", 0, "fem2" }, + { 0, "Gil", 0, "gil" }, + { 0, "Gr1", 0, "girl1" }, + { 0, "Gr2", 0, "girl2" }, + { 0, "Kan", 0, "kang" }, + { 0, "Kod", 0, "kodos" }, + { 0, "Mn1", 0, "male1" }, + { 0, "Mn2", 0, "male2" }, + { 0, "Grp", 0, "grandpa" }, + { 0, "Mol", 0, "moleman" }, + { 0, "Jas", 0, "jasper" }, + { 0, "Jim", 0, "jimbo" }, + { 0, "Kea", 0, "kearney" }, + { 0, "Kru", 0, "krusty" }, + { 0, "Len", 0, "lenny" }, + { 0, "Loe", 0, "lou" }, + { 0, "Lou", 0, "louie" }, + { 0, "Mil", 0, "milhouse" }, + { 0, "Moe", 0, "moe" }, + { 0, "Nel", 0, "nelson" }, + { 0, "Oto", 0, "otto" }, + { 0, "Pat", 0, "patty" }, + { 0, "Qim", 0, "quimby" }, + { 0, "Ral", 0, "ralph" }, + { 0, "Sea", 0, "captain" }, + { 0, "Sel", 0, "selma" }, + { 0, "Skn", 0, "skinner" }, + { 0, "Smi", 0, "smithers" }, + { 0, "Snk", 0, "snake" }, + { 0, "Svt", 0, "teen" }, + { 0, "Wig", 0, "wiggum" }, + { 0, "Wil", 0, "willie" }, + { 0, "Zom", 0, "zombie" }, + { 0, "Zm1", 0, "zombie1" }, + { 0, "Zm2", 0, "zombie2" }, + { 0, "Zm3", 0, "zombie3" }, + { 0, "Zm4", 0, "zombie4" }, + { 0, "Hib", 0, "hibbert" }, + { 0, "Bur", 0, "burns" }, + { 0, "Rod", 0, "rod" }, + { 0, "Todd", 0, "todd" }, + { 0, "Bkm", 0, "brockman" }, + { 0, "Hbn", 0, "homerbrain" }, + { 0, "Vm1", 0, "traffic1" }, + { 0, "Vm2", 0, "traffic2" }, + { 0, "Vm3", 0, "traffic3" }, + { 0, "Vm4", 0, "traffic4" } +}; + +static unsigned int characterTableLength = sizeof( characterTable ) / sizeof( CharacterTableEntry ); + +// +// The food guys +// +static unsigned int APU_INDEX = 4; +static unsigned int TEEN_INDEX = 47; + +// +// Arbitrary number, used in field name buffers +// +static const int FIELD_BUFFER_LEN = 64; +static const int MY_FILENAME_BUFFER_LEN = 150; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogLine::DialogLine +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogLine::DialogLine( IDaSoundResource* resource ) +{ + m_resource = resource; + m_characterIndex = -1; + m_role = ROLE_NONE; + m_conversationPosition = NOT_CONVERSATION_LINE; + m_ConversationName = 0; + + static bool tablesInitialized = false; + + if( !tablesInitialized ) + { + initializeTables(); + tablesInitialized = true; + } + + parseResourceFilename(); +} + +//============================================================================== +// DialogLine::~DialogLine +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogLine::~DialogLine() +{ +} + +//============================================================================== +// DialogLine::PlayLine +//============================================================================== +// Description: Play the dialog line +// +// Parameters: lineIndex - ignored, only used for other SelectableDialog subclasses +// player - what we use for the playing +// callback - who we call when we're done +// +// Return: N/A. +// +//============================================================================== +void DialogLine::PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + player.PlayResource( m_resource, callback ); +} + +//============================================================================= +// DialogLine::QueueLine +//============================================================================= +// Description: Buffer a line of dialog for playback +// +// Parameters: lineIndex - ignored, only used for other SelectableDialog subclasses +// player - what we use for the queueing +// +// Return: void +// +//============================================================================= +void DialogLine::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player ) +{ + player.QueueSound( m_resource ); +} + +//============================================================================= +// DialogLine::PlayQueuedLine +//============================================================================= +// Description: Play the sound we queued with QueueLine +// +// Parameters: player - what we use for the playing +// callback - if non-NULL, object to notify when playback done +// +// Return: void +// +//============================================================================= +void DialogLine::PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + player.PlayQueuedSound( callback ); +} + +//============================================================================= +// DialogLine::StripDirectoryCrud +//============================================================================= +// Description: Utility function for stripping directory prefix from filenames +// +// Parameters: filename - filename to strip +// buffer - holds stripped filename +// bufferLen - length of buffer +// +// Return: void +// +//============================================================================= +void DialogLine::StripDirectoryCrud( const char* filename, char* buffer, int bufferLen ) +{ + const char* charPtr; + int i, j; + int strippedLength = 0; + + i = strlen( filename ); + charPtr = &(filename[i]); + while( i > 0 ) + { + if( ( *charPtr == '/' ) || ( *charPtr == '\\' ) ) + { + break; + } + else + { + ++strippedLength; + --i; + --charPtr; + } + } + + // + // Copy stripped filename to buffer + // + if( i > 0 ) + { + ++charPtr; + } + + if( strippedLength > bufferLen ) + { + strippedLength = bufferLen; + } + + for( j = 0; j < strippedLength; j++ ) + { + buffer[j] = *charPtr++; + } +} + +//============================================================================= +// DialogLine::GetEventTableEntry +//============================================================================= +// Description: Comment +// +// Parameters: ( unsigned int index ) +// +// Return: EventTableEntry +// +//============================================================================= +const EventTableEntry* DialogLine::GetEventTableEntry( unsigned int index ) +{ + if( index >= eventTableLength ) + { + return( NULL ); + } + else + { + return( &(eventTable[index]) ); + } +} + +//============================================================================= +// DialogLine::GetEventTableSize +//============================================================================= +// Description: Return the number of entries in the event table +// +// Parameters: None +// +// Return: table size +// +//============================================================================= +unsigned int DialogLine::GetEventTableSize() +{ + return( eventTableLength ); +} + +//============================================================================= +// DialogLine::GetCharacterTableEntry +//============================================================================= +// Description: Comment +// +// Parameters: ( unsigned int index ) +// +// Return: CharacterTableEntry +// +//============================================================================= +const CharacterTableEntry* DialogLine::GetCharacterTableEntry( unsigned int index ) +{ + if( index >= characterTableLength ) + { + return( NULL ); + } + else + { + return( &(characterTable[index]) ); + } +} + +//============================================================================= +// DialogLine::GetCharacterTableSize +//============================================================================= +// Description: Return the number of entries in the character table +// +// Parameters: None +// +// Return: table size +// +//============================================================================= +unsigned int DialogLine::GetCharacterTableSize() +{ + return( characterTableLength ); +} + +//============================================================================= +// DialogLine::UsesCharacter +//============================================================================= +// Description: Indicate whether this dialog line comes from +// the character given +// +// Parameters: characterObj - character to match +// +// Return: true if character matches this line, false otherwise +// +//============================================================================= +bool DialogLine::UsesCharacter( tUID characterUID ) +{ + return( characterUID == GetCharacterUID( ) ); +} + +//============================================================================= +// DialogLine::AddMatchingDialog +//============================================================================= +// Description: Request to a SelectableDialog object (this one) to add a new +// DialogLine or such with the same characteristics. Since this +// object represents a single line, we need to create a +// DialogSelectionGroup and add self and the new dialog line to it, +// and put it in our spot in the list. +// +// Parameters: newDialog - dialogLine that matches this one +// +// Return: void +// +//============================================================================= +void DialogLine::AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ) +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + DialogSelectionGroup* group = new DialogSelectionGroup( *this, newDialog ); + + rAssert( group != NULL ); + + list.remove( this ); + list.push_back( group ); + + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); +} + +//============================================================================= +// DialogLine::GetConversationName +//============================================================================= +// Description: Returns the name of the conversation this line belongs to, +// if this is a conversation line. We'll calculate it on the +// fly, no sense in wasting more space than we already are. +// +// Parameters: None +// +// Return: radKey32 representation of conversation name field, or 0 +// if this isn't a conversation line +// +//============================================================================= + +void DialogLine::parseConversationName( ) +{ + bool fieldFound; + char fieldBuffer[FIELD_BUFFER_LEN]; + char filenameBuffer[MY_FILENAME_BUFFER_LEN]; + char tempBuffer[MY_FILENAME_BUFFER_LEN]; + + // + // Strip the directory crud + // + m_resource->GetFileNameAt( 0, tempBuffer, MY_FILENAME_BUFFER_LEN ); + StripDirectoryCrud( tempBuffer, filenameBuffer, MY_FILENAME_BUFFER_LEN ); + + fieldFound = getNameField( filenameBuffer, 0, fieldBuffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + if( ( fieldBuffer[0] == 'C') && ( fieldBuffer[1] == '\0' ) ) + { + // + // Conversation line + // + fieldFound = getNameField( filenameBuffer, 1, fieldBuffer, FIELD_BUFFER_LEN ); + m_ConversationName = radMakeKey32( fieldBuffer ); + } + else + { + // + // Not conversation line + // + rTuneAssert( false ); + } +} + +//============================================================================= +// DialogLine::IsFoodCharacter +//============================================================================= +// Description: Determine whether this character gets Askfood lines or +// Idlereply +// +// Parameters: theGuy - ambient character being talked to +// +// Return: true for Askfood, false for Idlereply +// +//============================================================================= +bool DialogLine::IsFoodCharacter( Character* theGuy ) +{ + tUID UID1; + bool retVal = false; + + choreo::Puppet* puppet = theGuy->GetPuppet(); + if( puppet != NULL ) + { + UID1 = puppet->GetPose()->GetSkeleton()->GetUID(); + + // + // Hard-coded hack + // + rAssert( strcmp( "Apu", characterTable[APU_INDEX].characterString ) == 0 ); + rAssert( strcmp( "Svt", characterTable[TEEN_INDEX].characterString ) == 0 ); + + if( ( UID1 == static_cast< tUID >( characterTable[APU_INDEX].realCharacterUID ) ) + || ( UID1 == static_cast< tUID >( characterTable[TEEN_INDEX].realCharacterUID ) ) ) + { + retVal = true; + } + } + + return( retVal ); +} + +//============================================================================= +// DialogLine::GetLifeInMsecsForEvent +//============================================================================= +// Description: Given an event, return the number of milliseconds that the +// dialog for that event should be allowed to live in the queue +// +// Parameters: eventID - ID for event +// +// Return: lifetime in msecs, 0 for infinite lifetime +// +//============================================================================= +unsigned int DialogLine::GetLifeInMsecsForEvent( EventEnum eventID ) +{ + unsigned int i; + unsigned int lifetime = 2500; // 2.5 sec. default + + for( i = 0; i < eventTableLength; i++ ) + { + if( eventTable[i].event == eventID ) + { + lifetime = eventTable[i].lifeInMsecs; + break; + } + } + + return( lifetime ); +} + +//============================================================================= +// DialogLine::FillCharacterName +//============================================================================= +// Description: Fill the given buffer with the text name of the character +// matching the given UID +// +// Parameters: buffer - buffer to write name to +// bufferSize - length of buffer +// characterUID - UID of character to find name for +// +// Return: void +// +//============================================================================= +void DialogLine::FillCharacterName( char* buffer, unsigned int bufferSize, tUID characterUID ) +{ + unsigned int i; + unsigned int tableSize; + const CharacterTableEntry* charTableLine; + + tableSize = GetCharacterTableSize(); + + for( i = 0; i < tableSize; i++ ) + { + charTableLine = GetCharacterTableEntry( i ); + if( charTableLine->realCharacterUID == characterUID ) + { + break; + } + } + + if( i < tableSize ) + { + rAssert( strlen( charTableLine->characterString ) < bufferSize ); + strcpy( buffer, charTableLine->characterString ); + } + else + { + rAssert( bufferSize >= 4 ); + strcpy( buffer, "???" ); + } +} + +//============================================================================= +// DialogLine::FillEventName +//============================================================================= +// Description: Fill the given buffer with the text name of the event +// matching the given ID +// +// Parameters: buffer - buffer to write name to +// bufferSize - length of buffer +// eventID - ID of event to find name for +// +// Return: void +// +//============================================================================= +void DialogLine::FillEventName( char* buffer, unsigned int bufferSize, EventEnum eventID ) +{ + unsigned int i; + unsigned int tableSize; + const EventTableEntry* eventTableLine; + + tableSize = GetEventTableSize(); + + for( i = 0; i < tableSize; i++ ) + { + eventTableLine = GetEventTableEntry( i ); + if( eventTableLine->event == eventID ) + { + break; + } + } + + if( i < tableSize ) + { + rAssert( strlen( eventTableLine->eventString ) < bufferSize ); + strcpy( buffer, eventTableLine->eventString ); + } + else + { + rAssert( bufferSize >= 4 ); + strcpy( buffer, "???" ); + } +} + +//============================================================================= +// DialogLine::PrintResourceName +//============================================================================= +// Description: Dump out the name of the sound resource. For debugging. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogLine::PrintResourceName() +{ +#ifndef RAD_RELEASE + char buffer[256]; + + rAssert( m_resource != NULL ); + + m_resource->GetFileNameAt( 0, buffer, 256 ); + rDebugPrintf( "DialogLine - %s\n", buffer ); +#endif +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogLine::parseResourceFilename +//============================================================================= +// Description: Look at the resource filename and fill out the dialog line +// attributes based on what we find +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogLine::parseResourceFilename() +{ + bool fieldFound; + char fieldBuffer[FIELD_BUFFER_LEN]; + char filenameBuffer[MY_FILENAME_BUFFER_LEN]; + char tempBuffer[MY_FILENAME_BUFFER_LEN]; + + // + // Strip the directory crud + // + m_resource->GetFileNameAt( 0, tempBuffer, MY_FILENAME_BUFFER_LEN ); + StripDirectoryCrud( tempBuffer, filenameBuffer, MY_FILENAME_BUFFER_LEN ); + + fieldFound = getNameField( filenameBuffer, 0, fieldBuffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + if( ( fieldBuffer[0] == 'C') && ( fieldBuffer[1] == '\0' ) ) + { + // + // Conversation line + // + parseConversationName( ); + matchOrderField( filenameBuffer, 2 ); + matchEventField( filenameBuffer, 3 ); + matchCharacterField( filenameBuffer, 4 ); + matchLevelField( filenameBuffer, 5 ); + } + else + { + // + // One-liner + // + matchRoleField( filenameBuffer, 0 ); + matchEventField( filenameBuffer, 1 ); + matchCharacterField( filenameBuffer, 2 ); + matchLevelField( filenameBuffer, 3 ); + } +} + +void DialogLine::matchRoleField( const char* filename, int field ) +{ + char buffer[FIELD_BUFFER_LEN]; + bool fieldFound; + + fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + switch( buffer[0] ) + { + case 'W': + m_role = ROLE_WALKER; + break; + case 'D': + m_role = ROLE_DRIVER; + break; + case 'P': + m_role = ROLE_PEDESTRIAN; + break; + case 'V': + m_role = ROLE_VILLAIN; + break; + default: + rAssertMsg( false, "Unknown role field in dialog file\n" ); + break; + } +} + +//============================================================================= +// DialogLine::matchEventField +//============================================================================= +// Description: Parse the field at the given position and store the eventEnum +// that matches it +// +// Parameters: field - number of field to parse +// +// Return: void +// +//============================================================================= +void DialogLine::matchEventField( const char* filename, int field ) +{ + unsigned int i; + char buffer[FIELD_BUFFER_LEN]; + radKey32 fieldKey; + bool fieldFound; + + // + // Event name. Use a lookup table to translate to the event + // enumeration. + // + fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + fieldKey = ::radMakeCaseInsensitiveKey32( buffer ); + for( i = 0; i < eventTableLength; i++ ) + { + if( fieldKey == eventTable[i].eventKey ) + { + m_event = eventTable[i].event; + break; + } + } +} + +//============================================================================= +// DialogLine::matchOrderField +//============================================================================= +// Description: Parse the field at the given position and store it as the value +// for the conversation position +// +// Parameters: field - number of field to parse +// +// Return: void +// +//============================================================================= +void DialogLine::matchOrderField( const char* filename, int field ) +{ + char buffer[FIELD_BUFFER_LEN]; + bool fieldFound; + + // + // Event name. Use a lookup table to translate to the event + // enumeration. + // + fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + rAssert( buffer[0] >= '0' ); + rAssert( buffer[0] <= '9' ); + + m_conversationPosition = buffer[0] - '0'; +} + +//============================================================================= +// DialogLine::matchCharacterField +//============================================================================= +// Description: Parse the field at the given position and store the characterEnum +// that matches it +// +// Parameters: field - number of field to parse +// +// Return: void +// +//============================================================================= +void DialogLine::matchCharacterField( const char* filename, int field ) +{ + unsigned int i; + char buffer[FIELD_BUFFER_LEN]; + radKey32 fieldKey; + bool fieldFound; + + // + // Character. Use another lookup table. + // + fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN ); + rAssert( fieldFound ); + + fieldKey = ::radMakeCaseInsensitiveKey32( buffer ); + for( i = 0; i < characterTableLength; i++ ) + { + if( fieldKey == characterTable[i].characterKey ) + { + m_characterIndex = i; + break; + } + } + + rAssert( ( i < characterTableLength ) + || ( filename[0] != 'C' ) ); +} + +//============================================================================= +// DialogLine::matchLevelField +//============================================================================= +// Description: Parse the field at the given position and store the level +// and mission numbers that match it +// +// Parameters: field - number of field to parse +// +// Return: void +// +//============================================================================= +void DialogLine::matchLevelField( const char* filename, int field ) +{ + char buffer[FIELD_BUFFER_LEN]; + bool fieldFound; + + // + // Level/Mission. + // + fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN ); + + if( fieldFound ) + { + if( ( buffer[0] == 'L' ) + && ( buffer[1] >= '0' ) + && ( buffer[1] <= '9' ) ) + { + // + // Level number, start to string is "Lx" + // + m_levelNum = buffer[1] - '0'; + rAssert( m_levelNum > 0 ); + rAssert( m_levelNum < GameplayManager::MAX_LEVELS ); + + if( strlen( buffer ) >= 4 ) + { + // + // Level and mission, expected string is "LxMy" + // + rAssert( ( buffer[2] == 'M' ) || ( buffer[2] == 'B' ) || ( buffer[2] == 'R' ) || ( buffer[2] == 'T' ) ); + m_missionNum = buffer[3] - '0'; + rAssert( m_missionNum >= 1 ); + rAssert( m_missionNum <= 7 ); + + // + // Bonus == 8 + // Races == 9-11 + // Tutorial == 12 + // + if( buffer[2] == 'B' ) + { + rAssert( m_missionNum == 1 ); + m_missionNum = BONUS_MISSION_NUMBER; + } + else if( buffer[2] == 'R' ) + { + rAssert( m_missionNum >= 1 ); + rAssert( m_missionNum <= 3 ); + m_missionNum += FIRST_RACE_MISSION_NUMBER - 1; + } + else if( buffer[2] == 'T' ) + { + rAssert( m_missionNum == 1 ); + m_missionNum = TUTORIAL_MISSION_NUMBER; + } + } + else + { + m_missionNum = NO_MISSION; + } + } + } +} + +//============================================================================= +// DialogLine::getNameField +//============================================================================= +// Description: Parse the filename for a particular field, and return it in +// the given buffer +// +// Parameters: filename -- name of file to parse +// field -- number of field to find (counting from 0) +// buffer -- buffer to copy field into +// bufferLen -- length of buffer +// +// Return: true if requested field exists, false otherwise +// +//============================================================================= +bool DialogLine::getNameField( const char* filename, int field, char* buffer, int bufferLen ) +{ + const char* currentChar; + int i; + int fieldCount = field; + bool fieldFound = false; + + currentChar = filename; + while( ( fieldCount > 0 ) && ( *currentChar != '\0' ) && ( *currentChar != '.' ) ) + { + if( *currentChar == '_' ) + { + --fieldCount; + } + + ++currentChar; + } + + if( ( *currentChar != '\0' ) && ( *currentChar != '.' ) ) + { + // + // Field found, copy to buffer. Since GC has no string functions (argh), + // use hand-rolled strncpy + // + for( i = 0; + ( (i < bufferLen-1) && (currentChar[i] != '\0') && (currentChar[i] != '_') && (currentChar[i] != '.') ); + i++ ) + { + buffer[i] = currentChar[i]; + } + buffer[i] = '\0'; + + fieldFound = true; + } + + return( fieldFound ); +} + +//============================================================================= +// DialogLine::initializeTables +//============================================================================= +// Description: Fill out the radKey32 entries in the event table. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogLine::initializeTables() +{ + unsigned int i; + + for( i = 0; i < eventTableLength; i++ ) + { + eventTable[i].eventKey = ::radMakeCaseInsensitiveKey32( eventTable[i].eventString ); + } + + for( i = 0; i < characterTableLength; i++ ) + { + characterTable[i].characterKey = ::radMakeCaseInsensitiveKey32( characterTable[i].characterString ); + characterTable[i].realCharacterUID = tEntity::MakeUID( characterTable[i].realCharacterName ); + } +} + +tUID DialogLine::GetCharacterUID( void ) +{ + if ( -1 == m_characterIndex ) + { + return 0; + } + + tUID uid = characterTable[ m_characterIndex ].realCharacterUID; + return uid; +} + +tUID DialogLine::GetDialogLineCharacterUID( unsigned int lineNum ) +{ + return GetCharacterUID( ); +} +
\ No newline at end of file diff --git a/game/code/sound/dialog/dialogline.h b/game/code/sound/dialog/dialogline.h new file mode 100644 index 0000000..1d72cdb --- /dev/null +++ b/game/code/sound/dialog/dialogline.h @@ -0,0 +1,172 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogline.h +// +// Description: Atomic unit of dialog. A DialogLine object represents a +// complete line of dialog spoken by a single character. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGLINE_H +#define DIALOGLINE_H + +//======================================== +// Nested Includes +//======================================== +#include <p3d/p3dtypes.hpp> + +#include <sound/dialog/playabledialog.h> +#include <sound/dialog/selectabledialoglist.h> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundresource.h> +//======================================== +// Forward References +//======================================== + +class Character; + +// +// Table entry structure for mapping between event strings in filenames +// and events responded to in dialog system +// +struct EventTableEntry +{ + const char* eventString; + radKey32 eventKey; + EventEnum event; + unsigned int lifeInMsecs; +}; + +// +// Table entry structure for mapping between character strings in filenames +// and tUIDs that we pull out of the Character objects. +// +struct CharacterTableEntry +{ + radInt64 realCharacterUID; + const char* characterString; + radKey32 characterKey; + const char* realCharacterName; +}; + +const char ROLE_NONE = 0; +const char ROLE_WALKER = 1; +const char ROLE_DRIVER = 2; +const char ROLE_PEDESTRIAN = 3; +const char ROLE_VILLAIN = 4; + +typedef char DialogRole; + +//============================================================================= +// +// Synopsis: DialogLine +// +//============================================================================= + +class DialogLine : public PlayableDialog +{ + public: + DialogLine( IDaSoundResource* resource ); + virtual ~DialogLine(); + + static const int NOT_CONVERSATION_LINE = -1; + + static const int BONUS_MISSION_NUMBER = 8; + static const int FIRST_RACE_MISSION_NUMBER = 9; + static const int TUTORIAL_MISSION_NUMBER = 12; + + int GetConversationPosition() { return( m_conversationPosition ); } + tUID GetCharacterUID(); + tUID GetDialogLineCharacterUID( unsigned int lineNum ); + bool IsVillainLine() { return( m_role == ROLE_VILLAIN ); } + + inline radKey32 GetConversationName( ); + + // + // Pure virtual functions from SelectableDialog + // + void PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + void QueueLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player ); + void PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + + unsigned int GetNumDialogLines() const { return( 1 ); } + bool UsesCharacter( tUID characterUID ); + void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ); + + static bool IsFoodCharacter( Character* theGuy ); + static unsigned int GetLifeInMsecsForEvent( EventEnum eventID ); + + // + // Utility for stripping the directory crud off of filenames + // + static void StripDirectoryCrud( const char* filename, char* buffer, int bufferLen ); + + // + // Accessors for tables + // + static const EventTableEntry* GetEventTableEntry( unsigned int index ); + static unsigned int GetEventTableSize(); + + static const CharacterTableEntry* GetCharacterTableEntry( unsigned int index ); + static unsigned int GetCharacterTableSize(); + + static void FillEventName( char* buffer, unsigned int bufferSize, EventEnum eventID ); + static void FillCharacterName( char* buffer, unsigned int bufferSize, tUID characterUID ); + + // + // For debugging + // + void PrintResourceName(); + + private: + //Prevent wasteful constructor creation. + DialogLine(); + DialogLine( const DialogLine& original ); + DialogLine& operator=( const DialogLine& rhs ); + + void parseResourceFilename(); + bool getNameField( const char* filename, int field, char* buffer, int bufferLen ); + void initializeTables(); + + void matchRoleField( const char* filename, int field ); + void matchOrderField( const char* filename, int field ); + void matchEventField( const char* filename, int field ); + void matchCharacterField( const char* filename, int field ); + void matchLevelField( const char* filename, int field ); + void parseConversationName( ); + // + // Sound resource to play + // + IDaSoundResource* m_resource; + + radKey32 m_ConversationName; + + DialogRole /* (char)*/ m_role; + + // + // Position within conversation if this is a conversation line + // + + char m_conversationPosition; + + // + // Character that this line belongs to + // + char m_characterIndex; +}; + +inline radKey32 DialogLine::GetConversationName( ) +{ + return m_ConversationName; +} + +#endif // DIALOGLINE_H + diff --git a/game/code/sound/dialog/dialoglist.cpp b/game/code/sound/dialog/dialoglist.cpp new file mode 100644 index 0000000..0dcab5e --- /dev/null +++ b/game/code/sound/dialog/dialoglist.cpp @@ -0,0 +1,1259 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialoglist.cpp +// +// Description: Loads and maintains the list of dialog lines and conversations +// (which group multiple dialog lines, and potentially link +// conversations to other conversations that occur later). +// +// History: 01/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> +#include <raddebugwatch.hpp> + +#include <p3d/anim/skeleton.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialoglist.h> + +#include <sound/dialog/dialogline.h> +#include <sound/dialog/conversationmatcher.h> +#include <sound/soundrenderer/idasoundresource.h> + +#include <memory/srrmemory.h> +#include <mission/gameplaymanager.h> +#include <render/Enums/RenderEnums.h> +#include <gameflow/gameflow.h> +#include <worldsim/character/charactermanager.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Arbitrary buffer length for filenames +// +static const int FILENAME_BUFFER_LEN = 100; + +// +// Stinky hack support +// +radKey32 DialogList::s_introKey = 0; +radKey32 DialogList::s_aztecKey = 0; +tUID DialogList::s_milhouseKey = 0; +tUID DialogList::s_nelsonKey = 0; +tUID DialogList::s_raceZombie1 = 0; +tUID DialogList::s_raceZombie2 = 0; + +enum pedDialogType +{ + PED_MALE1, + PED_MALE2, + PED_FEMALE1, + PED_FEMALE2, + PED_BOY1, + PED_BOY2, + PED_GIRL1, + PED_GIRL2, + PED_ZOMBIE1, + PED_ZOMBIE2, + PED_ZOMBIE3, + PED_ZOMBIE4, + + PED_NUM_TYPES +}; + +enum skinDialogType +{ + SKIN_APU, + SKIN_BART, + SKIN_HOMER, + SKIN_LISA, + SKIN_MARGE, + SKIN_BARNEY, + SKIN_MILHOUSE, + SKIN_NELSON, + SKIN_RALPH, + SKIN_CLETUS, + SKIN_ZOMBIE1, + SKIN_ZOMBIE2, + SKIN_ZOMBIE3, + SKIN_ZOMBIE4, + SKIN_OTTO, + SKIN_WILLIE, + SKIN_KEARNEY, + SKIN_SKINNER, + SKIN_GRANDPA, + SKIN_CBG, + SKIN_FRINK, + SKIN_SNAKE, + SKIN_SMITHERS, + + SKIN_NUM_TYPES +}; + +struct pedTypeInfo +{ + radInt64 pedUID; + const char* pedName; + pedDialogType dialogGroup; +}; + +static pedTypeInfo pedestrianNameTable[] = +{ + { 0, "boy", PED_BOY1 }, + { 0, "boy2", PED_BOY2 }, + { 0, "boy3", PED_BOY1 }, + { 0, "bum", PED_MALE1 }, + { 0, "busm1", PED_MALE2 }, + { 0, "busm2", PED_MALE1 }, + { 0, "busw1", PED_FEMALE1 }, + { 0, "const1", PED_MALE2 }, + { 0, "const2", PED_MALE1 }, + { 0, "farmr1", PED_MALE2 }, + { 0, "fem1", PED_FEMALE2 }, + { 0, "fem2", PED_FEMALE1 }, + { 0, "fem3", PED_FEMALE2 }, + { 0, "fem4", PED_FEMALE1 }, + { 0, "girl1", PED_GIRL1 }, + { 0, "girl2", PED_GIRL2 }, + { 0, "hooker", PED_FEMALE2 }, + { 0, "joger1", PED_FEMALE1 }, + { 0, "joger2", PED_MALE1 }, + { 0, "male1", PED_MALE2 }, + { 0, "male2", PED_MALE1 }, + { 0, "male3", PED_MALE2 }, + { 0, "male4", PED_MALE1 }, + { 0, "male5", PED_MALE2 }, + { 0, "male6", PED_MALE1 }, + { 0, "mobstr", PED_MALE2 }, + { 0, "nuclear", PED_MALE1 }, + { 0, "olady1", PED_FEMALE2 }, + { 0, "olady2", PED_FEMALE1 }, + { 0, "olady3", PED_FEMALE2 }, + { 0, "rednk1", PED_BOY2 }, + { 0, "rednk2", PED_BOY1 }, + { 0, "sail1", PED_MALE2 }, + { 0, "sail2", PED_MALE1 }, + { 0, "sail3", PED_MALE2 }, + { 0, "sail4", PED_MALE1 }, + { 0, "witch", PED_GIRL1 }, + { 0, "frankenstein", PED_BOY2 }, + { 0, "zfem1", PED_ZOMBIE3 }, + { 0, "zfem5", PED_ZOMBIE3 }, + { 0, "zmale1", PED_ZOMBIE1 }, + { 0, "zmale3", PED_ZOMBIE2 }, + { 0, "zmale4", PED_ZOMBIE4 } +}; + +static unsigned int pedestrianTableLength = sizeof( pedestrianNameTable ) / sizeof( pedTypeInfo ); + +struct skinTypeInfo +{ + radInt64 skinUID; + const char* skinName; + skinDialogType dialogGroup; +}; + +static skinTypeInfo skinNameTable[] = +{ + { 0, "a_american", SKIN_APU }, + { 0, "a_army", SKIN_APU }, + { 0, "a_besharp", SKIN_APU }, + { 0, "b_football", SKIN_BART }, + { 0, "b_hugo", SKIN_BART }, + { 0, "b_man", SKIN_BART }, + { 0, "b_military", SKIN_BART }, + { 0, "b_ninja", SKIN_BART }, + { 0, "b_tall", SKIN_BART }, + { 0, "h_donut", SKIN_HOMER }, + { 0, "h_evil", SKIN_HOMER }, + { 0, "h_fat", SKIN_HOMER }, + { 0, "h_scuzzy", SKIN_HOMER }, + { 0, "h_stcrobe", SKIN_HOMER }, + { 0, "h_undrwr", SKIN_HOMER }, + { 0, "reward_homer", SKIN_HOMER }, + { 0, "l_cool", SKIN_LISA }, + { 0, "l_florida", SKIN_LISA }, + { 0, "l_jersey", SKIN_LISA }, + { 0, "m_pink", SKIN_MARGE }, + { 0, "m_police", SKIN_MARGE }, + { 0, "m_prison", SKIN_MARGE }, + { 0, "brn_unf", SKIN_BARNEY }, + { 0, "reward_barney", SKIN_BARNEY }, + { 0, "b_milhouse", SKIN_MILHOUSE }, + { 0, "b_nelson", SKIN_NELSON }, + { 0, "b_ralph", SKIN_RALPH }, + { 0, "b_cletus", SKIN_CLETUS }, + { 0, "b_zmale1", SKIN_ZOMBIE1 }, + { 0, "b_zmale3", SKIN_ZOMBIE2 }, + { 0, "b_zfem5", SKIN_ZOMBIE3 }, + { 0, "b_zmale4", SKIN_ZOMBIE4 }, + { 0, "b_zfem1", SKIN_ZOMBIE3 }, + { 0, "b_skinner", SKIN_SKINNER }, + { 0, "b_grandpa", SKIN_GRANDPA }, + { 0, "b_cbg", SKIN_CBG }, + { 0, "b_barney", SKIN_BARNEY }, + { 0, "b_frink", SKIN_FRINK }, + { 0, "b_snake", SKIN_SNAKE }, + { 0, "b_smithers", SKIN_SMITHERS }, + { 0, "reward_otto", SKIN_OTTO }, + { 0, "reward_willie", SKIN_WILLIE }, + { 0, "reward_kearney", SKIN_KEARNEY } +}; + +static unsigned int skinTableLength = sizeof( skinNameTable ) / sizeof( skinTypeInfo ); + +struct pedDialogGroupInfo +{ + radInt64 pedUID; + const char* pedName; +}; + +// +// Size must be PED_NUM_TYPES +// +static pedDialogGroupInfo dialogGroupTable[] = +{ + { 0, "male1" }, + { 0, "male2" }, + { 0, "fem1" }, + { 0, "fem2" }, + { 0, "boy1" }, + { 0, "boy2" }, + { 0, "girl1" }, + { 0, "girl2" }, + { 0, "zombie1" }, + { 0, "zombie2" }, + { 0, "zombie3" }, + { 0, "zombie4" } +}; + +struct skinDialogGroupInfo +{ + radInt64 charUID; + const char* charName; +}; + +// +// Size must be SKIN_NUM_TYPES +// +static skinDialogGroupInfo skinDialogGroupTable[] = +{ + { 0, "apu" }, + { 0, "bart" }, + { 0, "homer" }, + { 0, "lisa" }, + { 0, "marge" }, + { 0, "barney" }, + { 0, "milhouse" }, + { 0, "nelson" }, + { 0, "ralph" }, + { 0, "cletus" }, + { 0, "zombie1" }, + { 0, "zombie2" }, + { 0, "zombie3" }, + { 0, "zombie4" }, + { 0, "otto" }, + { 0, "willie" }, + { 0, "kearney" }, + { 0, "skinner" }, + { 0, "grandpa" }, + { 0, "cbg" }, + { 0, "frink" }, + { 0, "snake" }, + { 0, "smithers" } +}; + +// +// Debug flag +// +bool DialogList::s_showDialogSpew = false; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogList::DialogList +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogList::DialogList() +{ + unsigned int i; + + // + // Lazy initialization + // + if( s_introKey == 0 ) + { + s_introKey = ::radMakeKey32( "intro" ); + s_aztecKey = ::radMakeKey32( "aztec" ); + s_milhouseKey = tEntity::MakeUID( "milhouse" ); + s_nelsonKey = tEntity::MakeUID( "nelson" ); + s_raceZombie1 = tEntity::MakeUID( "zmale3" ); + s_raceZombie2 = tEntity::MakeUID( "zfem1" ); + + // + // Also do the tables of UIDs we use to identify peds and skins + // + for( i = 0; i < pedestrianTableLength; i++ ) + { + pedestrianNameTable[i].pedUID = tEntity::MakeUID( pedestrianNameTable[i].pedName ); + } + + for( i = 0; i < PED_NUM_TYPES; i++ ) + { + dialogGroupTable[i].pedUID = tEntity::MakeUID( dialogGroupTable[i].pedName ); + } + + for( i = 0; i < skinTableLength; i++ ) + { + skinNameTable[i].skinUID = tEntity::MakeUID( skinNameTable[i].skinName ); + } + + for( i = 0; i < SKIN_NUM_TYPES; i++ ) + { + skinDialogGroupTable[i].charUID = tEntity::MakeUID( skinDialogGroupTable[i].charName ); + } + + // + // Debug spew + // + radDbgWatchAddBoolean( &s_showDialogSpew, "Show Dialog Spew", "Sound Info" ); + radDbgWatchAddFunction( "Print Dialog Coverage", &dumpDialogCoverage, this, "Sound Info" ); + } +} + +//============================================================================== +// DialogList::~DialogList +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogList::~DialogList() +{ +} + +//============================================================================= +// DialogList::OrganizeDialog +//============================================================================= +// Description: Go through the list of dialog resources, creating SelectableDialog +// objects organized as directed by the naming conventions. +// +// Parameters: namespaceObj -- object containing the list of dialog resources +// as read from the script file +// +// Return: void +// +//============================================================================= +void DialogList::OrganizeDialog( IRadNameSpace* namespaceObj ) +{ + int mission; + int level; + IDaSoundResource* resource; + DialogLine* newLine; + ConversationMatcher matcher; + SelectableDialogList* dialogList; + SelectableDialog* foundDialog; + + // + // Go through the list of sound resources looking for dialog + // + resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetFirst( NULL) ); + while( resource != NULL ) + { + if( isIndividualLine( resource ) ) + { + // + // Resource is dialog but not conversation. Create a DialogLine object + // to hold the information about it and store it in the appropriate list. + // +#ifdef RAD_GAMECUBE + newLine = new( GMA_GC_VMM ) DialogLine( resource ); +#else + newLine = new( GMA_PERSISTENT ) DialogLine( resource ); +#endif + if( newLine->IsLevelSpecific() ) + { + dialogList = &(m_missionLists[newLine->GetLevel() - 1][newLine->GetMission()]); + } + else + { + dialogList = &m_genericDialogList; + } + + // + // Search the list. If we've already got a dialog for the same situation, lump + // this one in with it, otherwise stash it straight into the list + // + foundDialog = searchDialogList( newLine->GetEvent(), newLine->GetCharacterUID(), 0, + *dialogList, 0, newLine->IsVillainLine(), false ); + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + if( foundDialog != NULL ) + { + foundDialog->AddMatchingDialog( *newLine, *dialogList ); + } + else + { + dialogList->push_back( newLine ); + } + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); + } + else if( isConversationLine( resource ) ) + { + // + // Resource is part of a conversation. Give it to the object + // responsible for matching the pieces together + // + matcher.AddNewLine( resource ); + } + // + // Otherwise, this isn't dialog, so we don't have to do anything with it + // + + // + // Next resource in the list + // + resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetNext( NULL) ); + } + + // + // Do a sanity check on the conversations + // + rAssert( matcher.AreAllConversationsComplete() ); + + // + // Add the completed conversations to the appropriate lists + // + for( level = 0; level < GameplayManager::MAX_LEVELS; level++ ) + { + for( mission = 0; mission < GameplayManager::MAX_MISSIONS; mission++ ) + { + matcher.AddConversationsToList( level + 1, mission, m_missionLists[level][mission] ); + } + } + + matcher.AddConversationsToList( SelectableDialog::NO_LEVEL, SelectableDialog::NO_MISSION, m_genericDialogList ); +} + +//============================================================================= +// DialogList::FindDialogForEvent +//============================================================================= +// Description: Search through the dialog lists to find something appropriate +// for the given dialog event +// +// Parameters: id - Event ID that we need to find dialog for +// character1 - character who will say the dialog +// character2 - if conversation, second character in conversation. +// NULL otherwise. +// charUID1 - if we're searching by UID, this is non-zero and +// character1 and character2 are NULL. +// charUID2 - second character, used for searching by UID +// convKey - name of conversation, 0 if not applicable +// +// Return: Const pointer to SelectableDialog object best matching the +// event, or NULL if nothing found +// +//============================================================================= +SelectableDialog* DialogList::FindDialogForEvent( EventEnum id, + Character* character1, + Character* character2, + tUID charUID1, + tUID charUID2, + radKey32 convKey, + bool isVillain ) +{ + int mission; + tUID char1UID; + tUID char2UID; + char nameBuffer[20]; + unsigned int aztecNumber; + GameplayManager* gameplayMgr = NULL; + // For indexing purposes, levels count from zero. + int level; + SelectableDialog* dialogMatch = NULL; + Mission* missionObj; + + if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ) + { + // + // Playing dialog in front end, so any level will do + // + level = 0; + missionObj = NULL; + } + else + { + gameplayMgr = GetGameplayManager(); + rAssert( gameplayMgr != NULL ); + + level = gameplayMgr->GetCurrentLevelIndex() - RenderEnums::L1; + missionObj = gameplayMgr->GetCurrentMission(); + } + + // + // First, search the list for the current mission. If it's a conversation + // event, those only happen during missions even if we're in Sunday Drive + // (they're the conversations that start the missions) so just start looking there. + // + if( missionObj != NULL ) + { + if( ( !(gameplayMgr->IsSuperSprint()) ) + && ( (!(missionObj->IsSundayDrive()) ) + || ( id == EVENT_CONVERSATION_INIT_DIALOG ) + || ( id == EVENT_TUTORIAL_DIALOG_PLAY ) ) ) + { + if( id == EVENT_TUTORIAL_DIALOG_PLAY ) + { + mission = DialogLine::TUTORIAL_MISSION_NUMBER; + } + else if( missionObj->IsBonusMission() ) + { + mission = DialogLine::BONUS_MISSION_NUMBER; + } + else if( missionObj->IsRaceMission() ) + { + mission = DialogLine::FIRST_RACE_MISSION_NUMBER + + ( gameplayMgr->GetCurrentMissionNum() - GameplayManager::MAX_MISSIONS ); + } + else + { + if( convKey == s_introKey ) + { + // + // Stinky race missions. The "intro" conversation happens before we've + // started the race. I can't rename them to simple L1, since characters like Homer + // have multiple C_intro_*_L1.rsd lines, so there's a naming clash. Ugh. + // To make matters worse, since some of these conversations involve only Homer (or + // whoever the driver is), we need to check if either character is Milhouse, Nelson, + // Ralph, or their zombie counterparts + // + rAssert( character1 != NULL ); + rAssert( character2 != NULL ); + + char1UID = getPuppetUID( character1 ); + char2UID = getPuppetUID( character2 ); + if( ( char1UID == s_milhouseKey ) + || ( char2UID == s_milhouseKey ) + || ( char1UID == s_raceZombie1 ) + || ( char2UID == s_raceZombie1 ) ) + { + mission = DialogLine::FIRST_RACE_MISSION_NUMBER; + } + else if( ( char1UID == s_nelsonKey ) + || ( char2UID == s_nelsonKey ) + || ( char1UID == s_raceZombie2 ) + || ( char2UID == s_raceZombie2 ) ) + { + mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 1; + } + else + { + // + // This had better be Ralph or zombie Ralph + // + mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 2; + } + } + else if( level == 0 ) + { + // + // Stinky level 1, tutorial mission screws everything up + // + mission = gameplayMgr->GetCurrentMissionIndex(); + } + else + { + mission = gameplayMgr->GetCurrentMissionIndex() + 1; + +#ifdef RAD_E3 + // + // E3 hack. L2M5 is our only mission, and it's going to + // come back as mission 1. Hack it to 5. + // + mission = 5; +#endif + } + + if ( convKey == s_aztecKey ) + { + // + // Another stinky hack. The teen at the Aztec needs randomized conversations. + // Conversations don't really randomize because the conversation builder assumes + // that identically-named conversations result from misnamed files. And the + // key isn't a straightforward randomization on the caller's end, since it's + // a scripted value. I'll handle it here. + // + aztecNumber = ( rand() % 4 ) + 1; + sprintf( nameBuffer, "aztec%d", aztecNumber ); + convKey = ::radMakeKey32( nameBuffer ); + } + } + + if( s_showDialogSpew ) + { + rTuneString( "Searching mission-specific dialog\n" ); + } + + if( character1 == NULL ) + { + // Already have UIDs + dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][mission], convKey, isVillain ); + } + else + { + // Take UID from character objects + dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][mission], convKey, isVillain ); + } + } + } + + if( dialogMatch == NULL ) + { + // + // No mission-specific dialog, search the level-specific stuff + // + if( s_showDialogSpew ) + { + rTuneString( "Searching level-specific dialog\n" ); + } + + if( character1 == NULL ) + { + dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][0], convKey, isVillain ); + } + else + { + dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][0], convKey, isVillain ); + } + + if( dialogMatch == NULL ) + { + // + // No mission- or level-specific dialog, search the generic list + // + if( s_showDialogSpew ) + { + rTuneString( "Searching generic dialog\n" ); + } + + if( character1 == NULL ) + { + dialogMatch = searchDialogList( id, charUID1, charUID2, m_genericDialogList, convKey, isVillain ); + } + else + { + dialogMatch = searchDialogList( id, character1, character2, m_genericDialogList, convKey, isVillain ); + } + } + } + + return( dialogMatch ); +} + +//============================================================================= +// DialogList::GetStinkySkinPointer +//============================================================================= +// Description: Given a UID, see if we can dig up a character for it looking +// through all the possible skins. +// +// Parameters: charUID - tUID of character +// +// Return: Character* if match found, NULL otherwise +// +//============================================================================= +Character* DialogList::GetStinkySkinPointer( tUID charUID ) +{ + int skinType; + unsigned int i; + Character* charPtr; + + for( skinType = 0; skinType < SKIN_NUM_TYPES; skinType++ ) + { + if( skinDialogGroupTable[skinType].charUID == charUID ) + { + break; + } + } + + if( skinType == SKIN_NUM_TYPES ) + { + // + // No skin exists for given character + // + return( NULL ); + } + + // + // At this point, the character has skins. Look for a match. + // + for( i = 0; i < skinTableLength; i++ ) + { + if( skinNameTable[i].dialogGroup == skinType ) + { + charPtr = GetCharacterManager()->GetCharacterByName( skinNameTable[i].skinUID ); + if( charPtr != NULL ) + { + return( charPtr ); + } + } + } + + // + // No skins found + // + return( NULL ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogList::hasOneLinerPrefix +//============================================================================= +// Description: Determines if resource name belongs to one-liner dialog. This +// is deemed to be true if it starts with a valid role abbreviation. +// +// Parameters: name - name of resource +// +// Return: true if role field found, false otherwise +// +//============================================================================= +bool DialogList::hasOneLinerPrefix( const char* name ) +{ + return( ( name[1] == '_' ) + && ( ( name[0] == 'W' ) + || ( name[0] == 'D' ) + || ( name[0] == 'P' ) + || ( name[0] == 'V' ) ) ); +} + +//============================================================================= +// DialogList::isIndividualLine +//============================================================================= +// Description: Test for whether given resource is a dialog one-liner +// +// Parameters: resource - sound resource to test +// +// Return: true if one-liner, false otherwise +// +//============================================================================= +bool DialogList::isIndividualLine( IDaSoundResource* resource ) +{ + char tempBuffer[FILENAME_BUFFER_LEN]; + char buffer[FILENAME_BUFFER_LEN]; + + // + // Get the first filename belonging to the resource. Don't bother checking + // for >1 file---if they exist, then the names had better be interchangable. + // + tempBuffer[0] = '\0'; + resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN ); + rAssert( strlen( tempBuffer ) > 0 ); + + DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN ); + + // + // Simple test: we'll call it a line if it has at least two underscores + // and no "C_" prefix + // + return( ( !hasConversationPrefix( buffer ) ) + && ( hasOneLinerPrefix( buffer ) ) + && ( underscoreCount( buffer ) > 1 ) ); +} + +//============================================================================= +// DialogList::isConversationLine +//============================================================================= +// Description: Test for whether given resource is part of a dialog +// conversation +// +// Parameters: resource - sound resource to test +// +// Return: true if conversation line, false otherwise +// +//============================================================================= +bool DialogList::isConversationLine( IDaSoundResource* resource ) +{ + char tempBuffer[FILENAME_BUFFER_LEN]; + char buffer[FILENAME_BUFFER_LEN]; + + // + // Get the first filename belonging to the resource. Don't bother checking + // for >1 file---if they exist, then the names had better be interchangable. + // + tempBuffer[0] = '\0'; + resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN ); + rAssert( strlen( tempBuffer ) > 0 ); + + DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN ); + + // + // Test: line belongs to conversation if it has at least three underscores + // and a "C_" prefix + // + return( hasConversationPrefix( buffer ) && + ( underscoreCount( buffer ) > 3 ) ); +} + +//============================================================================= +// DialogList::underscoreCount +//============================================================================= +// Description: Return number of underscores in the given string +// +// Parameters: name - string to count in +// +// Return: number of underscores found +// +//============================================================================= +unsigned int DialogList::underscoreCount( const char* name ) +{ + unsigned int i = 0; + unsigned int count = 0; + + + while( name[i] != '\0' ) + { + if( name[i] == '_' ) + { + ++count; + } + + i++; + } + + return( count ); +} + +//============================================================================= +// DialogList::searchDialogList +//============================================================================= +// Description: Comment +// +// Parameters: ( EventEnum id, tUID characterUID1, tUID characterUID2, SelectableDialog* list ) +// +// Return: SelectableDialog +// +//============================================================================= +SelectableDialog* DialogList::searchDialogList( EventEnum id, Character* character1, + Character* character2, SelectableDialogList& list, + radKey32 convName, bool isVillain ) +{ + tUID UID1 = 0; + tUID UID2 = 0; + + if( character1 == NULL ) + { + UID1 = 0; + } + else + { + // + // Can't just get the character UID, since they're not guaranteed to be consistent + // with the model you see on the screen. Need the skeleton UID, it appears + // + UID1 = getPuppetUID( character1 ); + } + + if( character2 == NULL ) + { + UID2 = 0; + } + else + { + UID2 = getPuppetUID( character2 ); + } + + return( searchDialogList( id, UID1, UID2, list, convName, isVillain, true ) ); +} + +//============================================================================= +// DialogList::searchDialogList +//============================================================================= +// Description: Comment +// +// Parameters: ( EventEnum id, tUID driverUID, SelectableDialogList& list ) +// +// Return: SelectableDialog +// +//============================================================================= +SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1, + tUID characterUID2, + SelectableDialogList& list, + radKey32 convName, + bool isVillain ) +{ + return( searchDialogList( id, characterUID1, characterUID2, list, convName, isVillain, true ) ); +} + +//============================================================================= +// DialogList::searchDialogList +//============================================================================= +// Description: Search the given list for a SelectableDialog object with the +// given event ID. +// +// Parameters: id - event ID to find a match for +// list - list to search +// +// Return: pointer to SelectableDialog object matching the event ID if +// it exists, NULL otherwise +// +//============================================================================= +SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1, + tUID characterUID2, SelectableDialogList& list, + radKey32 convName, bool isVillain, bool fuzzyPedMatch ) +{ + char eventName[30]; + char char1Name[30]; + char char2Name[30]; + char convBuffer[30]; + char villain[3]; + SelectableDialog* currentDialog; + SelectableDialogList::const_iterator iter = list.begin(); + SelectableDialog* returnValue = NULL; + + if( s_showDialogSpew ) + { + // + // Print a message for the stuff we're trying to match with + // + DialogLine::FillEventName( eventName, 30, id ); + DialogLine::FillCharacterName( char1Name, 30, characterUID1 ); + DialogLine::FillCharacterName( char2Name, 30, characterUID2 ); + if( convName != 0 ) + { + sprintf( convBuffer, ", conv %d", convName ); + } + else + { + convBuffer[0] = '\0'; + } + if( isVillain ) + { + villain[0] = 'V'; + } + else + { + villain[0] = 'W'; + } + villain[1] = '\0'; + rTunePrintf( "Dialog: Looking for event %s, char1 %s, char2 %s %s %s\n", + eventName, char1Name, char2Name, villain, convBuffer ); + } + + for( ; iter != list.end(); ++iter ) + { + currentDialog = *iter; + + if( s_showDialogSpew ) + { + // + // Print a message for the stuff we're currently looking at + // + DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() ); + DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) ); + + if( currentDialog->GetNumDialogLines() > 1 ) + { + DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) ); + } + else + { + char2Name[0] = '-'; + char2Name[1] = '\0'; + } + + if( convName != 0 ) + { + sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() ); + } + else + { + convBuffer[0] = '\0'; + } + + if( currentDialog->IsVillainLine() ) + { + villain[0] = 'V'; + } + else + { + villain[0] = 'W'; + } + villain[1] = '\0'; + + rTunePrintf( "Dialog: Matching against event %s, char1 %s, char2 %s %s %s\n", + eventName, char1Name, char2Name, villain, convBuffer ); + } + + if( ( currentDialog->GetEvent() == id ) + && ( currentDialog->IsVillainLine() == isVillain ) + // If a conversation name is supplied, that has to match + && ( ( convName == 0 ) + || ( currentDialog->GetConversationName() == convName ) ) ) + { + if( currentDialog->GetNumDialogLines() == 1 ) + { + // + // Match either character + // + if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch ) + || characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) ) + { + returnValue = currentDialog; + } + } + else + { + // + // Multi-line dialog. Match both. + // + if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch ) + && characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) ) + { + returnValue = currentDialog; + } + } + + if( returnValue != NULL ) + { + if( s_showDialogSpew ) + { + rTunePrintf( "Dialog: Match found\n" ); + } + + // + // We're done + // + break; + } + } + } + + return( returnValue ); +} + +//============================================================================= +// DialogList::characterMatches +//============================================================================= +// Description: Determine whether the given dialog matches the character +// given. UID of zero always matches. +// +// Parameters: characterObj - character to match +// dialog - dialog to match to +// fuzzyPedMatch - true if we want to fudge UIDs to group +// pedestrians, false otherwise +// +// Return: true if match, false otherwise +// +//============================================================================= +bool DialogList::characterMatches( tUID characterUID, SelectableDialog* dialog, + bool fuzzyPedMatch ) +{ + unsigned int i; + tUID effectiveUID; // Unix humour. Nyuck! + pedDialogType dialogType; + bool switchMade = false; + + if( characterUID == static_cast< tUID >( 0 ) ) + { + return( false ); + } + + // + // Argh!! We have a whole bunch of pedestrian UIDs which need to be mapped + // to eight dialog characters. If this actually shows in a profiler, we'll + // need to mark the peds in the Character objects when they're spawned somehow + // to avoid this search + // + + // + // Double argh!! Now we've got a bunch of character skins that are breaking + // the dialog system. We have to search for those as well. + // + effectiveUID = characterUID; + + if( fuzzyPedMatch ) + { + for( i = 0; i < pedestrianTableLength; i++ ) + { + if( effectiveUID == static_cast< tUID >( pedestrianNameTable[i].pedUID ) ) + { + // + // Is ped, map new UID + // + + // + // Another hack: zombie1/2 and zombie3/4 should be randomly chosen. + // TODO: leave zombie3 as zombie3 for E3. + // + dialogType = pedestrianNameTable[i].dialogGroup; + if( ( dialogType == PED_ZOMBIE1 ) + && ( dialog->GetEvent() != EVENT_CONVERSATION_INIT_DIALOG ) + && ( dialog->GetEvent() != EVENT_IN_GAMEPLAY_CONVERSATION ) ) + { + if( ( rand() % 2 ) == 0 ) + { + dialogType = PED_ZOMBIE2; + } + } + + effectiveUID = dialogGroupTable[dialogType].pedUID; + switchMade = true; + break; + } + } + + if( !switchMade ) + { + // + // Not a ped, check for skins + // + for( i = 0; i < skinTableLength; i++ ) + { + if( effectiveUID == static_cast< tUID >( skinNameTable[i].skinUID ) ) + { + // + // Is skin, map new UID + // + effectiveUID = skinDialogGroupTable[skinNameTable[i].dialogGroup].charUID; + break; + } + } + } + } + + return( dialog->UsesCharacter( effectiveUID ) ); +} + +//============================================================================= +// DialogList::getPuppetUID +//============================================================================= +// Description: Get UID for Choreo puppet for character +// +// Parameters: ( Character* characterPtr ) +// +// Return: tUID if skeleton found, 0 otherwise +// +//============================================================================= +tUID DialogList::getPuppetUID( Character* characterPtr ) +{ + const char* modelName; + rAssert( characterPtr != NULL ); + + modelName = GetCharacterManager()->GetModelName( characterPtr ); + if( modelName != NULL ) + { + return( tEntity::MakeUID( modelName ) ); + } + else + { + return( 0 ); + } +} + +void DialogList::dumpDialogCoverage( void* userData ) +{ +#ifndef RAD_RELEASE + SelectableDialogList::const_iterator iter; + int i, j; + SelectableDialog* currentDialog; + char eventName[30]; + char char1Name[30]; + char char2Name[30]; + char convBuffer[30]; + DialogList* listObj = static_cast<DialogList*>(userData); + + for( i = 0; i < GameplayManager::MAX_LEVELS; i++ ) + { + for( j = 0; j < GameplayManager::MAX_MISSIONS+1; j++ ) + { + rTunePrintf( "\nDialogue for level %d mission %d list\n", i, j ); + + iter = listObj->m_missionLists[i][j].begin(); + for( ; iter != listObj->m_missionLists[i][j].end(); ++iter ) + { + currentDialog = *iter; + if( currentDialog != NULL ) + { + DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() ); + DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) ); + + if( currentDialog->GetNumDialogLines() > 1 ) + { + DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) ); + } + else + { + char2Name[0] = '-'; + char2Name[1] = '\0'; + } + + sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() ); + + rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ", + eventName, char1Name, char2Name, convBuffer ); + currentDialog->PrintPlayedStatus(); + } + } + } + } + + rTuneString( "\nGeneric dialogue list:\n" ); + + iter = listObj->m_genericDialogList.begin(); + for( ; iter != listObj->m_genericDialogList.end(); ++iter ) + { + currentDialog = *iter; + if( currentDialog != NULL ) + { + DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() ); + DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) ); + + if( currentDialog->GetNumDialogLines() > 1 ) + { + DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) ); + } + else + { + char2Name[0] = '-'; + char2Name[1] = '\0'; + } + + sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() ); + + rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ", + eventName, char1Name, char2Name, convBuffer ); + currentDialog->PrintPlayedStatus(); + } + } + +#endif +}
\ No newline at end of file diff --git a/game/code/sound/dialog/dialoglist.h b/game/code/sound/dialog/dialoglist.h new file mode 100644 index 0000000..54692b0 --- /dev/null +++ b/game/code/sound/dialog/dialoglist.h @@ -0,0 +1,109 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialoglist.h +// +// Description: Loads and maintains the list of dialog lines and conversations +// (which group multiple dialog lines, and potentially link +// conversations to other conversations that occur later). +// +// History: 01/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGLIST_H +#define DIALOGLIST_H + +//======================================== +// Nested Includes +//======================================== +#include <mission/gameplaymanager.h> +#include <events/eventenum.h> +#include <sound/dialog/selectabledialoglist.h> + +//======================================== +// Forward References +//======================================== +struct IRadNameSpace; +struct IDaSoundResource; +class SelectableDialog; +class Character; + +//============================================================================= +// +// Synopsis: DialogList +// +//============================================================================= + +class DialogList +{ + public: + DialogList(); + virtual ~DialogList(); + + void OrganizeDialog( IRadNameSpace* namespaceObj ); + + SelectableDialog* FindDialogForEvent( EventEnum id, Character* character1, Character* character2, + tUID charUID1, tUID charUID2, radKey32 convKey, bool isVillain ); + + static Character* GetStinkySkinPointer( tUID charUID ); + + private: + //Prevent wasteful constructor creation. + DialogList( const DialogList& original ); + DialogList& operator=( const DialogList& rhs ); + + // + // Sound file naming convention tests + // + bool isIndividualLine( IDaSoundResource* resource ); + bool isConversationLine( IDaSoundResource* resource ); + + bool hasConversationPrefix( const char* name ) + { return( ( name[0] == 'C' ) && ( name[1] == '_' ) ); } + bool hasOneLinerPrefix( const char* name ); + unsigned int underscoreCount( const char* name ); + + SelectableDialog* searchDialogList( EventEnum id, tUID characterUID1, tUID characterUID2, + SelectableDialogList& list, radKey32 convName, bool isVillain, + bool fuzzyPedMatch ); + + SelectableDialog* searchDialogList( EventEnum id, tUID characterUID1, tUID characterUID2, + SelectableDialogList& list, radKey32 convName, bool isVillain ); + + SelectableDialog* searchDialogList( EventEnum id, Character* character1, + Character* character2, SelectableDialogList& list, + radKey32 convName, bool isVillain ); + + bool characterMatches( tUID characterUID, SelectableDialog* dialog, bool fuzzyPedMatch ); + + tUID getPuppetUID( Character* characterPtr ); + + static void dumpDialogCoverage( void* userData ); + + // + // List of level/mission dialogs + // + SelectableDialogList m_missionLists[GameplayManager::MAX_LEVELS][GameplayManager::MAX_MISSIONS+1]; + + // + // Generic dialog list + // + SelectableDialogList m_genericDialogList; + + static radKey32 s_introKey; + static radKey32 s_aztecKey; + static tUID s_milhouseKey; + static tUID s_nelsonKey; + static tUID s_raceZombie1; + static tUID s_raceZombie2; + + // + // Debug flag + // + static bool s_showDialogSpew; +}; + + +#endif // DIALOGLIST_H + diff --git a/game/code/sound/dialog/dialogpriorityqueue.cpp b/game/code/sound/dialog/dialogpriorityqueue.cpp new file mode 100644 index 0000000..5a57510 --- /dev/null +++ b/game/code/sound/dialog/dialogpriorityqueue.cpp @@ -0,0 +1,536 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogpriorityqueue.cpp +// +// Description: Responsible for managing the outstanding dialog playback requests. +// When the DialogCoordinator needs to play dialog, it hands the +// PlayableDialog object off, and the DialogPriorityQueue determines +// if it can be played, or if it should wait until some other dialog +// completes. When a PlayableDialog is ready for playback, it gets +// handed to the SimpsonsSoundPlayer. +// +// History: 04/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radtime.hpp> +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogpriorityqueue.h> + +#include <sound/dialog/dialogqueueelement.h> +#include <sound/dialog/selectabledialog.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundmanager.h> + +#include <memory/srrmemory.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Probability of optional play success as chance in 256 +// +static const unsigned int OPL_PROB = 64; + +//#define DEBUG_QUEUE_REFCOUNT + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogPriorityQueue::DialogPriorityQueue +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogPriorityQueue::DialogPriorityQueue() : +#ifdef SOUND_DEBUG_INFO_ENABLED + m_debugPage( 3, GetSoundManager()->GetDebugDisplay() ), +#endif + m_nowPlaying( NULL ), + m_permitQueueAdvance( true ) +{ +} + +//============================================================================== +// DialogPriorityQueue::~DialogPriorityQueue +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogPriorityQueue::~DialogPriorityQueue() +{ +} + +//============================================================================= +// DialogPriorityQueue::AddDialogToQueue +//============================================================================= +// Description: Place dialog on queue and play if possible +// +// Parameters: dialog - dialog to add to queue +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::AddDialogToQueue( SelectableDialog& dialog, rmt::Vector* posn ) +{ + DialogPriority priority; + DialogQueueElement* queueElement; + unsigned int diceRoll; + + // + // Check the priority on the dialog, and use it to find the appropriate + // spot in the queue. + // + priority = DialogQueueElement::CalculateDialogPriority( dialog ); + + if( priority == OccasionalPlayLine ) + { + // + // Random play + // + diceRoll = ( rand() % 100 ); + if( diceRoll >= DialogQueueElement::CalculateDialogProbability( dialog ) ) + { + return; + } + } + + // + // Ducking for dialog + // + if( m_nowPlaying == NULL ) + { + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, false ); + GetSoundManager()->MuteNISPlayers(); + } + + // + // Don't bother playing dialog if we're already playing the same thing. + // This can happen with collision events that get triggered zillions of + // times + // + if( ( m_nowPlaying == NULL ) + || !( m_nowPlaying->DialogMatches( &dialog ) ) ) + { + queueElement = new ( GMA_TEMP ) DialogQueueElement( &dialog ); + + if( priority == MustPlayImmediately ) + { + // + // Special case. Place this guy at the head of the queue and kill + // whatever's playing right now. + // + if( m_nowPlaying ) + { + // + // If we don't halt the dialog queue, the stop callback will advance + // it on us and we get two dialog lines + // + m_permitQueueAdvance = false; + + m_nowPlaying->StopDialog(); +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "AddDialogToQueue %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + // + // One more check. The StopDialog() above may already have made m_nowPlaying + // go away + // + if( m_nowPlaying != NULL ) + { + m_nowPlaying->Release(); + } + + m_permitQueueAdvance = true; + } + + m_nowPlaying = queueElement; + playDialog( posn ); + } + else + { + // + // Stick it in the list + // + queueElement->AddToQueue( &m_dialogQueue, posn ); + } + } + +#ifndef RAD_RELEASE + // + // Mark dialog as played for coverage testing purposes + // + dialog.MarkAsPlayed(); +#endif +} + +//============================================================================= +// DialogPriorityQueue::StopCurrentDialog +//============================================================================= +// Description: If we have something playing, stop it and yank it off the +// queue +// +// Parameters: None +// +// Return: true if there was dialog to stop, false otherwise +// +//============================================================================= +bool DialogPriorityQueue::StopCurrentDialog() +{ + bool dialogStopped = false; + + if( m_nowPlaying ) + { + // + // Stop it and let the callback do the rest + // + m_nowPlaying->StopDialog(); + dialogStopped = true; + } + + return( dialogStopped ); +} + +//============================================================================= +// DialogPriorityQueue::StopAllDialog +//============================================================================= +// Description: Kill the queue. This'll most likely be done when gameplay +// ends. +// +// Parameters: None +// +// Return: true if there was dialog to stop, false otherwise +// +//============================================================================= +bool DialogPriorityQueue::StopAllDialog() +{ + DialogQueueElement* current; + bool dialogStopped = false; + + // + // Just in case we're still in a paused state. Stopping paused dialogue doesn't + // seem to give us our stop callbacks + // + UnpauseDialog(); + + m_permitQueueAdvance = false; + + while( !m_dialogQueue.empty() ) + { + current = m_dialogQueue.front(); + current->RemoveSelfFromList(); + current->Release(); + } + + if( m_nowPlaying != NULL ) + { + m_nowPlaying->StopDialog(); + + // + // Check again to make sure that the dialog didn't remove itself. + // Since the queue was emptied first, nothing else should replace + // the currently playing dialog afterward. + // + if( m_nowPlaying != NULL ) + { +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "StopAllDialog %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + m_nowPlaying->Release(); + m_nowPlaying = NULL; + + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true ); + GetSoundManager()->UnmuteNISPlayers(); + } + + dialogStopped = true; + } + + m_permitQueueAdvance = true; + + // + // Since we don't seem to get the OnPlaybackComplete callback for the queue + // element, throw a couple of shutup events in case someone was mouth flapping + // + GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP ); + GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP ); + + return( dialogStopped ); +} + +//============================================================================= +// DialogPriorityQueue::PauseDialog +//============================================================================= +// Description: Pause the dialog players +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::PauseDialog() +{ + m_player1.Pause(); + m_player2.Pause(); + m_positionalPlayer1.Pause(); + m_positionalPlayer2.Pause(); +} + +//============================================================================= +// DialogPriorityQueue::UnpauseDialog +//============================================================================= +// Description: Unpause the dialog players +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::UnpauseDialog() +{ + if( m_player1.IsPaused() ) + { + m_player1.Continue(); + } + if( m_player2.IsPaused() ) + { + m_player2.Continue(); + } + if( m_positionalPlayer1.IsPaused() ) + { + m_positionalPlayer1.Continue(); + } + if( m_positionalPlayer2.IsPaused() ) + { + m_positionalPlayer2.Continue(); + } +} + +//============================================================================= +// DialogPriorityQueue::OnDialogLineComplete +//============================================================================= +// Description: Called when a line of dialog in the currently playing +// conversation is complete +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::OnDialogLineComplete() +{ + // + // TODO: Stop the mouth flapping and eye blinking + // +} + +//============================================================================= +// DialogPriorityQueue::OnDialogComplete +//============================================================================= +// Description: Called when the currently playing SelectableDialog is +// complete +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::OnDialogComplete() +{ + // + // TODO: Stop the mouth flapping and eye blinking + // + + // + // Get rid of the currently playing element + // +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "OnDialogComplete %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() ); +#endif + m_nowPlaying->Release(); + if( !m_dialogQueue.empty() && m_permitQueueAdvance ) + { + m_nowPlaying = m_dialogQueue.front(); + m_nowPlaying->RemoveSelfFromList(); + playDialog( m_nowPlaying->GetPosition() ); + } + else + { + m_nowPlaying = NULL; + + Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true ); + GetSoundManager()->UnmuteNISPlayers(); + } +} + +//============================================================================= +// DialogPriorityQueue::Service +//============================================================================= +// Description: Do servicing stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::ServiceOncePerFrame() +{ + DialogQueueElement::Service(); + + // + // Just to be safe, don't update the positional players here. The update + // function just does stuff for moving sounds and pausing out-of-range + // stuff, none of which applies here. It's theoretically safe to do the + // right thing and service these players, but we're way too close to final + // to change the status quo. + // + //m_positionalPlayer1.ServiceOncePerFrame(); + //m_positionalPlayer2.ServiceOncePerFrame(); + + advanceQueue(); + + serviceDebugPage(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogPriorityQueue::advanceQueue +//============================================================================= +// Description: Advance the queue if non-empty and nothing's playing. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::advanceQueue() +{ + if( ( m_nowPlaying == NULL ) && ( !m_dialogQueue.empty() ) && m_permitQueueAdvance ) + { + m_nowPlaying = m_dialogQueue.front(); + m_nowPlaying->RemoveSelfFromList(); + playDialog( m_nowPlaying->GetPosition() ); + } +} + +//============================================================================= +// DialogPriorityQueue::playDialog +//============================================================================= +// Description: Start a dialog line playing with the correct players +// +// Parameters: posn - position of speaker for positional dialogue. NULL +// if non-positional +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::playDialog( rmt::Vector* posn ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + if( posn != NULL ) + { + // + // Before starting playback, set up the positional stuff + // + // + // Get the positionalSoundSettings object for the wasp sound + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( "posn_dialog_settings" ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + m_positionalPlayer1.SetParameters( parameters ); + + m_positionalPlayer1.SetPosition( posn->x, posn->y, posn->z ); + + m_nowPlaying->PlayDialog( m_positionalPlayer1, m_positionalPlayer2, this, this ); + } + else + { + rTuneAssertMsg( false, "No min/max for positional dialogue? Bad, call Esan." ); + + // + // Handle gracefully in release + // + m_nowPlaying->PlayDialog( m_player1, m_player2, this, this ); + } + } + else + { + m_nowPlaying->PlayDialog( m_player1, m_player2, this, this ); + } +} + +//============================================================================= +// DialogPriorityQueue::serviceDebugPage +//============================================================================= +// Description: Update the SoundInfo data available in Watcher +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogPriorityQueue::serviceDebugPage() +{ +#ifdef SOUND_DEBUG_INFO_ENABLED + int i; + int queueLength; + DialogQueueType::const_iterator iter; + + if( m_nowPlaying != NULL ) + { + // + // For the debug page, nowPlaying is part of the queue + // + queueLength = m_dialogQueue.size() + 1; + m_debugPage.SetQueueLength( queueLength ); + + m_nowPlaying->FillDebugInfo( m_debugPage, 0 ); + i = 1; + for( iter = m_dialogQueue.begin(); iter != m_dialogQueue.end(); ++iter ) + { + (*iter)->FillDebugInfo( m_debugPage, i++ ); + } + } + else + { + m_debugPage.SetQueueLength( 0 ); + } +#endif +} diff --git a/game/code/sound/dialog/dialogpriorityqueue.h b/game/code/sound/dialog/dialogpriorityqueue.h new file mode 100644 index 0000000..7ef5cfe --- /dev/null +++ b/game/code/sound/dialog/dialogpriorityqueue.h @@ -0,0 +1,99 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogpriorityqueue.h +// +// Description: Responsible for managing the outstanding dialog playback requests. +// When the DialogCoordinator needs to play dialog, it hands the +// PlayableDialog object off, and the DialogPriorityQueue determines +// if it can be played, or if it should wait until some other dialog +// completes. When a PlayableDialog is ready for playback, it gets +// handed to the SimpsonsSoundPlayer. +// +// History: 04/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGPRIORITYQUEUE_H +#define DIALOGPRIORITYQUEUE_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/simpsonssoundplayer.h> +#include <sound/positionalsoundplayer.h> +#include <sound/dialog/dialogqueueelement.h> +#include <sound/dialog/dialogqueuetype.h> +#include <sound/dialog/dialogsounddebugpage.h> + +//======================================== +// Forward References +//======================================== +class SelectableDialog; + +//============================================================================= +// +// Synopsis: DialogPriorityQueue +// +//============================================================================= + +class DialogPriorityQueue : public DialogLineCompleteCallback, + public DialogCompleteCallback +{ + public: + DialogPriorityQueue(); + virtual ~DialogPriorityQueue(); + + void AddDialogToQueue( SelectableDialog& dialog, rmt::Vector* posn = NULL ); + + bool StopCurrentDialog(); + bool StopAllDialog(); + + void PauseDialog(); + void UnpauseDialog(); + + void OnDialogLineComplete(); + void OnDialogComplete(); + + void ServiceOncePerFrame(); + + private: + //Prevent wasteful constructor creation. + DialogPriorityQueue( const DialogPriorityQueue& original ); + DialogPriorityQueue& operator=( const DialogPriorityQueue& rhs ); + + void advanceQueue(); + void serviceDebugPage(); + void playDialog( rmt::Vector* posn ); + +#ifdef SOUND_DEBUG_INFO_ENABLED + DialogSoundDebugPage m_debugPage; +#endif + + // + // Dialog sound players. Two needed so that we can queue up a second + // line while the first is playing. + // + SimpsonsSoundPlayer m_player1; + SimpsonsSoundPlayer m_player2; + + PositionalSoundPlayer m_positionalPlayer1; + PositionalSoundPlayer m_positionalPlayer2; + + // + // Points to whatever's playing right this instant. Stored separately + // from the waiting queue objects. + // + DialogQueueElement* m_nowPlaying; + + // + // The queue + // + DialogQueueType m_dialogQueue; + + bool m_permitQueueAdvance; +}; + + +#endif // DIALOGPRIORITYQUEUE_H + diff --git a/game/code/sound/dialog/dialogqueueelement.cpp b/game/code/sound/dialog/dialogqueueelement.cpp new file mode 100644 index 0000000..047b626 --- /dev/null +++ b/game/code/sound/dialog/dialogqueueelement.cpp @@ -0,0 +1,951 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogqueueelement.cpp +// +// Description: Implement DialogQueueElement +// +// History: 04/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radtime.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogqueueelement.h> + +#include <sound/dialog/selectabledialog.h> +#include <sound/dialog/dialogsounddebugpage.h> +#include <sound/dialog/dialogline.h> +#include <sound/dialog/dialoglist.h> + +#include <memory/srrmemory.h> +#include <events/eventmanager.h> +#include <worldsim/avatarmanager.h> +#include <worldsim/character/charactermanager.h> + +//#define DEBUG_QUEUE_REFCOUNT + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +IRadTimerList* DialogQueueElement::s_timerList = NULL; +bool DialogQueueElement::s_watcherInitialized = false; + +struct LinePriorityTableEntry +{ + EventEnum eventID; + DialogPriority priority; + unsigned int probability; +#ifndef RAD_RELEASE + const char* eventName; +#endif +}; + +LinePriorityTableEntry priorityTable[] = +{ + { EVENT_GETINTOVEHICLE_START, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "GIC" +#endif + }, + { EVENT_BURNOUT, OccasionalPlayLine, 15 +#ifndef RAD_RELEASE + , "Burn" +#endif + }, + { EVENT_DESTINATION_REACHED, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Arrive" +#endif + }, + { EVENT_BIG_AIR, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Air" +#endif + }, + { EVENT_BIG_CRASH, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Damage" +#endif + }, + { EVENT_GETOUTOFVEHICLE_START, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "GOC" +#endif + }, + { EVENT_RACE_PASSED_AI, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Pass" +#endif + }, + { EVENT_RACE_GOT_PASSED_BY_AI, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Passed" +#endif + }, + { EVENT_KICK_NPC_SOUND, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "HitByW" +#endif + }, + { EVENT_PLAYER_CAR_HIT_NPC, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "HitByC" +#endif + }, + { EVENT_PEDESTRIAN_DODGE, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "NHitByC" +#endif + }, + { EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "HitP" +#endif + }, + { EVENT_PEDESTRIAN_SMACKDOWN, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Char" +#endif + }, + { EVENT_MINOR_VEHICLE_CRASH, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Mcrash" +#endif + }, + { EVENT_TRAFFIC_IMPEDED, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "CarWay" +#endif + }, + { EVENT_HIT_BREAKABLE, OccasionalPlayLine, 20 +#ifndef RAD_RELEASE + , "Break" +#endif + }, + { EVENT_COLLECT_OBJECT, OccasionalPlayLine, 50 +#ifndef RAD_RELEASE + , "ObjectW" +#endif + }, + { EVENT_BIG_BOOM_SOUND, ShouldPlayLine, 100 +#ifndef RAD_RELEASE + , "Dcar" +#endif + }, + { EVENT_MISSION_FAILURE, ShouldPlayLine, 100 +#ifndef RAD_RELEASE + , "Mfail" +#endif + }, + { EVENT_TAIL_LOST_DIALOG, ShouldPlayLine, 100 +#ifndef RAD_RELEASE + , "Tail" +#endif + }, + { EVENT_MISSION_SUCCESS_DIALOG, ShouldPlayLine, 100 +#ifndef RAD_RELEASE + , "Mvic" +#endif + }, + { EVENT_CARD_COLLECTED, MustPlayLine, 100 +#ifndef RAD_RELEASE + , "Card" +#endif + }, + { EVENT_PHONE_BOOTH_RIDE_REQUEST, MustPlayLine, 100 +#ifndef RAD_RELEASE + , "Askride" +#endif + }, + { EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, MustPlayLine, 100 +#ifndef RAD_RELEASE + , "Ridereply" +#endif + }, + { EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED, MustPlayLine, 100 +#ifndef RAD_RELEASE + , "Answer" +#endif + }, + { EVENT_MISSION_BRIEFING_ACCEPTED, MustPlayLine, 100 +#ifndef RAD_RELEASE + , "Mstart" +#endif + }, + { EVENT_CONVERSATION_INIT_DIALOG, MustPlayImmediately, 100 +#ifndef RAD_RELEASE + , "EVENT_CONVERSATION_INIT_DIALOG" +#endif + }, + { EVENT_IN_GAMEPLAY_CONVERSATION, MustPlayImmediately, 100 +#ifndef RAD_RELEASE + , "EVENT_IN_GAMEPLAY_CONVERSATION" +#endif + }, + { EVENT_TUTORIAL_DIALOG_PLAY, MustPlayImmediately, 100 +#ifndef RAD_RELEASE + , "EVENT_TUTORIAL_DIALOG_PLAY" +#endif + }, + { EVENT_DING_DONG, MustPlayImmediately, 100 +#ifndef RAD_RELEASE + , "EVENT_DING_DONG" +#endif + } +}; + +static unsigned int priorityTableLength = sizeof( priorityTable ) / sizeof( LinePriorityTableEntry ); + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogQueueElement::DialogQueueElement +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogQueueElement::DialogQueueElement( SelectableDialog* dialog ) : + m_dialog( dialog ), + m_player1( NULL ), + m_player2( NULL ), + m_lineDoneCallback( NULL ), + m_dialogDoneCallback( NULL ), + m_linesPlayed( 0 ), + m_queue( NULL ), + m_hasPosition( false ) +{ + unsigned int lifeInMsecs; + + rAssert( m_dialog != NULL ); + + if( s_timerList == NULL ) + { + // + // We need enough timers for each element in the queue, plus one for + // currently playing dialog and one to create before we test for the + // queue being full. Actually, we don't really need one for current + // dialog, but we'll call that safety margin. + // + ::radTimeCreateList( &s_timerList, MAX_QUEUE_ELEMENTS + 2, GMA_AUDIO_PERSISTENT ); + } + + m_timer = NULL; + + lifeInMsecs = DialogLine::GetLifeInMsecsForEvent( dialog->GetEvent() ); + + if( lifeInMsecs > 0 ) + { + s_timerList->CreateTimer( &m_timer, + lifeInMsecs, + this, + NULL, + true, + IRadTimer::ResetModeOneShot ); + rAssert( m_timer != NULL ); + } +} + +//============================================================================== +// DialogQueueElement::~DialogQueueElement +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogQueueElement::~DialogQueueElement() +{ + if( m_timer ) + { + m_timer->UnregisterCallback( this ); +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "~DialogQueueElement Timer: %d\n", m_timer->GetRefCount() ); +#endif + m_timer->Release(); + } +} + +//============================================================================= +// DialogQueueElement::OnTimerDone +//============================================================================= +// Description: If the timer goes off and calls this function, then the +// lifetime for the dialog has expired and we need to excuse +// ourselves from the queue. +// +// Parameters: elapsedTime - ignored +// pUserData - ignored +// +// Return: void +// +//============================================================================= +void DialogQueueElement::OnTimerDone( unsigned int elapsedTime, void * pUserData ) +{ + RemoveSelfFromList(); + + // + // We're now expendable. Delete self. + // +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "OnTimerDone %x: Count %d\n", this, GetRefCount() ); +#endif + Release(); +} + +//============================================================================= +// DialogQueueElement::AddToQueue +//============================================================================= +// Description: Add self to the given dialog queue +// +// Parameters: queue - queue to add self to +// posn - position of dialog speaker, NULL if non-positional +// +// Return: none +// +//============================================================================= +void DialogQueueElement::AddToQueue( DialogQueueType* queue, rmt::Vector* posn ) +{ + DialogQueueElement* lowerPriorityElement; + bool duplicateFound = false; + DialogPriority priority = CalculateDialogPriority( *m_dialog ); + + rAssert( queue != NULL ); + m_queue = queue; + + if( posn != NULL ) + { + m_hasPosition = true; + m_position = *posn; + } + else + { + m_hasPosition = false; + } + + if( m_queue->empty() ) + { + m_queue->push_front( this ); + } + else + { + DialogQueueType::iterator iter = m_queue->begin(); + + // + // Search the rest of the list to see if we've already got this + // dialog in it. It couldn't have been passed over yet because + // all the earlier stuff has a higher priority + // + for( ; iter != m_queue->end(); ++iter ) + { + if( (*iter)->DialogMatches( m_dialog ) ) + { + duplicateFound = true; + break; + } + } + + if( !duplicateFound ) + { + if( m_queue->size() >= MAX_QUEUE_ELEMENTS ) + { + // + // Dialog full, something has to go. Ditch the lowest priority + // one. If that's the one we're inserting, don't do it. If there's + // a lower-priority one in the queue, ditch that one instead. + // + if( iter == m_queue->end() ) + { + // + // Nothing lower + // +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "AddToQueue %x: Count %d\n", this, GetRefCount() ); +#endif + Release(); + } + else + { + lowerPriorityElement = *iter; + m_queue->insert( iter, this ); + m_queue->remove( lowerPriorityElement ); + lowerPriorityElement->Release(); + } + } + else + { + // + // Find spot to insert based on priority + // + iter = m_queue->begin(); + while( ( iter != m_queue->end() ) + && ( (*iter)->GetPriority() >= priority ) ) + { + ++iter; + } + + m_queue->insert( iter, this ); + } + } + else + { + // + // No need to add this to the queue, and the caller is now relying + // on this object for self-management, so delete self + // +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "AddToQueue %x: Count %d\n", this, GetRefCount() ); +#endif + Release(); + } + } +} + +//============================================================================= +// DialogQueueElement::RemoveSelfFromList +//============================================================================= +// Description: Pull ourself from the queue element list +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogQueueElement::RemoveSelfFromList() +{ + rAssert( m_queue != NULL ); + + m_queue->remove( this ); +} + +//============================================================================= +// DialogQueueElement::GetDialogPriority +//============================================================================= +// Description: Searches the priority table for a priority matching the event +// ID for this bit of dialog. +// +// Parameters: dialog - dialog to find priority for +// +// Return: DialogPriority value for dialog if found in table, +// UnknownPriority otherwise. +// +//============================================================================= +DialogPriority DialogQueueElement::CalculateDialogPriority( const SelectableDialog& dialog ) +{ + EventEnum eventID; + unsigned int i; + DialogPriority priority = UnknownPriority; + + eventID = dialog.GetEvent(); + for( i = 0; i < priorityTableLength; i++ ) + { + if( priorityTable[i].eventID == eventID ) + { + priority = priorityTable[i].priority; + break; + } + } + + return( priority ); +} + +//============================================================================= +// DialogQueueElement::CalculateDialogProbability +//============================================================================= +// Description: Searches the priority table for a probability matching the event +// ID for this bit of dialog. +// +// Parameters: dialog - dialog to find priority for +// +// Return: Probability as % for dialog if found in table, +// 100 otherwise. +// +//============================================================================= +unsigned int DialogQueueElement::CalculateDialogProbability( const SelectableDialog& dialog ) +{ + EventEnum eventID; + unsigned int i; + unsigned int probability = 100; + + eventID = dialog.GetEvent(); + for( i = 0; i < priorityTableLength; i++ ) + { + if( ( priorityTable[i].priority == OccasionalPlayLine ) + && ( priorityTable[i].eventID == eventID ) ) + { + probability = priorityTable[i].probability; + break; + } + } + + return( probability ); +} + +//============================================================================= +// DialogQueueElement::OnPlaybackComplete +//============================================================================= +// Description: Callback function, triggered when currently playing dialog +// is finished +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogQueueElement::OnPlaybackComplete() +{ + unsigned int numDialogLines = m_dialog->GetNumDialogLines(); + SimpsonsSoundPlayer* player; + SimpsonsSoundPlayer* queuer; // I think I made up a word + Character* npcPtr; + + // + // Reference self in case one of the callbacks we trigger + // here tries to delete this object + // + AddRef(); + + if( ++m_linesPlayed >= numDialogLines ) + { + if( m_dialogDoneCallback ) + { + if( isMouthFlappingEvent( m_dialog->GetEvent() ) ) + { + // + // Coordinate the mouth flapping. Find out whether the PC + // or the NPC just finished talking, and send out the appropriate + // talk/shutup events. + // + if( dialogLineIsWalker( m_linesPlayed ) ) + { + GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP ); + } + else + { + GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP ); + } + } + + // + // Tell the rest of the world that we're done + // + if( m_dialog->GetEvent() == EVENT_CONVERSATION_INIT_DIALOG ) + { +/* + if( m_dialog->GetMission() == static_cast<unsigned int>( DialogLine::TUTORIAL_MISSION_NUMBER ) ) + { + GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_DONE ); + } + else +*/ + { + GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE ); + } + } + else if( m_dialog->GetEvent() == EVENT_TUTORIAL_DIALOG_PLAY ) + { + GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_DONE ); + } + + m_dialogDoneCallback->OnDialogComplete(); + } + } + else + { + if( m_lineDoneCallback ) + { + m_lineDoneCallback->OnDialogLineComplete(); + } + + if( isMouthFlappingEvent( m_dialog->GetEvent() ) ) + { + // + // Coordinate the mouth flapping. Find out whether the PC + // or the NPC just finished talking, and send out the appropriate + // talk/shutup events. + // + if( dialogLineIsWalker( m_linesPlayed ) ) + { + GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP ); + } + else + { + GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP ); + } + + // + // Start up the next line + // + if( dialogLineIsWalker( m_linesPlayed + 1 ) ) + { + GetEventManager()->TriggerEvent( EVENT_PC_TALK ); + } + else + { + npcPtr = dialogLineIsNPC( m_linesPlayed + 1 ); + if( npcPtr != NULL ) + { + GetEventManager()->TriggerEvent( EVENT_NPC_TALK, npcPtr ); + } + } + } + + // + // Even line numbers on player 1, odd on player 2 + // + if( m_linesPlayed % 2 == 0 ) + { + player = m_player1; + queuer = m_player2; + } + else + { + player = m_player2; + queuer = m_player1; + } + m_dialog->PlayQueuedLine( *player, this ); + + // + // Queue dialog if necessary + // + if( numDialogLines > m_linesPlayed + 1 ) + { + m_dialog->QueueLine( m_linesPlayed + 1, *queuer ); + } + } + +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "OnPlaybackComplete %x: Count %d\n", this, GetRefCount() ); +#endif + Release(); +} + +//============================================================================= +// DialogQueueElement::OnSoundReady +//============================================================================= +// Description: Unused, required for SimpsonsSoundPlayerCallback interface +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogQueueElement::OnSoundReady() +{ +} + +//============================================================================= +// DialogQueueElement::PlayDialog +//============================================================================= +// Description: Comment +// +// Parameters: player - SimpsonsSoundPlayer to use for playback +// lineCallback - callback for when an individual line of +// dialog is done, but not the entire dialog +// dialogCallback - callback for when the entire dialog is done +// +// Return: void +// +//============================================================================= +void DialogQueueElement::PlayDialog( SimpsonsSoundPlayer& player1, + SimpsonsSoundPlayer& player2, + DialogLineCompleteCallback* lineCallback, + DialogCompleteCallback* dialogCallback ) +{ + Character* npcPtr; + + m_lineDoneCallback = lineCallback; + m_dialogDoneCallback = dialogCallback; + + m_player1 = &player1; + m_player2 = &player2; + m_dialog->PlayLine( m_linesPlayed, *m_player1, this ); + if( m_dialog->GetNumDialogLines() >= 2 ) + { + // + // Queue up the next line + // + m_dialog->QueueLine( m_linesPlayed + 1, *m_player2 ); + } + + if( isMouthFlappingEvent( m_dialog->GetEvent() ) ) + { + // + // Start the mouth flapping + // + if( dialogLineIsWalker( 1 ) ) + { + GetEventManager()->TriggerEvent( EVENT_PC_TALK ); + } + else + { + npcPtr = dialogLineIsNPC( 1 ); + if( npcPtr != NULL ) + { + GetEventManager()->TriggerEvent( EVENT_NPC_TALK, npcPtr ); + } + } + } + + if( m_timer ) + { + m_timer->UnregisterCallback( this ); +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "PlayDialog Timer: %d\n", m_timer->GetRefCount() ); +#endif + m_timer->Release(); + m_timer = NULL; + } +} + +//============================================================================= +// DialogQueueElement::StopDialog +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void DialogQueueElement::StopDialog() +{ + // + // Just in case of nasty callback action on the Stop() calls below + // + AddRef(); + + // + // Skip to the end of the dialog before triggering any stop callbacks + // + m_linesPlayed = m_dialog->GetNumDialogLines(); + + m_player1->Stop(); + m_player2->Stop(); + + if( m_dialog != NULL ) + { + if( isMouthFlappingEvent( m_dialog->GetEvent() ) ) + { + GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP ); + GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP ); + } + } + +#ifdef DEBUG_QUEUE_REFCOUNT + rTunePrintf( "StopDialog %x: Count %d\n", this, GetRefCount() ); +#endif + Release(); +} + +//============================================================================= +// DialogQueueElement::Service +//============================================================================= +// Description: Service the timer list +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogQueueElement::Service() +{ + if( s_timerList != NULL ) + { + s_timerList->Service(); + } + +#ifndef RAD_RELEASE + if( !s_watcherInitialized ) + { + // + // Register probability table in Watcher + // + unsigned int i; + + for( i = 0; i < priorityTableLength; i++ ) + { + if( priorityTable[i].priority == OccasionalPlayLine ) + { + radDbgWatchAddUnsignedInt( &(priorityTable[i].probability), + priorityTable[i].eventName, + "Dialogue Tuning", + NULL, + NULL, + 0, + 100 ); + } + } + + s_watcherInitialized = true; + } +#endif +} + +//============================================================================= +// DialogQueueElement::GetPosition +//============================================================================= +// Description: If this is a positional dialog, return a position +// +// Parameters: None +// +// Return: pointer to rmt::Vector with position if positional, NULL otherwise +// +//============================================================================= +rmt::Vector* DialogQueueElement::GetPosition() +{ + rmt::Vector* retVal; + + if( m_hasPosition ) + { + retVal = &m_position; + } + else + { + retVal = NULL; + } + + return( retVal ); +} + +void DialogQueueElement::FillDebugInfo( DialogSoundDebugPage& debugInfo, unsigned int lineNum ) +{ + unsigned int msecs; + + if( m_timer != NULL ) + { + msecs = m_timer->GetTimeout(); + } + else + { + msecs = 0; + } + + debugInfo.SetQueueEntry( lineNum, + m_dialog->GetEvent(), + m_dialog->GetMission(), + m_dialog->GetLevel(), + m_dialog->GetDialogLineCharacterUID( 1 ), + CalculateDialogPriority( *m_dialog ), + msecs ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogQueueElement::dialogLineIsWalker +//============================================================================= +// Description: Indicates whether the dialog line with the given number +// belongs to the player character +// +// Parameters: lineNum - line number to check +// +// Return: true if player character, false otherwise +// +//============================================================================= +bool DialogQueueElement::dialogLineIsWalker( unsigned int lineNum ) +{ + tUID characterUID; + Character* walker; + tUID walkerUID; + + characterUID = m_dialog->GetDialogLineCharacterUID( lineNum ); + walker = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter(); + rAssert( walker != NULL ); + walkerUID = walker->GetUID(); + + return( characterUID == walkerUID ); +} + +//============================================================================= +// DialogQueueElement::dialogLineIsNPC +//============================================================================= +// Description: Indicates whether the dialog line with the given number +// belongs to an NPC +// +// Parameters: lineNum - line number to check +// +// Return: pointer to character object if found, NULL otherwise +// +//============================================================================= +Character* DialogQueueElement::dialogLineIsNPC( unsigned int lineNum ) +{ + tUID speakerUID; + Character* npc; + + speakerUID = m_dialog->GetDialogLineCharacterUID( lineNum ); + npc = GetCharacterManager()->GetCharacterByName( speakerUID ); + + // + // If the character is an NPC, we'll get a pointer. If it's some + // character that doesn't exist in the world (e.g. Brockman in L7M1), + // then we get NULL. + // + if( npc != NULL ) + { + return( npc ); + } + + // + // P.S. Not only do we have to check the speaker's UID, we have to + // check for stinky skins as well + // + return( DialogList::GetStinkySkinPointer( speakerUID ) ); +} + +//============================================================================= +// DialogQueueElement::isMouthFlappingEvent +//============================================================================= +// Description: Indicate whether given dialogue event should trigger mouth +// flapping +// +// Parameters: theEvent - event to scrutinize +// +// Return: true if mouth flapping event, false otherwise +// +//============================================================================= +bool DialogQueueElement::isMouthFlappingEvent( EventEnum theEvent ) +{ + bool retVal = false; + + switch( theEvent ) + { + case EVENT_CONVERSATION_INIT_DIALOG: + case EVENT_AMBIENT_ASKFOOD: + case EVENT_AMBIENT_FOODREPLY: + case EVENT_AMBIENT_GREETING: + case EVENT_AMBIENT_RESPONSE: + case EVENT_PEDESTRIAN_SMACKDOWN: + case EVENT_CHARACTER_TIRED_NOW: + retVal = true; + break; + + default: + break; + } + + return( retVal ); +}
\ No newline at end of file diff --git a/game/code/sound/dialog/dialogqueueelement.h b/game/code/sound/dialog/dialogqueueelement.h new file mode 100644 index 0000000..54074b1 --- /dev/null +++ b/game/code/sound/dialog/dialogqueueelement.h @@ -0,0 +1,158 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogqueueelement.h +// +// Description: Wrapper for SelectableDialog objects in the dialog queue, +// to associate them with a timer and to allow reception of +// playback completion callbacks without having to bother +// the queue class with partially-complete conversations. +// +// History: 04/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGQUEUEELEMENT_H +#define DIALOGQUEUEELEMENT_H + +//======================================== +// Nested Includes +//======================================== +#include <radtime.hpp> + +#include <sound/simpsonssoundplayer.h> +#include <sound/dialog/dialogqueuetype.h> +#include <events/eventenum.h> + +//======================================== +// Forward References +//======================================== +class SelectableDialog; +class SimpsonsSoundPlayer; +class DialogSoundDebugPage; +class Character; + +// +// Four levels of playback priority +// +enum DialogPriority +{ + UnknownPriority, + + OccasionalPlayLine, + ShouldPlayLine, + MustPlayLine, + MustPlayImmediately +}; + +//============================================================================= +// +// Synopsis: DialogLineCompleteCallback - callback interface, triggered +// when a line of dialog is finished playing, but more +// dialog remains to be played +// +//============================================================================= + +class DialogLineCompleteCallback +{ + public: + virtual void OnDialogLineComplete() = 0; +}; + +//============================================================================= +// +// Synopsis: DialogCompleteCallback - callback interface, triggered +// when all of the dialog has been played +// +//============================================================================= + +class DialogCompleteCallback +{ + public: + virtual void OnDialogComplete() = 0; +}; + +//============================================================================= +// +// Synopsis: DialogQueueElement +// +//============================================================================= + +class DialogQueueElement : public IRadTimerCallback, + public SimpsonsSoundPlayerCallback, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "DialogQueueElement" ); + + DialogQueueElement( SelectableDialog* dialog ); + virtual ~DialogQueueElement(); + + static void CreateTimerList(); + + void OnTimerDone( unsigned int elapsedTime, void * pUserData ); + + void AddToQueue( DialogQueueType* queue, rmt::Vector* posn ); + void RemoveSelfFromList(); + + void PlayDialog( SimpsonsSoundPlayer& player1, + SimpsonsSoundPlayer& player2, + DialogLineCompleteCallback* lineCallback, + DialogCompleteCallback* dialogCallback ); + void StopDialog(); + + DialogPriority GetPriority() { return( CalculateDialogPriority( *m_dialog ) ); } + + bool DialogMatches( SelectableDialog* dialog ) { return( dialog == m_dialog ); } + + rmt::Vector* GetPosition(); + + void FillDebugInfo( DialogSoundDebugPage& debugInfo, unsigned int lineNum ); + + // + // SimpsonsSoundPlayer callbacks + // + void OnPlaybackComplete(); + void OnSoundReady(); + + static DialogPriority CalculateDialogPriority( const SelectableDialog& dialog ); + static unsigned int CalculateDialogProbability( const SelectableDialog& dialog ); + static void Service(); + + static const unsigned int MAX_QUEUE_ELEMENTS = 16; + + private: + //Prevent wasteful constructor creation. + DialogQueueElement(); + DialogQueueElement( const DialogQueueElement& original ); + DialogQueueElement& operator=( const DialogQueueElement& rhs ); + + bool dialogLineIsWalker( unsigned int lineNum ); + Character* dialogLineIsNPC( unsigned int lineNum ); + + bool isMouthFlappingEvent( EventEnum theEvent ); + + static IRadTimerList* s_timerList; + + static bool s_watcherInitialized; + + SelectableDialog* m_dialog; + IRadTimer* m_timer; + + SimpsonsSoundPlayer* m_player1; + SimpsonsSoundPlayer* m_player2; + + DialogLineCompleteCallback* m_lineDoneCallback; + DialogCompleteCallback* m_dialogDoneCallback; + + unsigned int m_linesPlayed; + + DialogQueueType* m_queue; + + rmt::Vector m_position; + bool m_hasPosition; +}; + + +#endif // DIALOGQUEUEELEMENT_H + diff --git a/game/code/sound/dialog/dialogqueuetype.h b/game/code/sound/dialog/dialogqueuetype.h new file mode 100644 index 0000000..178396d --- /dev/null +++ b/game/code/sound/dialog/dialogqueuetype.h @@ -0,0 +1,24 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogqueuetype.h +// +// Description: Typedef for STL list of dialog queue elements. Removed from +// DialogPriorityQueue to avoid unnecessary dependency. +// +// History: 9/25/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGQUEUETYPE_H +#define DIALOGQUEUETYPE_H + +class DialogQueueElement; + +#include <list> +#include <memory/stlallocators.h> + +typedef std::list< DialogQueueElement*, s2alloc<DialogQueueElement*> > DialogQueueType; + +#endif // DIALOGQUEUETYPE_H + diff --git a/game/code/sound/dialog/dialogselectiongroup.cpp b/game/code/sound/dialog/dialogselectiongroup.cpp new file mode 100644 index 0000000..3b3b0f0 --- /dev/null +++ b/game/code/sound/dialog/dialogselectiongroup.cpp @@ -0,0 +1,376 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogselectiongroup.cpp +// +// Description: Represents a set of dialog lines or conversations that can +// be randomly selected without affecting gameplay. For example, +// if Homer had three generic lines for playback whenever he is +// hit by a car, and all three can be substituted for each other +// freely, then those lines would be grouped by a DialogSelectionGroup +// object. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= +#define DONTCHECKVECTORRESIZING +//======================================== +// System Includes +//======================================== +#include <radtime.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogselectiongroup.h> + +#include <sound/simpsonssoundplayer.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Typical max number of elements in the vector, so as to not waste space +// +static const int TYPICAL_VECTOR_SIZE = 4; + +// +// Value for in-use dialog index when nothing being played +// +static const short DIALOG_NOT_IN_USE = -1; + +static const short DIALOG_NOT_YET_USED = -1; + + + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// DialogSelectionGroup::DialogSelectionGroup +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogSelectionGroup::DialogSelectionGroup( SelectableDialog& dialog1, + SelectableDialog& dialog2 ) : + m_currentlyPlayingDialog( DIALOG_NOT_IN_USE ), + m_lastSelection( DIALOG_NOT_YET_USED ) +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_dialogVector.reserve( TYPICAL_VECTOR_SIZE ); + + m_dialogVector.push_back( &dialog1 ); + m_dialogVector.push_back( &dialog2 ); + + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); +} + +//============================================================================== +// DialogSelectionGroup::~DialogSelectionGroup +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +DialogSelectionGroup::~DialogSelectionGroup() +{ +} + +//============================================================================== +// DialogSelectionGroup::PlayLine +//============================================================================== +// Description: Select a dialog if we haven't done it yet and play it. +// +// Parameters: lineIndex - index of dialog line to play +// player - sound player +// callback - callback for when the dialog line is complete +// +// Return: N/A. +// +//============================================================================== +void DialogSelectionGroup::PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + if( lineIndex == 0 ) + { + makeRandomSelection(); + } + else + { + rAssert( m_currentlyPlayingDialog != DIALOG_NOT_IN_USE ); + } + + m_dialogVector[m_currentlyPlayingDialog]->PlayLine( lineIndex, player, callback ); +} + +//============================================================================= +// DialogSelectionGroup::QueueLine +//============================================================================= +// Description: Select a dialog if we haven't done it yet and queue it for +// playback +// +// Parameters: lineIndex - index of dialog line to play +// player - sound player +// +// Return: void +// +//============================================================================= +void DialogSelectionGroup::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player ) +{ + if( lineIndex == 0 ) + { + makeRandomSelection(); + } + else + { + rAssert( m_currentlyPlayingDialog != DIALOG_NOT_IN_USE ); + } + + m_dialogVector[m_currentlyPlayingDialog]->QueueLine( lineIndex, player ); +} + +//============================================================================= +// DialogSelectionGroup::PlayQueuedLine +//============================================================================= +// Description: Play line of dialog that we queued earlier +// +// Parameters: player - sound player +// callback - callback for when the dialog line is complete +// +// Return: void +// +//============================================================================= +void DialogSelectionGroup::PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) +{ + m_dialogVector[m_currentlyPlayingDialog]->PlayQueuedLine( player, callback ); +} + +//============================================================================= +// DialogSelectionGroup::GetNumDialogLines +//============================================================================= +// Description: Return the number of dialog lines in the currently selected +// SelectableDialog object +// +// Parameters: None +// +// Return: void +// +//============================================================================= +unsigned int DialogSelectionGroup::GetNumDialogLines() const +{ + if( m_currentlyPlayingDialog == DIALOG_NOT_IN_USE ) + { + // + // Assume shortest case + // + return( 1 ); + } + else + { + return( m_dialogVector[m_currentlyPlayingDialog]->GetNumDialogLines() ); + } +} + +//============================================================================= +// DialogSelectionGroup::UsesCharacter +//============================================================================= +// Description: Indicate if any of the dialogs use the given character +// +// Parameters: characterObj - character to match +// +// Return: true if character used, false otherwise +// +//============================================================================= +bool DialogSelectionGroup::UsesCharacter( tUID characterUID ) +{ + if( m_dialogVector.empty() ) + { + return( false ); + } + else + { + return( m_dialogVector[0]->UsesCharacter( characterUID ) ); + } +} + +//============================================================================= +// DialogSelectionGroup::IsVillainLine +//============================================================================= +// Description: Indicate if the dialogs are villain +// +// Parameters: None +// +// Return: true if villain, false otherwise +// +//============================================================================= +bool DialogSelectionGroup::IsVillainLine() +{ + if( m_dialogVector.empty() ) + { + return( false ); + } + else + { + return( m_dialogVector[0]->IsVillainLine() ); + } +} + +//============================================================================= +// DialogSelectionGroup::GetDialogLineCharacterUID +//============================================================================= +// Description: Indicate which character is speaking with the given line +// +// Parameters: lineNum - indicates which dialog line we want the UID for +// +// Return: tUID of character associated with dialog line, zero if we +// haven't selected a character +// +//============================================================================= +tUID DialogSelectionGroup::GetDialogLineCharacterUID( unsigned int lineNum ) +{ + if( m_currentlyPlayingDialog == DIALOG_NOT_IN_USE ) + { + return( m_dialogVector[0]->GetDialogLineCharacterUID( lineNum ) ); + } + else + { + return( m_dialogVector[m_currentlyPlayingDialog]->GetDialogLineCharacterUID( lineNum ) ); + } +} + +//============================================================================= +// DialogSelectionGroup::AddMatchingDialog +//============================================================================= +// Description: Push given dialog onto list +// +// Parameters: newDialog - dialog to add +// +// Return: void +// +//============================================================================= +void DialogSelectionGroup::AddMatchingDialog( SelectableDialog& newDialog, + SelectableDialogList& list ) +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + m_dialogVector.push_back( &newDialog ); + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); +} + +//============================================================================= +// DialogSelectionGroup::GetEvent +//============================================================================= +// Description: Returns event associated with this group, which we'll get +// from a dialog in the list. +// +// Parameters: None +// +// Return: EventEnum representing event +// +//============================================================================= +EventEnum DialogSelectionGroup::GetEvent() const +{ + rAssert( m_dialogVector.size() > 0 ); + + return( m_dialogVector[0]->GetEvent() ); +} + +//============================================================================= +// DialogSelectionGroup::GetLevel +//============================================================================= +// Description: Returns level associated with this group, which we'll get +// from a dialog in the list. +// +// Parameters: None +// +// Return: index of level if one exists, NO_LEVEL otherwise +// +//============================================================================= +unsigned int DialogSelectionGroup::GetLevel() const +{ + rAssert( m_dialogVector.size() > 0 ); + + return( m_dialogVector[0]->GetLevel() ); +} + +//============================================================================= +// DialogSelectionGroup::GetMission +//============================================================================= +// Description: Returns event associated with this group, which we'll get +// from a dialog in the list. +// +// Parameters: None +// +// Return: index of mission if one exists, NO_MISSION otherwise +// +//============================================================================= +unsigned int DialogSelectionGroup::GetMission() const +{ + rAssert( m_dialogVector.size() > 0 ); + + return( m_dialogVector[0]->GetMission() ); +} + +//============================================================================= +// DialogSelectionGroup::GetConversationName +//============================================================================= +// Description: Not relevant in this class. +// +// Parameters: None +// +// Return: Zero, indicating we aren't a conversation +// +//============================================================================= +radKey32 DialogSelectionGroup::GetConversationName() +{ + return( 0 ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// DialogSelectionGroup::makeRandomSelection +//============================================================================= +// Description: Select dialog to play +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void DialogSelectionGroup::makeRandomSelection() +{ + if( m_lastSelection == DIALOG_NOT_YET_USED ) + { + // + // No real need to bother with true pseudo-randomization, just + // use the clock + // + m_lastSelection = static_cast<short>( radTimeGetMilliseconds() % m_dialogVector.size() ); + } + + m_currentlyPlayingDialog = m_lastSelection; + m_lastSelection = ( m_lastSelection + 1 ) % m_dialogVector.size(); +} + +#undef DONTCHECKVECTORRESIZING
\ No newline at end of file diff --git a/game/code/sound/dialog/dialogselectiongroup.h b/game/code/sound/dialog/dialogselectiongroup.h new file mode 100644 index 0000000..6da0d17 --- /dev/null +++ b/game/code/sound/dialog/dialogselectiongroup.h @@ -0,0 +1,91 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogselectiongroup.h +// +// Description: Represents a set of dialog lines or conversations that can +// be randomly selected without affecting gameplay. For example, +// if Homer had three generic lines for playback whenever he is +// hit by a car, and all three can be substituted for each other +// freely, then those lines would be grouped by a DialogSelectionGroup +// object. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGSELECTIONGROUP_H +#define DIALOGSELECTIONGROUP_H + +//======================================== +// Nested Includes +//======================================== +#include <vector> + +#include <sound/dialog/selectabledialog.h> + +//======================================== +// Forward References +//======================================== +class SimpsonsSoundPlayer; +struct SimpsonsSoundPlayerCallback; + +//============================================================================= +// +// Synopsis: DialogSelectionGroup +// +//============================================================================= + +class DialogSelectionGroup : public SelectableDialog +{ + public: + DialogSelectionGroup( SelectableDialog& dialog1, SelectableDialog& dialog2 ); + virtual ~DialogSelectionGroup(); + + void PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + void QueueLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player ); + void PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ); + + unsigned int GetNumDialogLines() const; + + bool UsesCharacter( tUID characterUID ); + tUID GetDialogLineCharacterUID( unsigned int lineNum ); + radKey32 GetConversationName(); + bool IsVillainLine(); + + void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ); + + // + // Overriding SelectableDialog methods + // + unsigned int GetMission() const; + unsigned int GetLevel() const; + EventEnum GetEvent() const; + + private: + //Prevent wasteful constructor creation. + DialogSelectionGroup(); + DialogSelectionGroup( const DialogSelectionGroup& original ); + DialogSelectionGroup& operator=( const DialogSelectionGroup& rhs ); + + void makeRandomSelection(); + + // + // List of SelectableDialog objects to choose from. Use std::vector + // for space conservation + // + typedef std::vector< SelectableDialog*, s2alloc<SelectableDialog*> > DialogVector; + + DialogVector m_dialogVector; + + short m_currentlyPlayingDialog; + short m_lastSelection; +}; + + +#endif // DIALOGSELECTIONGROUP_H + diff --git a/game/code/sound/dialog/dialogsounddebugpage.cpp b/game/code/sound/dialog/dialogsounddebugpage.cpp new file mode 100644 index 0000000..1823422 --- /dev/null +++ b/game/code/sound/dialog/dialogsounddebugpage.cpp @@ -0,0 +1,243 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogsounddebugpage.cpp +// +// Description: Implements a class for displaying dialog-related debug info +// on the screen, triggered using Watcher +// +// History: 1/20/2003 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/dialogsounddebugpage.h> + +#include <sound/dialog/dialogline.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// DialogSoundDebugPage::DialogSoundDebugPage +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +DialogSoundDebugPage::DialogSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) : + SoundDebugPage( pageNum, master ) +{ + unsigned int i; + + // + // Mark queue entries as empty by setting event ID to NUM_EVENTS + // + for( i = 0; i < MAX_QUEUE_SIZE; i++ ) + { + m_queueData[i].eventID = NUM_EVENTS; + } +} + +//============================================================================= +// DialogSoundDebugPage::~DialogSoundDebugPage +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +DialogSoundDebugPage::~DialogSoundDebugPage() +{ +} + +//============================================================================= +// DialogSoundDebugPage::SetQueueLength +//============================================================================= +// Description: Given queue length, clear out entries that are no longer in +// the queue +// +// Parameters: size - current size of queue +// +// Return: void +// +//============================================================================= +void DialogSoundDebugPage::SetQueueLength( unsigned int size ) +{ + unsigned int i; + + for( i = size; i < MAX_QUEUE_SIZE; i++ ) + { + m_queueData[i].eventID = NUM_EVENTS; + } +} + +//============================================================================= +// DialogSoundDebugPage::SetQueueEntry +//============================================================================= +// Description: Comment +// +// Parameters: ( unsigned int position, EventEnum event, unsigned int mission, unsigned int level, tUID theCharacter, DialogPriority priority, unsigned int msecsRemaining ) +// +// Return: void +// +//============================================================================= +void DialogSoundDebugPage::SetQueueEntry( unsigned int position, + EventEnum event, + unsigned int mission, + unsigned int level, + tUID theCharacter, + DialogPriority priority, + unsigned int msecsRemaining ) +{ + QueueInfo* data; + + if( position >= MAX_QUEUE_SIZE ) + { + // + // Don't bother displaying too much stuff + // + return; + } + + data = &m_queueData[position]; + data->eventID = event; + data->level = level; + data->mission = mission; + data->theCharacter = theCharacter; + data->priority = priority; + data->msecsRemaining = msecsRemaining; +} + +//***************************************************************************** +// +// Protected Member Functions +// +//***************************************************************************** + +//============================================================================= +// DialogSoundDebugPage::fillLineBuffer +//============================================================================= +// Description: Fill the given buffer with text to display on the screen +// at the given line +// +// Parameters: lineNum - line number on screen where buffer will be displayed +// buffer - to be filled in with text to display +// +// Return: void +// +//============================================================================= +void DialogSoundDebugPage::fillLineBuffer( int lineNum, char* buffer ) +{ + switch( lineNum ) + { + case 0: + strcpy( buffer, "Now playing:" ); + break; + + case 1: + fillQueueText( buffer, 0 ); + break; + + case 3: + strcpy( buffer, "Queued:" ); + break; + + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + fillQueueText( buffer, lineNum - 3 ); + break; + + default: + buffer[0] = '\0'; + break; + } +} + +//============================================================================= +// DialogSoundDebugPage::getNumLines +//============================================================================= +// Description: Returns number of lines that we'll display on screen +// +// Parameters: None +// +// Return: Line count +// +//============================================================================= +int DialogSoundDebugPage::getNumLines() +{ + return( 13 ); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** + +//============================================================================= +// DialogSoundDebugPage::fillQueueText +//============================================================================= +// Description: Fill the text buffer with information about one particular +// dialog queue entry +// +// Parameters: buffer - text buffer to fill +// index - index of m_queueData entry to fill out with +// +// Return: void +// +//============================================================================= +void DialogSoundDebugPage::fillQueueText( char* buffer, unsigned int index ) +{ + QueueInfo* info; + char eventName[30]; + char characterName[10]; + + rAssert( index >= 0 ); + rAssert( index <= MAX_QUEUE_SIZE ); + rAssert( buffer != NULL ); + + info = &m_queueData[index]; + if( info->eventID != NUM_EVENTS ) + { + DialogLine::FillEventName( eventName, 30, info->eventID ); + DialogLine::FillCharacterName( characterName, 10, info->theCharacter ); + sprintf( buffer, "Event: %s Char: %s Pri: %d L%dM%d", + eventName, + characterName, + info->priority, + info->level, + info->mission ); + } + else + { + buffer[0] = '\0'; + } +} diff --git a/game/code/sound/dialog/dialogsounddebugpage.h b/game/code/sound/dialog/dialogsounddebugpage.h new file mode 100644 index 0000000..9fb0652 --- /dev/null +++ b/game/code/sound/dialog/dialogsounddebugpage.h @@ -0,0 +1,89 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: dialogsounddebugpage.h +// +// Description: Declares a class for displaying dialog-related debug info +// on the screen, triggered using Watcher +// +// History: 1/20/2003 + Created -- Darren +// +//============================================================================= + +#ifndef DIALOGSOUNDDEBUGPAGE_H +#define DIALOGSOUNDDEBUGPAGE_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/sounddebug/sounddebugpage.h> + +#include <events/eventenum.h> +#include <sound/dialog/dialogqueueelement.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: DialogSoundDebugPage +// +//============================================================================= + +class DialogSoundDebugPage : public SoundDebugPage +{ + public: + DialogSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ); + virtual ~DialogSoundDebugPage(); + + void SetQueueLength( unsigned int size ); + + void SetQueueEntry( unsigned int position, + EventEnum event, + unsigned int mission, + unsigned int level, + tUID theCharacter, + DialogPriority priority, + unsigned int msecsRemaining ); + + protected: + // + // Pure virtual functions from SoundDebugPage + // + void fillLineBuffer( int lineNum, char* buffer ); + int getNumLines(); + + private: + //Prevent wasteful constructor creation. + DialogSoundDebugPage(); + DialogSoundDebugPage( const DialogSoundDebugPage& dialogsounddebugpage ); + DialogSoundDebugPage& operator=( const DialogSoundDebugPage& dialogsounddebugpage ); + + void fillQueueText( char* buffer, unsigned int index ); + + // + // Structure for holding queue information + // + struct QueueInfo + { + EventEnum eventID; + unsigned int mission; + unsigned int level; + tUID theCharacter; + DialogPriority priority; + unsigned int msecsRemaining; + }; + + static const unsigned int MAX_QUEUE_SIZE = 10; + + QueueInfo m_queueData[MAX_QUEUE_SIZE]; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //DIALOGSOUNDDEBUGPAGE_H diff --git a/game/code/sound/dialog/playabledialog.cpp b/game/code/sound/dialog/playabledialog.cpp new file mode 100644 index 0000000..c251354 --- /dev/null +++ b/game/code/sound/dialog/playabledialog.cpp @@ -0,0 +1,87 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: playabledialog.cpp +// +// Description: Abstract base class for the basic unit of dialog playback. +// A PlayableDialog object represents a piece of dialog which +// is to be played without interruption (unless it gets booted +// by a higher-priority PlayableDialog). +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/playabledialog.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// PlayableDialog::PlayableDialog +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +PlayableDialog::PlayableDialog() +{ +} + +//============================================================================== +// PlayableDialog::PlayableDialog +//============================================================================== +// Description: Constructor. +// +// Parameters: mission - mission number for dialog +// level - level number +// event - event that this dialog is played in response to +// +// Return: N/A. +// +//============================================================================== +PlayableDialog::PlayableDialog( unsigned int level, unsigned int mission, EventEnum event ) : + SelectableDialog( level, mission, event ) +{ +} + +//============================================================================== +// PlayableDialog::~PlayableDialog +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +PlayableDialog::~PlayableDialog() +{ +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/dialog/playabledialog.h b/game/code/sound/dialog/playabledialog.h new file mode 100644 index 0000000..5c7f16b --- /dev/null +++ b/game/code/sound/dialog/playabledialog.h @@ -0,0 +1,48 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: playabledialog.h +// +// Description: Abstract base class for the basic unit of dialog playback. +// A PlayableDialog object represents a piece of dialog which +// is to be played without interruption (unless it gets booted +// by a higher-priority PlayableDialog). +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef PLAYABLEDIALOG_H +#define PLAYABLEDIALOG_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/dialog/selectabledialog.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: PlayableDialog +// +//============================================================================= + +class PlayableDialog : public SelectableDialog +{ + public: + PlayableDialog(); + PlayableDialog( unsigned int level, unsigned int mission, EventEnum event ); + virtual ~PlayableDialog(); + + private: + //Prevent wasteful constructor creation. + PlayableDialog( const PlayableDialog& original ); + PlayableDialog& operator=( const PlayableDialog& rhs ); +}; + + +#endif // PLAYABLEDIALOG_H + diff --git a/game/code/sound/dialog/selectabledialog.cpp b/game/code/sound/dialog/selectabledialog.cpp new file mode 100644 index 0000000..c71fe42 --- /dev/null +++ b/game/code/sound/dialog/selectabledialog.cpp @@ -0,0 +1,214 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: selectabledialog.cpp +// +// Description: Abstract base class for groups of one or more lines of dialog, +// any of which can be selected randomly in response to a single +// sound event. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <raddebug.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/dialog/selectabledialog.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SelectableDialog::SelectableDialog +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SelectableDialog::SelectableDialog() : + m_missionNum( NO_MISSION ), + m_levelNum( NO_LEVEL ), + m_event( NUM_EVENTS ), +#ifndef RAD_RELEASE + m_hasBeenPlayed( false ), +#endif + m_nextListObject( NULL ) +{ +} + +//============================================================================== +// SelectableDialog::SelectableDialog +//============================================================================== +// Description: Constructor. +// +// Parameters: mission - mission number for dialog +// level - level number +// event - event that this dialog is played in response to +// +// Return: N/A. +// +//============================================================================== +SelectableDialog::SelectableDialog( unsigned int level, unsigned int mission, EventEnum event ) : + m_missionNum( mission ), + m_levelNum( level ), + m_event( event ), +#ifndef RAD_RELEASE + m_hasBeenPlayed( false ), +#endif + m_nextListObject( NULL ) +{ +} + +//============================================================================== +// SelectableDialog::~SelectableDialog +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SelectableDialog::~SelectableDialog() +{ +} + +//============================================================================= +// SelectableDialog::AddToDialogList +//============================================================================= +// Description: Place this object on the head of the list supplied +// +// Parameters: list - list to add to +// +// Return: void +// +//============================================================================= +void SelectableDialog::AddToDialogList( SelectableDialog** list ) +{ + m_nextListObject = *list; + *list = this; +} + +//============================================================================= +// SelectableDialog::AddToDialogList +//============================================================================= +// Description: Place this object in the list after the object supplied +// +// Parameters: listObj - object to add after +// +// Return: void +// +//============================================================================= +void SelectableDialog::AddToDialogList( SelectableDialog* listObj ) +{ + m_nextListObject = listObj->m_nextListObject; + listObj->m_nextListObject = this; +} + +//============================================================================= +// SelectableDialog::RemoveNextFromList +//============================================================================= +// Description: Pull out the object after this one in the list +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SelectableDialog::RemoveNextFromList() +{ + rAssert( m_nextListObject != NULL ); + m_nextListObject = m_nextListObject->m_nextListObject; +} + +//============================================================================= +// SelectableDialog::GetMission +//============================================================================= +// Description: Returns mission number associated with this dialog +// +// Parameters: None +// +// Return: unsigned int with the mission number, NO_MISSION for no mission +// +//============================================================================= +unsigned int SelectableDialog::GetMission() const +{ + return( m_missionNum ); +} + +//============================================================================= +// SelectableDialog::GetLevel +//============================================================================= +// Description: Returns level number associated with this dialog +// +// Parameters: None +// +// Return: unsigned int with the level number, NO_LEVEL for no level +// +//============================================================================= +unsigned int SelectableDialog::GetLevel() const +{ + return( m_levelNum ); +} + +//============================================================================= +// SelectableDialog::GetEvent +//============================================================================= +// Description: Returns event associated with this dialog +// +// Parameters: None +// +// Return: EventEnum +// +//============================================================================= +EventEnum SelectableDialog::GetEvent() const +{ + return( m_event ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +#ifndef RAD_RELEASE + +void SelectableDialog::MarkAsPlayed() +{ + m_hasBeenPlayed = true; +} + +void SelectableDialog::PrintPlayedStatus() +{ + if( m_hasBeenPlayed ) + { + rTuneString( "Played\n" ); + } + else + { + rTuneString( "NOT PLAYED\n" ); + } +} + +#endif diff --git a/game/code/sound/dialog/selectabledialog.h b/game/code/sound/dialog/selectabledialog.h new file mode 100644 index 0000000..c26125c --- /dev/null +++ b/game/code/sound/dialog/selectabledialog.h @@ -0,0 +1,118 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: selectabledialog.h +// +// Description: Abstract base class for groups of one or more lines of dialog, +// any of which can be selected randomly in response to a single +// sound event. +// +// History: 02/09/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SELECTABLEDIALOG_H +#define SELECTABLEDIALOG_H + +//======================================== +// Nested Includes +//======================================== +#include <events/eventenum.h> +#include <worldsim/character/character.h> +#include <sound/dialog/selectabledialoglist.h> + +//======================================== +// Forward References +//======================================== +class SimpsonsSoundPlayer; +struct SimpsonsSoundPlayerCallback; + +//============================================================================= +// +// Synopsis: SelectableDialog +// +//============================================================================= + +class SelectableDialog +{ + public: + SelectableDialog(); + SelectableDialog( unsigned int level, unsigned int mission, EventEnum event ); + virtual ~SelectableDialog(); + + static const int NO_MISSION = 0; + static const int NO_LEVEL = 0; + + bool IsLevelSpecific() { return( m_levelNum != NO_LEVEL ); } + + virtual unsigned int GetMission() const; + virtual unsigned int GetLevel() const; + virtual EventEnum GetEvent() const; + + virtual bool UsesCharacter( Character* characterObj ) + { return( UsesCharacter( characterObj->GetUID() ) ); } + virtual bool UsesCharacter( tUID characterUID ) = 0; + + virtual tUID GetDialogLineCharacterUID( unsigned int lineNum ) = 0; + + virtual bool IsVillainLine() = 0; + + void AddToDialogList( SelectableDialog** list ); + void AddToDialogList( SelectableDialog* listObj ); + SelectableDialog* GetNextInList() const { return( m_nextListObject ); } + void RemoveNextFromList(); + + virtual void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ) = 0; + + virtual void PlayLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) = 0; + virtual void QueueLine( unsigned int lineIndex, + SimpsonsSoundPlayer& player ) = 0; + virtual void PlayQueuedLine( SimpsonsSoundPlayer& player, + SimpsonsSoundPlayerCallback* callback ) = 0; + + virtual unsigned int GetNumDialogLines() const = 0; + + virtual radKey32 GetConversationName() = 0; + +#ifndef RAD_RELEASE + void MarkAsPlayed(); + void PrintPlayedStatus(); +#endif + + protected: + // + // Level, mission, and event values. Since these aren't really used + // in DialogSelectionGroup and Conversation objects, they should + // probably be separated out of this object someday if we want to + // save a few bytes and clean up the design a bit. + // + + // + // Level and mission number for this line. Set to NO_MISSION or + // NO_LEVEL if not applicable. + // + int m_missionNum; + int m_levelNum; + + // + // Event that this dialog is played in response to + // + EventEnum m_event; + + private: + //Prevent wasteful constructor creation. + SelectableDialog( const SelectableDialog& original ); + SelectableDialog& operator=( const SelectableDialog& rhs ); + +#ifndef RAD_RELEASE + bool m_hasBeenPlayed; +#endif + + SelectableDialog* m_nextListObject; +}; + + +#endif // SELECTABLEDIALOG_H + diff --git a/game/code/sound/dialog/selectabledialoglist.h b/game/code/sound/dialog/selectabledialoglist.h new file mode 100644 index 0000000..ddd5572 --- /dev/null +++ b/game/code/sound/dialog/selectabledialoglist.h @@ -0,0 +1,24 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: selectabledialoglist.h +// +// Description: Typedef for STL list of selectable dialogs. Removed from +// DialogList to avoid unnecessary dependency. +// +// History: 9/25/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SELECTABLEDIALOGLIST_H +#define SELECTABLEDIALOGLIST_H + +class SelectableDialog; + +#include <list> +#include <memory/stlallocators.h> + +typedef std::list< SelectableDialog*, s2alloc<SelectableDialog*> > SelectableDialogList; + +#endif // SELECTABLEDIALOGLIST_H + diff --git a/game/code/sound/listener.cpp b/game/code/sound/listener.cpp new file mode 100644 index 0000000..559cb11 --- /dev/null +++ b/game/code/sound/listener.cpp @@ -0,0 +1,236 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: listener.cpp +// +// Description: Implement Listener class, which tracks the camera and +// updates the RadSound listener position/orientation to match +// +// History: 02/08/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radmath/radmath.hpp> +#include <radsoundmath.hpp> +#include <radsound_hal.hpp> +#include <p3d/utility.hpp> +#include <p3d/vectorcamera.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/listener.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> + +#include <camera/supercammanager.h> +#include <camera/supercamcentral.h> +#include <camera/supercam.h> +#include <worldsim/avatarmanager.h> +#include <mission/gameplaymanager.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +static const float MAX_LISTENER_DIST_FROM_AVATAR = 10.0f; +static const float MAX_LISTENER_DIST_FROM_AVATAR_SQR = 100.0f; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// Listener::Listener +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Listener::Listener() : + m_theListener( NULL ), + m_feCamera( NULL ) +{ +} + +//============================================================================== +// Listener::~Listener +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Listener::~Listener() +{ +} + +//============================================================================= +// Listener::Initialize +//============================================================================= +// Description: Get a handle to the RadSound listener +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Listener::Initialize( Sound::daSoundRenderingManager& renderMgr ) +{ + m_theListener = renderMgr.GetTheListener(); +} + +//============================================================================= +// Listener::Update +//============================================================================= +// Description: Update the listener's position/orientation +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Listener::Update( ContextEnum context ) +{ + SuperCamCentral* scc; + SuperCam* camera; + rmt::Vector position; + rmt::Vector velocity; + rmt::Vector heading; + rmt::Vector up; + radSoundVector soundVector1; + radSoundVector soundVector2; + rmt::Vector diff; + Avatar* theAvatar; + rmt::Vector avatarPosn; + + + // + // Don't do anything if we don't have a listener yet + // + if( m_theListener == NULL ) + { + return; + } + + // + // Get the camera positioning and pass it on to the listener. + // QUESTION: is it valid to assume that we'll strictly use player 1? + // H2H positional sound hasn't really been resolved... + // + if( context == CONTEXT_FRONTEND ) + { + if( m_feCamera == NULL ) + { + m_feCamera = p3d::find<tVectorCamera>( "FE_Camera" ); + if( m_feCamera == NULL ) + { + // + // Camera hasn't been loaded yet, I guess. Early exit. + // + return; + } + } + + m_feCamera->GetPosition( &position ); + m_feCamera->GetDirection( &heading ); + m_feCamera->GetUpVector( &up ); + // + // This class doesn't provide velocities, assume zero. + // + velocity.Clear(); + } + else + { + m_feCamera = NULL; + + scc = GetSuperCamManager()->GetSCC( 0 ); + rAssert( scc != NULL ); + + camera = scc->GetActiveSuperCam(); + if( camera == NULL ) + { + // + // No camera, nothing to update to. + // + return; + } + + camera->GetPosition( &position ); + camera->GetVelocity( &velocity ); + camera->GetHeading( &heading ); + heading.NormalizeSafe(); + // + // Sadly, the camera defaults allow for a nonsense zero-length + // heading vector. Test for this case, and make up a more + // lenient default if found + // + if( ( heading.x == 0.0f ) && ( heading.y == 0.0f ) && ( heading.z == 0.0f ) ) + { + heading.x = 1.0f; + } + camera->GetCameraUp( &up ); + up.NormalizeSafe(); + + // + // We don't want the position to get too far away from the character, so clamp + // the distance to some arbitrary maximum + // + theAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 ); + if( ( theAvatar != NULL ) + && ( GetGameplayManager() != NULL ) + && ( !(GetGameplayManager()->IsSuperSprint()) ) ) + { + theAvatar->GetPosition( avatarPosn ); + diff = position - avatarPosn; + if( diff.MagnitudeSqr() > MAX_LISTENER_DIST_FROM_AVATAR_SQR ) + { + diff.Normalize(); + diff.Scale( MAX_LISTENER_DIST_FROM_AVATAR ); + position.Add( avatarPosn, diff ); + } + } + } + + // Position + soundVector1.SetElements( position.x, position.y, position.z ); + m_theListener->SetPosition( &soundVector1 ); + + // Velocity + soundVector1.SetElements( velocity.x, velocity.y, velocity.z ); + m_theListener->SetVelocity( &soundVector1 ); + + // Orientation + soundVector1.SetElements( heading.x, heading.y, heading.z ); + soundVector2.SetElements( up.x, up.y, up.z ); + + // + // Double-check the values, I'm not sure that DirectSound likes + // zero vectors + // + rAssert( ( heading.x != 0.0f ) + || ( heading.y != 0.0f ) + || ( heading.z != 0.0f ) ); + rAssert( ( up.x != 0.0f ) || ( up.y != 0.0f ) || ( up.z != 0.0f ) ); + + m_theListener->SetOrientation( &soundVector1, &soundVector2 ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/listener.h b/game/code/sound/listener.h new file mode 100644 index 0000000..d41a86b --- /dev/null +++ b/game/code/sound/listener.h @@ -0,0 +1,56 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: listener.h +// +// Description: Declaration of Listener class, which tracks the camera and +// updates the RadSound listener position/orientation to match +// +// History: 02/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef LISTENER_H +#define LISTENER_H + +//======================================== +// Nested Includes +//======================================== +#include <contexts/contextenum.h> +#include <sound/soundrenderer/soundrenderingmanager.h> + +//======================================== +// Forward References +//======================================== + +struct IRadSoundHalListener; +class tVectorCamera; + +//============================================================================= +// +// Synopsis: Listener +// +//============================================================================= + +class Listener +{ + public: + Listener(); + virtual ~Listener(); + + void Initialize( Sound::daSoundRenderingManager& renderMgr ); + void Update( ContextEnum context ); + + private: + //Prevent wasteful constructor creation. + Listener( const Listener& original ); + Listener& operator=( const Listener& rhs ); + + IRadSoundHalListener* m_theListener; + + tVectorCamera* m_feCamera; +}; + + +#endif // LISTENER_H + diff --git a/game/code/sound/movingpositional/actorplayer.cpp b/game/code/sound/movingpositional/actorplayer.cpp new file mode 100644 index 0000000..2ddac62 --- /dev/null +++ b/game/code/sound/movingpositional/actorplayer.cpp @@ -0,0 +1,194 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: actorplayer.cpp +// +// Description: Implement ActorPlayer +// +// History: 3/10/2003 + Created -- Esan +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/actorplayer.h> + +#include <ai/actor/actor.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// ActorPlayer::ActorPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +ActorPlayer::ActorPlayer() : + m_actor( NULL ) +{ +} + +//============================================================================= +// ActorPlayer::~ActorPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +ActorPlayer::~ActorPlayer() +{ +} + +//============================================================================= +// ActorPlayer::GetPosition +//============================================================================= +// Description: Get position of actor for moving positional sound +// +// Parameters: position - output parameter, to be filled with actor position +// +// Return: void +// +//============================================================================= +void ActorPlayer::GetPosition( radSoundVector& position ) +{ + rmt::Vector actorPosn; + + m_actor->GetPosition( &actorPosn ); + position.SetElements( actorPosn.x, actorPosn.y, actorPosn.z ); +} + +//============================================================================= +// ActorPlayer::GetVelocity +//============================================================================= +// Description: Get velocity of actor for moving positional sound +// +// Parameters: velocity - output parameter, to be filled with actor velocity +// +// Return: void +// +//============================================================================= +void ActorPlayer::GetVelocity( radSoundVector& velocity ) +{ + // + // No doppler for actors, therefore make velocity zero + // + velocity.SetElements( 0.0f, 0.0f, 0.0f ); +} + +//============================================================================= +// ActorPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Service stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void ActorPlayer::ServiceOncePerFrame() +{ + m_player.ServiceOncePerFrame(); +} + +//============================================================================= +// ActorPlayer::OnPlaybackComplete +//============================================================================= +// Description: Sound player callback. Do nothing, leave for subclasses +// to override +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void ActorPlayer::OnPlaybackComplete() +{ +} + +//============================================================================= +// ActorPlayer::OnSoundReady +//============================================================================= +// Description: Sound player callback. Do nothing, leave for subclasses +// to override +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void ActorPlayer::OnSoundReady() +{ +} + +//============================================================================= +// ActorPlayer::playSound +//============================================================================= +// Description: Comment +// +// Parameters: settings - positional sound attributes +// resourceName - name of sound resource to play +// theActor - the moving object that this sound is associated with +// +// Return: void +// +//============================================================================= +void ActorPlayer::playSound( positionalSoundSettings* settings, + const char* resourceName, + Actor* theActor ) +{ + rmt::Vector posn; + + m_actor = theActor; + + m_player.SetPositionCarrier( *this ); + m_player.SetParameters( settings ); + + theActor->GetPosition( &posn ); + m_player.SetPosition( posn.x, posn.y, posn.z ); + + m_player.PlaySound( resourceName, this ); +} + +//============================================================================= +// ActorPlayer::deactivate +//============================================================================= +// Description: Shut down the sound and disassociate from the actor +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void ActorPlayer::deactivate() +{ + m_actor = NULL; + m_player.Stop(); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/actorplayer.h b/game/code/sound/movingpositional/actorplayer.h new file mode 100644 index 0000000..2731d6c --- /dev/null +++ b/game/code/sound/movingpositional/actorplayer.h @@ -0,0 +1,77 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: actorplayer.h +// +// Description: Abstract base class for playing sounds associated with moving +// state props (e.g. wasps) +// +// History: 3/10/2003 + Created -- Darren +// +//============================================================================= + +#ifndef ACTORPLAYER_H +#define ACTORPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/positionalsoundplayer.h> + +//======================================== +// Forward References +//======================================== +class Actor; + +//============================================================================= +// +// Synopsis: ActorPlayer +// +//============================================================================= + +class ActorPlayer : public PositionCarrier, + public SimpsonsSoundPlayerCallback +{ +public: + ActorPlayer(); + virtual ~ActorPlayer(); + + bool IsActive() { return( m_actor != NULL ); } + virtual void Activate( Actor* theActor ) = 0; + + virtual void ServiceOncePerFrame(); + + // + // PositionCarrier functions + // + void GetPosition( radSoundVector& position ); + void GetVelocity( radSoundVector& velocity ); + + // + // SimpsonsSoundPlayerCallback functions + // + virtual void OnSoundReady(); + virtual void OnPlaybackComplete(); + +protected: + void playSound( positionalSoundSettings* settings, const char* resourceName, Actor* theActor ); + + virtual void deactivate(); + + Actor* m_actor; + + PositionalSoundPlayer m_player; + +private: + //Prevent wasteful constructor creation. + ActorPlayer( const ActorPlayer& actorplayer ); + ActorPlayer& operator=( const ActorPlayer& actorplayer ); +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //ACTORPLAYER_H diff --git a/game/code/sound/movingpositional/aivehiclesoundplayer.cpp b/game/code/sound/movingpositional/aivehiclesoundplayer.cpp new file mode 100644 index 0000000..3fc866f --- /dev/null +++ b/game/code/sound/movingpositional/aivehiclesoundplayer.cpp @@ -0,0 +1,99 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: aivehiclesoundplayer.cpp +// +// Description: Administers the playing of sound for an AI-controlled vehicle +// +// History: 1/4/2003 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/aivehiclesoundplayer.h> + +#include <worldsim/redbrick/vehicle.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// AIVehicleSoundPlayer::AIVehicleSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AIVehicleSoundPlayer::AIVehicleSoundPlayer() +{ +} + +//============================================================================= +// AIVehicleSoundPlayer::~AIVehicleSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AIVehicleSoundPlayer::~AIVehicleSoundPlayer() +{ +} + +//============================================================================= +// AIVehicleSoundPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Adjust the pitch for this vehicle +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void AIVehicleSoundPlayer::ServiceOncePerFrame() +{ + float pitch; + + VehiclePositionalSoundPlayer::ServiceOncePerFrame(); + + // + // Adjust pitch for vehicle speed if desired. I'll probably need to + // expose this for designer tuning later. + // + if( IsActive() && m_tiePitchToVelocity ) + { + pitch = 0.3f + ( 0.7f * ( m_vehicle->GetSpeedKmh() / m_vehicle->mDesignerParams.mDpTopSpeedKmh ) ); + if( pitch > 1.2f ) + { + pitch = 1.2f; + } + + m_player.SetPitch( pitch ); + } +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/aivehiclesoundplayer.h b/game/code/sound/movingpositional/aivehiclesoundplayer.h new file mode 100644 index 0000000..36e55ba --- /dev/null +++ b/game/code/sound/movingpositional/aivehiclesoundplayer.h @@ -0,0 +1,53 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: aivehiclesoundplayer.h +// +// Description: Administers the playing of sound for an AI-controlled vehicle +// +// History: 1/4/2003 + Created -- Darren +// +//============================================================================= + +#ifndef AIVEHICLESOUNDPLAYER_H +#define AIVEHICLESOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <radtime.hpp> + +#include <sound/movingpositional/vehicleposnsoundplayer.h> + +//======================================== +// Forward References +//======================================== +class Vehicle; + +//============================================================================= +// +// Synopsis: AIVehicleSoundPlayer +// +//============================================================================= + +class AIVehicleSoundPlayer : public VehiclePositionalSoundPlayer +{ + public: + AIVehicleSoundPlayer(); + virtual ~AIVehicleSoundPlayer(); + + void ServiceOncePerFrame(); + + private: + //Prevent wasteful constructor creation. + AIVehicleSoundPlayer( const AIVehicleSoundPlayer& aivehiclesoundplayer ); + AIVehicleSoundPlayer& operator=( const AIVehicleSoundPlayer& aivehiclesoundplayer ); +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //AIVEHICLESOUNDPLAYER_H diff --git a/game/code/sound/movingpositional/allmovingposn.cpp b/game/code/sound/movingpositional/allmovingposn.cpp new file mode 100644 index 0000000..5c0b72b --- /dev/null +++ b/game/code/sound/movingpositional/allmovingposn.cpp @@ -0,0 +1,9 @@ +#include <sound/movingpositional/movingsoundmanager.cpp> +#include <sound/movingpositional/vehicleposnsoundplayer.cpp> +#include <sound/movingpositional/aivehiclesoundplayer.cpp> +#include <sound/movingpositional/trafficsoundplayer.cpp> +#include <sound/movingpositional/avatarvehicleposnplayer.cpp> +#include <sound/movingpositional/actorplayer.cpp> +#include <sound/movingpositional/waspsoundplayer.cpp> +#include <sound/movingpositional/platformsoundplayer.cpp> +#include <sound/movingpositional/animobjsoundplayer.cpp>
\ No newline at end of file diff --git a/game/code/sound/movingpositional/animobjsoundplayer.cpp b/game/code/sound/movingpositional/animobjsoundplayer.cpp new file mode 100644 index 0000000..a65c662 --- /dev/null +++ b/game/code/sound/movingpositional/animobjsoundplayer.cpp @@ -0,0 +1,202 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: animobjsoundplayer.cpp +// +// Description: Plays sound for AnimCollisionEntityDSG objects +// +// History: 6/5/2003 + Created -- Esan (post-beta, yay!) +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> +#include <p3d/anim/pose.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/animobjsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> + +#include <events/eventdata.h> +#include <render/DSG/animcollisionentitydsg.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// AnimObjSoundPlayer::AnimObjSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AnimObjSoundPlayer::AnimObjSoundPlayer() : + m_joint( NULL ), + m_identity( NULL ) +{ +} + +//============================================================================= +// AnimObjSoundPlayer::~AnimObjSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AnimObjSoundPlayer::~AnimObjSoundPlayer() +{ + if( m_identity != NULL ) + { + m_identity->Release(); + } +} + +//============================================================================= +// AnimObjSoundPlayer::Activate +//============================================================================= +// Description: Start playing a sound for a particular platform +// +// Parameters: soundData - position and identify info for platform +// +// Return: void +// +//============================================================================= +void AnimObjSoundPlayer::Activate( AnimSoundDSGData* soundData ) +{ + rmt::Vector posn; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + // + // Get the positionalSoundSettings object for the platform sound + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundData->positionalSettingName ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + m_identity = soundData->soundObject; + rAssert( m_identity != NULL ); + + m_joint = soundData->animJoint; + rAssert( m_joint != NULL ); + + m_player.SetPositionCarrier( *this ); + m_player.SetParameters( parameters ); + + // + // Get world position of the platform through this joint + // + posn = m_joint->worldMatrix.Row( 3 ); + m_player.SetPosition( posn.x, posn.y, posn.z ); + + // + // Don't buffer, to save IOP + // + + m_player.PlaySound( soundData->soundName, NULL ); + } + else + { + rDebugString( "Couldn't play anim DSG platform sound, no matching settings found" ); + } +} + +//============================================================================= +// AnimObjSoundPlayer::Deactivate +//============================================================================= +// Description: Stop playing platform sound +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void AnimObjSoundPlayer::Deactivate() +{ + m_player.Stop(); + + m_identity = NULL; +} + +//============================================================================= +// AnimObjSoundPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Service the sound player +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void AnimObjSoundPlayer::ServiceOncePerFrame() +{ + m_player.ServiceOncePerFrame(); +} + +//============================================================================= +// AnimObjSoundPlayer::GetPosition +//============================================================================= +// Description: Get the platform's current position +// +// Parameters: position - filled in with platform position +// +// Return: void +// +//============================================================================= +void AnimObjSoundPlayer::GetPosition( radSoundVector& position ) +{ + rmt::Vector posn; + + rAssert( m_joint != NULL ); + posn = m_joint->worldMatrix.Row( 3 ); + position.SetElements( posn.x, posn.y, posn.z ); +} + +//============================================================================= +// AnimObjSoundPlayer::GetVelocity +//============================================================================= +// Description: Comment +// +// Parameters: ( radSoundVector& velocity ) +// +// Return: void +// +//============================================================================= +void AnimObjSoundPlayer::GetVelocity( radSoundVector& velocity ) +{ + // + // Doppler would be a big waste on those platforms anyway + // + velocity.SetElements( 0.0f, 0.0f, 0.0f ); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/animobjsoundplayer.h b/game/code/sound/movingpositional/animobjsoundplayer.h new file mode 100644 index 0000000..aa71fb1 --- /dev/null +++ b/game/code/sound/movingpositional/animobjsoundplayer.h @@ -0,0 +1,70 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: animobjsoundplayer.h +// +// Description: Plays sound for moving anim objs +// +// History: 6/5/2003 + Created -- Esan (post-beta, yay!) +// +//============================================================================= + +#ifndef ANIMOBJSOUNDPLAYER_H +#define ANIMOBJSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/positionalsoundplayer.h> +#include <p3d/anim/pose.hpp> + +//======================================== +// Forward References +//======================================== +struct AnimSoundDSGData; +struct radSoundVector; +class AnimCollisionEntityDSG; + +//============================================================================= +// +// Synopsis: AnimObjSoundPlayer +// +//============================================================================= + +class AnimObjSoundPlayer : public PositionCarrier +{ + public: + AnimObjSoundPlayer(); + virtual ~AnimObjSoundPlayer(); + + bool IsActive() { return( m_identity != NULL ); } + bool UsesObject( AnimCollisionEntityDSG* soundObject ) { return( soundObject == m_identity ); } + void Activate( AnimSoundDSGData* soundData ); + void Deactivate(); + + void ServiceOncePerFrame(); + + // + // PositionCarrier functions + // + void GetPosition( radSoundVector& position ); + void GetVelocity( radSoundVector& velocity ); + + private: + //Prevent wasteful constructor creation. + AnimObjSoundPlayer( const AnimObjSoundPlayer& animobjsoundplayer ); + AnimObjSoundPlayer& operator=( const AnimObjSoundPlayer& animobjsoundplayer ); + + tPose::Joint* m_joint; + AnimCollisionEntityDSG* m_identity; + + PositionalSoundPlayer m_player; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //ANIMOBJSOUNDPLAYER_H diff --git a/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp b/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp new file mode 100644 index 0000000..a1803b9 --- /dev/null +++ b/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp @@ -0,0 +1,208 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: avatarvehicleposnplayer.cpp +// +// Description: Implement AvatarVehiclePosnPlayer +// +// History: 3/7/2003 + Created -- NAME +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/avatarvehicleposnplayer.h> + +#include <sound/avatar/carsoundparameters.h> +#include <sound/soundrenderer/soundrenderingmanager.h> + +#include <events/eventmanager.h> +#include <mission/gameplaymanager.h> +#include <worldsim/redbrick/vehicle.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// AvatarVehiclePosnPlayer::AvatarVehiclePosnPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AvatarVehiclePosnPlayer::AvatarVehiclePosnPlayer() +{ + // + // Register events + // + GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_USER_VEHICLE_ADDED_TO_WORLD ); + GetEventManager()->AddListener( this, EVENT_USER_VEHICLE_REMOVED_FROM_WORLD ); + GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED ); +} + +//============================================================================= +// AvatarVehiclePosnPlayer::~AvatarVehiclePosnPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +AvatarVehiclePosnPlayer::~AvatarVehiclePosnPlayer() +{ + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// AvatarVehiclePosnPlayer::HandleEvent +//============================================================================= +// Description: Listen for events directing us to turn positional idle +// sound on and off +// +// Parameters: id - identifier for event being listened for +// pEventData - unused +// +// Return: void +// +//============================================================================= +void AvatarVehiclePosnPlayer::HandleEvent( EventEnum id, void* pEventData ) +{ + switch( id ) + { + case EVENT_GETOUTOFVEHICLE_END: + //Chuck: adding this for the stupid UFO beam, and all the wierdness that it generates. + if( GetGameplayManager()->GetCurrentVehicle()->IsVehicleDestroyed() == true) + { + Deactivate(); + return; + } + StartPositionalIdle(); + break; + case EVENT_VEHICLE_DESTROYED: + { + if(m_vehicle == static_cast<Vehicle*>(pEventData)) + { + Deactivate(); + } + break; + } + + + case EVENT_GETINTOVEHICLE_END: + Deactivate(); + break; + + case EVENT_USER_VEHICLE_ADDED_TO_WORLD: + StartPositionalIdle( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_USER_VEHICLE_REMOVED_FROM_WORLD: + Deactivate(); + break; + + default: + rAssertMsg( false, "Unexpected event in AvatarVehiclePosnPlayer::HandleEvent\n" ); + break; + } +} + +//============================================================================= +// AvatarVehiclePosnPlayer::StartPositionalIdle +//============================================================================= +// Description: Activate this player with an idle engine clip in the vehicle +// position +// +// Parameters: carPtr - if vehicle is already known, this is non-NULL +// +// Return: void +// +//============================================================================= +void AvatarVehiclePosnPlayer::StartPositionalIdle( Vehicle* carPtr /* = NULL */) +{ + Vehicle* theCar; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + carSoundParameters* parameters; + positionalSoundSettings* soundSettings; + + // + // Get name of idle sound clip + // + if( GetGameplayManager()->GetNumPlayers() == 1 ) + { + if( carPtr != NULL ) + { + theCar = carPtr; + } + else + { + theCar = GetGameplayManager()->GetCurrentVehicle(); + rAssert( theCar != NULL ); + + if(!theCar) + { + rDebugString( "Couldn't find avatar vehicle\n" ); + return; + } + } + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + nameSpaceObj = nameSpace->GetInstance( theCar->GetName() ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj ); + + // + // Now find some positional sound settings + // + nameSpaceObj = nameSpace->GetInstance( "avatar_idle" ); + if( nameSpaceObj != NULL ) + { + soundSettings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + Activate( soundSettings, parameters->GetEngineIdleClipName(), theCar ); + + m_player.SetPitch( parameters->GetIdleEnginePitch() ); + } + else + { + rDebugString( "Couldn't find positional settings for avatar vehicle\n" ); + } + } + else + { + rDebugString( "Couldn't find carSoundParameters for avatar vehicle\n" ); + } + } +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** + diff --git a/game/code/sound/movingpositional/avatarvehicleposnplayer.h b/game/code/sound/movingpositional/avatarvehicleposnplayer.h new file mode 100644 index 0000000..c283d4d --- /dev/null +++ b/game/code/sound/movingpositional/avatarvehicleposnplayer.h @@ -0,0 +1,58 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: avatarvehicleposnplayer.h +// +// Description: Plays positional idle for avatar vehicle +// +// History: 3/7/2003 + Created -- Esan +// +//============================================================================= + +#ifndef AVATARVEHICLEPOSNPLAYER_H +#define AVATARVEHICLEPOSNPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/movingpositional/vehicleposnsoundplayer.h> + +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: AvatarVehiclePosnPlayer +// +//============================================================================= + +class AvatarVehiclePosnPlayer : public VehiclePositionalSoundPlayer, + public EventListener +{ + public: + AvatarVehiclePosnPlayer(); + virtual ~AvatarVehiclePosnPlayer(); + + // + // EventListener functions + // + void HandleEvent( EventEnum id, void* pEventData ); + + void StartPositionalIdle( Vehicle* carPtr = NULL ); + + private: + //Prevent wasteful constructor creation. + AvatarVehiclePosnPlayer( const AvatarVehiclePosnPlayer& avatarvehicleposnplayer ); + AvatarVehiclePosnPlayer& operator=( const AvatarVehiclePosnPlayer& avatarvehicleposnplayer ); +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //AVATARVEHICLEPOSNPLAYER_H diff --git a/game/code/sound/movingpositional/movingsoundmanager.cpp b/game/code/sound/movingpositional/movingsoundmanager.cpp new file mode 100644 index 0000000..af4f542 --- /dev/null +++ b/game/code/sound/movingpositional/movingsoundmanager.cpp @@ -0,0 +1,747 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: movingsoundmanager.cpp +// +// Description: Manages the playing of moving sounds +// +// History: 1/4/2003 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/movingsoundmanager.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/avatar/carsoundparameters.h> + +#include <events/eventmanager.h> +#include <events/eventdata.h> +#include <ai/actor/actor.h> +#include <worldsim/redbrick/vehicle.h> +#include <constants/vehicleenum.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +tUID MovingSoundManager::s_waspUID = 0; + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// MovingSoundManager::MovingSoundManager +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +MovingSoundManager::MovingSoundManager() +{ + EventManager* eventMgr = GetEventManager(); + + // + // Start listening for events + // + eventMgr->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED ); + eventMgr->AddListener( this, EVENT_CHASE_VEHICLE_DESTROYED ); + eventMgr->AddListener( this, EVENT_TRAFFIC_SPAWN ); + eventMgr->AddListener( this, EVENT_TRAFFIC_REMOVE ); + eventMgr->AddListener( this, EVENT_ACTOR_CREATED ); + eventMgr->AddListener( this, EVENT_TRAFFIC_GOT_HIT ); + eventMgr->AddListener( this, EVENT_TRAFFIC_IMPEDED ); + eventMgr->AddListener( this, EVENT_BIG_BOOM_SOUND ); + eventMgr->AddListener( this, EVENT_MISSION_VEHICLE_CREATED ); + eventMgr->AddListener( this, EVENT_MISSION_VEHICLE_RELEASED ); + eventMgr->AddListener( this, EVENT_START_ANIMATION_SOUND ); + eventMgr->AddListener( this, EVENT_STOP_ANIMATION_SOUND ); + eventMgr->AddListener( this, EVENT_START_ANIM_ENTITY_DSG_SOUND ); + eventMgr->AddListener( this, EVENT_STOP_ANIM_ENTITY_DSG_SOUND ); + eventMgr->AddListener( this, static_cast<EventEnum>( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2 ) ); + eventMgr->AddListener( this, EVENT_AVATAR_VEHICLE_TOGGLE ); + + if( s_waspUID == static_cast< tUID >( 0 ) ) + { + s_waspUID = tEntity::MakeUID( "beecamera" ); + } + + TrafficSoundPlayer::InitializeClass( NUM_TRAFFIC_SOUND_PLAYERS ); +} + +//============================================================================= +// MovingSoundManager::~MovingSoundManager +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +MovingSoundManager::~MovingSoundManager() +{ + GetEventManager()->RemoveAll( this ); +} + +void MovingSoundManager::HandleEvent( EventEnum id, void* pEventData ) +{ + AnimSoundData* soundData; + AnimSoundDSGData* soundDSGData; + Vehicle* theCar; + + switch( id ) + { + case EVENT_CHASE_VEHICLE_SPAWNED: + // + // New chase AI vehicle, play siren for it + // + addAISound( "siren", static_cast<Vehicle*>(pEventData), false ); + break; + + case EVENT_CHASE_VEHICLE_DESTROYED: + // + // Chase AI vehicle gone, stop its siren sound + // + stopAISound( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_MISSION_VEHICLE_CREATED: + // + // New mission AI vehicle, play engine for it + // + theCar = static_cast<Vehicle*>(pEventData); + addAISound( getPositionalSettingName( theCar, true ), theCar, true ); + break; + + case EVENT_MISSION_VEHICLE_RELEASED: + // + // Mission AI vehicle gone, stop its engine + // + stopAISound( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_TRAFFIC_SPAWN: + // + // New traffic vehicle, play engine sound for it + // + theCar = static_cast<Vehicle*>(pEventData); + addTrafficSound( getPositionalSettingName( theCar, false ), theCar, true ); + break; + + case EVENT_TRAFFIC_REMOVE: + // + // Traffic vehicle gone + // + stopTrafficSound( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_AVATAR_VEHICLE_TOGGLE: + // + // Player avatar has gotten in or out of a vehicle. If it's a traffic + // vehicle with an overlay clip, we'll need to toggle it on or off. + // + toggleOverlayClip( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_ACTOR_CREATED: + startWaspSound( static_cast<Actor*>(pEventData) ); + break; + + case EVENT_TRAFFIC_GOT_HIT: + case EVENT_TRAFFIC_IMPEDED: + handleTrafficHornEvent( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_BIG_BOOM_SOUND: + makeCarGoBoom( static_cast<Vehicle*>(pEventData) ); + break; + + case EVENT_START_ANIMATION_SOUND: + soundData = static_cast<AnimSoundData*>(pEventData); + if( soundData->animJoint != NULL ) + { + startPlatformSound( soundData ); + } + break; + + case EVENT_STOP_ANIMATION_SOUND: + stopPlatformSound( static_cast<ActionButton::AnimSwitch*>(pEventData) ); + break; + + case EVENT_START_ANIM_ENTITY_DSG_SOUND: + soundDSGData = static_cast<AnimSoundDSGData*>(pEventData); + startAnimObjSound( soundDSGData ); + break; + + case EVENT_STOP_ANIM_ENTITY_DSG_SOUND: + stopAnimObjSound( static_cast<AnimCollisionEntityDSG*>(pEventData) ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2: + // + // If we're hitting this ambience boundary, then we're leaving the platform + // room, so kill all the platforms and save ourselves some streamers. + // + //stopAllPlatforms(); + break; + + default: + rAssertMsg( false, "Unexpected event in MovingSoundManager\n" ); + break; + } +} + +//============================================================================= +// MovingSoundManager::ServiceOncePerFrame +//============================================================================= +// Description: Do once-per-frame update stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MovingSoundManager::ServiceOncePerFrame() +{ + int i; + + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + m_trafficPlayer[i].ServiceOncePerFrame(); + } + for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ ) + { + m_aiPlayer[i].ServiceOncePerFrame(); + } + for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ ) + { + m_platformPlayer[i].ServiceOncePerFrame(); + } + for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ ) + { + m_animObjPlayer[i].ServiceOncePerFrame(); + } + for( i = 0; i < NUM_WASP_SOUND_PLAYERS; i++ ) + { + m_waspPlayer[i].ServiceOncePerFrame(); + } + + m_avatarVehiclePlayer.ServiceOncePerFrame(); + + TrafficSoundPlayer::ServiceTimerList(); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** + +//============================================================================= +// MovingSoundManager::addTrafficSound +//============================================================================= +// Description: Find an inactive sound player and start it up with the given +// sound and vehicle +// +// Parameters: soundName - name of sound resource to play for vehicle +// vehiclePtr - vehicle whose position the sound will be +// played from +// tiePitchToVelocity - set to true if we want the sound pitch +// to vary (i.e. engine sound) +// +// Return: void +// +//============================================================================= +void MovingSoundManager::addTrafficSound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity ) +{ + int i; + carSoundParameters* carSettings; + + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + if( !m_trafficPlayer[i].IsActive() ) + { + m_trafficPlayer[i].ActivateByName( soundName, vehiclePtr ); + m_trafficPlayer[i].TiePitchToVelocity( tiePitchToVelocity ); + + // + // Play overlay clips for traffic if they've got 'em (e.g. quimby truck) + // + if( hasOverlayClip( vehiclePtr, &carSettings ) ) + { + m_trafficPlayer[i].AddOverlayClip( carSettings, soundName ); + } + break; + } + } + +#ifdef RAD_DEBUG + if( i == NUM_TRAFFIC_SOUND_PLAYERS ) + { + rDebugString( "AI Vehicle sound dropped" ); + } +#endif +} + +//============================================================================= +// MovingSoundManager::stopTrafficSound +//============================================================================= +// Description: Stop the sound coming from the given vehicle +// +// Parameters: vehiclePtr - vehicle whose sound is to be stopped. +// +// Return: void +// +//============================================================================= +void MovingSoundManager::stopTrafficSound( Vehicle* vehiclePtr ) +{ + int i; + + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) ) + { + m_trafficPlayer[i].Deactivate(); + } + } +} + +//============================================================================= +// MovingSoundManager::addAISound +//============================================================================= +// Description: Find an inactive sound player and start it up with the given +// sound and vehicle +// +// Parameters: soundName - name of sound resource to play for vehicle +// vehiclePtr - vehicle whose position the sound will be +// played from +// tiePitchToVelocity - set to true if we want the sound pitch +// to vary (i.e. engine sound) +// +// Return: void +// +//============================================================================= +void MovingSoundManager::addAISound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity ) +{ + int i; + + for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ ) + { + if( !m_aiPlayer[i].IsActive() ) + { + m_aiPlayer[i].ActivateByName( soundName, vehiclePtr ); + m_aiPlayer[i].TiePitchToVelocity( tiePitchToVelocity ); + break; + } + } + +#ifdef RAD_DEBUG + if( i == NUM_AI_SOUND_PLAYERS ) + { + rDebugString( "AI Vehicle sound dropped" ); + } +#endif +} + +//============================================================================= +// MovingSoundManager::stopAISound +//============================================================================= +// Description: Stop the sound coming from the given vehicle +// +// Parameters: vehiclePtr - vehicle whose sound is to be stopped. +// +// Return: void +// +//============================================================================= +void MovingSoundManager::stopAISound( Vehicle* vehiclePtr ) +{ + int i; + + for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ ) + { + if( m_aiPlayer[i].UsesVehicle( vehiclePtr ) ) + { + m_aiPlayer[i].Deactivate(); + } + } +} + +//============================================================================= +// MovingSoundManager::handleTrafficHornEvent +//============================================================================= +// Description: Horn the horn for the specified car +// +// Parameters: vehiclePtr - pointer to vehicle to honk +// +// Return: void +// +//============================================================================= +void MovingSoundManager::handleTrafficHornEvent( Vehicle* vehiclePtr ) +{ + int i; + + rAssert( vehiclePtr != NULL ); + + // + // Find the car matching the vehicle pointer + // + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) ) + { + m_trafficPlayer[i].HonkHorn(); + } + } +} + +//============================================================================= +// MovingSoundManager::makeCarGoBoom +//============================================================================= +// Description: If the vehicle in the explosion event matches one of our +// vehicles, play a positional explosion +// +// Parameters: vehiclePtr - pointer to exploding car +// +// Return: void +// +//============================================================================= +void MovingSoundManager::makeCarGoBoom( Vehicle* vehiclePtr ) +{ + int i; + + rAssert( vehiclePtr != NULL ); + + if( vehiclePtr->mVehicleType == VT_USER ) + { + // + // User vehicles are handled by the sound effect system + // + return; + } + + // + // Find the car matching the vehicle pointer + // + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) ) + { + m_trafficPlayer[i].BlowUp(); + break; + } + } + + if( i == NUM_TRAFFIC_SOUND_PLAYERS ) + { + // + // It's not a traffic vehicle, try AI + // + for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ ) + { + if( m_aiPlayer[i].UsesVehicle( vehiclePtr ) ) + { + m_aiPlayer[i].BlowUp(); + break; + } + } + } +} + +//============================================================================= +// MovingSoundManager::startPlatformSound +//============================================================================= +// Description: Find a player for the moving platform and hook it up +// +// Parameters: soundData - data we need to identify platform identity and +// position +// +// Return: void +// +//============================================================================= +void MovingSoundManager::startPlatformSound( AnimSoundData* soundData ) +{ + int i; + + for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ ) + { + if( !m_platformPlayer[i].IsActive() ) + { + m_platformPlayer[i].Activate( soundData ); + break; + } + } + +#ifdef RAD_DEBUG + if( i == NUM_PLATFORM_SOUND_PLAYERS ) + { + rDebugString( "AI Vehicle sound dropped" ); + } +#endif +} + +//============================================================================= +// MovingSoundManager::stopPlatformSound +//============================================================================= +// Description: Find the player for this object and stop it +// +// Parameters: soundObject - platform identity +// +// Return: void +// +//============================================================================= +void MovingSoundManager::stopPlatformSound( ActionButton::AnimSwitch* soundObject ) +{ + int i; + + for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ ) + { + if( m_platformPlayer[i].UsesObject( soundObject ) ) + { + m_platformPlayer[i].Deactivate(); + break; + } + } +} + +//============================================================================= +// MovingSoundManager::stopAllPlatforms +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MovingSoundManager::stopAllPlatforms() +{ + int i; + + for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ ) + { + m_platformPlayer[i].Deactivate(); + } +} + +//============================================================================= +// MovingSoundManager::startAnimObjSound +//============================================================================= +// Description: Find a player for the moving platform and hook it up +// +// Parameters: soundData - data we need to identify platform identity and +// position +// +// Return: void +// +//============================================================================= +void MovingSoundManager::startAnimObjSound( AnimSoundDSGData* soundData ) +{ + int i; + + for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ ) + { + if( !m_animObjPlayer[i].IsActive() ) + { + m_animObjPlayer[i].Activate( soundData ); + break; + } + } + +#ifdef RAD_DEBUG + if( i == NUM_ANIM_OBJ_SOUND_PLAYERS ) + { + rDebugString( "AnimObj sound dropped" ); + } +#endif +} + +//============================================================================= +// MovingSoundManager::stopAnimObjSound +//============================================================================= +// Description: Find the player for this object and stop it +// +// Parameters: soundObject - platform identity +// +// Return: void +// +//============================================================================= +void MovingSoundManager::stopAnimObjSound( AnimCollisionEntityDSG* soundObject ) +{ + int i; + + for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ ) + { + if( m_animObjPlayer[i].UsesObject( soundObject ) ) + { + m_animObjPlayer[i].Deactivate(); + break; + } + } +} + +//============================================================================= +// MovingSoundManager::hasOverlayClip +//============================================================================= +// Description: Check whether the given vehicle has an overlay clip that +// we want to move around with its engine sounds +// +// Parameters: vehiclePtr - vehicle we're finding a clip name for +// parameters - pointer which is pointed at car sound settings +// if clip exists, NULL otherwise +// +// Return: true if overlay clip found, false otherwise +// +//============================================================================= +bool MovingSoundManager::hasOverlayClip( Vehicle* vehiclePtr, carSoundParameters** parameters ) +{ + IRadNameSpace* nameSpace; + const char* clipName; + bool retVal = false; + + rAssert( vehiclePtr != NULL ); + rAssert( parameters != NULL ); + + // + // Ignore the husk + // + if( vehiclePtr->mVehicleID == VehicleEnum::HUSKA ) + { + return( false ); + } + + // + // Find the settings for this positional sound first + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + *parameters = reinterpret_cast<carSoundParameters*>( nameSpace->GetInstance( vehiclePtr->GetName() ) ); + if( *parameters != NULL ) + { + clipName = (*parameters)->GetOverlayClipName(); + if( clipName != NULL ) + { + retVal = true; + } + else + { + *parameters = NULL; + } + } + + return( retVal ); +} + +//============================================================================= +// MovingSoundManager::toggleOverlayClip +//============================================================================= +// Description: Need to switch the overlay clip for the given vehicle +// if it's a traffic vehicle, because the player is using it +// +// Parameters: vehiclePtr - vehicle being gotten in or out of +// +// Return: void +// +//============================================================================= +void MovingSoundManager::toggleOverlayClip( Vehicle* vehiclePtr ) +{ + int i; + carSoundParameters* parameters; + + for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ ) + { + if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) ) + { + if( hasOverlayClip( vehiclePtr, ¶meters ) ) + { + m_trafficPlayer[i].ToggleOverlayClip( parameters, getPositionalSettingName( vehiclePtr, false ) ); + } + } + } +} + +//============================================================================= +// MovingSoundManager::getPositionalSettingName +//============================================================================= +// Description: Figure out which positional values to use for traffic sounds +// for the given vehicle. +// +// Parameters: vehiclePtr - traffic vehicle +// isMissionVehicle - if true, use louder positional setting +// +// Return: name of positional settings object +// +//============================================================================= +const char* MovingSoundManager::getPositionalSettingName( Vehicle* vehiclePtr, bool isMissionVehicle ) +{ + VehicleEnum::VehicleID carID; + const char* settingName; + + rAssert( vehiclePtr != NULL ); + + carID = vehiclePtr->mVehicleID; + if( carID == VehicleEnum::COFFIN ) + { + settingName = "coffin_posn"; + } + else if( carID == VehicleEnum::HALLO ) + { + settingName = "hearse_posn"; + } + else if( carID == VehicleEnum::CHEARS ) + { + settingName = "chase_hearse_posn"; + } + else if( isMissionVehicle ) + { + settingName = "loud_ai_vehicle"; + } + else + { + settingName = "generic_traffic"; + } + + return( settingName ); +} + +//============================================================================= +// MovingSoundManager::startWaspSound +//============================================================================= +// Description: Start making waspy noises +// +// Parameters: wasp - wasp to start making noise +// +// Return: void +// +//============================================================================= +void MovingSoundManager::startWaspSound( Actor* wasp ) +{ + int i; + + if( wasp->GetStatePropUID() == s_waspUID ) + { + for( i = 0; i < NUM_WASP_SOUND_PLAYERS; i++ ) + { + if( !m_waspPlayer[i].IsActive() ) + { + m_waspPlayer[i].Activate( wasp ); + break; + } + } + } +} diff --git a/game/code/sound/movingpositional/movingsoundmanager.h b/game/code/sound/movingpositional/movingsoundmanager.h new file mode 100644 index 0000000..1e0d133 --- /dev/null +++ b/game/code/sound/movingpositional/movingsoundmanager.h @@ -0,0 +1,108 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: movingsoundmanager.h +// +// Description: Manages the playing of moving positional sounds +// +// History: 1/4/2003 + Created -- Esan +// +//============================================================================= + +#ifndef MOVINGSOUNDMANAGER_H +#define MOVINGSOUNDMANAGER_H + +//======================================== +// Nested Includes +//======================================== +#include <p3d/p3dtypes.hpp> + +#include <events/eventlistener.h> +#include <sound/movingpositional/trafficsoundplayer.h> +#include <sound/movingpositional/aivehiclesoundplayer.h> +#include <sound/movingpositional/avatarvehicleposnplayer.h> +#include <sound/movingpositional/waspsoundplayer.h> +#include <sound/movingpositional/platformsoundplayer.h> +#include <sound/movingpositional/animobjsoundplayer.h> + +//======================================== +// Forward References +//======================================== +class Vehicle; +class carSoundParameters; +struct AnimSoundData; +struct AnimSoundDSGData; +class AnimCollisionEntityDSG; + +namespace ActionButton +{ + class AnimSwitch; +}; + +//============================================================================= +// +// Synopsis: MovingSoundManager +// +//============================================================================= + +class MovingSoundManager : public EventListener +{ + public: + MovingSoundManager(); + virtual ~MovingSoundManager(); + + void ServiceOncePerFrame(); + + // + // EventListener functions + // + void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + MovingSoundManager( const MovingSoundManager& original ); + MovingSoundManager& operator=( const MovingSoundManager& rhs ); + + void addTrafficSound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity ); + void stopTrafficSound( Vehicle* vehiclePtr ); + void addAISound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity ); + void stopAISound( Vehicle* vehiclePtr ); + void handleTrafficHornEvent( Vehicle* vehiclePtr ); + void makeCarGoBoom( Vehicle* vehiclePtr ); + void startPlatformSound( AnimSoundData* soundData ); + void stopPlatformSound( ActionButton::AnimSwitch* soundObject ); + void stopAllPlatforms(); + void startAnimObjSound( AnimSoundDSGData* soundData ); + void stopAnimObjSound( AnimCollisionEntityDSG* soundObject ); + void startWaspSound( Actor* wasp ); + bool hasOverlayClip( Vehicle* vehiclePtr, carSoundParameters** parameters ); + void toggleOverlayClip( Vehicle* vehiclePtr ); + const char* getPositionalSettingName( Vehicle* vehiclePtr, bool isMissionVehicle ); + + const static int NUM_TRAFFIC_SOUND_PLAYERS = 5; + TrafficSoundPlayer m_trafficPlayer[NUM_TRAFFIC_SOUND_PLAYERS]; + + const static int NUM_AI_SOUND_PLAYERS = 5; + AIVehicleSoundPlayer m_aiPlayer[NUM_AI_SOUND_PLAYERS]; + + AvatarVehiclePosnPlayer m_avatarVehiclePlayer; + + const static int NUM_WASP_SOUND_PLAYERS = 2; + WaspSoundPlayer m_waspPlayer[NUM_WASP_SOUND_PLAYERS]; + + const static int NUM_PLATFORM_SOUND_PLAYERS = 8; + PlatformSoundPlayer m_platformPlayer[NUM_PLATFORM_SOUND_PLAYERS]; + + const static int NUM_ANIM_OBJ_SOUND_PLAYERS = 5; + AnimObjSoundPlayer m_animObjPlayer[NUM_ANIM_OBJ_SOUND_PLAYERS]; + + static tUID s_waspUID; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //MOVINGSOUNDMANAGER_H diff --git a/game/code/sound/movingpositional/platformsoundplayer.cpp b/game/code/sound/movingpositional/platformsoundplayer.cpp new file mode 100644 index 0000000..2803e8a --- /dev/null +++ b/game/code/sound/movingpositional/platformsoundplayer.cpp @@ -0,0 +1,209 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: platformsoundplayer.cpp +// +// Description: Plays sound for moving platforms +// +// History: 5/29/2003 + Created -- Esan (two days to beta, yay!) +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> +#include <p3d/anim/pose.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/platformsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> + +#include <events/eventdata.h> +#include <ai/actionbuttonhandler.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// PlatformSoundPlayer::PlatformSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +PlatformSoundPlayer::PlatformSoundPlayer() : + m_joint( NULL ), + m_identity( NULL ) +{ +} + +//============================================================================= +// PlatformSoundPlayer::~PlatformSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +PlatformSoundPlayer::~PlatformSoundPlayer() +{ + if( m_identity != NULL ) + { + //m_identity->Release(); + } +} + +//============================================================================= +// PlatformSoundPlayer::Activate +//============================================================================= +// Description: Start playing a sound for a particular platform +// +// Parameters: soundData - position and identify info for platform +// +// Return: void +// +//============================================================================= +void PlatformSoundPlayer::Activate( AnimSoundData* soundData ) +{ + rmt::Vector posn; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + // + // Get the positionalSoundSettings object for the platform sound + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundData->positionalSettingName ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + m_joint = soundData->animJoint; + rAssert( m_joint != NULL ); + + m_identity = soundData->soundObject; + rAssert( m_identity != NULL ); + //m_identity->AddRef(); + + m_player.SetPositionCarrier( *this ); + m_player.SetParameters( parameters ); + + // + // Get world position of the platform through this joint + // + posn = m_joint->worldMatrix.Row( 3 ); + m_player.SetPosition( posn.x, posn.y, posn.z ); + + // + // Don't buffer, to save IOP + // + + m_player.PlaySound( soundData->soundName, NULL ); + } + else + { + rDebugString( "Couldn't play platform sound, no matching settings found" ); + } +} + +//============================================================================= +// PlatformSoundPlayer::Deactivate +//============================================================================= +// Description: Stop playing platform sound +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void PlatformSoundPlayer::Deactivate() +{ + m_player.Stop(); + + if( m_identity != NULL ) + { + //m_identity->Release(); + m_identity = NULL; + } + + m_joint = NULL; +} + +//============================================================================= +// PlatformSoundPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Service the sound player +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void PlatformSoundPlayer::ServiceOncePerFrame() +{ + m_player.ServiceOncePerFrame(); +} + +//============================================================================= +// PlatformSoundPlayer::GetPosition +//============================================================================= +// Description: Get the platform's current position +// +// Parameters: position - filled in with platform position +// +// Return: void +// +//============================================================================= +void PlatformSoundPlayer::GetPosition( radSoundVector& position ) +{ + rmt::Vector posn; + + rAssert( m_joint != NULL ); + posn = m_joint->worldMatrix.Row( 3 ); + position.SetElements( posn.x, posn.y, posn.z ); +} + +//============================================================================= +// PlatformSoundPlayer::GetVelocity +//============================================================================= +// Description: Comment +// +// Parameters: ( radSoundVector& velocity ) +// +// Return: void +// +//============================================================================= +void PlatformSoundPlayer::GetVelocity( radSoundVector& velocity ) +{ + // + // Doppler would be a big waste on those platforms anyway + // + velocity.SetElements( 0.0f, 0.0f, 0.0f ); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/platformsoundplayer.h b/game/code/sound/movingpositional/platformsoundplayer.h new file mode 100644 index 0000000..67bcdd1 --- /dev/null +++ b/game/code/sound/movingpositional/platformsoundplayer.h @@ -0,0 +1,74 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: platformsoundplayer.h +// +// Description: Plays sound for moving platforms +// +// History: 5/29/2003 + Created -- Esan (two days to beta, yay!) +// +//============================================================================= + +#ifndef PLATFORMSOUNDPLAYER_H +#define PLATFORMSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/positionalsoundplayer.h> +#include <p3d/anim/pose.hpp> + +//======================================== +// Forward References +//======================================== +struct AnimSoundData; +struct radSoundVector; + +namespace ActionButton +{ + class AnimSwitch; +}; + +//============================================================================= +// +// Synopsis: PlatformSoundPlayer +// +//============================================================================= + +class PlatformSoundPlayer : public PositionCarrier +{ + public: + PlatformSoundPlayer(); + virtual ~PlatformSoundPlayer(); + + bool IsActive() { return( m_identity != NULL ); } + bool UsesObject( ActionButton::AnimSwitch* soundObject ) { return( soundObject == m_identity ); } + void Activate( AnimSoundData* soundData ); + void Deactivate(); + + void ServiceOncePerFrame(); + + // + // PositionCarrier functions + // + void GetPosition( radSoundVector& position ); + void GetVelocity( radSoundVector& velocity ); + + private: + //Prevent wasteful constructor creation. + PlatformSoundPlayer( const PlatformSoundPlayer& platformsoundplayer ); + PlatformSoundPlayer& operator=( const PlatformSoundPlayer& platformsoundplayer ); + + tPose::Joint* m_joint; + ActionButton::AnimSwitch* m_identity; + + PositionalSoundPlayer m_player; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //PLATFORMSOUNDPLAYER_H diff --git a/game/code/sound/movingpositional/trafficsoundplayer.cpp b/game/code/sound/movingpositional/trafficsoundplayer.cpp new file mode 100644 index 0000000..5013d74 --- /dev/null +++ b/game/code/sound/movingpositional/trafficsoundplayer.cpp @@ -0,0 +1,458 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: trafficsoundplayer.cpp +// +// Description: Administers the playing of sound for a traffic vehicle +// +// History: 1/4/2003 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/trafficsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundfx/positionalsoundsettings.h> +#include <sound/avatar/carsoundparameters.h> + +#include <worldsim/redbrick/vehicle.h> +#include <worldsim/redbrick/trafficlocomotion.h> +#include <worldsim/traffic/trafficmanager.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +IRadTimerList* TrafficSoundPlayer::s_timerList = NULL; + +static const unsigned int s_minHonkShortMsecs = 250; +static const unsigned int s_maxHonkShortMsecs = 500; +static const unsigned int s_minHonkLongMsecs = 500; +static const unsigned int s_maxHonkLongMsecs = 1000; +static const unsigned int s_minHonkDelay = 250; +static const unsigned int s_maxHonkDelay = 500; + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// TrafficSoundPlayer::TrafficSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +TrafficSoundPlayer::TrafficSoundPlayer( ) : + m_hornTimer( NULL ), + m_vehicleParameters( NULL ), + m_honkCount( 0 ), + m_pitchMultiplier( 1.0f ) +{ +} + +//============================================================================= +// TrafficSoundPlayer::~TrafficSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +TrafficSoundPlayer::~TrafficSoundPlayer() +{ + if( m_hornTimer != NULL ) + { + m_hornTimer->UnregisterCallback( this ); + m_hornTimer->Release(); + } +} + +//============================================================================= +// TrafficSoundPlayer::InitializeClass +//============================================================================= +// Description: Prep the timer list for tracking horn times +// +// Parameters: numVehicles - number of cars, and therefore timers we need +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::InitializeClass( unsigned int numVehicles ) +{ + if( s_timerList == NULL ) + { + ::radTimeCreateList( &s_timerList, numVehicles, GMA_AUDIO_PERSISTENT ); + } +} + +//============================================================================= +// TrafficSoundPlayer::Activate +//============================================================================= +// Description: Before we start playing any sound, set a random pitch adjustment +// to give this thing a little variety +// +// Parameters: soundSettings - positional stuff for traffic +// resourceName - name of engine sound +// theCar - pointer to traffic vehicle object +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::Activate( positionalSoundSettings* soundSettings, + const char* resourceName, + Vehicle* theCar ) +{ + unsigned int randomNumber; + + // + // Pick a random pitch multiplier from 0.8 to 1.2. Arbitrary numbers. + // + randomNumber = rand() % 401; + m_pitchMultiplier = 0.8f + ( static_cast<float>(randomNumber) / 1000.0f ); + + VehiclePositionalSoundPlayer::Activate( soundSettings, resourceName, theCar ); + + m_player.SetPitch( 0.5f ); +} + +//============================================================================= +// TrafficSoundPlayer::Deactivate +//============================================================================= +// Description: Stop playing sound. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::Deactivate() +{ + VehiclePositionalSoundPlayer::Deactivate(); + + if( m_hornTimer != NULL ) + { + m_hornTimer->UnregisterCallback( this ); + m_hornTimer->Release(); + m_hornTimer = NULL; + } + + m_hornPlayer.Stop(); + + m_overlayPlayer.Stop(); +} + +//============================================================================= +// TrafficSoundPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Adjust the pitch for this vehicle +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::ServiceOncePerFrame() +{ + float pitch; + + VehiclePositionalSoundPlayer::ServiceOncePerFrame(); + + // + // Adjust pitch for vehicle speed if desired. I'll probably need to + // expose this for designer tuning later. + // + if( IsActive() && m_tiePitchToVelocity ) + { + pitch = 0.5f + ( 0.5f * ( m_vehicle->mTrafficLocomotion->mActualSpeed / TrafficManager::GetInstance()->GetDesiredTrafficSpeed() ) ); + pitch *= m_pitchMultiplier; + + // + // Arbitrary cap, just in case. Being paranoid. + // + if( pitch > 1.5f ) + { + pitch = 1.5f; + } + + m_player.SetPitch( pitch ); + } + + m_hornPlayer.ServiceOncePerFrame(); + m_overlayPlayer.ServiceOncePerFrame(); +} + +//============================================================================= +// TrafficSoundPlayer::ServiceTimerList +//============================================================================= +// Description: Service the timer list. Static function, since we only +// need to service this static member once per frame, not once +// per object per frame. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::ServiceTimerList() +{ + if( s_timerList != NULL ) + { + s_timerList->Service(); + } +} + +//============================================================================= +// TrafficSoundPlayer::OnTimerDone +//============================================================================= +// Description: Stop the traffic horn associated with this timer +// +// Parameters: elapsedTime - unused +// pUserData - unused +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::OnTimerDone( unsigned int elapsedTime, void * pUserData ) +{ + unsigned int timeout; + rmt::Vector position; + positionalSoundSettings* settings; + IRadNameSpace* nameSpace; + + if( m_hornPlayer.IsInUse() ) + { + m_hornPlayer.Stop(); + + if( m_honkCount == 0 ) + { + // + // Last honk done + // + m_hornTimer->Release(); + m_hornTimer = NULL; + } + else + { + // + // Random amount of silence before next honk + // + timeout = s_minHonkDelay + ( rand() % ( s_maxHonkDelay - s_minHonkDelay ) ); + m_hornTimer->SetTimeout( timeout ); + m_hornTimer->Start(); + } + } + else + { + // + // Silence done, start another honk + // + rAssert( m_honkCount > 0 ); + + // + // Find the settings for this positional sound first + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + settings = reinterpret_cast<positionalSoundSettings*>( nameSpace->GetInstance( "traffic_horn" ) ); + rAssert( settings != NULL ); + + m_vehicle->GetPosition( &position ); + m_hornPlayer.SetPosition( position.x, position.y, position.z ); + m_hornPlayer.SetPositionCarrier( *this ); + m_hornPlayer.SetParameters( settings ); + m_hornPlayer.PlaySound( "horn" ); + + --m_honkCount; + + // + // Reset the timer + // + if( m_honkCount > 0 ) + { + timeout = s_minHonkShortMsecs + ( rand() % ( s_maxHonkShortMsecs - s_minHonkShortMsecs ) ); + } + else + { + timeout = s_minHonkLongMsecs + ( rand() % ( s_maxHonkLongMsecs - s_minHonkLongMsecs ) ); + } + + m_hornTimer->SetTimeout( timeout ); + m_hornTimer->Start(); + } +} + +//============================================================================= +// TrafficSoundPlayer::HonkHorn +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::HonkHorn() +{ + unsigned int hornTime; + rmt::Vector position; + positionalSoundSettings* settings; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + float diceRoll; + float probability; + + rAssert( m_vehicle != NULL ); + + if( m_hornTimer != NULL ) + { + // + // Already honking + // + return; + } + + // + // Find the settings for this positional sound first + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( "traffic_horn" ); + if( nameSpaceObj != NULL ) + { + settings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + probability = settings->GetPlaybackProbability(); + if( probability < 1.0f ) + { + // + // Random play + // + diceRoll = (static_cast<float>( rand() % 100 )) / 100.0f; + if( diceRoll >= probability ) + { + return; + } + } + + // + // Pick a random number of honks from 1 to 3. Pick a random time for + // the honk, giving the last one a better chance of being longer. + // + m_honkCount = rand() % 3; + if( m_honkCount > 0 ) + { + hornTime = s_minHonkShortMsecs + ( rand() % ( s_maxHonkShortMsecs - s_minHonkShortMsecs ) ); + } + else + { + hornTime = s_minHonkLongMsecs + ( rand() % ( s_maxHonkLongMsecs - s_minHonkLongMsecs ) ); + } + + s_timerList->CreateTimer( &m_hornTimer, hornTime, this, NULL, true, + IRadTimer::ResetModeOneShot ); + + // + // Start honking + // + m_vehicle->GetPosition( &position ); + m_hornPlayer.SetPosition( position.x, position.y, position.z ); + m_hornPlayer.SetPositionCarrier( *this ); + m_hornPlayer.SetParameters( settings ); + m_hornPlayer.PlaySound( "horn" ); + } + else + { + rDebugString( "Couldn't find settings for traffic horn\n" ); + } +} + +//============================================================================= +// TrafficSoundPlayer::AddOverlayClip +//============================================================================= +// Description: Play a positional clip along with the engine +// +// Parameters: parameters - car sound description for traffic vehicle +// posnSettingsName - name of object with positional settings +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::AddOverlayClip( carSoundParameters* parameters, const char* posnSettingsName ) +{ + const char* clipName; + positionalSoundSettings* settings; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + rmt::Vector position; + + rAssert( parameters != NULL ); + + m_vehicleParameters = parameters; + clipName = parameters->GetOverlayClipName(); + if( clipName != NULL ) + { + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( posnSettingsName ); + if( nameSpaceObj != NULL ) + { + settings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + m_vehicle->GetPosition( &position ); + m_overlayPlayer.SetPosition( position.x, position.y, position.z ); + m_overlayPlayer.SetPositionCarrier( *this ); + m_overlayPlayer.SetParameters( settings ); + m_overlayPlayer.PlaySound( clipName ); + } + else + { + rTuneAssertMsg( false, "Huh? Positional settings for overlay clip disappeared" ); + } + } +} + +//============================================================================= +// TrafficSoundPlayer::ToggleOverlayClip +//============================================================================= +// Description: Comment +// +// Parameters: ( carSoundParameters* parameters, const char* posnSettingsName ) +// +// Return: void +// +//============================================================================= +void TrafficSoundPlayer::ToggleOverlayClip( carSoundParameters* parameters, const char* posnSettingsName ) +{ + if( m_overlayPlayer.IsInUse() ) + { + m_overlayPlayer.Stop(); + } + else + { + AddOverlayClip( parameters, posnSettingsName ); + } +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/trafficsoundplayer.h b/game/code/sound/movingpositional/trafficsoundplayer.h new file mode 100644 index 0000000..1f5472e --- /dev/null +++ b/game/code/sound/movingpositional/trafficsoundplayer.h @@ -0,0 +1,83 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: trafficsoundplayer.h +// +// Description: Administers the playing of sound for a traffic vehicle +// +// History: 1/4/2003 + Created -- Darren +// +//============================================================================= + +#ifndef TRAFFICSOUNDPLAYER_H +#define TRAFFICSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <radtime.hpp> + +#include <sound/movingpositional/vehicleposnsoundplayer.h> + +//======================================== +// Forward References +//======================================== +class Vehicle; +class carSoundParameters; + +//============================================================================= +// +// Synopsis: TrafficSoundPlayer +// +//============================================================================= + +class TrafficSoundPlayer : public VehiclePositionalSoundPlayer, + public IRadTimerCallback +{ + public: + TrafficSoundPlayer( ); + virtual ~TrafficSoundPlayer(); + + static void InitializeClass( unsigned int numVehicles ); + + void Activate( positionalSoundSettings* soundSettings, + const char* resourceName, + Vehicle* theCar ); + void Deactivate(); + + void ServiceOncePerFrame(); + static void ServiceTimerList(); + + void HonkHorn(); + void AddOverlayClip( carSoundParameters* parameters, const char* posnSettingsName ); + void ToggleOverlayClip( carSoundParameters* parameters, const char* posnSettingsName ); + + // + // IRadTimerCallback + // + void OnTimerDone( unsigned int elapsedTime, void* pUserData ); + + private: + //Prevent wasteful constructor creation. + TrafficSoundPlayer( const TrafficSoundPlayer& trafficsoundplayer ); + TrafficSoundPlayer& operator=( const TrafficSoundPlayer& trafficsoundplayer ); + + IRadTimer* m_hornTimer; + PositionalSoundPlayer m_hornPlayer; + + carSoundParameters* m_vehicleParameters; + PositionalSoundPlayer m_overlayPlayer; + + unsigned int m_honkCount; + float m_pitchMultiplier; + + static IRadTimerList* s_timerList; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //TRAFFICSOUNDPLAYER_H diff --git a/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp b/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp new file mode 100644 index 0000000..8fbb446 --- /dev/null +++ b/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp @@ -0,0 +1,258 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclepositionalsoundplayer.cpp +// +// Description: Implement VehiclePositionalSoundPlayer +// +// History: 3/7/2003 + Created -- Esan +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/vehicleposnsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundfx/positionalsoundsettings.h> + +#include <worldsim/redbrick/vehicle.h> + + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// VehiclePositionalSoundPlayer::VehiclePositionalSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +VehiclePositionalSoundPlayer::VehiclePositionalSoundPlayer( ) : + m_vehicle( NULL ), + m_tiePitchToVelocity( false ) +{ +} + +//============================================================================= +// VehiclePositionalSoundPlayer::~VehiclePositionalSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +VehiclePositionalSoundPlayer::~VehiclePositionalSoundPlayer() +{ +} + +//============================================================================= +// AIVehicleSoundPlayer::ActivateByName +//============================================================================= +// Description: Start playing given sound for given vehicle +// +// Parameters: soundName - name of positional sound setting object containing +// data on sound to play +// theCar - vehicle to associate sound with +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::ActivateByName( const char* soundName, Vehicle* theCar ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + // + // Find the tunable sound settings that go with the name + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundName ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + Activate( parameters, parameters->GetClipName(), theCar ); + } + else + { + rDebugString( "Couldn't play AI car sound, no matching settings found" ); + } +} + +//============================================================================= +// AIVehicleSoundPlayer::Activate +//============================================================================= +// Description: Start playing given sound for given vehicle +// +// Parameters: soundSettings - positional sound setting object containing +// data on sound to play +// resourceName - name of sound resource to play positionally +// theCar - vehicle to associate sound with +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::Activate( positionalSoundSettings* soundSettings, + const char* resourceName, + Vehicle* theCar ) +{ + rmt::Vector posn; + + m_vehicle = theCar; + + m_player.SetPositionCarrier( *this ); + m_player.SetParameters( soundSettings ); + + theCar->GetPosition( &posn ); + m_player.SetPosition( posn.x, posn.y, posn.z ); + + m_player.PlaySound( resourceName, NULL ); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::Deactivate +//============================================================================= +// Description: Stop playing sound. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::Deactivate() +{ + m_vehicle = NULL; + m_tiePitchToVelocity = false; + + m_player.Stop(); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::GetPosition +//============================================================================= +// Description: Return position of vehicle we're playing sound for +// +// Parameters: position - vector to be filled in with position +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::GetPosition( radSoundVector& position ) +{ + rmt::Vector vehiclePosn; + + m_vehicle->GetPosition( &vehiclePosn ); + position.SetElements( vehiclePosn.x, vehiclePosn.y, vehiclePosn.z ); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::GetVelocity +//============================================================================= +// Description: Return velocity of vehicle we're playing sound for +// +// Parameters: velocity - vector to be filled in with velocity +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::GetVelocity( radSoundVector& velocity ) +{ + rmt::Vector vehicleVel; + + m_vehicle->GetVelocity( &vehicleVel ); + velocity.SetElements( vehicleVel.x, vehicleVel.y, vehicleVel.z ); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::ServiceOncePerFrame() +{ + m_player.ServiceOncePerFrame(); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::UsesVehicle +//============================================================================= +// Description: Indicate whether we're currently playing sound for given +// vehicle +// +// Parameters: car - vehicle to match against +// +// Return: true if we're playing sound for that car, false otherwise +// +//============================================================================= +bool VehiclePositionalSoundPlayer::UsesVehicle( Vehicle* car ) +{ + return( ( m_vehicle != NULL ) && ( car == m_vehicle ) ); +} + +//============================================================================= +// VehiclePositionalSoundPlayer::BlowUp +//============================================================================= +// Description: Play an explosion sound. Yay! +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void VehiclePositionalSoundPlayer::BlowUp() +{ + IRadNameSpace* nameSpace; + positionalSoundSettings* settings; + rmt::Vector position; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + settings = reinterpret_cast<positionalSoundSettings*>( nameSpace->GetInstance( "collision_sounds" ) ); + if( settings != NULL ) + { + m_vehicle->GetPosition( &position ); + m_explosionPlayer.SetPosition( position.x, position.y, position.z ); + m_explosionPlayer.SetPositionCarrier( *this ); + m_explosionPlayer.SetParameters( settings ); + m_explosionPlayer.PlaySound( "generic_car_explode" ); + } + else + { + rDebugString( "Couldn't find positional explosion settings for AI vehicle" ); + } +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/movingpositional/vehicleposnsoundplayer.h b/game/code/sound/movingpositional/vehicleposnsoundplayer.h new file mode 100644 index 0000000..dc47e2f --- /dev/null +++ b/game/code/sound/movingpositional/vehicleposnsoundplayer.h @@ -0,0 +1,81 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: vehiclepositionalsoundplayer.h +// +// Description: Base class for moving vehicle sounds. This includes traffic +// vehicles and positional idle on the user vehicle +// +// History: 3/7/2003 + Created -- Esan +// +//============================================================================= + +#ifndef VEHICLEPOSITIONALSOUNDPLAYER_H +#define VEHICLEPOSITIONALSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/positionalsoundplayer.h> + +//======================================== +// Forward References +//======================================== +class Vehicle; + +//============================================================================= +// +// Synopsis: VehiclePositionalSoundPlayer +// +//============================================================================= + +class VehiclePositionalSoundPlayer : public PositionCarrier +{ + public: + VehiclePositionalSoundPlayer( ); + virtual ~VehiclePositionalSoundPlayer(); + + bool IsActive() { return( m_vehicle != NULL ); } + void ActivateByName( const char* soundName, Vehicle* theCar ); + virtual void Activate( positionalSoundSettings* soundSettings, + const char* resourceName, + Vehicle* theCar ); + virtual void Deactivate(); + + bool UsesVehicle( Vehicle* car ); + + virtual void ServiceOncePerFrame(); + + void TiePitchToVelocity( bool flag ) { m_tiePitchToVelocity = flag; } + + void BlowUp(); + + // + // PositionCarrier functions + // + void GetPosition( radSoundVector& position ); + void GetVelocity( radSoundVector& velocity ); + + protected: + Vehicle* m_vehicle; + + PositionalSoundPlayer m_player; + + bool m_tiePitchToVelocity; + + + private: + //Prevent wasteful constructor creation. + VehiclePositionalSoundPlayer( const VehiclePositionalSoundPlayer& vehiclepositionalsoundplayer ); + VehiclePositionalSoundPlayer& operator=( const VehiclePositionalSoundPlayer& vehiclepositionalsoundplayer ); + + PositionalSoundPlayer m_explosionPlayer; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //VEHICLEPOSITIONALSOUNDPLAYER_H diff --git a/game/code/sound/movingpositional/waspsoundplayer.cpp b/game/code/sound/movingpositional/waspsoundplayer.cpp new file mode 100644 index 0000000..97eb98a --- /dev/null +++ b/game/code/sound/movingpositional/waspsoundplayer.cpp @@ -0,0 +1,260 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: waspsoundplayer.cpp +// +// Description: Implement WaspSoundPlayer +// +// History: 3/10/2003 + Created -- Esan +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/movingpositional/waspsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <events/eventmanager.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// WaspSoundPlayer::WaspSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +WaspSoundPlayer::WaspSoundPlayer() : + m_isFadingIn( false ), + m_attacking( false ), + m_blowingUp( false ) +{ + EventManager* eventMgr = GetEventManager(); + + eventMgr->AddListener( this, EVENT_ACTOR_REMOVED ); + eventMgr->AddListener( this, EVENT_WASP_CHARGING ); + eventMgr->AddListener( this, EVENT_WASP_CHARGED ); + eventMgr->AddListener( this, EVENT_WASP_ATTACKING ); + eventMgr->AddListener( this, EVENT_WASP_BLOWED_UP ); +} + +//============================================================================= +// WaspSoundPlayer::~WaspSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +WaspSoundPlayer::~WaspSoundPlayer() +{ + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// WaspSoundPlayer::Activate +//============================================================================= +// Description: Start playing wasp noises +// +// Parameters: theActor - wasp object +// soundName - name of sound resource to start playing for wasp +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::Activate( Actor* theActor ) +{ + // + // Play the fade-in sound + // + playWaspSound( "wasp_fade_in", theActor ); + m_isFadingIn = true; +} + +//============================================================================= +// WaspSoundPlayer::HandleEvent +//============================================================================= +// Description: Look for actor destruction and disassociate when it happens +// +// Parameters: id - event ID +// pEventData - event user data +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::HandleEvent( EventEnum id, void* pEventData ) +{ + // + // Make sure we've got the event for the right wasp + // + if( static_cast<Actor*>(pEventData) != m_actor ) + { + return; + } + + switch( id ) + { + case EVENT_ACTOR_REMOVED: + deactivate(); + break; + + case EVENT_WASP_CHARGING: + playWaspSound( "wasp_charging", m_actor ); + break; + + case EVENT_WASP_CHARGED: + playWaspSound( "wasp_charged_idle", m_actor ); + break; + + case EVENT_WASP_ATTACKING: + playWaspSound( "wasp_attack", m_actor ); + m_attacking = true; + break; + + case EVENT_WASP_BLOWED_UP: + playWaspSound( "wasp_destroyed", m_actor ); + m_blowingUp = true; + break; + + default: + rAssertMsg( false, "Unexpected event in WaspSoundPlayer::HandleEvent\n" ); + break; + } +} + +//============================================================================= +// WaspSoundPlayer::OnPlaybackComplete +//============================================================================= +// Description: Playback callback, so that we can make sound transitions +// if necessary +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::OnPlaybackComplete() +{ + if( m_isFadingIn || m_attacking ) + { + m_isFadingIn = false; + m_attacking = false; + + playWaspSound( "wasp_idle", m_actor ); + } + else if( m_blowingUp ) + { + // + // Wasp destroyed, deactivate player + // + m_blowingUp = false; + deactivate(); + } +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** + +//============================================================================= +// WaspSoundPlayer::deactivate +//============================================================================= +// Description: Stop sound playback and free player for next wasp +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::deactivate() +{ + safeStop(); + + ActorPlayer::deactivate(); +} + +//============================================================================= +// WaspSoundPlayer::safeStop +//============================================================================= +// Description: Stop sound without triggering playback callbacks +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::safeStop() +{ + // + // Kill the callbacks before stopping, so we don't get sounds + // lingering around after we think we're stopped + // + m_isFadingIn = false; + m_attacking = false; + + m_player.Stop(); +} + +//============================================================================= +// WaspSoundPlayer::playWaspSound +//============================================================================= +// Description: Play a sound for the wasp +// +// Parameters: soundName - name of sound resource +// theActor - wasp actor object +// +// Return: void +// +//============================================================================= +void WaspSoundPlayer::playWaspSound( const char* soundName, Actor* theActor ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + + // + // Kill the old sound, disabling any callback action first. + // + safeStop(); + + // + // Get the positionalSoundSettings object for the wasp sound + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( "wasp_settings" ) ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + playSound( parameters, soundName, theActor ); + } + else + { + rDebugString( "Couldn't play wasp sound, no matching settings found" ); + } +} diff --git a/game/code/sound/movingpositional/waspsoundplayer.h b/game/code/sound/movingpositional/waspsoundplayer.h new file mode 100644 index 0000000..f368b14 --- /dev/null +++ b/game/code/sound/movingpositional/waspsoundplayer.h @@ -0,0 +1,71 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: waspsoundplayer.h +// +// Description: Plays sound for those flying bugs +// +// History: 3/10/2003 + Created -- Esan +// +//============================================================================= + +#ifndef WASPSOUNDPLAYER_H +#define WASPSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/movingpositional/actorplayer.h> + +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: WaspSoundPlayer +// +//============================================================================= + +class WaspSoundPlayer : public ActorPlayer, + public EventListener +{ + public: + WaspSoundPlayer(); + virtual ~WaspSoundPlayer(); + + // + // ActorPlayer functions + // + void Activate( Actor* theActor ); + void OnPlaybackComplete(); + + // + // EventListener functions + // + void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + WaspSoundPlayer( const WaspSoundPlayer& waspsoundplayer ); + WaspSoundPlayer& operator=( const WaspSoundPlayer& waspsoundplayer ); + + void playWaspSound( const char* soundName, Actor* theActor ); + + void deactivate(); + void safeStop(); + + bool m_isFadingIn; + bool m_attacking; + bool m_blowingUp; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //WASPSOUNDPLAYER_H diff --git a/game/code/sound/music/allmusic.cpp b/game/code/sound/music/allmusic.cpp new file mode 100644 index 0000000..0b5fb3f --- /dev/null +++ b/game/code/sound/music/allmusic.cpp @@ -0,0 +1 @@ +#include <sound/music/musicplayer.cpp> diff --git a/game/code/sound/music/musicplayer.cpp b/game/code/sound/music/musicplayer.cpp new file mode 100644 index 0000000..b03dfbb --- /dev/null +++ b/game/code/sound/music/musicplayer.cpp @@ -0,0 +1,2284 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: musicplayer.cpp +// +// Description: Implement MusicPlayer class. Plays all music in +// the game using RadMusic. +// +// History: 17/07/2002 + Created -- Darren +// +//============================================================================= + + +//======================================== +// Project Includes +//======================================== +#include <sound/music/musicplayer.h> + +#include <sound/soundmanager.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/dasoundgroup.h> + +#include <main/commandlineoptions.h> +#include <memory/srrmemory.h> +#include <events/eventmanager.h> +#include <gameflow/gameflow.h> +#include <loading/loadingmanager.h> +#include <loading/soundfilehandler.h> +#include <mission/missionmanager.h> +#include <worldsim/character/character.h> +#include <worldsim/avatarmanager.h> +#include <meta/eventlocator.h> +#include <worldsim/character/charactermanager.h> +#include <worldsim/character/character.h> + +#include <Render/Enums/RenderEnums.h> + +#include <interiors/interiormanager.h> + +#include <radload/radload.hpp> + +//======================================== +// System Includes +//======================================== +#include <string.h> + +#include <radmusic/radmusic.hpp> +#include <p3d/entity.hpp> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +static const char* AMBIENT_FILE_NAME = "sound\\ambience\\ambience.rms"; +static const char* LEVEL_FILE_NAME_PREFIX = "sound\\music\\L"; +static const char* LEVEL_FILE_NAME_SUFFIX = "_music.rms"; +static const char* MUSIC_SEARCH_PATH = "sound\\music\\"; +static const char* AMBIENCE_SEARCH_PATH = "sound\\ambience\\"; + +struct InteriorIDEntry +{ + radInt64 id; + const char* name; + MusicEventList musicEvent; +}; + +static InteriorIDEntry interiorNameTable[] = +{ + { 0, "KwikEMart", MEVENT_INTERIOR_KWIK_E_MART }, + { 0, "SpringfieldElementary", MEVENT_INTERIOR_SCHOOL }, + { 0, "SimpsonsHouse", MEVENT_INTERIOR_HOUSE }, + { 0, "Krustylu", MEVENT_INTERIOR_HOUSE }, + { 0, "dmv", MEVENT_INTERIOR_DMV } +}; + +static int interiorTableLength = sizeof( interiorNameTable ) / sizeof( InteriorIDEntry ); + +// +// Tables for mapping music/ambience events to indices into radMusic scripts +// + +struct MusicScriptTableEntry +{ + const char* name; + radKey32 nameKey; + int scriptIndex; +}; +static const int NO_INDEX = -1; + +static MusicScriptTableEntry musicEventTable[] = +{ + { "movie", 0, 0 }, + { "pause", 0, 0 }, + { "unpause", 0, 0 }, + { "FE", 0, 0 }, + { "Loading_screen", 0, 0 }, + { "Newspaper_Spin", 0, 0 }, + { "SuperSprint", 0, 0 }, + { "Win_SuperSprint", 0, 0 }, + { "Lose_SuperSprint", 0, 0 }, + { "Level_Intro", 0, 0 }, + { "Sunday_Drive_start", 0, 0 }, + { "Sunday_Drive_Get_out_of_Car", 0, 0 }, + { "Store", 0, 0 }, + { "Enter_StoneCutters_Tunnel", 0, 0 }, + { "Exit_StoneCutters_Tunnel", 0, 0 }, + { "OF_explore_mission", 0, 0 }, + { "OF_found_card", 0, 0 }, + { "OF_time_out", 0, 0 }, + { "OF_Apu_Oasis", 0, 0 }, + { "OF_House", 0, 0 }, + { "OF_KiwkEMart", 0, 0 }, + { "OF_School", 0, 0 }, + { "OF_Moes", 0, 0 }, + { "OF_DMV", 0, 0 }, + { "OF_Android_Dungeon", 0, 0 }, + { "OF_Observatory", 0, 0 }, + { "Win_3Street_Races", 0, 0 }, + { "Level_Completed", 0, 0 }, + { "Destroy_Camera_Bonus", 0, 0 }, + { "StoneCutters", 0, 0 }, + { "Social_Club", 0, 0 }, + { "DuffBeer", 0, 0 }, + { "Hit_and_Run", 0, 0 }, + { "Hit_and_Run_Caught", 0, 0 }, + { "Wasp_Attack", 0, 0 }, + { "Gated_Mission", 0, 0 }, + { "ScaryMusic01", 0, 0 }, + { "Credits", 0, 0 } +}; + +static unsigned int musicEventTableLength = sizeof( musicEventTable ) / sizeof( MusicScriptTableEntry ); + +struct MissionNameScriptEntry +{ + radKey32 nameKey; + int scriptIndex; +}; +static const unsigned int MISSION_TABLE_SIZE = 81; +static MissionNameScriptEntry missionScriptTable[MISSION_TABLE_SIZE]; +static unsigned int missionScriptTableLength; + +static const unsigned int RACE_TABLE_SIZE = 12; +static MissionNameScriptEntry raceScriptTable[RACE_TABLE_SIZE]; +static unsigned int raceScriptTableLength; + +struct MatrixStateScriptEntry +{ + radKey32 stateNameKey; + unsigned int stateIndex; + radKey32 stateValueNameKey; + unsigned int stateValueIndex; +}; +static const unsigned int MATRIX_TABLE_SIZE = 6; +static MatrixStateScriptEntry matrixStateTable[MATRIX_TABLE_SIZE]; +static unsigned int matrixStateTableLength; + +// Should change to whatever the global constant is +static const int NUM_LEVELS = 7; +static const int NUM_STARTING_MISSIONS = 8; + +static AmbientEventList startingAmbiences[NUM_LEVELS][NUM_STARTING_MISSIONS] = +{ + // L1 + { + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PP_ROOM_2 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_COUNTRY_HIGHWAY - LocatorEvent::AMBIENT_SOUND_CITY ) ) + }, + + // L2 + { + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + }, + + // L3 + { + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_FOREST_HIGHWAY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_QUAY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_QUAY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + }, + + // L4 + { + AEVENT_INTERIOR_HOUSE, + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + }, + + // L5 + { + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + }, + + // L6 + { + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + }, + + // L7 + { + AEVENT_INTERIOR_HOUSE, + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_HALLOWEEN1 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE, + AEVENT_INTERIOR_HOUSE, + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + static_cast<AmbientEventList>( AEVENT_TRIGGER_START + + ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ), + AEVENT_INTERIOR_HOUSE // unused + } +}; + +static LocatorEvent::Event startingExteriorAmbiences[NUM_LEVELS] = +{ + LocatorEvent::AMBIENT_SOUND_SUBURBS, + LocatorEvent::AMBIENT_SOUND_CITY, + LocatorEvent::AMBIENT_SOUND_LIGHT_CITY, + LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT, + LocatorEvent::AMBIENT_SOUND_CITY, + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10, + LocatorEvent::AMBIENT_SOUND_HALLOWEEN1 +}; + +static int s_PostHitAndRunTimer = -99; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// MusicPlayer::MusicPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +MusicPlayer::MusicPlayer( Sound::IDaSoundTuner& tuner ) : + m_lastServiceTime( ::radTimeGetMilliseconds( ) ), + m_isLoadingMusic( false ), + m_isInCar( false ), + m_radLoadRequest( NULL ), + m_musicPerformance( NULL ), + m_musicComposition( NULL ), + m_isLoadingAmbient( false ), + m_ambientPerformance( NULL ), + m_ambientComposition( NULL ), + m_currentAmbient( AEVENT_TRIGGER_START ), + m_ambiencePlaying( false ), + m_onApuRooftop( false ), + m_stoneCutterSong( false ), + m_LBCSong( false ), + m_delayedMusicStart( false ), + m_wasp( NULL ) +{ + EventManager* eventMgr; + unsigned int event; + + // + // Register as an event listener + // + + eventMgr = GetEventManager(); + + eventMgr->AddListener( this, EVENT_GETINTOVEHICLE_END ); + eventMgr->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); + eventMgr->AddListener( this, EVENT_INTERIOR_SWITCH ); + eventMgr->AddListener( this, EVENT_MISSION_DRAMA ); + eventMgr->AddListener( this, EVENT_MISSION_FAILURE ); + eventMgr->AddListener( this, EVENT_MISSION_SUCCESS ); + eventMgr->AddListener( this, EVENT_CARD_COLLECTED ); + eventMgr->AddListener( this, EVENT_FE_START_GAME_SELECTED ); + eventMgr->AddListener( this, EVENT_HIT_AND_RUN_START ); + eventMgr->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT ); + eventMgr->AddListener( this, EVENT_HIT_AND_RUN_EVADED ); + + // + // Temporarily remove wasp music + // + //eventMgr->AddListener( this, EVENT_WASP_CHARGING ); + //eventMgr->AddListener( this, EVENT_WASP_BLOWED_UP ); + + eventMgr->AddListener( this, EVENT_ACTOR_REMOVED ); + eventMgr->AddListener( this, EVENT_CHANGE_MUSIC ); + eventMgr->AddListener( this, EVENT_CHANGE_MUSIC_STATE ); + eventMgr->AddListener( this, EVENT_STAGE_TRANSITION_FAILED ); + eventMgr->AddListener( this, EVENT_COMPLETED_ALLSTREETRACES ); + eventMgr->AddListener( this, EVENT_MISSION_RESET ); + eventMgr->AddListener( this, EVENT_CHARACTER_POS_RESET ); + eventMgr->AddListener( this, EVENT_PLAY_CREDITS ); + eventMgr->AddListener( this, EVENT_PLAY_FE_MUSIC ); + eventMgr->AddListener( this, EVENT_PLAY_MUZAK ); + //eventMgr->AddListener( this, EVENT_PLAY_IDLE_MUSIC ); + eventMgr->AddListener( this, EVENT_SUPERSPRINT_WIN ); + eventMgr->AddListener( this, EVENT_SUPERSPRINT_LOSE ); + eventMgr->AddListener( this, EVENT_STOP_THE_MUSIC ); + + // + // Register all of the ambience events + // + for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY; + event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16; + ++event ) + { + if( ( event < EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS ) + || ( ( event > EVENT_LOCATOR + LocatorEvent::FAR_PLANE ) + && ( event < EVENT_LOCATOR + LocatorEvent::GOO_DAMAGE ) ) + || ( event > EVENT_LOCATOR + LocatorEvent::TRAP ) ) + { + eventMgr->AddListener( this, static_cast<EventEnum>(event) ); + } + } + + // + // Fill in the script tables with name keys + // + initializeTableNameKeys(); +} + +//============================================================================== +// MusicPlayer::~MusicPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +MusicPlayer::~MusicPlayer() +{ + // + // Deregister from EventManager + // + GetEventManager()->RemoveAll( this ); + + if( m_musicPerformance != NULL ) + { + radmusic::performance_stop( m_musicPerformance ); + radmusic::performance_delete( & m_musicPerformance ); + } + + if ( m_musicComposition != NULL ) + { + radmusic::composition_delete( & m_musicComposition ); + } + + if( m_ambientPerformance != NULL ) + { + radmusic::performance_stop( m_ambientPerformance ); + radmusic::performance_delete( & m_ambientPerformance ); + } + + if ( m_ambientComposition != NULL ) + { + radmusic::composition_delete( & m_ambientComposition ); + } +} + +void MusicPlayer::TriggerMusicEvent( MusicEventList event ) +{ + int scriptIndex; + int tableIndex; + + if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) ) + { + // + // Much necessary hack: screen out music we're tired of hearing + // + if( CommandLineOptions::Get( CLO_NO_AVRIL ) ) + { + if( ( event == MEVENT_FE ) + || ( event == MEVENT_SUNDAY_DRIVE_START ) + || ( event == MEVENT_STREETRACE_START ) ) + { + // Silence + event = MEVENT_MOVIE; + } + } + + // + // Get the script index for this event from the appropriate table + // + if( event < MEVENT_END_STANDARD_EVENTS ) + { + scriptIndex = musicEventTable[event].scriptIndex; + } + else if( event < MEVENT_END_MISSION_EVENTS ) + { + tableIndex = ( ( event - MEVENT_MISSION_START ) + calculateMissionIndex() ); + scriptIndex = missionScriptTable[tableIndex].scriptIndex; + } + else + { + tableIndex = ( ( event - MEVENT_STREETRACE_START ) + calculateMissionIndex() ); + scriptIndex = raceScriptTable[tableIndex].scriptIndex; + } + + // + // TODO: Uncomment after all the missions are set up and ready to go + // + //rAssertMsg( ( scriptIndex != NO_INDEX ), "Music event not found in script? (E-mail Esan, then continue)" ); + + if( ( scriptIndex != NO_INDEX ) + && ( m_musicPerformance != NULL ) ) + { + radmusic::performance_trigger_event( + m_musicPerformance, + scriptIndex ); + } + } +} + +void MusicPlayer::TriggerAmbientEvent( unsigned int event ) +{ + unsigned int scriptLength; + + if( m_ambientPerformance == NULL ) + { + return; + } + + if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) ) + { + scriptLength = radmusic::performance_num_events( m_ambientPerformance ); + + if( event < scriptLength ) + { + radmusic::performance_trigger_event( m_ambientPerformance, event ); + } + else + { + // + // TODO: reinstate this after MS 17, get ambience for all interiors + // + + //rAssertMsg( false, "Invalid ambient event received, ignored\n" ); + } + } +} + +//============================================================================= +// MusicPlayer::HandleEvent +//============================================================================= +// Description: Listen for when the player gets in and out of cars +// +// Parameters: id - event ID +// pEventData - user data, unused +// +// Return: void +// +//============================================================================= +void MusicPlayer::HandleEvent( EventEnum id, void* pEventData ) +{ + bool inCar; + int interiorIndex; + MusicStateData* musicState; + EventLocator* pLocator = (EventLocator*)pEventData; + Character* pCharacter = NULL; + int levelIndex; + int missionIndex; + + static bool s_HitAndRun = false; + static int previousTime = 0, recentTime = 0; + static EventEnum previousEvent = EVENT_LOCATOR; + static EventEnum recentEvent = EVENT_LOCATOR; + static EventLocator* previousLocator = NULL; + static EventLocator* recentLocator = NULL; + static bool bSpecialCaseMusic = false; + static MusicEventList lastSpecialMusic = MEVENT_MOVIE; // default + + // bmc: don't pay attention to music events when in hit and run mode + /* + if ( s_HitAndRun ) + { + if ( id != EVENT_HIT_AND_RUN_CAUGHT && id != EVENT_HIT_AND_RUN_EVADED && + id != EVENT_MISSION_FAILURE && id != EVENT_MISSION_SUCCESS && id != EVENT_MISSION_RESET ) + return; + else + s_HitAndRun = false; + } + */ + + switch( id ) + { + case EVENT_FE_START_GAME_SELECTED: + TriggerMusicEvent( MEVENT_NEWSPAPER_SPIN ); + break; + + case EVENT_GETINTOVEHICLE_END: + m_isInCar = true; + + if ( !s_HitAndRun ) + { + if ( bSpecialCaseMusic ) + { + TriggerMusicEvent( lastSpecialMusic ); + } + else + { + startMusic(); + } + } + break; + + case EVENT_GETOUTOFVEHICLE_END: + // bmc: have to validate that it's not an ai player getting out of the car... + pCharacter = static_cast<Character*>( pEventData ); + if ( !pCharacter->IsNPC() ) + { + m_isInCar = false; + + // if playing special music then we don't want to mess with it... + if ( !bSpecialCaseMusic ) + { + if ( !s_HitAndRun && !musicLockedOnForStage() ) + { + if( currentMissionIsSundayDrive() ) + { + TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_GET_OUT_OF_CAR ); + } + else if( currentMissionIsRace() ) + { + TriggerMusicEvent( MEVENT_STREETRACE_GET_OUT_OF_CAR ); + } + else + { + TriggerMusicEvent( MEVENT_GET_OUT_OF_CAR ); + } + turnAmbienceOn( m_currentAmbient ); + } + } + } + break; + + case EVENT_MISSION_RESET: + { + s_HitAndRun = false; + bSpecialCaseMusic = false; + lastSpecialMusic = MEVENT_MOVIE; + // bmc: play music if just declined restarting of a mission + if ( GetGameplayManager()->IsSundayDrive() && + GetInteriorManager()->GetInterior() == static_cast< tUID >( 0 ) && (int)pEventData == 0 ) + { + startMusic(); + } + + inCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar(); + if( inCar != m_isInCar ) + { + m_isInCar = inCar; + startMusic(); + } + else if ( inCar ) + { + // bmc: another hack -- if restarting a mission and we're in a car to start it then play the damn music + startMusic(); + } + +#if defined( RAD_XBOX ) || defined( RAD_WIN32 ) + // XBox seems to like this syntax better. + bool jumpStage = reinterpret_cast<bool>( pEventData ); +#else + bool jumpStage = (bool)( pEventData ); +#endif + + if( jumpStage ) + { + // + // Make sure we're playing the right ambience + // + levelIndex = calculateLevelIndex(); + missionIndex = GetGameplayManager()->GetCurrentMissionIndex(); + + if( startingAmbiences[levelIndex][missionIndex] == AEVENT_INTERIOR_HOUSE ) + { + m_currentAmbient = AEVENT_TRIGGER_START + ( startingExteriorAmbiences[calculateLevelIndex()] - LocatorEvent::AMBIENT_SOUND_CITY ); + } + else + { + m_currentAmbient = startingAmbiences[levelIndex][missionIndex]; + } + if( !inCar ) + { + turnAmbienceOn( startingAmbiences[levelIndex][missionIndex] ); + } + } + } + break; + + case EVENT_CHARACTER_POS_RESET: + if ( pEventData ) + pCharacter = static_cast<Character*>( pEventData ); + + if ( pCharacter && pCharacter->IsNPC() ) + break; + + inCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar(); + if( inCar != m_isInCar ) + { + m_isInCar = inCar; + startMusic(); + } + + break; + + case EVENT_INTERIOR_SWITCH: + // + // We sometimes get these events on loading screens. Filter them out. + // + if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY ) + { + break; + } + + if(pEventData) + { + // switching to interior + interiorIndex = calculateInteriorIndex( GetInteriorManager()->GetInterior() ); + turnAmbienceOn( AEVENT_INTERIOR_KWIK_E_MART + interiorIndex ); + if( !musicLockedOnForStage() ) + { + TriggerMusicEvent( interiorNameTable[interiorIndex].musicEvent ); + } + } + else if( !m_isInCar ) + { + // switching to exterior + turnAmbienceOn( m_currentAmbient ); + if( !musicLockedOnForStage() ) + { + TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION ); + } + } + break; + + case EVENT_MISSION_DRAMA: + TriggerMusicEvent( MEVENT_MISSION_DRAMA ); + break; + + case EVENT_CHANGE_MUSIC: + triggerMusicMissionEventByName( static_cast<radKey32*>( pEventData ) ); + break; + + case EVENT_CHANGE_MUSIC_STATE: + musicState = static_cast<MusicStateData*>( pEventData ); + triggerMusicStateChange( musicState->stateKey, musicState->stateEventKey ); + break; + + case EVENT_MISSION_SUCCESS: + s_HitAndRun = false; + if( currentMissionIsRace() ) + { + TriggerMusicEvent( MEVENT_STREETRACE_WIN ); + } + else + { + TriggerMusicEvent( MEVENT_WIN_MISSION ); + } + playPostMissionSounds(); + break; + + case EVENT_MISSION_FAILURE: + s_HitAndRun = false; + if( currentMissionIsRace() ) + { + TriggerMusicEvent( MEVENT_STREETRACE_LOSE ); + } + else + { + TriggerMusicEvent( MEVENT_LOSE_MISSION ); + } + + //playPostMissionSounds(); + break; + + case EVENT_STAGE_TRANSITION_FAILED: + TriggerMusicEvent( MEVENT_GATED_MISSION ); + break; + + case EVENT_CARD_COLLECTED: + // + // If we're in the car, don't interrupt the music, we'll play + // a sound effect instead + // + if( !m_isInCar ) + { + TriggerMusicEvent( MEVENT_OF_FOUND_CARD ); + } + break; + + case EVENT_HIT_AND_RUN_START: + TriggerMusicEvent( MEVENT_HIT_AND_RUN_START ); + s_HitAndRun = true; + // bmc: queueing this after the hit and run music seems to do the trick when hit and run ends...but only on evade... + //startMusic(); + break; + + case EVENT_HIT_AND_RUN_CAUGHT: + TriggerMusicEvent( MEVENT_HIT_AND_RUN_CAUGHT ); + s_HitAndRun = false; + // begin counter -- (x) ticks until startMusic call... + s_PostHitAndRunTimer = 110; + break; + + case EVENT_HIT_AND_RUN_EVADED: + // bmc: play nothing... + //TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_START ); + s_HitAndRun = false; + // begin counter -- (x) ticks until startMusic call... + s_PostHitAndRunTimer = 10; + break; + + case EVENT_WASP_CHARGING: + if( !m_isInCar ) + { + m_wasp = static_cast<Actor*>( pEventData ); + TriggerMusicEvent( MEVENT_WASP_ATTACK ); + } + break; + + case EVENT_WASP_BLOWED_UP: + case EVENT_ACTOR_REMOVED: + if( static_cast<Actor*>( pEventData ) == m_wasp ) + { + TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION ); + + m_wasp = NULL; + } + break; + + case EVENT_COMPLETED_ALLSTREETRACES: + TriggerMusicEvent( MEVENT_WIN_3_RACES ); + break; + + case EVENT_DESTROYED_ALLCAMERAS: + TriggerMusicEvent( MEVENT_DESTROY_CAMERA_BONUS ); + break; + + case EVENT_SUPERSPRINT_WIN: + TriggerMusicEvent( MEVENT_SUPERSPRINT_WIN ); + break; + + case EVENT_SUPERSPRINT_LOSE: + TriggerMusicEvent( MEVENT_SUPERSPRINT_LOSE ); + break; + + case EVENT_PLAY_CREDITS: + TriggerMusicEvent( MEVENT_CREDITS ); + break; + + case EVENT_PLAY_FE_MUSIC: + TriggerMusicEvent( MEVENT_FE ); + break; + + case EVENT_PLAY_MUZAK: + TriggerMusicEvent( MEVENT_STORE ); + break; + + case EVENT_PLAY_IDLE_MUSIC: + // bmc: do nothing here -- it fucks things over anyway + // TriggerMusicEvent( MEVENT_OF_TIME_OUT ); + break; + + case EVENT_STOP_THE_MUSIC: + TriggerMusicEvent( MEVENT_MOVIE ); + break; + + default: + // + // This should be triggered with ambience events. Easier to handle here + // than writing dozens of cases above + // + rAssert( id >= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY ); + rAssert( id <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16 ); + rAssert( ! ( ( id >= EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS ) + && ( id <= EVENT_LOCATOR + LocatorEvent::FAR_PLANE ) ) ); + rAssert( ! ( ( id >= EVENT_LOCATOR + LocatorEvent::GOO_DAMAGE ) + && ( id <= EVENT_LOCATOR + LocatorEvent::TRAP ) ) ); + + // don't play specific music if in hit and run + if ( s_HitAndRun || !pLocator->GetPlayerEntered() ) + break; + + +#ifdef RAD_DEBUG + char temp[256]; + + radmusic::performance_event_name( m_ambientPerformance, + AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY), + temp, + 256 ); + + rDebugPrintf( "Hit ambient trigger for %s\n", temp ); +#endif + + if( m_ambiencePlaying ) + { + turnAmbienceOn( AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY) ); + } + else + { + m_currentAmbient = AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY); + } + + // + // Icky special case + // + unsigned int now = ::radTimeGetMilliseconds(); + + // bmc: thrashing of events when driving over transition volumes is quite hazardous... + // implementing a system with a time threshold on it, so for e.g. when thrashing quickly + // between two volumes ABBAABABABA etc. will only handle B. ...however still troublesome + // if the player continuously drives back and forth over the volumes...may become confused + // as to which one to actually choose and end up with the wrong one :( + + if ( id == recentEvent ) + { + // ignore + id = id; + } + else if ( id == previousEvent && ( ( now - previousTime ) < ( 500 ) ) ) + { + rDebugPrintf( "Sound ignoring likely thrashing event id = %d with time delay %d\n", id, now-previousTime ); + id = id; + } + else + { + if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER9 ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_STONECUTTER_SONG; + TriggerMusicEvent( MEVENT_STONECUTTER_SONG ); + bSpecialCaseMusic = true; + } + else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER8 ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_LBC_SONG; + TriggerMusicEvent( MEVENT_LBC_SONG ); + bSpecialCaseMusic = true; + } + //else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_TUNNEL ) ) + // && ( GetGameplayManager()->IsSundayDrive() ) ) + //{ + // TriggerMusicEvent( MEVENT_STONECUTTER_SONG ); + // bSpecialCaseMusic = true; + //} + else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_HALL ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_ENTER_STONECUTTER_TUNNEL; + TriggerMusicEvent( MEVENT_ENTER_STONECUTTER_TUNNEL ); + bSpecialCaseMusic = true; + } + else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_KWIK_E_MART_ROOFTOP ) ) || + ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER3 ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_APU_OASIS; + TriggerMusicEvent( MEVENT_APU_OASIS ); + bSpecialCaseMusic = true; + } + else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_HALLOWEEN2 ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_SCARYMUSIC; + TriggerMusicEvent( MEVENT_SCARYMUSIC ); + bSpecialCaseMusic = true; + } + else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_DUFF_EXTERIOR ) ) || + ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_BREWERY_NIGHT ) ) + && ( GetGameplayManager()->IsSundayDrive() ) ) + { + lastSpecialMusic = MEVENT_DUFF_SONG; + TriggerMusicEvent( MEVENT_DUFF_SONG ); + bSpecialCaseMusic = true; + } + else if( bSpecialCaseMusic ) + { + startMusic(); // bmc: test + bSpecialCaseMusic = false; + lastSpecialMusic = MEVENT_MOVIE; // default + } + + previousEvent = recentEvent; + previousTime = recentTime; + previousLocator = recentLocator; + + recentEvent = id; + recentTime = now; + recentLocator = pLocator; + } + break; + + } +} + +//============================================================================= +// MusicPlayer::Service +//============================================================================= +// Description: Extract the composition from the radload request, create the +// performance and hook up the composition. +// +// Parameters: None +// +// Return: void +// +//============================================================================= + +void MusicPlayer::SetUpPerformance( + radmusic::performance ** ppPerformance, + radmusic::composition ** ppComposition, + const char * searchPath ) +{ + rAssert( NULL != ppPerformance ); + rAssert( NULL != ppComposition ); + rAssert( NULL == *ppPerformance ); + rAssert( NULL == *ppComposition ); + + radmusic::radload_composition_adapter * compAdapter = + radLoadFind< radmusic::radload_composition_adapter >( + m_radLoadRequest->GetInventory( ), "my_composition" ); + + rAssert( NULL != compAdapter ); + + *ppComposition = compAdapter->p_composition; + + rAssert( NULL != *ppComposition ); + + compAdapter->p_composition = NULL; + + m_radLoadRequest->Release( ); + m_radLoadRequest = NULL; + + if ( CommandLineOptions::Get( CLO_FIREWIRE ) ) + { + *ppPerformance = radmusic::performance_new( *ppComposition, searchPath, radMemorySpace_Local ); + } + else + { + *ppPerformance = radmusic::performance_new( *ppComposition, searchPath ); + } + + rAssert( *ppPerformance != NULL ); +} + +//============================================================================= +// MusicPlayer::Service +//============================================================================= +// Description: Service RadMusic, and check the composition loader if we're +// still in a loading state +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::Service() +{ + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + unsigned int now = ::radTimeGetMilliseconds( ); + unsigned int dif = now - m_lastServiceTime; + + if ( m_musicPerformance != NULL ) + { + radmusic::performance_update( m_musicPerformance, dif ); + } + + if ( m_ambientPerformance != NULL ) + { + radmusic::performance_update( m_ambientPerformance, dif ); + } + + m_lastServiceTime = now; + + // + // According to Tim, this can be changed to a synchronous + // load on a thread using: + // + // radmusic::composition_new_from_file( const char * p_file_name ); + // + + if( m_isLoadingMusic && ( m_radLoadRequest->IsComplete( ) ) ) + { + SetUpPerformance( & m_musicPerformance, & m_musicComposition, MUSIC_SEARCH_PATH ); + + m_isLoadingMusic = false; + + // + // Construct the event lookup tables + // + buildEventTables(); + + m_loadCompleteCallback->LoadCompleted(); + } + else if( m_isLoadingAmbient && m_radLoadRequest->IsComplete( ) ) + { + SetUpPerformance( & m_ambientPerformance, & m_ambientComposition, AMBIENCE_SEARCH_PATH ); + + m_isLoadingAmbient = false; + + rAssert( radmusic::performance_num_events( m_ambientPerformance ) == AEVENT_NUM_EVENTS ); + + + m_loadCompleteCallback->LoadCompleted(); + } + + HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT ); + + // bmc: hit-and-run post music hack + if ( s_PostHitAndRunTimer > 0 ) + { + if ( --s_PostHitAndRunTimer == 0 ) + { + s_PostHitAndRunTimer = -99; + startMusic(); + } + } + + // My own music hack -- DE + if( m_delayedMusicStart ) + { + if( m_musicPerformance == NULL ) + { + m_delayedMusicStart = false; + } + else + { + // + // Check to see if the mission success/fail stinger is done yet + // + if( radmusic::performance_is_state_steady_idle( m_musicPerformance ) ) + { + char regionName[ 64 ]; + radmusic::debug_performance_current_region_name( m_musicPerformance, regionName, 64 ); + + if( strcmp( regionName, "stopped" ) == 0 ) + { + m_delayedMusicStart = false; + startMusic(); + } + } + } + } +} + +//============================================================================= +// MusicPlayer::QueueRadmusicScriptLoad +//============================================================================= +// Description: Queue the RadMusic composition script files in the loading +// manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::QueueRadmusicScriptLoad() +{ + GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, AMBIENT_FILE_NAME, GMA_LEVEL_AUDIO ); + + // + // For the front end, any music script will do right now + // + QueueMusicLevelLoad( RenderEnums::L1 ); +} + +//============================================================================= +// MusicPlayer::QueueMusicLevelLoad +//============================================================================= +// Description: Queue the music script for the given level in the loading +// manager +// +// Parameters: level - enumeration indicating which level we're loading +// +// Return: void +// +//============================================================================= +void MusicPlayer::QueueMusicLevelLoad( RenderEnums::LevelEnum level ) +{ + char musicScriptName[100]; + int levelNum = level - RenderEnums::L1 + 1; + + //TODO: Esan are we going to play special music here? + if ( levelNum > RenderEnums::numLevels ) + { + levelNum -= RenderEnums::numLevels; + } + + sprintf( musicScriptName, "%s%d%s", LEVEL_FILE_NAME_PREFIX, levelNum, LEVEL_FILE_NAME_SUFFIX ); + GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, musicScriptName, GMA_LEVEL_AUDIO ); +} + +//============================================================================= +// MusicPlayer::LoadRadmusicScript +//============================================================================= +// Description: Load the RadMusic script +// +// Parameters: fileHandler - completion callback object +// +// Return: void +// +//============================================================================= +void MusicPlayer::LoadRadmusicScript( const char* filename, SoundFileHandler* fileHandler ) +{ + rAssert( NULL == m_radLoadRequest ); + + if( strcmp( filename, AMBIENT_FILE_NAME ) == 0 ) + { + m_isLoadingAmbient = true; + } + else + { + UnloadRadmusicScript(); + m_isLoadingMusic = true; + } + + m_loadCompleteCallback = fileHandler; + + // We have to keep the string around--problem with radload. + + strncpy( n_currentLoadName, filename, 64 ); + + radLoadOptions options; + options.filename = n_currentLoadName; + options.allocator = GMA_MUSIC; + + HeapMgr()->PushHeap(GMA_MUSIC); + radLoadInstance()->Load( & options, & m_radLoadRequest ); + HeapMgr()->PopHeap(GMA_MUSIC); +} + +//============================================================================= +// MusicPlayer::UnloadRadmusicScript +//============================================================================= +// Description: Dump the music script, if it's loaded +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::UnloadRadmusicScript() +{ + if( NULL != m_musicPerformance ) + { + radmusic::performance_stop( m_musicPerformance ); + radmusic::performance_delete( & m_musicPerformance ); + + m_musicPerformance = NULL; + } + + if ( NULL != m_musicComposition ) + { + radmusic::composition_delete( & m_musicComposition ); + + m_musicComposition = NULL; + } +} + +//============================================================================= +// MusicPlayer::OnFrontEndStart +//============================================================================= +// Description: Notify RadMusic composition that we've started the front end +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnFrontEndStart() +{ + TriggerMusicEvent( MEVENT_FE ); +} + +//============================================================================= +// MusicPlayer::OnFrontEndFinish +//============================================================================= +// Description: Notify RadMusic composition that we've finished the front end +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnFrontEndFinish() +{ + if( CommandLineOptions::Get( CLO_NO_MUSIC ) ) + { + return; + } +} + +//============================================================================= +// MusicPlayer::OnGameplayStart +//============================================================================= +// Description: Notify RadMusic composition that we've started gameplay +// +// Parameters: playerInCar - should be true if player 0 is in the car, meaning +// that we play music instead of ambient sound +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnGameplayStart( bool playerInCar ) +{ + int levelIndex; + int missionIndex; + + m_isInCar = playerInCar; + + if( !(GetGameplayManager()->IsSuperSprint()) ) + { + TriggerMusicEvent( MEVENT_LEVEL_INTRO ); + } + + // + // We want music playing for in-car characters. SuperSprint is a special + // case, since the characters can't be placed in cars until they select + // their cars, but we want music at gameplay start anyway + // + if( playerInCar || GetGameplayManager()->IsSuperSprint() ) + { + startMusic(); + } + else + { + levelIndex = calculateLevelIndex(); + missionIndex = GetGameplayManager()->GetCurrentMissionIndex(); + + if( startingAmbiences[levelIndex][missionIndex] == AEVENT_INTERIOR_HOUSE ) + { + m_currentAmbient = AEVENT_TRIGGER_START + ( startingExteriorAmbiences[calculateLevelIndex()] - LocatorEvent::AMBIENT_SOUND_CITY ); + } + else + { + m_currentAmbient = startingAmbiences[levelIndex][missionIndex]; + } + turnAmbienceOn( startingAmbiences[levelIndex][missionIndex] ); + } +} + +//============================================================================= +// MusicPlayer::OnGameplayFinish +//============================================================================= +// Description: Notify RadMusic composition that we've finished gameplay +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnGameplayFinish() +{ + TriggerMusicEvent( MEVENT_LOADING_SCREEN ); + + turnAmbienceOff( AEVENT_FRONTEND ); + + m_delayedMusicStart = false; +} + +//============================================================================= +// MusicPlayer::OnPauseStart +//============================================================================= +// Description: Entering pause menu, pause the music +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnPauseStart() +{ + TriggerMusicEvent( MEVENT_PAUSE ); + turnAmbienceOff( AEVENT_PAUSE ); +} + +//============================================================================= +// MusicPlayer::OnPauseEnd +//============================================================================= +// Description: Leaving pause menu, restart the music +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnPauseEnd() +{ + TriggerMusicEvent( MEVENT_UNPAUSE ); + turnAmbienceOn( AEVENT_UNPAUSE ); +} + +//============================================================================= +// MusicPlayer::OnStoreStart +//============================================================================= +// Description: Triggered when we enter a reward screen, like the clothes +// shop +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnStoreStart() +{ + TriggerMusicEvent( MEVENT_STORE ); +} + +//============================================================================= +// MusicPlayer::OnStoreEnd +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MusicPlayer::OnStoreEnd() +{ + // + // We actually reuse this for supersprint + // + if( !(GetGameplayManager()->IsSuperSprint()) ) + { + if( GetInteriorManager()->GetInterior() == static_cast< tUID >( 0 ) ) + { + TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION ); + } + else + { + TriggerMusicEvent( interiorNameTable[calculateInteriorIndex( GetInteriorManager()->GetInterior() )].musicEvent ); + } + } +} + +//============================================================================= +// MusicPlayer::StopForMovie +//============================================================================= +// Description: Stop the music for a movie +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::StopForMovie() +{ + TriggerMusicEvent( MEVENT_PAUSE ); + turnAmbienceOff( AEVENT_PAUSE ); +} + +//============================================================================= +// MusicPlayer::ResumeAfterMovie +//============================================================================= +// Description: Start the FE music again after an FMV +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::ResumeAfterMovie() +{ + TriggerMusicEvent( MEVENT_UNPAUSE ); + turnAmbienceOn( AEVENT_UNPAUSE ); +} + +bool MusicPlayer::IsStoppedForMovie( void ) +{ + char regionName[ 64 ]; + if ( !m_musicPerformance ) + { + return true; + } + radmusic::debug_performance_current_region_name( m_musicPerformance, regionName, 64 ); + + bool steadyIdle = radmusic::performance_is_state_steady_idle( m_musicPerformance ); + + bool paused = ( strcmp( regionName, "stopped" ) == 0 || strcmp( regionName, "pause_region" ) == 0 ); + + return paused && steadyIdle; +} + +//============================================================================= +// MusicPlayer::RestartSupersprintMusic +//============================================================================= +// Description: Give the supersprint music a kick +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::RestartSupersprintMusic() +{ + TriggerMusicEvent( MEVENT_SUPERSPRINT ); +} + +//============================================================================= +// MusicPlayer::GetVolume +//============================================================================= +// Description: Get the volume. Duh. +// +// Parameters: None +// +// Return: Float value for volume +// +//============================================================================= +float MusicPlayer::GetVolume() +{ + rAssert( NULL != m_musicPerformance ); + if ( NULL == m_musicPerformance ) + { + return 0.0f; + } + else + { + return( radmusic::performance_volume( m_musicPerformance ) ); + } +} + +//============================================================================= +// MusicPlayer::SetVolume +//============================================================================= +// Description: Set the volume. Also duh. +// +// Parameters: volume - new volume setting +// +// Return: void +// +//============================================================================= +void MusicPlayer::SetVolume( float volume ) +{ + if ( NULL != m_musicPerformance ) + { + radmusic::performance_volume( m_musicPerformance, volume ); + } +} + +//============================================================================= +// MusicPlayer::GetAmbienceVolume +//============================================================================= +// Description: Get the ambience volume. +// +// Parameters: None +// +// Return: Float value for volume +// +//============================================================================= +float MusicPlayer::GetAmbienceVolume() +{ + rAssert( NULL != m_ambientPerformance ); + if ( NULL == m_ambientPerformance ) + { + return 0.0f; + } + else + { + return( radmusic::performance_volume( m_ambientPerformance ) ); + } +} + +//============================================================================= +// MusicPlayer::SetAmbienceVolume +//============================================================================= +// Description: Set the ambience volume. +// +// Parameters: volume - new volume setting +// +// Return: void +// +//============================================================================= +void MusicPlayer::SetAmbienceVolume( float volume ) +{ + rAssert( NULL != m_ambientPerformance ); + if ( NULL != m_ambientPerformance ) + { + radmusic::performance_volume( m_ambientPerformance, volume ); + } +} + +//============================================================================= +// MusicPlayer::GetBeatValue +//============================================================================= +// Description: Pass on the beat from the music performance +// +// Parameters: None +// +// Return: float from 0.0f to 4.0f +// +//============================================================================= +float MusicPlayer::GetBeatValue() +{ + float beat = 0.0f; + + if( m_musicPerformance != NULL ) + { + radmusic::debug_performance_current_beat( m_musicPerformance, &beat ); + } + + return( beat ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// MusicPlayer::calculateLevelIndex +//============================================================================= +// Description: Figure out an index for the current level (0 for L1, 1 for L2...) +// +// Parameters: None +// +// Return: Index +// +//============================================================================= +int MusicPlayer::calculateLevelIndex() +{ + RenderEnums::LevelEnum level; + + level = GetGameplayManager()->GetCurrentLevelIndex(); + + return( static_cast<int>( level - RenderEnums::L1 ) ); +} + +//============================================================================= +// MusicPlayer::calculateMissionIndex +//============================================================================= +// Description: Figure out an index for the current mission (0 for M1, 1 for M2...). +// Use -1 for Sunday Drive. +// +// Parameters: None +// +// Return: Index +// +//============================================================================= +int MusicPlayer::calculateMissionIndex() +{ + int index; + int missionIndex; + Mission* currentMission; + GameplayManager* gameplayMgr = GetGameplayManager(); + + if( gameplayMgr->IsSundayDrive() ) + { + index = -1; + } + else + { + currentMission = gameplayMgr->GetCurrentMission(); + rAssert( currentMission != NULL ); + + if( currentMission->IsRaceMission() ) + { + index = ( gameplayMgr->GetCurrentMissionNum() - GameplayManager::MAX_MISSIONS ) * ( MEVENT_END_RACE_EVENTS - MEVENT_STREETRACE_START ); + } + else if( currentMission->IsBonusMission() ) + { + index = 8 * ( MEVENT_END_MISSION_EVENTS - MEVENT_MISSION_START ); // 0-7 are regular missions, 8 is bonus + } + else + { + missionIndex = gameplayMgr->GetCurrentMissionIndex(); + + // + // Stupid dummy level 1 and its special-case training mission + // + if( gameplayMgr->GetCurrentLevelIndex() != RenderEnums::L1 ) + { + missionIndex += 1; + } + + index = missionIndex * ( MEVENT_END_MISSION_EVENTS - MEVENT_MISSION_START ); + } + } + + return( index ); +} + +//============================================================================= +// MusicPlayer::calculateInteriorIndex +//============================================================================= +// Description: Figure out an index for the interior being entered +// +// Parameters: None +// +// Return: Index starting from 0, to be added to first interior event +// in ambient list +// +//============================================================================= +int MusicPlayer::calculateInteriorIndex( tUID interiorID ) +{ + int i; + int retVal = 2; // Default to Simpsons house sound + + // + // We should've be calculating this unless we're actually inside + // + rAssert( interiorID != static_cast< tUID >( 0 ) ); + + if( interiorNameTable[0].id == static_cast< tUID >( 0 ) ) + { + // + // Initialize table + // + for( i = 0; i < interiorTableLength; i++ ) + { + interiorNameTable[i].id = tName::MakeUID( interiorNameTable[i].name ); + } + } + + for( i = 0; i < interiorTableLength; i++ ) + { + if( interiorID == static_cast< tUID >( interiorNameTable[i].id ) ) + { + retVal = i; + break; + } + } + + return( retVal ); +} + +//============================================================================= +// MusicPlayer::musicLockedOnForStage +//============================================================================= +// Description: Checks the current mission stage to see if we need to keep +// the music playing when we get out of the car +// +// Parameters: None +// +// Return: True if music stays on, false otherwise +// +//============================================================================= +bool MusicPlayer::musicLockedOnForStage() +{ + Mission* mission; + MissionStage* stage; + bool musicLocked = false; + + mission = GetGameplayManager()->GetCurrentMission(); + if( mission != NULL ) + { + stage = mission->GetCurrentStage(); + if( stage != NULL ) + { + musicLocked = stage->GetMusicAlwaysOnFlag(); + } + } + + return( musicLocked ); +} + +//============================================================================= +// MusicPlayer::startMusic +//============================================================================= +// Description: Figure out which music track to play and play it +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::startMusic() +{ + // bmc: if calling startMusic then hit and run *must* be over...clean it up + s_PostHitAndRunTimer = -99; + // bmc + + int mission; + bool onFoot = false; + + mission = calculateMissionIndex(); + if( mission >= 0 ) + { + if( currentMissionIsRace() ) + { + TriggerMusicEvent( MEVENT_STREETRACE_START ); + } + else if ( m_isInCar ) + { + TriggerMusicEvent( MEVENT_MISSION_START ); + } + else + { + TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION ); + onFoot = true; + } + + } + else if( GetGameplayManager()->IsSuperSprint() ) + { + // + // Play Fox's licensed music. Fox sucks. + // + TriggerMusicEvent( MEVENT_SUPERSPRINT ); + } + else if ( m_isInCar ) + { + TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_START ); + } + else + { + TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION ); + onFoot = true; + } + + if( !onFoot ) + { + turnAmbienceOff( AEVENT_PAUSE ); + } + else if( ( GetInteriorManager() != NULL ) + && ( GetInteriorManager()->IsInside() ) ) + { + triggerCurrentInteriorAmbience(); + } + else + { + turnAmbienceOn( m_currentAmbient ); + } +} + +//============================================================================= +// MusicPlayer::playPostMissionSounds +//============================================================================= +// Description: Figure out what to play when the mission ends +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicPlayer::playPostMissionSounds() +{ + if( !m_isInCar ) + { + if( ( GetInteriorManager() != NULL ) + && ( GetInteriorManager()->IsInside() ) ) + { + triggerCurrentInteriorAmbience(); + } + else + { + turnAmbienceOn( m_currentAmbient ); + } + } + + m_delayedMusicStart = true; +} + +//============================================================================= +// MusicPlayer::turnAmbienceOn +//============================================================================= +// Description: Send the given ambience event and mark ambience as not +// playing +// +// Parameters: event - event to give to ambient performance object (shouldn't +// be pause or stop event) +// +// Return: void +// +//============================================================================= +void MusicPlayer::turnAmbienceOn( unsigned int event ) +{ + TriggerAmbientEvent( event ); + + if( ( event != AEVENT_UNPAUSE ) && ( event <= AEVENT_TRIGGER_END ) ) + { + m_currentAmbient = event; + } + m_ambiencePlaying = true; +} + +//============================================================================= +// MusicPlayer::turnAmbienceOff +//============================================================================= +// Description: Send the given ambience event and mark ambience as playing +// +// Parameters: event - event to give to ambient performance object (should +// be pause or stop event) +// +// Return: void +// +//============================================================================= +void MusicPlayer::turnAmbienceOff( unsigned int event ) +{ + rAssert( ( event == AEVENT_PAUSE ) || ( event == AEVENT_MOVIE ) + || ( event == AEVENT_FRONTEND ) ); + + TriggerAmbientEvent( event ); + m_ambiencePlaying = false; +} + +//============================================================================= +// MusicPlayer::currentMissionIsRace +//============================================================================= +// Description: As the name says +// +// Parameters: None +// +// Return: True if there's a current mission and it's a race, false +// otherwise +// +//============================================================================= +bool MusicPlayer::currentMissionIsRace() +{ + bool retVal = false; + Mission* theMission = GetGameplayManager()->GetCurrentMission(); + + if( ( theMission != NULL ) && ( theMission->IsRaceMission() ) ) + { + retVal = true; + } + + return( retVal ); +} + +//============================================================================= +// MusicPlayer::currentMissionIsSundayDrive +//============================================================================= +// Description: As the name says +// +// Parameters: None +// +// Return: True if there's a current mission and it's Sunday Drive, false +// otherwise +// +//============================================================================= +bool MusicPlayer::currentMissionIsSundayDrive() +{ + bool retVal = false; + Mission* theMission = GetGameplayManager()->GetCurrentMission(); + + if( ( theMission != NULL ) && ( theMission->IsSundayDrive() ) ) + { + retVal = true; + } + + return( retVal ); +} + +//============================================================================= +// MusicPlayer::initializeTableNameKeys +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MusicPlayer::initializeTableNameKeys() +{ + unsigned int i; + char temp[256]; + unsigned int tableIndex; + char prefix[10]; + + // + // We've already got strings for the musicEventTable, just make keys + // + for( i = 0; i < musicEventTableLength; i++ ) + { + musicEventTable[i].nameKey = ::radMakeCaseInsensitiveKey32( musicEventTable[i].name ); + } + + // + // For the mission and race tables, generate strings and store the keys + // + tableIndex = 0; + for( i = 0; i < 9; i++ ) // min. 8 missions per level + bonus + { + if( i == 8 ) + { + strcpy( prefix, "Bonus" ); + } + else + { + sprintf( prefix, "M%d", i ); + } + + sprintf( temp, "%s_start", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "%s_drama", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "%s_win", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "%s_lose", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "%s_get_out_of_car", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "%s_10sec_to_go", prefix ); + missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + } + // + // Record the length of this table + // + rAssert( tableIndex <= MISSION_TABLE_SIZE ); + missionScriptTableLength = tableIndex; + + // + // Just to be safe + // + for( i = missionScriptTableLength; i < MISSION_TABLE_SIZE; i++ ) + { + missionScriptTable[i].nameKey = 0; + missionScriptTable[i].scriptIndex = NO_INDEX; + } + + tableIndex = 0; + for( i = 0; i < 3; i++ ) // 3 race missions + { + sprintf( temp, "StreetRace0%d_start", i+1 ); + raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "StreetRace0%d_win", i+1 ); + raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "StreetRace0%d_lose", i+1 ); + raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + + sprintf( temp, "StreetRace0%d_getoutofcar", i+1 ); + raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp ); + } + // + // Record the length of this table + // + rAssert( tableIndex <= RACE_TABLE_SIZE ); + raceScriptTableLength = tableIndex; + + // + // Just to be safe one more time + // + for( i = raceScriptTableLength; i < RACE_TABLE_SIZE; i++ ) + { + raceScriptTable[i].nameKey = 0; + raceScriptTable[i].scriptIndex = NO_INDEX; + } +} + +//============================================================================= +// MusicPlayer::buildEventTables +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MusicPlayer::buildEventTables() +{ + unsigned int i, j; + unsigned int scriptLength; + char temp[256]; + radKey32 tempKey; + bool found; + unsigned int numStates; + unsigned int numStateEvents; + radKey32 stateKey; + radKey32 stateEventKey; + unsigned int currentTableLine; + + // + // First, clear the old values out + // + for( i = 0; i < musicEventTableLength; i++ ) + { + musicEventTable[i].scriptIndex = NO_INDEX; + } + + for( i = 0; i < MISSION_TABLE_SIZE; i++ ) + { + missionScriptTable[i].scriptIndex = NO_INDEX; + } + + for( i = 0; i < RACE_TABLE_SIZE; i++ ) + { + raceScriptTable[i].scriptIndex = NO_INDEX; + } + + for( i = 0; i < MATRIX_TABLE_SIZE; i++ ) + { + matrixStateTable[i].stateNameKey = 0; + matrixStateTable[i].stateIndex = NO_INDEX; + matrixStateTable[i].stateValueNameKey = 0; + matrixStateTable[i].stateValueIndex = NO_INDEX; + } + + // + // Now, go through each event in the script and search for a match + // in the tables. If one is found, fill in its index + // + scriptLength = radmusic::performance_num_events( m_musicPerformance ); + for( i = 0; i < scriptLength; i++ ) + { + radmusic::performance_event_name( m_musicPerformance, i, temp, 256 ); + tempKey = ::radMakeCaseInsensitiveKey32( temp ); + + found = false; + + // + // Check standard music events first + // + for( j = 0; j < musicEventTableLength; j++ ) + { + if( musicEventTable[j].nameKey == tempKey ) + { + musicEventTable[j].scriptIndex = i; + found = true; + break; + } + } + + // + // If not there, try mission events + // + if( !found ) + { + for( j = 0; j < missionScriptTableLength; j++ ) + { + if( missionScriptTable[j].nameKey == tempKey ) + { + missionScriptTable[j].scriptIndex = i; + found = true; + break; + } + } + } + + // + // Finally, try race events + // + if( !found ) + { + for( j = 0; j < raceScriptTableLength; j++ ) + { + if( raceScriptTable[j].nameKey == tempKey ) + { + raceScriptTable[j].scriptIndex = i; + found = true; + break; + } + } + } + + // + // If we get here without a match, Marc's got an event we're not using + // anywhere yet. Assert on this at some point once the script appears + // stabilized. + // + //rAssert( found ); + } + + // + // Now, build the matrix event table. Go through all the states, and make + // a table of all the state/event pairs + // + currentTableLine = 0; + + numStates = radmusic::performance_num_states( m_musicPerformance ); + for( i = 0; i < numStates; i++ ) + { + radmusic::performance_state_name( m_musicPerformance, i, temp, 256 ); + stateKey = ::radMakeCaseInsensitiveKey32( temp ); + + numStateEvents = radmusic::performance_num_state_values( m_musicPerformance, i ); + for( j = 0; j < numStateEvents; j++ ) + { + radmusic::performance_state_value_name( m_musicPerformance, i, j, temp, 256 ); + stateEventKey = ::radMakeCaseInsensitiveKey32( temp ); + + rAssert( currentTableLine < MATRIX_TABLE_SIZE ); + + matrixStateTable[currentTableLine].stateIndex = i; + matrixStateTable[currentTableLine].stateValueIndex = j; + matrixStateTable[currentTableLine].stateNameKey = stateKey; + matrixStateTable[currentTableLine++].stateValueNameKey = stateEventKey; + } + } + + matrixStateTableLength = currentTableLine; +} + +//============================================================================= +// MusicPlayer::triggerMusicMissionEventByName +//============================================================================= +// Description: Trigger a music event by matching the key to the event names +// in the mission event table +// +// Parameters: key - radKey for the name of the event to trigger +// +// Return: void +// +//============================================================================= +void MusicPlayer::triggerMusicMissionEventByName( radKey32* key ) +{ + unsigned int i; + + rAssert( key != NULL ); + + if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) ) + { + for( i = 0; i < missionScriptTableLength; i++ ) + { + if( missionScriptTable[i].nameKey == *key ) + { + if( missionScriptTable[i].scriptIndex != NO_INDEX ) + { + radmusic::performance_trigger_event( m_musicPerformance, + missionScriptTable[i].scriptIndex ); + } + break; + } + } + } +} + +//============================================================================= +// MusicPlayer::triggerMusicStateChange +//============================================================================= +// Description: Change the radMusic state +// +// Parameters: stateKey - name of state +// stateEventKey - name of event +// +// Return: void +// +//============================================================================= +void MusicPlayer::triggerMusicStateChange( radKey32 stateKey, radKey32 stateEventKey ) +{ + unsigned int i; + + // + // Look for a match in the matrix table + // + for( i = 0; i < matrixStateTableLength; i++ ) + { + if( ( matrixStateTable[i].stateNameKey == stateKey ) + && ( matrixStateTable[i].stateValueNameKey == stateEventKey ) ) + { + // + // Match, trigger the state change + // + radmusic::performance_state_value( m_musicPerformance, + matrixStateTable[i].stateIndex, + matrixStateTable[i].stateValueIndex ); + + // + // We need to trigger an event to prompt the change. Presumably + // we do this only for in-car stuff. + // + if( m_isInCar ) + { + TriggerMusicEvent( MEVENT_MISSION_START ); + } + break; + } + } + + if( i == matrixStateTableLength ) + { + rAssertMsg( false, "Mission script calling non-existent radMusic state" ); + } +} + +//============================================================================= +// MusicPlayer::triggerCurrentInteriorAmbience +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void MusicPlayer::triggerCurrentInteriorAmbience() +{ + int interiorIndex; + + interiorIndex = calculateInteriorIndex( GetInteriorManager()->GetInterior() ); + turnAmbienceOn( AEVENT_INTERIOR_KWIK_E_MART + interiorIndex ); +}
\ No newline at end of file diff --git a/game/code/sound/music/musicplayer.h b/game/code/sound/music/musicplayer.h new file mode 100644 index 0000000..046c0a3 --- /dev/null +++ b/game/code/sound/music/musicplayer.h @@ -0,0 +1,344 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: musicplayer.h +// +// Description: Declaration of the MusicPlayer class. Plays all music in +// the game using RadMusic. +// +// History: 17/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef MUSICPLAYER_H +#define MUSICPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <p3d/p3dtypes.hpp> + +#include <events/eventlistener.h> +#include <render/Enums/RenderEnums.h> + +//======================================== +// Forward References +//======================================== + +namespace Sound +{ + struct IDaSoundTuner; +} +namespace radmusic +{ + struct performance; + struct composition; +} + +class SoundFileHandler; +class radLoadRequest; +class Actor; + +// +// Events for interactive music. +// +enum MusicEventList +{ + MEVENT_MOVIE, + + MEVENT_PAUSE, + MEVENT_UNPAUSE, + + MEVENT_FE, + + MEVENT_LOADING_SCREEN, + MEVENT_NEWSPAPER_SPIN, + + MEVENT_SUPERSPRINT, + MEVENT_SUPERSPRINT_WIN, + MEVENT_SUPERSPRINT_LOSE, + + MEVENT_LEVEL_INTRO, + + MEVENT_SUNDAY_DRIVE_START, + MEVENT_SUNDAY_DRIVE_GET_OUT_OF_CAR, + + MEVENT_STORE, + + MEVENT_ENTER_STONECUTTER_TUNNEL, + MEVENT_EXIT_STONECUTTER_TUNNEL, + + MEVENT_OF_EXPLORE_MISSION, + MEVENT_OF_FOUND_CARD, + MEVENT_OF_TIME_OUT, + + MEVENT_APU_OASIS, + + MEVENT_INTERIOR_HOUSE, + MEVENT_INTERIOR_KWIK_E_MART, + MEVENT_INTERIOR_SCHOOL, + MEVENT_INTERIOR_MOES, + MEVENT_INTERIOR_DMV, + MEVENT_INTERIOR_ANDROID_DUNGEON, + MEVENT_INTERIOR_OBSERVATORY, + + MEVENT_WIN_3_RACES, + MEVENT_LEVEL_COMPLETED, + MEVENT_DESTROY_CAMERA_BONUS, + + MEVENT_STONECUTTER_SONG, + MEVENT_LBC_SONG, + MEVENT_DUFF_SONG, + + MEVENT_HIT_AND_RUN_START, + MEVENT_HIT_AND_RUN_CAUGHT, + + MEVENT_WASP_ATTACK, + + MEVENT_GATED_MISSION, + + MEVENT_SCARYMUSIC, + + MEVENT_CREDITS, + + MEVENT_END_STANDARD_EVENTS, + + MEVENT_MISSION_START, + MEVENT_MISSION_DRAMA, + MEVENT_WIN_MISSION, + MEVENT_LOSE_MISSION, + MEVENT_GET_OUT_OF_CAR, + MEVENT_10_SEC_TO_GO, + + MEVENT_END_MISSION_EVENTS, + + MEVENT_STREETRACE_START, + MEVENT_STREETRACE_WIN, + MEVENT_STREETRACE_LOSE, + MEVENT_STREETRACE_GET_OUT_OF_CAR, + + MEVENT_END_RACE_EVENTS, + + MEVENT_NUM_EVENTS +}; + +// +// Events for ambient sound script. Not actually stored anywhere, but it gives +// me some identifiers to play with +// +enum AmbientEventList +{ + AEVENT_FRONTEND, + + AEVENT_MOVIE, + AEVENT_PAUSE, + AEVENT_UNPAUSE, + + AEVENT_TRIGGER_START, + + AEVENT_TRIGGER_END = 73, + + AEVENT_INTERIOR_KWIK_E_MART, + AEVENT_INTERIOR_SCHOOL, + AEVENT_INTERIOR_HOUSE, + AEVENT_INTERIOR_KRUSTYLU, + AEVENT_INTERIOR_DMV, + + AEVENT_NUM_EVENTS +}; + +//============================================================================= +// +// Synopsis: MusicPlayer +// +//============================================================================= + +class MusicPlayer : public EventListener +{ + public: + MusicPlayer( Sound::IDaSoundTuner& tuner ); + virtual ~MusicPlayer(); + + void Service(); + + void QueueRadmusicScriptLoad(); + void QueueMusicLevelLoad( RenderEnums::LevelEnum level ); + + // + // Notify loading system when script file load complete + // + void LoadRadmusicScript( const char* filename, SoundFileHandler* fileHandler ); + void UnloadRadmusicScript(); + + // + // Look for notification when the player gets in and out of the car + // + void HandleEvent( EventEnum id, void* pEventData ); + + // + // Functions for notification of change in game state + // + void OnFrontEndStart(); + void OnFrontEndFinish(); + + void OnGameplayStart( bool playerInCar ); + void OnGameplayFinish(); + + void OnPauseStart(); + void OnPauseEnd(); + + void OnStoreStart(); + void OnStoreEnd(); + + void StopForMovie(); + void ResumeAfterMovie(); + + bool IsStoppedForMovie(); + + void RestartSupersprintMusic(); + + // + // Volume controls + // + float GetVolume(); + void SetVolume( float volume ); + float GetAmbienceVolume(); + void SetAmbienceVolume( float volume ); + + // + // Beat values + // + float GetBeatValue(); + + private: + //Prevent wasteful constructor creation. + MusicPlayer(); + MusicPlayer( const MusicPlayer& original ); + MusicPlayer& operator=( const MusicPlayer& rhs ); + + void SetUpPerformance( + radmusic::performance **, + radmusic::composition **, + const char * searchPath ); + + void TriggerMusicEvent( MusicEventList event ); + void TriggerAmbientEvent( unsigned int event ); + void triggerMusicMissionEventByName( radKey32* key ); + + unsigned int m_lastServiceTime; + + // Needed for polling the composition loader + bool m_isLoadingMusic; + + // True if player currently in car, false otherwise + bool m_isInCar; + + // Composition loader + + radLoadRequest* m_radLoadRequest; + char n_currentLoadName[ 64 ]; // hack for radload memory leak. + + // Performance object + radmusic::performance * m_musicPerformance; + radmusic::composition * m_musicComposition; + + // + // Ambient script stuff + // + bool m_isLoadingAmbient; + + radmusic::performance * m_ambientPerformance; + radmusic::composition * m_ambientComposition; + + // File load completion callback object + SoundFileHandler* m_loadCompleteCallback; + + // Current ambient sound + unsigned int m_currentAmbient; + bool m_ambiencePlaying; + + // + // Special music cases + // + bool m_onApuRooftop; + bool m_stoneCutterSong; + bool m_LBCSong; + + // + // Delayed music start + // + bool m_delayedMusicStart; + + // + // Wasp that triggered music + // + Actor* m_wasp; + + // + // Calculate offset for current level + // + int calculateLevelIndex(); + + // + // Calculate offset for current mission + // + int calculateMissionIndex(); + + // + // Calculate offset for interior we're about to enter + // + int calculateInteriorIndex( tUID interiorID ); + + // + // Returns flag indicating whether we use ambient on-foot stuff for + // this stage + // + bool musicLockedOnForStage(); + + // + // Trigger the events to start music + // + void startMusic(); + + // + // Figure out what to do after the mission ends + // + void playPostMissionSounds(); + + // + // Turn ambient sound on and off + // + void turnAmbienceOn( unsigned int event ); + void turnAmbienceOff( unsigned int event ); + + // + // True if current mission is a race, false otherwise + // + bool currentMissionIsRace(); + + // + // True if we're in Sunday Drive, false otherwise + // + bool currentMissionIsSundayDrive(); + + // + // Construct tables for faster lookup of music events in script + // + void initializeTableNameKeys(); + void buildEventTables(); + + // + // Change the radMusic state + // + void triggerMusicStateChange( radKey32 stateKey, radKey32 stateEventKey ); + + int findStandardMusicEvent( MusicEventList event ); + int findMissionEvent( MusicEventList event, int mission ); + int findRaceEvent( MusicEventList event, int race ); + + void triggerCurrentInteriorAmbience(); +}; + + +#endif // MUSICPLAYER_H + diff --git a/game/code/sound/nis/allnissound.cpp b/game/code/sound/nis/allnissound.cpp new file mode 100644 index 0000000..b943327 --- /dev/null +++ b/game/code/sound/nis/allnissound.cpp @@ -0,0 +1 @@ +#include <sound/nis/nissoundplayer.cpp> diff --git a/game/code/sound/nis/nissoundplayer.cpp b/game/code/sound/nis/nissoundplayer.cpp new file mode 100644 index 0000000..14d118e --- /dev/null +++ b/game/code/sound/nis/nissoundplayer.cpp @@ -0,0 +1,388 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: nissoundplayer.cpp +// +// Description: Implement NISSoundPlayer, which interacts with the dialog +// system to provide NIS sounds. +// +// History: 10/5/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +// Foundation Tech + +//======================================== +// Project Includes +//======================================== +#include <sound/nis/nissoundplayer.h> + +#include <sound/soundmanager.h> + +#include <events/eventmanager.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Table of NIS resources. +// + +NISPlayerGroup::NISPlayerGroup() : + m_soundID( 0 ), + m_loadCallback( NULL ), + m_playCallback( NULL ), + m_soundQueued( false ) +{ +} + +NISPlayerGroup::~NISPlayerGroup() +{ +} + +void NISPlayerGroup::LoadSound( radKey32 soundID, NISSoundLoadedCallback* callback ) +{ + // + // For NIS playback, don't use buffered data sources. Hopefully we + // won't be play these in situations where skipping is likely. If it + // happens, then we can experiment with short buffers, I suppose. + // Stinky limited IOP memory. + // + m_soundQueued = m_player.QueueSound( soundID, this ); + + if( m_soundQueued ) + { + m_loadCallback = callback; + m_soundID = soundID; + } +} + +void NISPlayerGroup::PlaySound( rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback ) +{ + radSoundVector position; + rmt::Vector midpoint; + + if( m_soundQueued ) + { + // + // Calculate position to play at + // + rAssert( box != NULL ); + midpoint = box->Mid(); + position.SetElements( midpoint.x, midpoint.y, midpoint.z ); + + m_playCallback = callback; + m_player.PlayQueuedSound( position, this ); + } +} + +void NISPlayerGroup::StopAndDumpSound() +{ + // Just in case. + m_loadCallback = NULL; + m_playCallback = NULL; + m_soundQueued = false; + m_soundID = 0; + + m_player.Stop(); +} + +bool NISPlayerGroup::IsSoundIDLoaded( radKey32 soundID ) +{ + return( m_soundID == soundID ); +} + +void NISPlayerGroup::Continue() +{ + if( m_player.IsPaused() ) + { + m_player.Continue(); + } +} + +//============================================================================= +// NISPlayerGroup::OnSoundReady +//============================================================================= +// Description: Called from m_player when sound is cued. Notify the +// callback object +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void NISPlayerGroup::OnSoundReady() +{ + if( m_loadCallback ) + { + m_loadCallback->NISSoundLoaded(); + m_loadCallback = NULL; + } +} + +//============================================================================= +// NISPlayerGroup::OnPlaybackComplete +//============================================================================= +// Description: Called from m_player when playback is done. Notify the +// callback object +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void NISPlayerGroup::OnPlaybackComplete() +{ + if( m_playCallback ) + { + m_playCallback->NISSoundPlaybackComplete(); + m_playCallback = NULL; + } + + m_soundID = 0; +} + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// NISSoundPlayer::NISSoundPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +NISSoundPlayer::NISSoundPlayer() : + m_currentFEGag( 0 ) +{ + // + // Register as an event listener + // + GetEventManager()->AddListener( this, EVENT_FE_GAG_INIT ); + GetEventManager()->AddListener( this, EVENT_FE_GAG_START ); + GetEventManager()->AddListener( this, EVENT_FE_GAG_STOP ); +} + +//============================================================================== +// NISSoundPlayer::~NISSoundPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +NISSoundPlayer::~NISSoundPlayer() +{ +} + +//============================================================================= +// NISSoundPlayer::LoadNISSound +//============================================================================= +// Description: Cue an NIS sound for playback +// +// Parameters: NISSound - sound to load +// callback - object to notify when loading is complete +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback ) +{ + unsigned int i; + + for( i = 0; i < NUM_NIS_PLAYERS; i++ ) + { + if( m_NISPlayers[i].IsFree() ) + { + m_NISPlayers[i].LoadSound( NISSoundID, callback ); + break; + } + } + + rAssert( i < NUM_NIS_PLAYERS ); +} + +//============================================================================= +// NISSoundPlayer::PlayNISSound +//============================================================================= +// Description: Play the cued NIS sound +// +// Parameters: callback - object to notify when playback is complete +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::PlayNISSound( radKey32 NISSoundID, rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback ) +{ + unsigned int i; + + if( NISSoundID == 0 ) + { + rDebugString( "Attempting to play NIS sound where no sound file specified. Ignored.\n" ); + } + else + { + for( i = 0; i < NUM_NIS_PLAYERS; i++ ) + { + if( m_NISPlayers[i].IsSoundIDLoaded( NISSoundID ) ) + { + m_NISPlayers[i].PlaySound( box, callback ); + break; + } + } + + if( i >= NUM_NIS_PLAYERS ) + { + rTuneString( "Attempting to play NIS sound which wasn't loaded. Tell Cory.\n" ); + //rTuneAssert( false ); + } + } +} + +//============================================================================= +// NISSoundPlayer::StopAndDumpNISSound +//============================================================================= +// Description: Stop the NIS sound if it's playing +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::StopAndDumpNISSound( radKey32 NISSoundID ) +{ + unsigned int i; + + for( i = 0; i < NUM_NIS_PLAYERS; i++ ) + { + if( m_NISPlayers[i].IsSoundIDLoaded( NISSoundID ) ) + { + m_NISPlayers[i].StopAndDumpSound(); + break; + } + } +} + +//============================================================================= +// NISSoundPlayer::PauseAllNISPlayers +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::PauseAllNISPlayers() +{ + unsigned int i; + + for( i = 0; i < NUM_NIS_PLAYERS; i++ ) + { + m_NISPlayers[i].Pause(); + } +} + +//============================================================================= +// NISSoundPlayer::ContinueAllNISPlayers +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::ContinueAllNISPlayers() +{ + unsigned int i; + + for( i = 0; i < NUM_NIS_PLAYERS; i++ ) + { + m_NISPlayers[i].Continue(); + } +} + +//============================================================================= +// NISSoundPlayer::HandleEvent +//============================================================================= +// Description: Take care of NIS events thrown by the front end. +// +// Parameters: id - event ID +// pEventData - FE gag name (radKey32) for EVENT_FE_GAG_INIT, unused for rest +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::HandleEvent( EventEnum id, void* pEventData ) +{ + rmt::Box3D box; + rmt::Vector low; + rmt::Vector high; + radKey32 gagName = 0; + + switch( id ) + { + case EVENT_FE_GAG_INIT: + gagName = reinterpret_cast<radKey32>( pEventData ); + loadFEGag( gagName ); + break; + + case EVENT_FE_GAG_START: + // + // Use a hardcoded position here for now. Not ideal, but the + // window isn't going anywhere soon. + // + low.Set( -2.04f, 1.9f, -0.2f ); + high.Set( -2.0f, 1.96f, -0.1f ); + box.Set( low, high ); + + PlayNISSound( m_currentFEGag, &box, NULL ); + break; + + case EVENT_FE_GAG_STOP: + StopAndDumpNISSound( m_currentFEGag ); + break; + + default: + rAssertMsg( false, "Huh? Shouldn't get an event here." ); + break; + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// NISSoundPlayer::loadFEGag +//============================================================================= +// Description: Find the appropriate FE gag and prep it for playback +// +// Parameters: gagIndex - index for gag, sent from front end +// +// Return: void +// +//============================================================================= +void NISSoundPlayer::loadFEGag( radKey32 gagKey ) +{ + m_currentFEGag = gagKey; + + LoadNISSound( gagKey, NULL ); +} diff --git a/game/code/sound/nis/nissoundplayer.h b/game/code/sound/nis/nissoundplayer.h new file mode 100644 index 0000000..e9475a6 --- /dev/null +++ b/game/code/sound/nis/nissoundplayer.h @@ -0,0 +1,115 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: nissoundplayer.h +// +// Description: Declare NISSoundPlayer, which interacts with the dialog +// system to provide NIS sounds. +// +// History: 10/5/2002 + Created -- Darren +// +//============================================================================= + +#ifndef NISSOUNDPLAYER_H +#define NISSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <radmath/radmath.hpp> + +#include <sound/nisenum.h> +#include <sound/positionalsoundplayer.h> + +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== +struct NISSoundLoadedCallback; +struct NISSoundPlaybackCompleteCallback; + +//============================================================================= +// +// Synopsis: NISSoundPlayer +// +//============================================================================= + +class NISPlayerGroup : public SimpsonsSoundPlayerCallback +{ + public: + NISPlayerGroup(); + virtual ~NISPlayerGroup(); + + void LoadSound( radKey32 soundID, NISSoundLoadedCallback* callback ); + void PlaySound( rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback ); + void StopAndDumpSound(); + void Pause() { m_player.Pause(); } + void Continue(); + + bool IsSoundIDLoaded( radKey32 soundID ); + bool IsFree() { return( m_soundID == 0 ); } + + // + // SimpsonsSoundPlayerCallback interface functions + // + void OnSoundReady(); + void OnPlaybackComplete(); + + protected: + private: + //Prevent wasteful constructor creation. + NISPlayerGroup( const NISPlayerGroup& original ); + NISPlayerGroup& operator=( const NISPlayerGroup& rhs ); + + PositionalSoundPlayer m_player; + + radKey32 m_soundID; + + NISSoundLoadedCallback* m_loadCallback; + NISSoundPlaybackCompleteCallback* m_playCallback; + + bool m_soundQueued; +}; + +//============================================================================= +// +// Synopsis: NISSoundPlayer +// +//============================================================================= + +class NISSoundPlayer : public EventListener +{ + public: + NISSoundPlayer(); + virtual ~NISSoundPlayer(); + + void LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback ); + void PlayNISSound( radKey32 NISSoundID, rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback ); + void StopAndDumpNISSound( radKey32 NISSoundID ); + + void PauseAllNISPlayers(); + void ContinueAllNISPlayers(); + + // + // EventListener functions + // + void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + NISSoundPlayer( const NISSoundPlayer& original ); + NISSoundPlayer& operator=( const NISSoundPlayer& rhs ); + + void loadFEGag( radKey32 gagKey ); + + static const unsigned int NUM_NIS_PLAYERS = 6; + + NISPlayerGroup m_NISPlayers[NUM_NIS_PLAYERS]; + + radKey32 m_currentFEGag; +}; + + +#endif // NISSOUNDPLAYER_H + diff --git a/game/code/sound/nisenum.h b/game/code/sound/nisenum.h new file mode 100644 index 0000000..f34949f --- /dev/null +++ b/game/code/sound/nisenum.h @@ -0,0 +1,41 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: nisenum.h +// +// Description: Enumeration for the NIS sounds +// +// History: 10/5/2002 + Created -- Darren +// +//============================================================================= + +#ifndef NISENUM_H +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: nisenum.h +// +// Description: Enumeration of NIS sounds +// +// History: + Created -- Darren +// +//============================================================================= + +#define NISENUM_H + +enum NISSoundEnum +{ + MOLEMAN_GAG, + GRAMPA_GAG, + FRINK_GAG, + BARNEY_GAG, + MOE_GAG, + SNAKE_GAG, + SEA_CAPTAIN_GAG, + DR_NICK_GAG, + + NIS_ENUM_COUNT +}; + +#endif // NISENUM_H + diff --git a/game/code/sound/positionalsoundplayer.cpp b/game/code/sound/positionalsoundplayer.cpp new file mode 100644 index 0000000..f3693eb --- /dev/null +++ b/game/code/sound/positionalsoundplayer.cpp @@ -0,0 +1,283 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: positionalsoundplayer.cpp +// +// Description: Implement PositionalSoundPlayer +// +// History: 12/18/2002 + Created -- NAME +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +#include <sound/soundrenderer/soundplayer.h> +//======================================== +// Project Includes +//======================================== +#include <sound/positionalsoundplayer.h> + +#include <sound/soundfx/positionalsoundsettings.h> + +#include <memory/srrmemory.h> + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +static const float POSITIONAL_PAUSE_BUFFER_DIST = 25.0f; + +//============================================================================= +// PositionCarrier::PositionCarrier +//============================================================================= +// Description: Constructor. +// +// Parameters: None +// +// Return: N/A +// +//============================================================================= +PositionCarrier::PositionCarrier() +{ +} + +//============================================================================= +// PositionCarrier::~PositionCarrier +//============================================================================= +// Description: Destructor +// +// Parameters: None +// +// Return: N/A +// +//============================================================================= +PositionCarrier::~PositionCarrier() +{ +} + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// PositionalSoundPlayer::PositionalSoundPlayer +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +PositionalSoundPlayer::PositionalSoundPlayer( ) : + m_positionCarrier( NULL ), + m_positionalSettings( NULL ), + m_minDist( 3.0f ), + m_maxDist( 100.f ), + m_position( 0.0f, 0.0f, 0.0f ), + m_outOfRange( false ) +{ + m_Type = Type_Positional; +} + +//============================================================================= +// PositionalSoundPlayer::~PositionalSoundPlayer +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +PositionalSoundPlayer::~PositionalSoundPlayer() +{ + delete m_positionCarrier; + + if( m_positionalSettings != NULL ) + { + m_positionalSettings->Release(); + m_positionalSettings = NULL; + } +} + +bool PositionalSoundPlayer::PlayResource( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback /* = NULL */) +{ + bool canPlay; + + canPlay = QueueSound( resource, callback ); + if( canPlay ) + { + // + // m_position should have been set in an earlier call to SetPosition. + // If not, then it should be a moving sound and should therefore + // get set later by the service function. + // + PlayQueuedSound( m_position, callback ); + } + + return( canPlay ); +} + +//============================================================================= +// PositionalSoundPlayer::PlayQueuedSound +//============================================================================= +// Description: Sets the position for the sound, then calls PlayQueuedSound +// in the SimpsonsSoundPlayer base class +// +// Parameters: position - position that sound is to be played at +// callback - object to inform when playback complete +// +// Return: void +// +//============================================================================= +void PositionalSoundPlayer::PlayQueuedSound( radSoundVector& position, + SimpsonsSoundPlayerCallback* callback ) +{ + // + // Before playing the sound, create a positional group and set the position. + // The group is reference counted, so the player will release it when + // the sound resource is released. + // + + if ( m_playa ) + { + radSoundVector velocity( 0.0f, 0.0f, 0.0f ); + + m_playa->SetPositionAndVelocity( & position, & velocity ); + m_playa->SetMinMaxDistance( m_minDist, m_maxDist ); + } + + SimpsonsSoundPlayer::PlayQueuedSound( callback ); +} + +void PositionalSoundPlayer::ServiceOncePerFrame() +{ + radSoundVector position; + radSoundVector velocity; + + radSoundVector distanceVector; + radSoundVector soundPosition; + radSoundVector listenerPosition; + float positionalMax; + float distance; + + // + // Update the positional group with new vehicle position + // + if( ( m_positionCarrier != NULL ) ) + { + m_positionCarrier->GetPosition( position ); + m_positionCarrier->GetVelocity( velocity ); + + if ( m_playa ) + { + m_playa->SetPositionAndVelocity( & position, & velocity ); + } + } + + // + // Do unpause/pause on players when they get in and out of range + // + if( ( m_playa != NULL ) && ( m_positionalSettings != NULL ) ) + { + ::radSoundHalListenerGet()->GetPosition( &listenerPosition ); + m_playa->GetPositionalGroup()->GetPosition( &soundPosition ); + distanceVector = listenerPosition - soundPosition; + distance = distanceVector.GetLength(); + + positionalMax = m_positionalSettings->GetMaxDistance(); + + if( m_outOfRange && ( distance <= ( positionalMax + POSITIONAL_PAUSE_BUFFER_DIST ) ) ) + { + if( IsPaused() ) + { + Continue(); + } + m_outOfRange = false; + } + else if( ( !m_outOfRange ) && ( distance > ( positionalMax + POSITIONAL_PAUSE_BUFFER_DIST ) ) ) + { + Pause(); + m_outOfRange = true; + } + } +} + +void PositionalSoundPlayer::SetPositionCarrier( PositionCarrier& movingSound ) +{ + m_positionCarrier = &movingSound; +} + +//============================================================================= +// PositionalSoundPlayer::SetParameters +//============================================================================= +// Description: Set the min/max and whatever else from the tunable +// sound settings object +// +// Parameters: settings - object containing sound settings +// +// Return: void +// +//============================================================================= +void PositionalSoundPlayer::SetParameters( positionalSoundSettings* settings ) +{ + m_minDist = settings->GetMinDistance(); + m_maxDist = settings->GetMaxDistance(); + + m_positionalSettings = settings; + m_positionalSettings->AddRef(); + + if( m_playa != NULL ) + { + m_playa->SetMinMaxDistance( m_minDist, m_maxDist ); + } +} + +//============================================================================= +// PositionalSoundPlayer::SetPosition +//============================================================================= +// Description: Set the position for the sound +// +// Parameters: x,y,z - position +// +// Return: void +// +//============================================================================= +void PositionalSoundPlayer::SetPosition( float x, float y, float z ) +{ + m_position.SetElements( x, y, z ); +} + +//***************************************************************************** +// +// Protected Member Functions +// +//***************************************************************************** + +void PositionalSoundPlayer::dumpSoundPlayer() +{ + // + // Get rid of the positional group + // + + if( m_positionalSettings != NULL ) + { + m_positionalSettings->Release(); + m_positionalSettings = NULL; + } + + m_positionCarrier = NULL; + + // + // Let the parent clean up now + // + SimpsonsSoundPlayer::dumpSoundPlayer(); +}
\ No newline at end of file diff --git a/game/code/sound/positionalsoundplayer.h b/game/code/sound/positionalsoundplayer.h new file mode 100644 index 0000000..dbc1032 --- /dev/null +++ b/game/code/sound/positionalsoundplayer.h @@ -0,0 +1,109 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: positionalsoundplayer.h +// +// Description: Declaration of wrapper class for playing positional sounds +// +// History: 12/18/2002 + Created -- Darren +// +//============================================================================= + +#ifndef POSITIONALSOUNDPLAYER_H +#define POSITIONALSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <radsoundmath.hpp> + +#include <sound/simpsonssoundplayer.h> + +//======================================== +// Forward References +//======================================== +class positionalSoundSettings; +struct IRadSoundHalPositionalGroup; + +//============================================================================= +// +// Synopsis: PositionCarrier +// +//============================================================================= + +class PositionCarrier +{ + public: + PositionCarrier(); + virtual ~PositionCarrier(); + + virtual void GetPosition( radSoundVector& position ) = 0; + virtual void GetVelocity( radSoundVector& velocity ) = 0; + + protected: + private: + //Prevent wasteful constructor creation. + PositionCarrier( const PositionCarrier& positioncarrier ); + PositionCarrier& operator=( const PositionCarrier& positioncarrier ); +}; + +//============================================================================= +// +// Synopsis: PositionalSoundPlayer +// +//============================================================================= + +class PositionalSoundPlayer : public SimpsonsSoundPlayer +{ + public: + PositionalSoundPlayer( ); + virtual ~PositionalSoundPlayer(); + + bool PlayResource( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback = NULL ); + void PlayQueuedSound( radSoundVector& position, + SimpsonsSoundPlayerCallback* callback = NULL ); + + void SetPositionCarrier( PositionCarrier& movingSound ); + void UnsetPositionCarrier(); + + void SetParameters( positionalSoundSettings* settings ); + positionalSoundSettings* GetParameters() { return( m_positionalSettings ); } + + void ServiceOncePerFrame(); + + void SetPosition( float x, float y, float z ); + + protected: + // + // Called when we're done with the sound renderer player object + // + void dumpSoundPlayer(); + + private: + //Prevent wasteful constructor creation. + PositionalSoundPlayer( const PositionalSoundPlayer& positionalsoundplayer ); + PositionalSoundPlayer& operator=( const PositionalSoundPlayer& positionalsoundplayer ); + + // + // Pointer to sound source, used only if source is moving + // + PositionCarrier* m_positionCarrier; + + positionalSoundSettings* m_positionalSettings; + + float m_minDist; + float m_maxDist; + + radSoundVector m_position; + + bool m_outOfRange; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //POSITIONALSOUNDPLAYER_H diff --git a/game/code/sound/simpsonssoundplayer.cpp b/game/code/sound/simpsonssoundplayer.cpp new file mode 100644 index 0000000..a5eb0c7 --- /dev/null +++ b/game/code/sound/simpsonssoundplayer.cpp @@ -0,0 +1,539 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: simpsonssoundplayer.cpp +// +// Description: Implement SimpsonsSoundPlayer class, which interacts with +// the sound renderer to play sounds, and tracks the player +// resources in use. +// +// History: 29/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/simpsonssoundplayer.h> + +#include <sound/soundmanager.h> +#include <sound/soundloader.h> +#include <sound/soundrenderercallback.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <sound/soundrenderer/playermanager.h> +#include <sound/soundrenderer/idasoundresource.h> + +#include <memory/srrmemory.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +unsigned int SimpsonsSoundPlayer::s_playersCreated = 0; +unsigned int SimpsonsSoundPlayer::s_clipPlayersInUse = 0; +unsigned int SimpsonsSoundPlayer::s_streamPlayersInUse = 0; +Sound::daSoundResourceManager* SimpsonsSoundPlayer::s_resourceManager = NULL; +Sound::daSoundPlayerManager* SimpsonsSoundPlayer::s_playerManager = NULL; +SoundLoader* SimpsonsSoundPlayer::s_soundLoader = NULL; + +// +// Limits on the number of players that can be playing clips/streams at once. +// Numbers are arbitrary and subject to experimentation +// +static const unsigned int s_maxActiveClipPlayersAllowed = 25; +static const unsigned int s_maxActiveStreamPlayersAllowed = 8; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SimpsonsSoundPlayer::SimpsonsSoundPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SimpsonsSoundPlayer::SimpsonsSoundPlayer() : + m_playa( NULL ), + m_callback( NULL ) + +{ + m_Type = Type_NonPositional; + // + // Get resource manager ptr and sound loader ptr if that hasn't been done yet + // + if( s_resourceManager == NULL ) + { + s_resourceManager = Sound::daSoundRenderingManagerGet()->GetResourceManager(); + } + + if( s_playerManager == NULL ) + { + s_playerManager = Sound::daSoundRenderingManagerGet()->GetPlayerManager(); + } + + if( s_soundLoader == NULL ) + { + s_soundLoader = SoundManager::GetInstance()->GetSoundLoader(); + } + + // + // Update statistics + // + ++s_playersCreated; +} + +//============================================================================== +// SimpsonsSoundPlayer::~SimpsonsSoundPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SimpsonsSoundPlayer::~SimpsonsSoundPlayer() +{ + // + // Update statistics + // + --s_playersCreated; + + if( m_callback != NULL ) + { + m_callback->CancelGameCallbackAndRelease(); + } + + rAssert( m_playa == NULL ); +} + +//============================================================================= +// SimpsonsSoundPlayer::PlaySound +//============================================================================= +// Description: Play the sound resource with the given name +// +// Parameters: resourceName - string with name of resource to play +// callback - optional callback on playback completion +// +// Return: true if sound could be played, false otherwise +// +//============================================================================= +bool SimpsonsSoundPlayer::PlaySound( const char* resourceName, + SimpsonsSoundPlayerCallback* callback ) +{ + return( PlaySound( ::radMakeKey32( resourceName ), callback ) ); +} + + +//============================================================================= +// SimpsonsSoundPlayer::PlayResource +//============================================================================= +// Description: Play the sound resource +// +// Parameters: resource - resource to play +// callback - object to notify when we're done +// +// Return: true if sound could be played, false otherwise +// +//============================================================================= +bool SimpsonsSoundPlayer::PlayResource( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback /* = NULL */ ) +{ + bool canPlay; + + canPlay = QueueSound( resource, callback ); + if( canPlay ) + { + PlayQueuedSound(); + } + + return( canPlay ); +} + +//============================================================================= +// SimpsonsSoundPlayer::PlaySound +//============================================================================= +// Description: Comment +// +// Parameters: resourceKey - hashed value of the name of the sound resource +// to play +// +// Return: true if sound could be played, false otherwise +// +//============================================================================= +bool SimpsonsSoundPlayer::PlaySound( Sound::daResourceKey resourceKey, + SimpsonsSoundPlayerCallback* callback ) +{ + IDaSoundResource* resource; + bool retVal; + + resource = s_resourceManager->FindResource( resourceKey ); + + if( resource != NULL ) + { + retVal = PlayResource( resource, callback ); + } + else + { + rDebugString( "Tried to play missing sound resource\n" ); + retVal = false; + } + + return( retVal ); +} + +//============================================================================= +// SimpsonsSoundPlayer::QueueSound +//============================================================================= +// Description: Queue up a sound for playback, but don't play it. Useful +// for streamed dialog +// +// Parameters: resourceName - name of sound resource to queue +// callback - playback completion callback, unused if NULL +// playUnbuffered - if streamer, don't use buffered data source +// +// Return: true if sound could be played, false otherwise +// +//============================================================================= +bool SimpsonsSoundPlayer::QueueSound( radKey32 resourceID, + SimpsonsSoundPlayerCallback* callback ) +{ + IDaSoundResource* resource = s_resourceManager->FindResource( resourceID ); + + if( resource != NULL ) + { + return( QueueSound( resource, callback ) ); + } + else + { + rDebugPrintf( "Couldn't play sound resource ID %d\n", resourceID ); + return( false ); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::QueueSound +//============================================================================= +// Description: Queue up a sound for playback, but don't play it. Useful +// for streamed dialog +// +// Parameters: resource - sound resource to queue +// callback - playback completion callback, unused if NULL +// playUnbuffered - if streamer, don't use buffered data source +// +// Return: true if sound could be played, false otherwise +// +//============================================================================= +bool SimpsonsSoundPlayer::QueueSound( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback ) +{ + if( m_playa != NULL ) + { + rDebugString( "Dropped sound, player busy\n" ); + return( false ); + } + + // + // Make sure we haven't maxed our limit on playback of this type of + // resource + // + if( resource->GetType() == IDaSoundResource::CLIP ) + { + if( s_clipPlayersInUse < s_maxActiveClipPlayersAllowed ) + { + ++s_clipPlayersInUse; + } + else + { + rAssertMsg( false, "Reached maximum allowable number of clip players\n" ); + return( false ); + } + } + else + { + rAssert( resource->GetType() == IDaSoundResource::STREAM ); + + if( s_streamPlayersInUse < s_maxActiveStreamPlayersAllowed ) + { + ++s_streamPlayersInUse; + } + else + { + rAssertMsg( false, "Reached maximum allowable number of stream players\n" ); + return( false ); + } + } + + s_playerManager->CaptureFreePlayer( &m_playa, + resource, + Type_Positional == m_Type ); + rAssert( m_playa != NULL ); + + // + // Reset trim, just to be safe + // + m_playa->SetExternalTrim( 1.0f ); + + // + // Create a callback object and point it toward whoever wants the callback + // + if( ( callback != NULL ) && ( m_callback == NULL ) ) + { + m_callback = new(GMA_TEMP) SoundRenderingPlayerCallback( *this, callback ); + m_playa->RegisterSoundPlayerStateCallback( m_callback, NULL ); + } + + return( true ); +} + +//============================================================================= +// SimpsonsSoundPlayer::PlayQueuedSound +//============================================================================= +// Description: Play the sound we've queued for playback +// +// Parameters: callback - callback to trigger on playback completion, ignored +// if NULL +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::PlayQueuedSound( SimpsonsSoundPlayerCallback* callback ) +{ + rAssert( m_playa != NULL ); + + // + // Play should not get stuck on pauses. If this causes problems, + // then I'll have to figure out how to make 100% sure that all + // paused streams (e.g. NIS) get unpaused in a proper fashion + // + if( m_playa->IsPaused() ) + { + m_playa->UberContinue(); + } + + m_playa->Play(); + + // + // If requested, create a callback object and point it toward whoever + // wants the callback + // + if( m_callback == NULL ) + { + m_callback = new(GMA_TEMP) SoundRenderingPlayerCallback( *this, callback ); + m_playa->RegisterSoundPlayerStateCallback( m_callback, NULL ); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::OnPlaybackComplete +//============================================================================= +// Description: Callback from the sound renderer callback object when the +// clip stops playback (not called for looping clips) +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::OnPlaybackComplete() +{ + dumpSoundPlayer(); +} + +//============================================================================= +// SimpsonsSoundPlayer::Stop +//============================================================================= +// Description: Stop playing sound. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::Stop() +{ + if( m_playa != NULL ) + { + // + // Stop() doesn't seem to play well with paused players. They tend + // to remain paused when you reuse them later. + // + if( m_playa->IsPaused() ) + { + m_playa->Continue(); + } + + m_playa->Stop(); + + if( m_playa != NULL ) + { + // + // The player didn't get released because we don't have + // a callback set (presumably), so we do it ourselves + // + dumpSoundPlayer(); + } + + rAssert( m_playa == NULL ); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::Pause +//============================================================================= +// Description: Pause the sound player +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::Pause() +{ + if( m_playa != NULL ) + { + m_playa->Pause(); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::Continue +//============================================================================= +// Description: Unpause the sound player +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::Continue() +{ + if( m_playa != NULL ) + { + m_playa->Continue(); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::IsPaused +//============================================================================= +// Description: Indicate whether the player is paused +// +// Parameters: None +// +// Return: true if paused, false if not or player unused +// +//============================================================================= +bool SimpsonsSoundPlayer::IsPaused() +{ + if( m_playa != NULL ) + { + return( m_playa->IsPaused() ); + } + else + { + return( false ); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::SetPitch +//============================================================================= +// Description: Set the pitch of the currently playing clip +// +// Parameters: pitch - pitch setting to apply +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::SetPitch( float pitch ) +{ + if( m_playa != NULL ) + { + m_playa->SetPitch( pitch ); + } + else + { + rDebugString( "Can't set pitch without associated player\n" ); + } +} + +//============================================================================= +// SimpsonsSoundPlayer::SetTrim +//============================================================================= +// Description: Set the trim of the currently playing clip +// +// Parameters: trim - trim setting to apply +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::SetTrim( float trim ) +{ + if( m_playa != NULL ) + { + m_playa->SetExternalTrim( trim ); + } + else + { + rDebugString( "Can't set trim without associated player\n" ); + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// SimpsonsSoundPlayer::dumpSoundPlayer +//============================================================================= +// Description: To be called when we're done with the sound renderer's player +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SimpsonsSoundPlayer::dumpSoundPlayer() +{ + if( m_callback ) + { + m_callback->CancelGameCallbackAndRelease(); + m_playa->UnregisterSoundPlayerStateCallback( m_callback, NULL ); + m_callback = NULL; + } + + if( m_playa->GetPlayerType() == IDaSoundResource::CLIP ) + { + --s_clipPlayersInUse; + } + else + { + rAssert( m_playa->GetPlayerType() != IDaSoundResource::UNKNOWN ); + --s_streamPlayersInUse; + } + + if( m_playa->IsCaptured() ) + { + m_playa->UnCapture(); + } + m_playa = NULL; +}
\ No newline at end of file diff --git a/game/code/sound/simpsonssoundplayer.h b/game/code/sound/simpsonssoundplayer.h new file mode 100644 index 0000000..98ad6b3 --- /dev/null +++ b/game/code/sound/simpsonssoundplayer.h @@ -0,0 +1,136 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: simpsonssoundplayer.h +// +// Description: Declaration for SimpsonsSoundPlayer class, which interacts with +// the sound renderer to play sounds, and tracks the player +// resources in use. +// +// History: 29/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SIMPSONSSOUNDPLAYER_H +#define SIMPSONSSOUNDPLAYER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundrenderer/soundsystem.h> + +//======================================== +// Forward References +//======================================== +namespace Sound +{ + class daSoundResourceManager; + class daSoundClipStreamPlayer; + class daSoundPlayerManager; +} + +struct IDaSoundResource; +class SoundLoader; +class SoundRenderingPlayerCallback; +struct IRadSoundHalPositionalGroup; + +//============================================================================= +// +// Synopsis: SimpsonsSoundPlayerCallback +// +//============================================================================= + +struct SimpsonsSoundPlayerCallback +{ + virtual void OnSoundReady() = 0; + virtual void OnPlaybackComplete() = 0; +}; + +//============================================================================= +// +// Synopsis: SimpsonsSoundPlayer +// +//============================================================================= + +class SimpsonsSoundPlayer +{ + public: + SimpsonsSoundPlayer(); + virtual ~SimpsonsSoundPlayer(); + + bool PlaySound( const char* resourceName, SimpsonsSoundPlayerCallback* callback = NULL ); + + bool PlaySound( Sound::daResourceKey resourceKey, SimpsonsSoundPlayerCallback* callback = NULL ); + + virtual bool PlayResource( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback = NULL ); + + bool QueueSound( const char* resourceName, + SimpsonsSoundPlayerCallback* callback = NULL ) + { return( QueueSound( ::radMakeKey32( resourceName ), callback ) ); } + + bool QueueSound( radKey32 resourceKey, + SimpsonsSoundPlayerCallback* callback = NULL ); + + bool QueueSound( IDaSoundResource* resource, + SimpsonsSoundPlayerCallback* callback = NULL ); + + virtual void PlayQueuedSound( SimpsonsSoundPlayerCallback* callback = NULL ); + + void Stop(); + void Pause(); + void Continue(); + bool IsPaused(); + + void OnPlaybackComplete(); + + bool IsInUse() { return( m_playa != NULL ); } + + void SetPitch( float pitch ); + void SetTrim( float trim ); + + protected: + + // + // Sound renderer's player object + // + Sound::daSoundClipStreamPlayer* m_playa; + + // + // Call when we're done with the sound renderer player object + // + virtual void dumpSoundPlayer(); + + protected: + + enum Type { Type_Positional, Type_NonPositional } m_Type; + + private: + //Prevent wasteful constructor creation. + SimpsonsSoundPlayer( const SimpsonsSoundPlayer& original ); + SimpsonsSoundPlayer& operator=( const SimpsonsSoundPlayer& rhs ); + + // + // Sound renderer resource manager + // + static Sound::daSoundResourceManager* s_resourceManager; + static Sound::daSoundPlayerManager* s_playerManager; + static SoundLoader* s_soundLoader; + + // + // Statistics on players in use + // + static unsigned int s_playersCreated; + static unsigned int s_clipPlayersInUse; + static unsigned int s_streamPlayersInUse; + + // + // Callback object for playback completion + // + SoundRenderingPlayerCallback* m_callback; + +}; + + +#endif // SIMPSONSSOUNDPLAYER_H + diff --git a/game/code/sound/soundcluster.cpp b/game/code/sound/soundcluster.cpp new file mode 100644 index 0000000..99e5d4f --- /dev/null +++ b/game/code/sound/soundcluster.cpp @@ -0,0 +1,276 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundcluster.cpp +// +// Description: Implement SoundCluster +// +// History: 26/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundcluster.h> + +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundresourcemanager.h> + +#include <loading/soundfilehandler.h> +#include <memory/srrmemory.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +static const radKey32 NULL_SOUND_KEY = 0; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundCluster::SoundCluster +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundCluster::SoundCluster( int clusterIndex, + IRadNameSpace* soundNamespace ) : + m_isLoaded( false ), + m_namespace( soundNamespace ), + m_loadCompleteCallbackObj( NULL ) +{ + int i; + + // + // Initialize the sound list to zeroes, which is hopefully an unlikely radKey32 value + // + for( i = 0; i < MAX_RESOURCES; i++ ) + { + m_soundList[i] = NULL_SOUND_KEY; + } + +#ifdef RAD_DEBUG + m_clusterIndex = clusterIndex; +#endif +} + +//============================================================================== +// SoundCluster::~SoundCluster +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundCluster::~SoundCluster() +{ +} + +//============================================================================= +// SoundCluster::LoadSounds +//============================================================================= +// Description: Loads the sounds listed in m_soundList +// +// Parameters: none +// +// Return: void +// +//============================================================================= +void SoundCluster::LoadSounds( SoundFileHandler* callbackObj /* = NULL */ ) +{ + IDaSoundResource* resource; + int i; + + MEMTRACK_PUSH_GROUP( "Sound" ); + + for( i = 0; i < MAX_RESOURCES; i++ ) + { + if( m_soundList[i] != NULL_SOUND_KEY ) + { + resource = reinterpret_cast< IDaSoundResource* > + ( + m_namespace->GetInstance( m_soundList[i] ) + ); + + if( resource != NULL ) + { + resource->CaptureResource(); + } +#ifdef RAD_DEBUG + else + { + // + // Sound not found, spew debug message + // + rDebugPrintf( "Sound resource #%d couldn't be found in cluster #%d\n", i, m_clusterIndex ); + } +#endif + } + } + + // + // Register for notification on load completion + // + Sound::daSoundRenderingManagerGet()->GetDynaLoadManager()->AddCompletionCallback( this, NULL ); + + m_loadCompleteCallbackObj = callbackObj; + + // + // Tell the resource manager that we're using this namespace + // + Sound::daSoundRenderingManagerGet()->GetResourceManager()->SetActiveResource( m_namespace ); + + MEMTRACK_POP_GROUP( "Sound" ); +} + +//============================================================================= +// SoundCluster::UnloadSounds +//============================================================================= +// Description: Unloads the sounds listed in m_soundList +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundCluster::UnloadSounds() +{ + IDaSoundResource* resource; + int i; + + for( i = 0; i < MAX_RESOURCES; i++ ) + { + if( m_soundList[i] != NULL_SOUND_KEY ) + { + resource = reinterpret_cast< IDaSoundResource* > + ( + m_namespace->GetInstance( m_soundList[i] ) + ); + + if( resource != NULL ) + { + resource->ReleaseResource(); + } +#ifdef RAD_DEBUG + else + { + // + // Sound not found, spew debug message + // + rDebugPrintf( "Tried to free unloaded sound resource #%d in cluster #%d\n", i, m_clusterIndex ); + } +#endif + } + } + + // + // Tell the resource manager that we're no longer using this namespace + // + Sound::daSoundRenderingManagerGet()->GetResourceManager()->ReleaseActiveResource( m_namespace ); + + + m_isLoaded = false; +} + +//============================================================================= +// SoundCluster::AddResource +//============================================================================= +// Description: Add given sound resource to the list of resources associated +// with this cluster +// +// Parameters: resourceKey - radKey32 crunched from sound resource name +// +// Return: true if we could add it to the list, false otherwise +// +//============================================================================= +bool SoundCluster::AddResource( Sound::daResourceKey resourceKey ) +{ + int i; + bool added = false; + + for( i = 0; i < MAX_RESOURCES; i++ ) + { + if( m_soundList[i] == NULL_SOUND_KEY ) + { + m_soundList[i] = resourceKey; + added = true; + break; + } + } + + return( added ); +} + +//============================================================================= +// SoundCluster::ContainsResource +//============================================================================= +// Description: Search the sound list and return a bool indicating whether +// this sound cluster contains the indicated resource +// +// Parameters: resourceKey - hashed name of resource to search for +// +// Return: true if resource present, false otherwise +// +//============================================================================= +bool SoundCluster::ContainsResource( Sound::daResourceKey resourceKey ) +{ + int i; + + rAssert( resourceKey != NULL_SOUND_KEY ); + + for( i = 0; i < MAX_RESOURCES; i++ ) + { + if( m_soundList[i] == resourceKey ) + { + return( true ); + } + } + + return( false ); +} + +//============================================================================= +// SoundCluster::OnDynaLoadOperationsComplete +//============================================================================= +// Description: Called when the sound renderer is finished loading the cluster +// +// Parameters: pUserData - user data, unused +// +// Return: void +// +//============================================================================= +void SoundCluster::OnDynaLoadOperationsComplete( void* pUserData ) +{ + m_isLoaded = true; + if( m_loadCompleteCallbackObj != NULL ) + { + m_loadCompleteCallbackObj->LoadCompleted(); + m_loadCompleteCallbackObj = NULL; + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundcluster.h b/game/code/sound/soundcluster.h new file mode 100644 index 0000000..2abec0c --- /dev/null +++ b/game/code/sound/soundcluster.h @@ -0,0 +1,103 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundcluster.h +// +// Description: Declaration of SoundCluster class. Used to allocate a group +// of related sounds in sound memory. This is an abstract base +// class, the subclasses are used to differentiate between clips +// and streams. +// +// History: 26/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDCLUSTER_H +#define SOUNDCLUSTER_H + +//======================================== +// Nested Includes +//======================================== + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/sounddynaload.h> + +//======================================== +// Forward References +//======================================== + +class SoundFileHandler; +struct IRadNameSpace; + +//============================================================================= +// +// Synopsis: SoundCluster +// +//============================================================================= + +class SoundCluster : public Sound::IDaSoundDynaLoadCompletionCallback, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "SoundCluster" ); + + SoundCluster( int clusterIndex, + IRadNameSpace* soundNamespace ); + virtual ~SoundCluster(); + + bool AddResource( const char* resourceName ) + { return( AddResource( ::radMakeKey32( resourceName ) ) ); } + bool AddResource( Sound::daResourceKey resourceKey ); + + bool IsLoaded() const { return m_isLoaded; } + + void LoadSounds( SoundFileHandler* callbackObj = NULL ); + void UnloadSounds(); + + bool ContainsResource( const char* resourceName ) + { return ContainsResource( ::radMakeKey32( resourceName ) ); } + bool ContainsResource( Sound::daResourceKey resourceKey ); + + IRadNameSpace* GetMyNamespace() { return( m_namespace ); } + + // + // Interface for sound renderer load completion callback + // + void OnDynaLoadOperationsComplete( void* pUserData ); + + private: + //Prevent wasteful constructor creation. + SoundCluster(); + SoundCluster( const SoundCluster& original ); + SoundCluster& operator=( const SoundCluster& rhs ); + + static const int MAX_RESOURCES = 80; + + // + // True if the sounds for this cluster are allocated in sound memory + // + bool m_isLoaded; + + // + // List of sound clips + // + Sound::daResourceKey m_soundList[MAX_RESOURCES]; + + // + // Namespace containing these resources + // + IRadNameSpace* m_namespace; + + // + // Callback object on load completion + // + SoundFileHandler* m_loadCompleteCallbackObj; + +#ifdef RAD_DEBUG + int m_clusterIndex; +#endif +}; + + +#endif // SOUNDCLUSTER_H + diff --git a/game/code/sound/soundclusternameenum.h b/game/code/sound/soundclusternameenum.h new file mode 100644 index 0000000..daa6fcf --- /dev/null +++ b/game/code/sound/soundclusternameenum.h @@ -0,0 +1,51 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundclusternameenum.h +// +// Description: Definition for SoundClusterName enum +// +// History: 11/18/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDCLUSTERNAMEENUM_H +#define SOUNDCLUSTERNAMEENUM_H + +#include <constants/vehicleenum.h> + +enum SoundClusterName +{ + SC_ALWAYS_LOADED, + SC_FRONTEND, + SC_INGAME, + + SC_LEVEL_SUBURBS, + SC_LEVEL_DOWNTOWN, + SC_LEVEL_SEASIDE, + + SC_LEVEL1, + SC_LEVEL2, + SC_LEVEL3, + SC_LEVEL4, + SC_LEVEL5, + SC_LEVEL6, + SC_LEVEL7, + SC_MINIGAME, + + SC_NEVER_LOADED, + + SC_CHAR_APU, + SC_CHAR_BART, + SC_CHAR_HOMER, + SC_CHAR_LISA, + SC_CHAR_MARGE, + + SC_CAR_BASE, + + SC_MAX_CLUSTERS = SC_CAR_BASE + VehicleEnum::NUM_VEHICLES // SC_CAR_BASE + 53 cars +}; + + +#endif // SOUNDCLUSTERNAMEENUM_H + diff --git a/game/code/sound/soundcollisiondata.h b/game/code/sound/soundcollisiondata.h new file mode 100644 index 0000000..2a986bf --- /dev/null +++ b/game/code/sound/soundcollisiondata.h @@ -0,0 +1,58 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundcollisiondata.h +// +// Description: Declaration for SoundCollisionData structure, which carries +// collision information through the event manager to the sound +// system +// +// History: 04/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDCOLLISIONDATA_H +#define SOUNDCOLLISIONDATA_H + +//======================================== +// Nested Includes +//======================================== +#include <render/DSG/collisionentitydsg.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: SoundCollisionData +// +//============================================================================= + +class SoundCollisionData +{ + public: + SoundCollisionData( float intensity, CollisionEntityDSG* objA, CollisionEntityDSG* objB ) + { + //impulse = vector; + mIntensity = intensity; + collObjA = objA; + collObjB = objB; + } + ~SoundCollisionData() {} + + float mIntensity; // 0.0f - 1.0f + CollisionEntityDSG* collObjA; + CollisionEntityDSG* collObjB; + + private: + // + // In this case, allow the shallow-copy assignment and copy constructors. + // We don't want the default constructor, though. + // + SoundCollisionData(); +}; + + +#endif // SOUNDCOLLISIONDATA_H + diff --git a/game/code/sound/sounddebug/allsounddebug.cpp b/game/code/sound/sounddebug/allsounddebug.cpp new file mode 100644 index 0000000..de95350 --- /dev/null +++ b/game/code/sound/sounddebug/allsounddebug.cpp @@ -0,0 +1,2 @@ +#include <sound/sounddebug/sounddebugdisplay.cpp> +#include <sound/sounddebug/sounddebugpage.cpp> diff --git a/game/code/sound/sounddebug/sounddebugdisplay.cpp b/game/code/sound/sounddebug/sounddebugdisplay.cpp new file mode 100644 index 0000000..b31a7e9 --- /dev/null +++ b/game/code/sound/sounddebug/sounddebugdisplay.cpp @@ -0,0 +1,438 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddebugdisplay.cpp +// +// Description: Implement SoundDebugDisplay +// +// History: 10/26/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <pddi/pddi.hpp> +#include <p3d/utility.hpp> + +#include <raddebugwatch.hpp> +#include <radsound_hal.hpp> +#include <radtypeinfo.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/sounddebug/sounddebugdisplay.h> + +#include <sound/sounddebug/sounddebugpage.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/playermanager.h> + +#include <render/IntersectManager/IntersectManager.h> +#include <render/DSG/StaticPhysDSG.h> +#include <render/DSG/DynaPhysDSG.h> +#include <render/DSG/animcollisionentitydsg.h> +#include <worldsim/avatarmanager.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** +// +// Watcher tunables +// +bool SoundDebugDisplay::s_isVisible = false; +int SoundDebugDisplay::s_red = 255; +int SoundDebugDisplay::s_green = 255; +int SoundDebugDisplay::s_blue = 0; +// Text position +int SoundDebugDisplay::s_leftOffset = 0; +int SoundDebugDisplay::s_topOffset = 0; +// Displayed page +unsigned int SoundDebugDisplay::s_page = 0; +bool SoundDebugDisplay::s_dumpToWindow = false; +// Name display radius +float SoundDebugDisplay::s_radius = 2.0f; +// Type info +bool SoundDebugDisplay::s_dumpTypeInfoToWindow = false; + +static const int NUM_VISIBLE_LINES = 15; +static const int NUM_ENTITIES = ( NUM_VISIBLE_LINES * SoundDebugDisplay::MAX_DEBUG_PAGES ); + +static const int LEFT = 40; +static const int TOP = 40; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundDebugDisplay::SoundDebugDisplay +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundDebugDisplay::SoundDebugDisplay( Sound::daSoundRenderingManager* soundMgr ) : + m_renderMgr( soundMgr ) +{ + int i; + const char* sectionName = "Sound Info"; + + // + // Register the watcher variables + // + radDbgWatchAddBoolean( &s_isVisible, "Set Visibility", sectionName, 0, 0 ); + radDbgWatchAddBoolean( &s_dumpToWindow, "Dump To Window", sectionName, 0, 0 ); + radDbgWatchAddUnsignedInt( &s_page, "Select Page", sectionName, 0, 0, 0, MAX_DEBUG_PAGES - 1 ); + radDbgWatchAddInt( &s_leftOffset, "Left Position", sectionName, 0, 0, -1000, 1000 ); + radDbgWatchAddInt( &s_topOffset, "Top Position", sectionName, 0, 0, -1000, 1000 ); + radDbgWatchAddInt( &s_red, "Text - Red", sectionName, 0, 0, 0, 255 ); + radDbgWatchAddInt( &s_green, "Text - Green", sectionName, 0, 0, 0, 255 ); + radDbgWatchAddInt( &s_blue, "Text - Blue", sectionName, 0, 0, 0, 255 ); + radDbgWatchAddFloat( &s_radius, "Name Display Radius", sectionName, 0, 0, 1.0f, 10.0f ); +#ifdef RAD_DEBUG + // RadScript only provides this capability in debug + radDbgWatchAddBoolean( &s_dumpTypeInfoToWindow, "Dump Type Info To Window", sectionName ); +#endif + + for( i = 0; i < MAX_DEBUG_PAGES; i++ ) + { + m_debugPages[i] = NULL; + } +} + +//============================================================================== +// SoundDebugDisplay::~SoundDebugDisplay +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundDebugDisplay::~SoundDebugDisplay() +{ + radDbgWatchDelete( &s_isVisible ); + radDbgWatchDelete( &s_dumpToWindow ); + radDbgWatchDelete( &s_page ); + radDbgWatchDelete( &s_leftOffset ); + radDbgWatchDelete( &s_topOffset ); + radDbgWatchDelete( &s_red ); + radDbgWatchDelete( &s_green ); + radDbgWatchDelete( &s_blue ); + radDbgWatchDelete( &s_radius ); +#ifdef RAD_DEBUG + radDbgWatchDelete( &s_dumpTypeInfoToWindow ); +#endif +} + +//============================================================================= +// SoundDebugDisplay::RegisterPage +//============================================================================= +// Description: Register a debug page for display +// +// Parameters: page - page to register +// +// Return: void +// +//============================================================================= +void SoundDebugDisplay::RegisterPage( SoundDebugPage* page ) +{ + int i; + + for( i = 0; i < MAX_DEBUG_PAGES; i++ ) + { + if( m_debugPages[i] == NULL ) + { + m_debugPages[i] = page; + break; + } + } +} + +//============================================================================= +// SoundDebugDisplay::DeregisterPage +//============================================================================= +// Description: Deregister a debug page for display +// +// Parameters: page - page to deregister +// +// Return: void +// +//============================================================================= +void SoundDebugDisplay::DeregisterPage( SoundDebugPage* page ) +{ + int i; + + for( i = 0; i < MAX_DEBUG_PAGES; i++ ) + { + if( m_debugPages[i] == page ) + { + m_debugPages[i] = NULL; + break; + } + } +} + +//============================================================================= +// SoundDebugDisplay::Render +//============================================================================= +// Description: Draw the sound debug info +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundDebugDisplay::Render() +{ + if ( Sound::daSoundRenderingManager::GetInstance( ) ) + { + Sound::daSoundRenderingManager::GetInstance( )->Render( ); + } + +#ifndef RAD_RELEASE + Avatar* theAvatar; + rmt::Vector position; + rmt::Vector* positionPtr = NULL; + int i; + tColour stringColour; + + if( s_dumpTypeInfoToWindow ) + { + s_dumpTypeInfoToWindow = false; +#ifdef RADSCRIPT_DEBUG + ::radTypeInfoSystemGet()->DebugDump(); +#endif + } + + if( !s_isVisible ) + { + return; + } + + // + // The new, good way + // + if( s_page > 1 ) + { + stringColour = tColour(s_red, s_green, s_blue); + + for( i = 0; i < MAX_DEBUG_PAGES; i++ ) + { + if( ( m_debugPages[i] != NULL ) + && ( m_debugPages[i]->GetPage() == s_page ) ) + { + m_debugPages[i]->Render( LEFT + s_leftOffset, TOP + s_topOffset, + stringColour, s_dumpToWindow ); + break; + } + } + } + else + { + // + // The old, bad way + // + + // + // Get the avatar position + // + theAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 ); + if( theAvatar != NULL ) + { + // + // Presumably we're in game if we get here + // + theAvatar->GetPosition( position ); + positionPtr = &position; + } + + if( s_page == 0 ) + { + renderPositionAndHeapInfo( positionPtr ); + } + else + { + renderNearbyObjectNames( positionPtr ); + } + } +#endif +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +void SoundDebugDisplay::renderPositionAndHeapInfo( rmt::Vector* position ) +{ +#ifndef RAD_RELEASE + char buffy[128]; + tColour stringColour = tColour(s_red, s_green, s_blue); + IRadSoundHalSystem::Stats radSoundStats; + + // + // Display the avatar position + // + if( position != NULL ) + { + sprintf( buffy, "Avatar posn: %f %f %f", position->x, position->y, position->z ); + renderTextLine( buffy, LEFT, TOP, stringColour ); + } + + // + // Display the radSound memory stats + // + strcpy( buffy, "RadSound stats:" ); + renderTextLine( buffy, LEFT, TOP + 20, stringColour ); + + ::radSoundHalSystemGet()->GetStats( &radSoundStats ); + sprintf( buffy, "Buffers: %d Voices: %d/%d PosVoices: %d/%d", + radSoundStats.m_NumBuffers, + radSoundStats.m_NumVoicesPlaying, + radSoundStats.m_NumVoices, + radSoundStats.m_NumPosVoicesPlaying, + radSoundStats.m_NumPosVoices ); + renderTextLine( buffy, LEFT, TOP + 40, stringColour ); + + sprintf( buffy, "Buffer memory used: %d Effects memory used: %d", + radSoundStats.m_BufferMemoryUsed, + radSoundStats.m_EffectsMemoryUsed ); + renderTextLine( buffy, LEFT, TOP + 60, stringColour ); + + sprintf( buffy, "Sound memory free: %d", + radSoundStats.m_TotalFreeSoundMemory ); + renderTextLine( buffy, LEFT, TOP + 80, stringColour ); + + sprintf( buffy, "Sound renderer stats:" ); + renderTextLine( buffy, LEFT, TOP + 120, stringColour ); + + sprintf( buffy, "Clip players used: %d", m_renderMgr->GetPlayerManager()->GetNumUsedClipPlayers() ); + renderTextLine( buffy, LEFT, TOP + 140, stringColour ); + + sprintf( buffy, "Stream players used: %d", m_renderMgr->GetPlayerManager()->GetNumUsedStreamPlayers() ); + renderTextLine( buffy, LEFT, TOP + 160, stringColour ); + + s_dumpToWindow = false; +#endif +} + +void SoundDebugDisplay::renderNearbyObjectNames( rmt::Vector* position ) +{ +#ifndef RAD_RELEASE + char buffy[128]; + tColour stringColour = tColour(s_red, s_green, s_blue); + const char* noSound = "NO SOUND"; + ReserveArray<StaticPhysDSG*> statics; + ReserveArray<DynaPhysDSG*> dynamics; + ReserveArray<AnimCollisionEntityDSG*> animatics; + CollisionEntityDSG* collEntityArray[NUM_ENTITIES]; + int arrayIndex = 0; + int i; + + if( position == NULL ) + { + // + // No character to find nearby objects for, draw nothing + // + return; + } + + // + // Get the intersect goodness + // + GetIntersectManager()->FindStaticPhysElems( *position, s_radius, statics ); + GetIntersectManager()->FindDynaPhysElems( *position, s_radius, dynamics ); + GetIntersectManager()->FindAnimPhysElems( *position, s_radius, animatics ); + + // + // Populate the entity array. Simplifies the code slightly + // to do it this way. This is debug stuff, doesn't need to be + // pretty. + // + for( i = 0; i < statics.mUseSize; i++ ) + { + if( arrayIndex >= NUM_ENTITIES ) + { + break; + } + collEntityArray[arrayIndex] = statics[i]; + ++arrayIndex; + } + for( i = 0; i < dynamics.mUseSize; i++ ) + { + if( arrayIndex >= NUM_ENTITIES ) + { + break; + } + collEntityArray[arrayIndex] = dynamics[i]; + ++arrayIndex; + } + for( i = 0; i < animatics.mUseSize; i++ ) + { + if( arrayIndex >= NUM_ENTITIES ) + { + break; + } + collEntityArray[arrayIndex] = animatics[i]; + ++arrayIndex; + } + + // + // Fill the rest of the array with NULLs + // + for( i = arrayIndex; i < NUM_ENTITIES; i++ ) + { + collEntityArray[i] = NULL; + } + + int startIndex = (s_page - 1) * NUM_VISIBLE_LINES; + int endIndex = startIndex + NUM_VISIBLE_LINES; + + for( i = startIndex; i < endIndex; ++i ) + { + + if( collEntityArray[i] == NULL ) + { + break; + } + + if( collEntityArray[i]->GetCollisionAttributes() == NULL ) + { + sprintf( buffy, "%-32s\t%-32s", collEntityArray[i]->GetName(), noSound ); + } + else + { + sprintf( buffy, "%-32s\t%-32s", collEntityArray[i]->GetName(), + collEntityArray[i]->GetCollisionAttributes()->GetSound() ); + } + + renderTextLine( buffy, LEFT, TOP + ((i % NUM_VISIBLE_LINES)*20), stringColour ); + } + + s_dumpToWindow = false; +#endif +} + +void SoundDebugDisplay::renderTextLine( const char* text, int leftPosn, + int topPosn, tColour& colour ) +{ + p3d::pddi->DrawString( text, leftPosn + s_leftOffset, topPosn + s_topOffset, colour ); + + if ( s_dumpToWindow ) + { + rDebugString( text ); + } +} diff --git a/game/code/sound/sounddebug/sounddebugdisplay.h b/game/code/sound/sounddebug/sounddebugdisplay.h new file mode 100644 index 0000000..170f5f1 --- /dev/null +++ b/game/code/sound/sounddebug/sounddebugdisplay.h @@ -0,0 +1,86 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddebugdisplay.h +// +// Description: Declaration for the SoundDebugDisplay class, used for +// printing debugging/tuning information on screen +// +// History: 10/26/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDDEBUGDISPLAY_H +#define SOUNDDEBUGDISPLAY_H + +//======================================== +// Nested Includes +//======================================== +#include <radmath/radmath.hpp> + +//======================================== +// Forward References +//======================================== +class SoundDebugPage; + +namespace Sound +{ + class daSoundRenderingManager; +} + + +//============================================================================= +// +// Synopsis: SoundDebugDisplay +// +//============================================================================= + +class SoundDebugDisplay +{ + public: + SoundDebugDisplay( Sound::daSoundRenderingManager* renderMgr ); + virtual ~SoundDebugDisplay(); + + void Render(); + + void RegisterPage( SoundDebugPage* page ); + void DeregisterPage( SoundDebugPage* page ); + + static const int MAX_DEBUG_PAGES = 5; + + // + // Watcher tunables + // + static bool s_isVisible; + static int s_red; + static int s_green; + static int s_blue; + // Text position + static int s_leftOffset; + static int s_topOffset; + // Displayed page + static unsigned int s_page; + static bool s_dumpToWindow; + // Name display radius + static float s_radius; + // Type info + static bool s_dumpTypeInfoToWindow; + + private: + //Prevent wasteful constructor creation. + SoundDebugDisplay( const SoundDebugDisplay& original ); + SoundDebugDisplay& operator=( const SoundDebugDisplay& rhs ); + + void renderPositionAndHeapInfo( rmt::Vector* position ); + void renderNearbyObjectNames( rmt::Vector* position ); + + void renderTextLine( const char* text, int leftPosn, int topPosn, tColour& colour ); + + SoundDebugPage* m_debugPages[MAX_DEBUG_PAGES]; + + Sound::daSoundRenderingManager* m_renderMgr; +}; + + +#endif // SOUNDDEBUGDISPLAY_H + diff --git a/game/code/sound/sounddebug/sounddebugpage.cpp b/game/code/sound/sounddebug/sounddebugpage.cpp new file mode 100644 index 0000000..70aee13 --- /dev/null +++ b/game/code/sound/sounddebug/sounddebugpage.cpp @@ -0,0 +1,161 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddebugpage.cpp +// +// Description: Implementation of SoundDebugPage class, which is used for +// displaying a chunk of debug info in Watcher (or whatever +// our display mechanism happens to be) +// +// History: 11/22/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <p3d/utility.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/sounddebug/sounddebugpage.h> + +#include <sound/sounddebug/sounddebugdisplay.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundDebugPage::SoundDebugPage +//============================================================================== +// Description: Constructor. +// +// Parameters: None +// +// Return: N/A. +// +//============================================================================== +SoundDebugPage::SoundDebugPage() : + m_pageNum( 0 ), + m_displayMaster( NULL ) +{ + // + // Presumably, we're saving full initialization for later. + // + +#ifndef SOUND_DEBUG_INFO_ENABLED + // + // Don't want to build these in release, so make it die horribly. + // + rReleaseAssertMsg( false, "Oops, shouldn't be storing sound debug info in release" ); +#endif +} + +//============================================================================== +// SoundDebugPage::SoundDebugPage +//============================================================================== +// Description: Constructor. +// +// Parameters: pageNum - page we'll display info on in Watcher's SoundInfo. +// +// Return: N/A. +// +//============================================================================== +SoundDebugPage::SoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) : +m_pageNum( pageNum ), +m_displayMaster( master ) +{ +#ifndef SOUND_DEBUG_INFO_ENABLED + // + // Don't want to build these in release, so make it die horribly. + // + rReleaseAssertMsg( false, "Oops, shouldn't be storing sound debug info in release" ); +#endif + + // + // Tell debug display master about our existence + // + master->RegisterPage( this ); +} + +//============================================================================== +// SoundDebugPage::~SoundDebugPage +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundDebugPage::~SoundDebugPage() +{ + m_displayMaster->DeregisterPage( this ); +} + +//============================================================================= +// SoundDebugPage::LazyInitialization +//============================================================================= +// Description: Comment +// +// Parameters: ( unsigned int pageNum, SoundDebugDisplay* master ) +// +// Return: void +// +//============================================================================= +void SoundDebugPage::LazyInitialization( unsigned int pageNum, SoundDebugDisplay* master ) +{ + m_pageNum = pageNum; + m_displayMaster = master; + + m_displayMaster->RegisterPage( this ); +} + +//============================================================================= +// SoundDebugPage::Render +//============================================================================= +// Description: Draw our debug info. This relies on subclasses providing the +// info and telling us how many lines to draw +// +// Parameters: leftPosn - leftmost horizontal position to write text to +// topPosn - topmost vertical position to write text to +// colour - colour of pddi text to use +// dumpToWindow - also write text as debug string if true +// +// Return: void +// +//============================================================================= +void SoundDebugPage::Render( int leftPosn, int topPosn, tColour& colour, bool dumpToWindow ) +{ + int i; + char lineBuffer[255]; + int numLines = getNumLines(); + + for( i = 0; i < numLines; i++ ) + { + fillLineBuffer( i, lineBuffer ); + + p3d::pddi->DrawString( lineBuffer, leftPosn, topPosn + (i * 20), colour ); + + if ( dumpToWindow ) + { + rDebugString( lineBuffer ); + } + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/sounddebug/sounddebugpage.h b/game/code/sound/sounddebug/sounddebugpage.h new file mode 100644 index 0000000..088f14d --- /dev/null +++ b/game/code/sound/sounddebug/sounddebugpage.h @@ -0,0 +1,67 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddebugpage.h +// +// Description: Declaration of SoundDebugPage class, which is used for displaying +// a chunk of debug info in Watcher (or whatever our display +// mechanism happens to be) +// +// History: 11/22/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDDEBUGPAGE_H +#define SOUNDDEBUGPAGE_H + +//======================================== +// Nested Includes +//======================================== +#include <p3d/p3dtypes.hpp> + +//======================================== +// Forward References +//======================================== +class SoundDebugDisplay; + +#ifndef RAD_RELEASE +#define SOUND_DEBUG_INFO_ENABLED +#endif + +//============================================================================= +// +// Synopsis: SoundDebugPage +// +//============================================================================= + +class SoundDebugPage +{ + public: + SoundDebugPage(); + SoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ); + virtual ~SoundDebugPage(); + + void LazyInitialization( unsigned int pageNum, SoundDebugDisplay* master ); + + unsigned int GetPage() { return( m_pageNum ); } + + void Render( int leftPosn, int topPosn, tColour& colour, bool dumpToWindow ); + + protected: + + virtual void fillLineBuffer( int lineNum, char* buffer ) = 0; + virtual int getNumLines() = 0; + + private: + //Prevent wasteful constructor creation. + SoundDebugPage( const SoundDebugPage& original ); + SoundDebugPage& operator=( const SoundDebugPage& rhs ); + + unsigned int m_pageNum; + + SoundDebugDisplay* m_displayMaster; +}; + + +#endif // SOUNDDEBUGPAGE_H + diff --git a/game/code/sound/soundfx/allsoundfx.cpp b/game/code/sound/soundfx/allsoundfx.cpp new file mode 100644 index 0000000..4ee5e0b --- /dev/null +++ b/game/code/sound/soundfx/allsoundfx.cpp @@ -0,0 +1,14 @@ +#include <sound/soundfx/soundeffectplayer.cpp> +#include <sound/soundfx/soundfxfrontendlogic.cpp> +#include <sound/soundfx/soundfxgameplaylogic.cpp> +#include <sound/soundfx/soundfxlogic.cpp> +#include <sound/soundfx/soundfxpauselogic.cpp> +#include <sound/soundfx/reverbcontroller.cpp> +#include <sound/soundfx/reverbsettings.cpp> +#include <sound/soundfx/positionalsoundsettings.cpp> + +#ifdef RAD_PS2 +#include <sound/soundfx/ps2reverbcontroller.cpp> +#else +#include <sound/soundfx/gcreverbcontroller.cpp> +#endif
\ No newline at end of file diff --git a/game/code/sound/soundfx/gcreverbcontroller.cpp b/game/code/sound/soundfx/gcreverbcontroller.cpp new file mode 100644 index 0000000..4b6336a --- /dev/null +++ b/game/code/sound/soundfx/gcreverbcontroller.cpp @@ -0,0 +1,108 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: gcreverbcontroller.cpp +// +// Description: Implementation for the GCReverbController class, which provides +// the GC-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radsound_gcn.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/gcreverbcontroller.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// GCReverbController::GCReverbController +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +GCReverbController::GCReverbController() +{ + m_reverbInterface = ::radSoundEffectStdReverbGcnCreate( GMA_PERSISTENT ); + + registerReverbEffect( m_reverbInterface ); +} + +//============================================================================== +// GCReverbController::~GCReverbController +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +GCReverbController::~GCReverbController() +{ + m_reverbInterface->Release(); + m_reverbInterface = NULL; +} + +//============================================================================= +// GCReverbController::SetReverbOn +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void GCReverbController::SetReverbOn( reverbSettings* settings ) +{ + SetReverbGain( settings->GetGain() ); + m_reverbInterface->SetPreDelay( settings->GetGCPreDelay() ); + m_reverbInterface->SetReverbTime( settings->GetGCReverbTime() ); + m_reverbInterface->SetColoration( settings->GetGCColoration() ); + m_reverbInterface->SetDamping( settings->GetGCDamping() ); +} + +//============================================================================= +// GCReverbController::SetReverbOff +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void GCReverbController::SetReverbOff() +{ + SetReverbGain( 0.0f ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundfx/gcreverbcontroller.h b/game/code/sound/soundfx/gcreverbcontroller.h new file mode 100644 index 0000000..3e1e101 --- /dev/null +++ b/game/code/sound/soundfx/gcreverbcontroller.h @@ -0,0 +1,54 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: gcreverbcontroller.h +// +// Description: Declaration for the GCReverbController class, which provides +// the GC-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +#ifndef GCREVERBCONTROLLER_H +#define GCREVERBCONTROLLER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/reverbcontroller.h> + +//======================================== +// Forward References +//======================================== +struct IRadSoundEffectStdReverbGcn; + +//============================================================================= +// +// Synopsis: GCReverbController +// +//============================================================================= + +class GCReverbController : public ReverbController +{ + public: + GCReverbController(); + virtual ~GCReverbController(); + + void SetReverbOn( reverbSettings* settings ); + void SetReverbOff(); + + private: + //Prevent wasteful constructor creation. + GCReverbController( const GCReverbController& original ); + GCReverbController& operator=( const GCReverbController& rhs ); + + // + // Radsound's GC reverb interface + // + IRadSoundEffectStdReverbGcn* m_reverbInterface; +}; + + +#endif // GCREVERBCONTROLLER_H + diff --git a/game/code/sound/soundfx/ipositionalsoundsettings.h b/game/code/sound/soundfx/ipositionalsoundsettings.h new file mode 100644 index 0000000..3989621 --- /dev/null +++ b/game/code/sound/soundfx/ipositionalsoundsettings.h @@ -0,0 +1,59 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: ipositionalsoundsettings.h +// +// Description: RadScript interface for positional sound settings +// +// History: 12/20/2002 + Created -- Darren +// +//============================================================================= + +#ifndef IPOSITIONALSOUNDSETTINGS_H +#define IPOSITIONALSOUNDSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: IPositionalSoundSettings +// +//============================================================================= + +class IPositionalSoundSettings : public IRefCount +{ + // + // Name of sound resource to play + // + virtual void SetClipName( const char* clipName ) = 0; + + // + // Distance at which volume is maxed + // + virtual void SetMinDistance( float min ) = 0; + + // + // Distance at which volume reaches zero + // + virtual void SetMaxDistance( float max ) = 0; + + // + // Probability of sound playing + // + virtual void SetPlaybackProbability( float prob ) = 0; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //IPOSITIONALSOUNDSETTINGS_H diff --git a/game/code/sound/soundfx/ireverbsettings.h b/game/code/sound/soundfx/ireverbsettings.h new file mode 100644 index 0000000..c1288f3 --- /dev/null +++ b/game/code/sound/soundfx/ireverbsettings.h @@ -0,0 +1,73 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: ireverbsettings.h +// +// Description: Declaration for IReverbSettings class, which is the interface +// visible in RadTuner for creating and storing a set of reverb +// parameters. +// +// History: 11/3/2002 + Created -- Darren +// +//============================================================================= + +#ifndef IREVERBSETTINGS_H +#define IREVERBSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: IReverbSettings +// +//============================================================================= + +struct IReverbSettings : public IRefCount +{ + virtual void SetGain( float gain ) = 0; + virtual void SetFadeInTime( float milliseconds ) = 0; + virtual void SetFadeOutTime( float milliseconds ) = 0; + + // + // See radsound_<platform name>.hpp for details on this stuff + // + virtual void SetXboxRoom( int mBvalue ) = 0; + virtual void SetXboxRoomHF( int mBvalue ) = 0; + virtual void SetXboxRoomRolloffFactor( float value ) = 0; + virtual void SetXboxDecayTime( float value ) = 0; + virtual void SetXboxDecayHFRatio( float value ) = 0; + virtual void SetXboxReflections( int mBvalue ) = 0; + virtual void SetXboxReflectionsDelay( float value ) = 0; + virtual void SetXboxReverb( int mBvalue ) = 0; + virtual void SetXboxReverbDelay( float value ) = 0; + virtual void SetXboxDiffusion( float value ) = 0; + virtual void SetXboxDensity( float value ) = 0; + virtual void SetXboxHFReference( float value ) = 0; + + // No RadTuner interface for enumerations as far as I know, so + // we'll have to cast whatever integer we get here + virtual void SetPS2ReverbMode( int mode ) = 0; + + virtual void SetPS2Delay( float delayTime ) = 0; + virtual void SetPS2Feedback( float feedback ) = 0; + + virtual void SetGCPreDelay( float milliseconds ) = 0; + virtual void SetGCReverbTime( float milliseconds ) = 0; + virtual void SetGCColoration( float coloration ) = 0; + virtual void SetGCDamping( float damping ) = 0; + + // Must be defined for all platforms cause of the script. + virtual void SetWinEnvironmentDiffusion( float diffusion ) = 0; + virtual void SetWinAirAbsorptionHF( float value ) = 0; +}; + + +#endif // IREVERBSETTINGS_H + diff --git a/game/code/sound/soundfx/positionalsoundsettings.cpp b/game/code/sound/soundfx/positionalsoundsettings.cpp new file mode 100644 index 0000000..670bf9b --- /dev/null +++ b/game/code/sound/soundfx/positionalsoundsettings.cpp @@ -0,0 +1,180 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: positionalsoundsettings.cpp +// +// Description: Implement positionalSoundSettings +// +// History: 12/20/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <string.h> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/positionalsoundsettings.h> + +#include <memory/srrmemory.h> + + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +// +// Initialially the list is empty +// +positionalSoundSettings* radLinkedClass< positionalSoundSettings >::s_pLinkedClassHead = NULL; +positionalSoundSettings* radLinkedClass< positionalSoundSettings >::s_pLinkedClassTail = NULL; + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// positionalSoundSettings::positionalSoundSettings +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +positionalSoundSettings::positionalSoundSettings() : + radRefCount( 0 ), + m_clipName( NULL ), + m_minDist( 10.0f ), + m_maxDist( 100.0f ), + m_playProbability( 1.0f ) +{ +} + +//============================================================================= +// positionalSoundSettings::~positionalSoundSettings +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +positionalSoundSettings::~positionalSoundSettings() +{ + delete m_clipName; +} + +//============================================================================= +// positionalSoundSettings::SetClipName +//============================================================================= +// Description: Set name of the sound resource to be played at this position +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void positionalSoundSettings::SetClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + m_clipName = new(GMA_PERSISTENT) char[strlen(clipName)+1]; + strcpy( m_clipName, clipName ); +} + +//============================================================================= +// positionalSoundSettings::SetMinDistance +//============================================================================= +// Description: Set distance at which volume is maxed (kinda counterintuitive, +// but that seems to be the convention) +// +// Parameters: min - distance to set +// +// Return: void +// +//============================================================================= +void positionalSoundSettings::SetMinDistance( float min ) +{ + rAssert( min >= 0.0f ); + + m_minDist = min; +} + +//============================================================================= +// positionalSoundSettings::SetMaxDistance +//============================================================================= +// Description: Set the distance at which the volume reaches zero +// +// Parameters: max - distance to set +// +// Return: void +// +//============================================================================= +void positionalSoundSettings::SetMaxDistance( float max ) +{ + rAssert( max >= 0.0f ); + + m_maxDist = max; +} + +//============================================================================= +// positionalSoundSettings::SetPlaybackProbability +//============================================================================= +// Description: Set probability that sound will be played when we enter +// the trigger volume +// +// Parameters: prob - probability on scale of 0.0 to 1.0 +// +// Return: void +// +//============================================================================= +void positionalSoundSettings::SetPlaybackProbability( float prob ) +{ + rAssert( prob >= 0.0f ); + rAssert( prob <= 1.0f ); + + m_playProbability = prob; +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** + +//****************************************************************************** +// Factory functions +//****************************************************************************** + +//============================================================================== +// PositionalSettingsObjCreate +//============================================================================== +// Description: Factory function for creating positionalSoundSettings objects. +// Called by RadScript. +// +// Parameters: ppParametersObj - Address of ptr to new object +// allocator - FTT pool to allocate object within +// +// Return: N/A. +// +//============================================================================== +void PositionalSettingsObjCreate +( + IPositionalSoundSettings** ppParametersObj, + radMemoryAllocator allocator +) +{ + rAssert( ppParametersObj != NULL ); + (*ppParametersObj) = new ( allocator ) positionalSoundSettings( ); + (*ppParametersObj)->AddRef( ); +} diff --git a/game/code/sound/soundfx/positionalsoundsettings.h b/game/code/sound/soundfx/positionalsoundsettings.h new file mode 100644 index 0000000..8174598 --- /dev/null +++ b/game/code/sound/soundfx/positionalsoundsettings.h @@ -0,0 +1,80 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: positionalsoundsettings.h +// +// Description: Declaration of object encapsulating positional sound settings +// +// History: 12/20/2002 + Created -- Darren +// +//============================================================================= + +#ifndef POSITIONALSOUNDSETTINGS_H +#define POSITIONALSOUNDSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radlinkedclass.hpp> + +#include <sound/soundfx/ipositionalsoundsettings.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: positionalSoundSettings +// +//============================================================================= + +class positionalSoundSettings: public IPositionalSoundSettings, + public radLinkedClass< positionalSoundSettings >, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "positionalSoundSettings" ); + positionalSoundSettings(); + virtual ~positionalSoundSettings(); + + void SetClipName( const char* clipName ); + const char* GetClipName() { return( m_clipName ); } + + void SetMinDistance( float min ); + float GetMinDistance() { return( m_minDist ); } + + void SetMaxDistance( float max ); + float GetMaxDistance() { return( m_maxDist ); } + + void SetPlaybackProbability( float prob ); + float GetPlaybackProbability() { return( m_playProbability ); } + + private: + //Prevent wasteful constructor creation. + positionalSoundSettings( const positionalSoundSettings& positionalsoundsettings ); + positionalSoundSettings& operator=( const positionalSoundSettings& positionalsoundsettings ); + + // + // Settings + // + char* m_clipName; + float m_minDist; + float m_maxDist; + float m_playProbability; +}; + +//============================================================================= +// Factory Functions +//============================================================================= + +// +// Create a positionalSoundSettings object +// +void PositionalSettingsObjCreate +( + IPositionalSoundSettings** ppSettings, + radMemoryAllocator allocator +); + +#endif //POSITIONALSOUNDSETTINGS_H diff --git a/game/code/sound/soundfx/ps2reverbcontroller.cpp b/game/code/sound/soundfx/ps2reverbcontroller.cpp new file mode 100644 index 0000000..cf88160 --- /dev/null +++ b/game/code/sound/soundfx/ps2reverbcontroller.cpp @@ -0,0 +1,124 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: ps2reverbcontroller.cpp +// +// Description: Implementation for the PS2ReverbController class, which provides +// the PS2-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radsound_ps2.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/ps2reverbcontroller.h> + +#include <sound/soundfx/reverbsettings.h> + +#include <memory/srrmemory.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// PS2ReverbController::PS2ReverbController +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +PS2ReverbController::PS2ReverbController() +{ + m_reverbInterface = ::radSoundCreateEffectPs2( GMA_PERSISTENT ); + + // + // Wheee!!!! + // + m_reverbInterface->SetMode( IRadSoundEffectPs2::Off ); + + registerReverbEffect( m_reverbInterface ); +} + +//============================================================================== +// PS2ReverbController::~PS2ReverbController +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +PS2ReverbController::~PS2ReverbController() +{ + m_reverbInterface->Release(); + m_reverbInterface = NULL; +} + +//============================================================================= +// PS2ReverbController::SetReverbOn +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void PS2ReverbController::SetReverbOn( reverbSettings* settings ) +{ + if( settings != NULL ) + { + m_reverbInterface->SetMode( static_cast<IRadSoundEffectPs2::Mode>(settings->GetPS2ReverbMode()) ); + m_reverbInterface->SetGain( settings->GetGain() ); + m_reverbInterface->SetDelay( settings->GetPS2Delay() ); + m_reverbInterface->SetFeedback( settings->GetPS2Feedback() ); + + prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(), + settings->GetFadeOutTime() ); + } + else + { + rReleaseString( "Settings is NULL\n" ); + } +} + +//============================================================================= +// PS2ReverbController::SetReverbOff +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void PS2ReverbController::SetReverbOff() +{ + startFadeOut(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundfx/ps2reverbcontroller.h b/game/code/sound/soundfx/ps2reverbcontroller.h new file mode 100644 index 0000000..d756bc2 --- /dev/null +++ b/game/code/sound/soundfx/ps2reverbcontroller.h @@ -0,0 +1,54 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: ps2reverbcontroller.h +// +// Description: Declaration for the PS2ReverbController class, which provides +// the PS2-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +#ifndef PS2REVERBCONTROLLER_H +#define PS2REVERBCONTROLLER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/reverbcontroller.h> + +//======================================== +// Forward References +//======================================== +struct IRadSoundEffectPs2; + +//============================================================================= +// +// Synopsis: PS2ReverbController +// +//============================================================================= + +class PS2ReverbController : public ReverbController +{ + public: + PS2ReverbController(); + virtual ~PS2ReverbController(); + + void SetReverbOn( reverbSettings* settings ); + void SetReverbOff(); + + private: + //Prevent wasteful constructor creation. + PS2ReverbController( const PS2ReverbController& original ); + PS2ReverbController& operator=( const PS2ReverbController& rhs ); + + // + // Radsound's PS2 reverb interface + // + IRadSoundEffectPs2* m_reverbInterface; +}; + + +#endif // PS2REVERBCONTROLLER_H + diff --git a/game/code/sound/soundfx/reverbcontroller.cpp b/game/code/sound/soundfx/reverbcontroller.cpp new file mode 100644 index 0000000..672d542 --- /dev/null +++ b/game/code/sound/soundfx/reverbcontroller.cpp @@ -0,0 +1,410 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: reverbcontroller.cpp +// +// Description: Implementation for ReverbController, which turns reverb on +// and off and sets control values. +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radsound_hal.hpp> +#include <radnamespace.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/reverbcontroller.h> + +#include <sound/soundfx/reverbsettings.h> +#include <sound/soundrenderer/soundrenderingmanager.h> + +#include <events/eventmanager.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// ReverbController::ReverbController +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +ReverbController::ReverbController() : + m_targetGain( 0.0f ), + m_currentGain( 0.0f ), + m_fadeInMultiplier( 1.0f ), + m_fadeOutMultiplier( 1.0f ), + m_lastReverb( NULL ), + m_queuedReverb( NULL ) +{ + unsigned int event; + EventManager* eventMgr = GetEventManager(); + + rAssert( eventMgr != NULL ); + + // + // Register all of the ambience events + // + for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY; + event < EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS; + ++event ) + { + eventMgr->AddListener( this, static_cast<EventEnum>(event) ); + } + eventMgr->AddListener(this, EVENT_MISSION_RESET); + eventMgr->AddListener(this, EVENT_MISSION_CHARACTER_RESET); + + // bmc -- add registering of extra ambient sound events (placeholder and otherwise) + for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT; + event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER9; + ++event ) + { + eventMgr->AddListener( this, static_cast<EventEnum>(event) ); + } + + for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT; + event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16; + ++event ) + { + eventMgr->AddListener( this, static_cast<EventEnum>(event) ); + } + // bmc + + +} + +//============================================================================== +// ReverbController::~ReverbController +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +ReverbController::~ReverbController() +{ + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// ReverbController::SetReverbGain +//============================================================================= +// Description: Set the gain on the entire reverb system +// +// Parameters: gain - 0 to 1 gain value +// +// Return: None +// +//============================================================================= +void ReverbController::SetReverbGain( float gain ) +{ + ::radSoundHalSystemGet()->SetAuxGain( REVERB_AUX_EFFECT_NUMBER, gain ); +} + +//============================================================================= +// ReverbController::HandleEvent +//============================================================================= +// Description: Listen for events that will cause us to switch reverb modes +// on and off +// +// Parameters: id - event ID +// pEventData - user data, unused +// +// Return: void +// +//============================================================================= +void ReverbController::HandleEvent( EventEnum id, void* pEventData ) +{ + reverbSettings* settingsObj = NULL; + reverbSettings* oldSettings = m_lastReverb; + + switch( id ) + { + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_1: + settingsObj = getReverbSettings( "pproom1" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2: + settingsObj = getReverbSettings( "pproom2" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_3: + settingsObj = getReverbSettings( "pproom3" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_TUNNEL_1: + settingsObj = getReverbSettings( "PP_tunnel_01" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_TUNNEL_2: + settingsObj = getReverbSettings( "PP_tunnel_02" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_BURNS_TUNNEL: + settingsObj = getReverbSettings( "burns_tunnel" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_MANSION_INTERIOR: + settingsObj = getReverbSettings( "mansion_interior" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_SEWERS: + settingsObj = getReverbSettings( "sewers" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_TUNNEL: + settingsObj = getReverbSettings( "stonecuttertunnel" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_HALL: + settingsObj = getReverbSettings( "stonecutterhall" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STADIUM_TUNNEL: + settingsObj = getReverbSettings( "stadiumtunnel" ); + break; + + case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_KRUSTYLU_EXTERIOR: + settingsObj = getReverbSettings( "krustylu" ); + break; + + default: + // + // This should be triggered by any ambient sound trigger that + // isn't mapped to reverb above + // + SetReverbOff(); + m_lastReverb = NULL; + break; + } + + if( settingsObj != NULL ) + { + // + // No sense in switching to something we're already doing. That only + // seems to lead to unnecessary clicks on PS2 anyway. + // + if( settingsObj != oldSettings ) + { + if( m_currentGain > 0.0f ) + { + // + // We can't do a sudden switch without hearing popping. Queue up this + // switch to occur after we fade out + // + prepareFadeSettings( 0.0f, settingsObj->GetFadeInTime(), settingsObj->GetFadeOutTime() ); + m_queuedReverb = m_lastReverb; + } + else + { + SetReverbOn( settingsObj ); + } + } + } +} + +//============================================================================= +// ReverbController::ServiceOncePerFrame +//============================================================================= +// Description: Handle the fading in and fading out of reverb +// +// Parameters: elapsedTime - elapsed time since last update +// +// Return: void +// +//============================================================================= +void ReverbController::ServiceOncePerFrame( unsigned int elapsedTime ) +{ + if( m_currentGain == m_targetGain ) + { + if( ( m_targetGain == 0.0f ) + && ( m_queuedReverb != NULL ) ) + { + // + // We're done fading out, now we can make the switch and fade + // back in + // + SetReverbOn( m_queuedReverb ); + m_queuedReverb = NULL; + } + + return; + } + else if( m_currentGain < m_targetGain ) + { + m_currentGain += ( elapsedTime * m_fadeInMultiplier ); + if( m_currentGain > m_targetGain ) + { + m_currentGain = m_targetGain; + } + } + else + { + m_currentGain -= ( elapsedTime * m_fadeOutMultiplier ); + if( m_currentGain < m_targetGain ) + { + m_currentGain = m_targetGain; + } + } + + SetReverbGain( m_currentGain ); +} + +//============================================================================= +// ReverbController::PauseReverb +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void ReverbController::PauseReverb() +{ + if( m_lastReverb != NULL ) + { + SetReverbOff(); + } +} + +//============================================================================= +// ReverbController::UnpauseReverb +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void ReverbController::UnpauseReverb() +{ + if( m_lastReverb != NULL ) + { + SetReverbOn( m_lastReverb ); + } +} + +//****************************************************************************** +// +// Protected Member Functions +// +//****************************************************************************** + +void ReverbController::registerReverbEffect( IRadSoundHalEffect* reverbEffect ) +{ + ::radSoundHalSystemGet()->SetAuxEffect( REVERB_AUX_EFFECT_NUMBER, reverbEffect ); + ::radSoundHalSystemGet()->SetAuxGain( REVERB_AUX_EFFECT_NUMBER, 0.0f ); + + Sound::daSoundRenderingManagerGet()->GetTheListener()->SetEnvironmentAuxSend( REVERB_AUX_EFFECT_NUMBER ); + Sound::daSoundRenderingManagerGet()->GetTheListener()->SetEnvEffectsEnabled( true ); +} + +//============================================================================= +// ReverbController::prepareFadeSettings +//============================================================================= +// Description: Set up the member values for fading reverb in and out +// +// Parameters: targetGain - gain value to gradually move to +// fadeInTime - time in seconds for the fade, assuming we're +// going from 0.0 to 1.0 +// fadeOutTime - similar to fadeInTime +// +// Return: void +// +//============================================================================= +void ReverbController::prepareFadeSettings( float targetGain, float fadeInTime, float fadeOutTime ) +{ + m_targetGain = targetGain; + + m_fadeInMultiplier = fadeInTime; + if( m_fadeInMultiplier == 0.0f ) + { + m_fadeInMultiplier = 1.0f; + } + else + { + m_fadeInMultiplier = 1.0f / m_fadeInMultiplier; + } + m_fadeOutMultiplier = fadeOutTime; + if( m_fadeOutMultiplier == 0.0f ) + { + m_fadeOutMultiplier = 1.0f; + } + else + { + m_fadeOutMultiplier = 1.0f / m_fadeInMultiplier; + } +} + +//============================================================================= +// ReverbController::startFadeOut +//============================================================================= +// Description: Set up for the gradual reverb fadeout +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void ReverbController::startFadeOut() +{ + m_targetGain = 0.0f; +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// ReverbController::getReverbSettings +//============================================================================= +// Description: Retrieve the object containing a group of reverb settings +// +// Parameters: objName - name of reverbSettings object to find +// +// Return: void +// +//============================================================================= +reverbSettings* ReverbController::getReverbSettings( const char* objName ) +{ + IRadNameSpace* nameSpace; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + if ( objName != NULL ) + { + rTunePrintf( "\n\nAUDIO: Reverb Settings: [%s]\n\n\n", objName ); + } + + m_lastReverb = static_cast<reverbSettings*>(nameSpace->GetInstance( objName )); + + return( m_lastReverb ); +}
\ No newline at end of file diff --git a/game/code/sound/soundfx/reverbcontroller.h b/game/code/sound/soundfx/reverbcontroller.h new file mode 100644 index 0000000..d3bc037 --- /dev/null +++ b/game/code/sound/soundfx/reverbcontroller.h @@ -0,0 +1,83 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: reverbcontroller.h +// +// Description: Declaration for the ReverbController class, which turns reverb +// on and off and applies the required control values. +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +#ifndef REVERBCONTROLLER_H +#define REVERBCONTROLLER_H + +//======================================== +// Nested Includes +//======================================== +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +struct IRadSoundHalEffect; +class reverbSettings; + +//============================================================================= +// +// Synopsis: ReverbController +// +//============================================================================= + +class ReverbController : public EventListener +{ + public: + ReverbController(); + virtual ~ReverbController(); + + void SetReverbGain( float gain ); + + virtual void SetReverbOn( reverbSettings* settings ) = 0; + virtual void SetReverbOff() = 0; + + virtual void PauseReverb(); + virtual void UnpauseReverb(); + + // + // EventListener interface + // + void HandleEvent( EventEnum id, void* pEventData ); + + void ServiceOncePerFrame( unsigned int elapsedTime ); + + protected: + static const int REVERB_AUX_EFFECT_NUMBER = 0; + + void registerReverbEffect( IRadSoundHalEffect* reverbEffect ); + void prepareFadeSettings( float targetGain, float fadeInTime, float fadeOutTime ); + void startFadeOut(); + + private: + //Prevent wasteful constructor creation. + ReverbController( const ReverbController& original ); + ReverbController& operator=( const ReverbController& rhs ); + + reverbSettings* getReverbSettings( const char* objName ); + + // + // Used for gradual changes in reverb + // + float m_targetGain; + float m_currentGain; + float m_fadeInMultiplier; + float m_fadeOutMultiplier; + + reverbSettings* m_lastReverb; + reverbSettings* m_queuedReverb; +}; + + +#endif // REVERBCONTROLLER_H + diff --git a/game/code/sound/soundfx/reverbsettings.cpp b/game/code/sound/soundfx/reverbsettings.cpp new file mode 100644 index 0000000..d44c200 --- /dev/null +++ b/game/code/sound/soundfx/reverbsettings.cpp @@ -0,0 +1,133 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: reverbsettings.cpp +// +// Description: Definition for the reverbSettings class, which stores sets +// of reverb parameters to be applied whenever we want that +// reverby sound thing happening. NOTE: lower-case "r" needed +// to make RadScript happy. +// +// History: 11/5/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/reverbsettings.h> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Initialially the list is empty +// +reverbSettings* radLinkedClass< reverbSettings >::s_pLinkedClassHead = NULL; +reverbSettings* radLinkedClass< reverbSettings >::s_pLinkedClassTail = NULL; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// reverbSettings::reverbSettings +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +reverbSettings::reverbSettings() : + radRefCount( 0 ), + + m_gain( 0.0f ), + m_fadeInTime( 0.0f ), + m_fadeOutTime( 0.0f ), + + m_xboxRoom( 0 ), + m_xboxRoomHF( 0 ), + m_xboxRoomRolloff( 0.0f ), + m_xboxDecay( 0.0f ), + m_xboxDecayHFRatio( 0.0f ), + m_xboxReflections( 0 ), + m_xboxReflectionsDelay( 0.0f ), + m_xboxReverb( 0 ), + m_xboxReverbDelay( 0.0f ), + m_xboxDiffusion( 0.0f ), + m_xboxDensity( 0.0f ), + m_xboxHFReference( 0.0f ), + + m_ps2ReverbMode( 0 ), + m_ps2Delay( 0.0f ), + m_ps2Feedback( 0.0f ), + + m_gcPreDelay( 0.0f ), + m_gcReverbTime( 0.0f ), + m_gcColoration( 0.0f ), + m_gcDamping( 0.0f ), + + m_winEnvironmentDiffusion( 1.0f ), + m_winAirAbsorptionHF( -5.0f ) +{ +} + +//============================================================================== +// reverbSettings::~reverbSettings +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +reverbSettings::~reverbSettings() +{ +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//****************************************************************************** +// Factory functions +//****************************************************************************** + +//============================================================================== +// ReverbSettingsObjCreate +//============================================================================== +// Description: Factory function for creating reverbSettings objects. +// Called by RadScript. +// +// Parameters: ppSettings - Address of ptr to new object +// allocator - FTT pool to allocate object within +// +// Return: N/A. +// +//============================================================================== +void ReverbSettingsObjCreate +( + IReverbSettings** ppSettings, + radMemoryAllocator allocator +) +{ + rAssert( ppSettings != NULL ); + (*ppSettings) = new ( allocator ) reverbSettings( ); + (*ppSettings)->AddRef( ); +} + diff --git a/game/code/sound/soundfx/reverbsettings.h b/game/code/sound/soundfx/reverbsettings.h new file mode 100644 index 0000000..8ea3bbe --- /dev/null +++ b/game/code/sound/soundfx/reverbsettings.h @@ -0,0 +1,158 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: reverbsettings.h +// +// Description: Declaration for the reverbSettings class, which stores sets +// of reverb parameters to be applied whenever we want that +// reverby sound thing happening. NOTE: lower-case "r" needed +// to make RadScript happy. +// +// History: 11/5/2002 + Created -- Darren +// +//============================================================================= + +#ifndef REVERBSETTINGS_H +#define REVERBSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <sound/soundfx/ireverbsettings.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: reverbSettings +// +//============================================================================= + +class reverbSettings : public IReverbSettings, + public radLinkedClass< reverbSettings >, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "reverbSettings" ); + + reverbSettings(); + virtual ~reverbSettings(); + + void SetGain( float gain ) { m_gain = gain; } + float GetGain() { return( m_gain ); } + void SetFadeInTime( float milliseconds ) { m_fadeInTime = milliseconds; } + float GetFadeInTime() { return( m_fadeInTime ); } + void SetFadeOutTime( float milliseconds ) { m_fadeOutTime = milliseconds; } + float GetFadeOutTime() { return( m_fadeOutTime ); } + + // + // See radsound_<platform name>.hpp for details on this stuff + // + void SetXboxRoom( int mBvalue ) { m_xboxRoom = mBvalue; } + int GetXboxRoom() { return( m_xboxRoom ); } + void SetXboxRoomHF( int mBvalue ) { m_xboxRoomHF = mBvalue; } + int GetXboxRoomHF() { return( m_xboxRoomHF ); } + void SetXboxRoomRolloffFactor( float value ) { m_xboxRoomRolloff = value; } + float GetXboxRoomRolloffFactor() { return( m_xboxRoomRolloff ); } + void SetXboxDecayTime( float value ) { m_xboxDecay = value; } + float GetXboxDecayTime() { return( m_xboxDecay ); } + void SetXboxDecayHFRatio( float value ) { m_xboxDecayHFRatio = value; } + float GetXboxDecayHFRatio() { return( m_xboxDecayHFRatio ); } + void SetXboxReflections( int mBvalue ) { m_xboxReflections = mBvalue; } + int GetXboxReflections() { return( m_xboxReflections ); } + void SetXboxReflectionsDelay( float value ) { m_xboxReflectionsDelay = value; } + float GetXboxReflectionsDelay() { return( m_xboxReflectionsDelay ); } + void SetXboxReverb( int mBvalue ) { m_xboxReverb = mBvalue; } + int GetXboxReverb() { return( m_xboxReverb ); } + void SetXboxReverbDelay( float value ) { m_xboxReverbDelay = value; } + float GetXboxReverbDelay() { return( m_xboxReverbDelay ); } + void SetXboxDiffusion( float value ) { m_xboxDiffusion = value; } + float GetXboxDiffusion() { return( m_xboxDiffusion ); } + void SetXboxDensity( float value ) { m_xboxDensity = value; } + float GetXboxDensity() { return( m_xboxDensity ); } + void SetXboxHFReference( float value ) { m_xboxHFReference = value; } + float GetXboxHFReference() { return( m_xboxHFReference ); } + + // No RadTuner interface for enumerations as far as I know, so + // we'll have to cast whatever integer we get here + void SetPS2ReverbMode( int mode ) { m_ps2ReverbMode = mode; } + int GetPS2ReverbMode() { return( m_ps2ReverbMode ); } + + void SetPS2Delay( float delayTime ) { m_ps2Delay = delayTime; } + float GetPS2Delay() { return( m_ps2Delay ); } + void SetPS2Feedback( float feedback ) { m_ps2Feedback = feedback; } + float GetPS2Feedback() { return( m_ps2Feedback ); } + + void SetGCPreDelay( float milliseconds ) { m_gcPreDelay = milliseconds; } + float GetGCPreDelay() { return( m_gcPreDelay ); } + void SetGCReverbTime( float milliseconds ) { m_gcReverbTime = milliseconds; } + float GetGCReverbTime() { return( m_gcReverbTime ); } + void SetGCColoration( float coloration ) { m_gcColoration = coloration; } + float GetGCColoration() { return( m_gcColoration ); } + void SetGCDamping( float damping ) { m_gcDamping = damping; } + float GetGCDamping() { return( m_gcDamping ); } + + // Must be defined for all platforms cause of the script. + void SetWinEnvironmentDiffusion( float diffusion ) { m_winEnvironmentDiffusion = diffusion; } + float GetWinEnvironmentDiffusion() const { return m_winEnvironmentDiffusion; } + void SetWinAirAbsorptionHF( float value ) { m_winAirAbsorptionHF = value; } + float GetWinAirAbsorptionHF() const { return m_winAirAbsorptionHF; } + + private: + //Prevent wasteful constructor creation. + reverbSettings( const reverbSettings& original ); + reverbSettings& operator=( const reverbSettings& rhs ); + + // + // Reverb parameters + // + float m_gain; + float m_fadeInTime; + float m_fadeOutTime; + + int m_xboxRoom; + int m_xboxRoomHF; + float m_xboxRoomRolloff; + float m_xboxDecay; + float m_xboxDecayHFRatio; + int m_xboxReflections; + float m_xboxReflectionsDelay; + int m_xboxReverb; + float m_xboxReverbDelay; + float m_xboxDiffusion; + float m_xboxDensity; + float m_xboxHFReference; + + int m_ps2ReverbMode; + float m_ps2Delay; + float m_ps2Feedback; + + float m_gcPreDelay; + float m_gcReverbTime; + float m_gcColoration; + float m_gcDamping; + + float m_winEnvironmentDiffusion; + float m_winAirAbsorptionHF; +}; + +//============================================================================= +// Factory Functions +//============================================================================= + +// +// Create a reverbSettings object +// +void ReverbSettingsObjCreate +( + IReverbSettings** ppSettings, + radMemoryAllocator allocator +); + +#endif // REVERBSETTINGS_H + diff --git a/game/code/sound/soundfx/soundeffectplayer.cpp b/game/code/sound/soundfx/soundeffectplayer.cpp new file mode 100644 index 0000000..5847956 --- /dev/null +++ b/game/code/sound/soundfx/soundeffectplayer.cpp @@ -0,0 +1,249 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundeffectplayer.cpp +// +// Description: Implement SoundEffectPlayer, which switches between +// the objects that direct the soundfx logic for various game +// states +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/soundeffectplayer.h> + +#include <sound/soundfx/soundfxfrontendlogic.h> +#include <sound/soundfx/soundfxgameplaylogic.h> +#include <sound/soundfx/soundfxpauselogic.h> + +#ifdef RAD_XBOX +#include <sound/soundfx/xboxreverbcontroller.h> +#elif RAD_WIN32 +#include <sound/soundfx/win32reverbcontroller.h> +#elif RAD_PS2 +#include <sound/soundfx/ps2reverbcontroller.h> +#else +#include <sound/soundfx/gcreverbcontroller.h> +#endif + +#include <memory/srrmemory.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundEffectPlayer::SoundEffectPlayer +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundEffectPlayer::SoundEffectPlayer() : + m_reverbController( NULL ), + m_currentState( FXSTATE_INVALID ) +{ + unsigned int i; + + for( i = 0; i < FXSTATE_MAX_STATES; i++ ) + { + m_logicObjects[i] = NULL; + } + + initialize(); +} + +//============================================================================== +// SoundEffectPlayer::~SoundEffectPlayer +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundEffectPlayer::~SoundEffectPlayer() +{ + unsigned int i; + + for( i = 0; i < FXSTATE_MAX_STATES; i++ ) + { + if( m_logicObjects[i] != NULL ) + { + delete m_logicObjects[i]; + } + } + + delete m_reverbController; +} + +//============================================================================= +// SoundEffectPlayer::ServiceOncePerFrame +//============================================================================= +// Description: Positional sound servicing +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::ServiceOncePerFrame( unsigned int elapsedTime ) +{ + if( m_currentState != FXSTATE_INVALID ) + { + m_logicObjects[m_currentState]->ServiceOncePerFrame( elapsedTime ); + } + + m_reverbController->ServiceOncePerFrame( elapsedTime ); +} + +void SoundEffectPlayer::OnPauseStart() +{ + m_reverbController->PauseReverb(); +} + +void SoundEffectPlayer::OnPauseEnd() +{ + m_reverbController->UnpauseReverb(); +} + +//============================================================================= +// SoundEffectPlayer::PlayCarOptionStinger +//============================================================================= +// Description: The following four functions play stinger sounds for the +// level settings in the options menu +// +// Parameters: trim - trim setting to play it at +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::PlayCarOptionStinger( float trim ) +{ + playStinger( "car_stinger", trim ); +} + +void SoundEffectPlayer::PlayDialogOptionStinger( float trim ) +{ + playStinger( "dialog_stinger", trim ); +} + +void SoundEffectPlayer::PlayMusicOptionStinger( float trim ) +{ + playStinger( "music_stinger", trim ); +} + +void SoundEffectPlayer::PlaySfxOptionStinger( float trim ) +{ + playStinger( "sfx_stinger", trim ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// SoundEffectPlayer::initialize +//============================================================================= +// Description: Create the sound FX logic objects during initialization +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::initialize() +{ + m_logicObjects[FXSTATE_FRONTEND] = new(GMA_PERSISTENT) SoundFXFrontEndLogic(); + m_logicObjects[FXSTATE_GAMEPLAY] = new(GMA_PERSISTENT) SoundFXGameplayLogic(); + m_logicObjects[FXSTATE_PAUSED] = new(GMA_PERSISTENT) SoundFXPauseLogic(); + +#ifdef RAD_PS2 + m_reverbController = new(GMA_PERSISTENT) PS2ReverbController(); +#elif RAD_XBOX + m_reverbController = new(GMA_PERSISTENT) XboxReverbController(); +#elif RAD_WIN32 + m_reverbController = new(GMA_PERSISTENT) Win32ReverbController(); +#else + m_reverbController = new(GMA_PERSISTENT) GCReverbController(); +#endif +} + +//============================================================================= +// SoundEffectPlayer::setSFXState +//============================================================================= +// Description: Switch to a new sound FX state +// +// Parameters: newState - state to be switched to +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::setSFXState( SFXState newState ) +{ + rAssert( newState < FXSTATE_MAX_STATES ); + + if( m_currentState != FXSTATE_INVALID ) + { + m_logicObjects[m_currentState]->UnregisterEventListeners(); + } + + m_currentState = newState; + m_logicObjects[m_currentState]->RegisterEventListeners(); +} + +//============================================================================= +// SoundEffectPlayer::doCleanup +//============================================================================= +// Description: Shut down anything that might be playing +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::doCleanup() +{ + m_logicObjects[m_currentState]->Cleanup(); + + m_reverbController->SetReverbOff(); +} + +//============================================================================= +// SoundEffectPlayer::playStinger +//============================================================================= +// Description: Play the given stinger resource at the specified trim +// +// Parameters: stingerName - name of sound resource for stinger +// trim - volume to play it at +// +// Return: void +// +//============================================================================= +void SoundEffectPlayer::playStinger( const char* stingerName, float trim ) +{ + m_stingerPlayer.PlaySound( stingerName ); + m_stingerPlayer.SetTrim( trim ); +} diff --git a/game/code/sound/soundfx/soundeffectplayer.h b/game/code/sound/soundfx/soundeffectplayer.h new file mode 100644 index 0000000..3985a97 --- /dev/null +++ b/game/code/sound/soundfx/soundeffectplayer.h @@ -0,0 +1,122 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundeffectplayer.h +// +// Description: Declaration for SoundEffectPlayer class, which switches between +// the objects that direct the soundfx logic for various game +// states +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDEFFECTPLAYER_H +#define SOUNDEFFECTPLAYER_H + +//======================================== +// Nested Includes +//======================================== + +#include <sound/simpsonssoundplayer.h> + +//======================================== +// Forward References +//======================================== + +class SoundFXLogic; +class ReverbController; + +//============================================================================= +// +// Synopsis: SoundEffectPlayer +// +//============================================================================= + +class SoundEffectPlayer +{ + public: + SoundEffectPlayer(); + virtual ~SoundEffectPlayer(); + + // + // Start playing front end sounds + // + void OnFrontEndStart() { setSFXState( FXSTATE_FRONTEND ); } + + // + // Start playing gameplay sounds + // + void OnGameplayStart() { setSFXState( FXSTATE_GAMEPLAY ); } + void OnGameplayEnd() { doCleanup(); } + + void OnPauseStart(); + void OnPauseEnd(); + + void ServiceOncePerFrame( unsigned int elapsedTime ); + + void PlayCarOptionStinger( float trim ); + void PlayDialogOptionStinger( float trim ); + void PlayMusicOptionStinger( float trim ); + void PlaySfxOptionStinger( float trim ); + + private: + //Prevent wasteful constructor creation. + SoundEffectPlayer( const SoundEffectPlayer& original ); + SoundEffectPlayer& operator=( const SoundEffectPlayer& rhs ); + + void initialize(); + + // + // Game states in which sound effects are played + // + enum SFXState + { + FXSTATE_FRONTEND, + FXSTATE_GAMEPLAY, + FXSTATE_PAUSED, + + FXSTATE_MAX_STATES, + + FXSTATE_INVALID + }; + + // + // Set a new SFX state + // + void setSFXState( SFXState newState ); + + // + // Shut down anything that might still be playing + // + void doCleanup(); + + // + // Play a stinger (duh) + // + void playStinger( const char* stingerName, float trim ); + + // + // FX logic objects, one for each state + // + SoundFXLogic* m_logicObjects[FXSTATE_MAX_STATES]; + + // + // Reverb controller + // + ReverbController* m_reverbController; + + // + // Current SFX state + // + SFXState m_currentState; + + // + // Options menu stinger player + // + SimpsonsSoundPlayer m_stingerPlayer; +}; + + +#endif // SOUNDEFFECTPLAYER_H + diff --git a/game/code/sound/soundfx/soundfxfrontendlogic.cpp b/game/code/sound/soundfx/soundfxfrontendlogic.cpp new file mode 100644 index 0000000..d2a07aa --- /dev/null +++ b/game/code/sound/soundfx/soundfxfrontendlogic.cpp @@ -0,0 +1,137 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxfrontendlogic.cpp +// +// Description: Implements the SoundFXFrontEndLogic class, which handles +// the translation of events into sound effects for the front +// end. +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/soundfxfrontendlogic.h> + +#include <sound/soundmanager.h> + +#include <events/eventmanager.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundFXFrontEndLogic::SoundFXFrontEndLogic +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXFrontEndLogic::SoundFXFrontEndLogic() +{ +} + +//============================================================================== +// SoundFXFrontEndLogic::~SoundFXFrontEndLogic +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXFrontEndLogic::~SoundFXFrontEndLogic() +{ +} + +//============================================================================= +// SoundFXFrontEndLogic::RegisterEventListeners +//============================================================================= +// Description: Register as listener of sound effect events with Event Manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXFrontEndLogic::RegisterEventListeners() +{ + GetEventManager()->AddListener( this, EVENT_FE_MENU_SELECT ); + GetEventManager()->AddListener( this, EVENT_FE_MENU_BACK ); + GetEventManager()->AddListener( this, EVENT_FE_MENU_UPORDOWN ); + GetEventManager()->AddListener( this, EVENT_FE_CHEAT_SUCCESS ); + GetEventManager()->AddListener( this, EVENT_FE_CHEAT_FAILURE ); + GetEventManager()->AddListener( this, EVENT_FE_CREDITS_NEW_LINE ); +} + +//============================================================================= +// SoundFXFrontEndLogic::HandleEvent +//============================================================================= +// Description: Play sound effects in response to events +// +// Parameters: id - Sound effect event identifier +// pEventData - Currently unused +// +// Return: void +// +//============================================================================= +void SoundFXFrontEndLogic::HandleEvent( EventEnum id, void* pEventData ) +{ + switch( id ) + { + case EVENT_FE_MENU_SELECT: + playSFXSound( "accept", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_MENU_BACK: + playSFXSound( "back", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_MENU_UPORDOWN: + playSFXSound( "scroll", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CHEAT_SUCCESS: + playSFXSound( "cheat_success", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CHEAT_FAILURE: + playSFXSound( "cheat_fail", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CREDITS_NEW_LINE: + playCreditLine( reinterpret_cast<int>(pEventData) ); + break; + + default: + rAssertMsg( false, "Huh? Unexpected sound FX event\n" ); + break; + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + diff --git a/game/code/sound/soundfx/soundfxfrontendlogic.h b/game/code/sound/soundfx/soundfxfrontendlogic.h new file mode 100644 index 0000000..eebe139 --- /dev/null +++ b/game/code/sound/soundfx/soundfxfrontendlogic.h @@ -0,0 +1,50 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxfrontendlogic.h +// +// Description: Declaration for the SoundFXFrontEndLogic class, which handles +// the translation of events into sound effects for the front +// end. +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDFXFRONTENDLOGIC_H +#define SOUNDFXFRONTENDLOGIC_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/soundfxlogic.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: SoundFXFrontEndLogic +// +//============================================================================= + +class SoundFXFrontEndLogic : public SoundFXLogic +{ + public: + SoundFXFrontEndLogic(); + virtual ~SoundFXFrontEndLogic(); + + void RegisterEventListeners(); + + virtual void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + SoundFXFrontEndLogic( const SoundFXFrontEndLogic& original ); + SoundFXFrontEndLogic& operator=( const SoundFXFrontEndLogic& rhs ); +}; + + +#endif // SOUNDFXFRONTENDLOGIC_H + diff --git a/game/code/sound/soundfx/soundfxgameplaylogic.cpp b/game/code/sound/soundfx/soundfxgameplaylogic.cpp new file mode 100644 index 0000000..20b995e --- /dev/null +++ b/game/code/sound/soundfx/soundfxgameplaylogic.cpp @@ -0,0 +1,1212 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxgameplaylogic.cpp +// +// Description: Implements the SoundFXGameplayLogic class, which handles +// the translation of events into sound effects for the game +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radnamespace.hpp> +#include <radtime.hpp> +#include <radsound_hal.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/soundfxgameplaylogic.h> + +#include <sound/soundmanager.h> +#include <sound/soundcollisiondata.h> +#include <sound/soundfx/positionalsoundsettings.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <sound/avatar/carsoundparameters.h> +#include <sound/tuning/globalsettings.h> + +#include <events/eventmanager.h> +#include <meta/scriptlocator.h> +#include <worldsim/physicsairef.h> +#include <worldsim/character/character.h> +#include <worldsim/vehiclecentral.h> +#include <worldsim/avatarmanager.h> +#include <mission/gameplaymanager.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// These are compared against the collision impulse value, which is 0 - 1. +// + +static const float ARBITRARY_IMPULSE_THRESHOLD = 0.01f; + +// Above this value, send big crash event to dialog system +static const float ARBITRARY_BIG_CRASH_THRESHOLD = 0.15f; + +// These values determine whether to play small, medium, or big crash +// sounds for vehicles +static const float ARBITRARY_VEHICLE_MEDIUM_CRASH_THRESHOLD = 0.15f; +static const float ARBITRARY_VEHICLE_BIG_CRASH_THRESHOLD = 0.25f; + +static const char* s_hydrantSprayName = "hydrant_spray"; + +// +// Arbitrary value. If a collision occurs between the avatar vehicle +// and an object, where the difference between their distances is greater +// than this, then the avatar hit a wall or something, so don't average +// the positions. +// +static const float POSITIONAL_COLLISION_MAX_DISTANCE_SQR = 100.0f; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundFXGameplayLogic::SoundFXGameplayLogic +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXGameplayLogic::SoundFXGameplayLogic() : + m_collisionMinMax( NULL ), + m_coinCounter( 0 ), + m_lastRonkTime( 0 ), + m_globalSettings( NULL ) +{ +} + +//============================================================================== +// SoundFXGameplayLogic::~SoundFXGameplayLogic +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXGameplayLogic::~SoundFXGameplayLogic() +{ +} + +//============================================================================= +// SoundFXGameplayLogic::RegisterEventListeners +//============================================================================= +// Description: Register as listener of sound effect events with Event Manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::RegisterEventListeners() +{ + EventManager* eventMgr = GetEventManager(); + + eventMgr->AddListener( this, EVENT_COLLISION ); + eventMgr->AddListener( this, EVENT_FOOTSTEP ); + eventMgr->AddListener( this, EVENT_BIG_RED_SWITCH_PRESSED ); + eventMgr->AddListener( this, EVENT_JUMP_TAKEOFF ); + eventMgr->AddListener( this, EVENT_JUMP_LANDING ); + eventMgr->AddListener( this, EVENT_POSITIONAL_SOUND_TRIGGER_HIT ); + eventMgr->AddListener( this, EVENT_GETINTOVEHICLE_START ); + eventMgr->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); + eventMgr->AddListener( this, EVENT_FE_MENU_SELECT ); + eventMgr->AddListener( this, EVENT_FE_MENU_UPORDOWN ); + eventMgr->AddListener( this, EVENT_FE_MENU_BACK ); + eventMgr->AddListener( this, EVENT_COLLECTED_COINS ); + eventMgr->AddListener( this, EVENT_LOST_COINS ); + eventMgr->AddListener( this, EVENT_SPAWNED_COINS ); + eventMgr->AddListener( this, EVENT_PHONE_BOOTH_BUSY ); + eventMgr->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC ); + eventMgr->AddListener( this, EVENT_BIG_BOOM_SOUND ); + eventMgr->AddListener( this, EVENT_CARD_COLLECTED ); + eventMgr->AddListener( this, EVENT_COLLECTED_WRENCH ); + eventMgr->AddListener( this, EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT ); + eventMgr->AddListener( this, EVENT_MISSION_COLLECTIBLE_PICKED_UP ); + eventMgr->AddListener( this, EVENT_KICK_NPC ); + eventMgr->AddListener( this, EVENT_OBJECT_KICKED ); + eventMgr->AddListener( this, EVENT_WASP_BULLET_FIRED ); + eventMgr->AddListener( this, EVENT_WASP_BULLET_MISSED ); + eventMgr->AddListener( this, EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS ); + eventMgr->AddListener( this, EVENT_START_ANIMATION_SOUND ); + eventMgr->AddListener( this, EVENT_MISSION_SUCCESS ); + eventMgr->AddListener( this, EVENT_STAGE_COMPLETE ); + eventMgr->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT ); + eventMgr->AddListener( this, EVENT_HIT_AND_RUN_METER_THROB ); + eventMgr->AddListener( this, EVENT_SHOW_MISSION_OBJECTIVE ); + eventMgr->AddListener( this, EVENT_PLAY_BIRD_SOUND ); + eventMgr->AddListener( this, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::BOUNCEPAD) ); + eventMgr->AddListener( this, EVENT_FE_PAUSE_MENU_END ); + eventMgr->AddListener( this, EVENT_FE_PAUSE_MENU_START ); + eventMgr->AddListener( this, EVENT_FE_CANCEL ); + eventMgr->AddListener( this, EVENT_FE_CONTINUE ); + eventMgr->AddListener( this, EVENT_FE_LOCKED_OUT ); + eventMgr->AddListener( this, EVENT_BARREL_BLOWED_UP ); + eventMgr->AddListener( this, EVENT_FE_CREDITS_NEW_LINE ); +} + +//============================================================================= +// SoundFXGameplayLogic::HandleEvent +//============================================================================= +// Description: Play sound effects in response to events +// +// Parameters: id - Sound effect event identifier +// pEventData - Currently unused +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::HandleEvent( EventEnum id, void* pEventData ) +{ + Vehicle* vehiclePtr; + unsigned int ronkTime; + const char* soundName; + RenderEnums::LevelEnum level; + AnimSoundData* animData; + + switch( id ) + { + case EVENT_COLLISION: + handleCollisionEvent( static_cast<SoundCollisionData*>(pEventData) ); + break; + + case EVENT_FOOTSTEP: + handleFootstepEvent( static_cast<Character*>(pEventData) ); + break; + + case EVENT_JUMP_TAKEOFF: + playSFXSound( "jump", false ); + break; + + case EVENT_JUMP_LANDING: + // + // TEMPORARY: we need a surface-sensitive solution + // + playSFXSound( "feet_concrete_jump", false ); + break; + + case EVENT_BIG_RED_SWITCH_PRESSED: + handleSwitchEvent(); + break; + + case EVENT_POSITIONAL_SOUND_TRIGGER_HIT: + playPositionalSound( static_cast<ScriptLocator*>(pEventData) ); + break; + + case EVENT_GETINTOVEHICLE_START: + case EVENT_GETOUTOFVEHICLE_END: + playCarDoorSound( id, static_cast<Character*>(pEventData) ); + break; + + case EVENT_FE_MENU_SELECT: + playSFXSound( "accept", false, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + case EVENT_FE_MENU_UPORDOWN: + playSFXSound( "scroll", false, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + case EVENT_FE_MENU_BACK: + playSFXSound( "back", false, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + case EVENT_FE_LOCKED_OUT: + playSFXSound( "locked_out", false ); + break; + + case EVENT_COLLECTED_COINS: + playCoinCollectSound(); + break; + + case EVENT_SPAWNED_COINS: + // + // Should this do anything? + // + break; + + case EVENT_LOST_COINS: + playSFXSound( "coin_lose", true ); + break; + + case EVENT_PHONE_BOOTH_BUSY: + playSFXSound( "phonebusy", false ); + break; + + case EVENT_PLAYER_CAR_HIT_NPC: + { + // + // Set up special collision data for the collision code, because + // it won't be getting a collision event. I like saying "collision". + // + SoundCollisionData collData( 0.0f, static_cast<CollisionEntityDSG*>(pEventData), NULL ); + + handleCollisionEvent( &collData ); + } + break; + + case EVENT_KICK_NPC: + playSFXSound( "car_hit_pedestrian", true ); + break; + + case EVENT_OBJECT_KICKED: + handleObjectKick( static_cast<CollisionEntityDSG*>(pEventData) ); + break; + + case EVENT_BIG_BOOM_SOUND: + vehiclePtr = static_cast<Vehicle*>(pEventData); + rAssert( vehiclePtr != NULL ); + if( vehiclePtr->mVehicleType == VT_USER ) + { + playSFXSound( "generic_car_explode", false ); + } + break; + + case EVENT_BARREL_BLOWED_UP: + playSFXSound( "generic_car_explode", false ); + break; + + case EVENT_CARD_COLLECTED: + // + // Only play card sound effect in car, the music system + // handles it when on foot + // + if( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() ) + { + playSFXSound( "card_collect", false ); + } + break; + + case EVENT_COLLECTED_WRENCH: + playSFXSound( "wrench_collect", false ); + break; + + case EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT: + // + // Play only if we haven't played the last one within a half-second. + // Use a simple timestamp to determine this. + // + ronkTime = ::radTimeGetMilliseconds(); + if( ( ronkTime < m_lastRonkTime ) + || ( ( ronkTime - m_lastRonkTime ) > 500 ) ) + { + m_lastRonkTime = ronkTime; + playSFXSound( "suspension_ronks", true ); + } + break; + + case EVENT_MISSION_COLLECTIBLE_PICKED_UP: + handleCollection(); + break; + + case EVENT_WASP_BULLET_FIRED: + playSFXSound( "wasp_lasers", true ); + break; + + case EVENT_WASP_BULLET_MISSED: + playSFXSound( "laser_miss", true ); + break; + + case EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS: + playSFXSound( "character_zapped", true ); + break; + + case EVENT_START_ANIMATION_SOUND: + animData = static_cast<AnimSoundData*>( pEventData ); + soundName = animData->soundName; + if( ( soundName != NULL ) && ( animData->animJoint == NULL ) ) + { + playSFXSound( soundName, false ); + } + break; + + case EVENT_MISSION_SUCCESS: + playSFXSound( "mission_complete", false ); + break; + + case EVENT_STAGE_COMPLETE: + playSFXSound( "stage_complete", false ); + break; + + case EVENT_HIT_AND_RUN_CAUGHT: + playSFXSound( "busted", false ); + break; + + case EVENT_HIT_AND_RUN_METER_THROB: + playSFXSound( "rage_warning", false ); + break; + + case EVENT_SHOW_MISSION_OBJECTIVE: + playSFXSound( "pop_up", true ); + break; + + case EVENT_PLAY_BIRD_SOUND: + // + // Pick the correct bird effect for the level + // + level = GetGameplayManager()->GetCurrentLevelIndex(); + if( ( level == RenderEnums::L2 ) + || ( level == RenderEnums::L5 ) ) + { + playSFXSound( "pigeon_takeoff", false ); + } + else if( ( level == RenderEnums::L3 ) + || ( level == RenderEnums::L6 ) ) + { + playSFXSound( "gull_takeoff", false ); + } + break; + + case EVENT_LOCATOR + LocatorEvent::BOUNCEPAD : + playSFXSound( "trampoline", true ); + break; + + case EVENT_FE_PAUSE_MENU_START: + playSFXSound( "pause_on", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_PAUSE_MENU_END: + playSFXSound( "pause_off", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CONTINUE: + playSFXSound( "continue", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CANCEL: + playSFXSound( "cancel", true, false, NULL, GetSoundManager()->GetSfxVolume() ); + break; + + case EVENT_FE_CREDITS_NEW_LINE: + playCreditLine( reinterpret_cast<int>(pEventData) ); + break; + + default: + rAssertMsg( false, "Unexpected event received in SoundFXGameplayLogic\n" ); + break; + } +} + +//============================================================================= +// SoundFXGameplayLogic::OnPlaybackComplete +//============================================================================= +// Description: No longer used, now that tutorial lines are played through +// the dialog system +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::OnPlaybackComplete() +{ +} + +//============================================================================= +// SoundFXGameplayLogic::Cleanup +//============================================================================= +// Description: Called on gameplay exit. Use this to shut down any +// positional sounds that might be playing. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::Cleanup() +{ + int i; + + for( i = 0; i < s_numPositionalSounds; i++ ) + { + if( m_positionalSounds[i].IsInUse() ) + { + m_positionalSounds[i].Stop(); + } + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// SoundFXGameplayLogic::getGlobalSettings +//============================================================================= +// Description: Initialize the member pointer to the global settings object +// if not done yet, and return it +// +// Parameters: None +// +// Return: pointer to globalSettings object +// +//============================================================================= +globalSettings* SoundFXGameplayLogic::getGlobalSettings() +{ + IRadNameSpace* nameSpace; + + if( m_globalSettings == NULL ) + { + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + m_globalSettings = static_cast<globalSettings*>(nameSpace->GetInstance( "tuner" )); + rAssert( m_globalSettings != NULL ); + } + + return( m_globalSettings ); +} + +//============================================================================= +// SoundFXGameplayLogic::handleCollisionEvent +//============================================================================= +// Description: Play a collision sound appropriate for this event, unless we've +// already received an event for this collision. A typical collision +// throws a lot of duplicate events, apparently, so we gotta screen +// 'em out. +// +// Parameters: collisionData - data we need pertaining to the collision, passed +// through the event mechanism +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::handleCollisionEvent( SoundCollisionData* collisionData ) +{ + int i; + + // + // FOR NOW: filter out collisions that don't involve the user's car. Later, + // we might want to do AI-car collisions within a certain radius or + // something + // + + //TODO (Cary): I think that this should search the avatar manager for each vehicle in the + //collision pair (or always take A so that vehicles don't get doubled events) and test to + //see if the vehicle is user controlled. The code as it is only sends events for player 0 + //and won't work for multiplayer. + Vehicle* pVehicle = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle(); + + if( !pVehicle ) + { + return; + } + + // + // See if we're already playing a sound for this collision, or if all collision + // players are in use. If so, exit + // + for( i = 0; i < s_numCollisionSounds; i++ ) + { + if( m_collisionSounds[i].soundPlayer.IsInUse() ) + { + if( collisionPairMatches( i, collisionData->collObjA, collisionData->collObjB ) ) + { + // Collision sound already being played, do nothing + return; + } + } + else + { + break; + } + } + + if( i == s_numCollisionSounds ) + { + // + // All players are being used + // + return; + } + + CollisionAttributes* attributes; + CollisionEntityDSG* collEntities[2]; + const char* vehicleCrashName = NULL; + const char* nonVehicleCrashName = NULL; + CollisionEntityDSG* hydrantObject = NULL; + bool hydrantHit = false; + EventEnum minorCrash = EVENT_MINOR_VEHICLE_CRASH; + EventEnum bigCrash = EVENT_BIG_VEHICLE_CRASH; + Vehicle* collisionVehicle = NULL; + CollisionEntityDSG* otherEntity = NULL; + rmt::Vector diffVector; + rmt::Vector avatarPosition; + rmt::Vector* vectorPtr = NULL; + bool avatarVehicleInvolved = false; + + collEntities[0] = collisionData->collObjA; + collEntities[1] = collisionData->collObjB; + + // + // NOTE: this code assumes that we have at most one vehicle and one non-vehicle + // in the collision pair. If we have two non-vehicles, one is lost. Eh. + // + for( i = 0; i < 2; i++ ) + { + // + // If the DSG object is NULL, one of the objects is a fence or something. + // Ignore and move on. + // + if( collEntities[i] != NULL ) + { + if( collEntities[i]->GetAIRef() == PhysicsAIRef::redBrickVehicle ) + { + // + // Vehicle, play smashy sound + // + if( collisionData->mIntensity > ARBITRARY_VEHICLE_BIG_CRASH_THRESHOLD ) + { + vehicleCrashName = "large_car_crash"; + } + else if( collisionData->mIntensity > ARBITRARY_VEHICLE_MEDIUM_CRASH_THRESHOLD ) + { + vehicleCrashName = "medium_car_crash"; + } + else + { + vehicleCrashName = "small_car_crash"; + } + + collisionVehicle = static_cast<Vehicle*>( collEntities[i] ); + + if( collEntities[i] == pVehicle ) + { + // + // Collision involves the user vehicle, store the other + // entity + // + if( i == 0 ) + { + otherEntity = collEntities[1]; + } + else + { + otherEntity = collEntities[0]; + } + + avatarVehicleInvolved = true; + } + } + else + { + // + // Non-vehicle, get attributes + // + attributes = collEntities[i]->GetCollisionAttributes(); + if( attributes == NULL ) + { + // + // No attributes. Character, I assume? It'd be nice if the + // Character class filled this in. Something to try. + // + nonVehicleCrashName = "car_hit_pedestrian"; + } + else + { + nonVehicleCrashName = attributes->GetSound(); + + // + // Stinky special case + // + if( attributes->GetBreakable() == BreakablesEnum::eHydrantBreaking ) + { + hydrantHit = true; + hydrantObject = collEntities[i]; + } + } + + // + // This eliminates vehicle-on-vehicle collision, so don't use that + // type of event for the dialog system + // + minorCrash = EVENT_MINOR_CRASH; + bigCrash = EVENT_BIG_CRASH; + } + } + else + { + // + // This eliminates vehicle-on-vehicle collision, so don't use that + // type of event for the dialog system + // + minorCrash = EVENT_MINOR_CRASH; + bigCrash = EVENT_BIG_CRASH; + } + } + + + // + // Based on how hard the crash is, pass on an event that the dialog + // system can use (Q: is this the best place for this? If other + // game components start using it, this should be moved). + // + if( avatarVehicleInvolved ) + { + if( collisionData->mIntensity < ARBITRARY_BIG_CRASH_THRESHOLD ) + { + if( collisionData->mIntensity >= ARBITRARY_IMPULSE_THRESHOLD ) + { + if( minorCrash == EVENT_MINOR_CRASH ) + { + // + // Can't be vehicle-on-vehicle + // + collisionVehicle = NULL; + } + GetEventManager()->TriggerEvent( minorCrash, collisionVehicle ); + } + } + else + { + if( bigCrash == EVENT_BIG_CRASH ) + { + // + // Isn't vehicle-on-vehicle + // + collisionVehicle = NULL; + } + GetEventManager()->TriggerEvent( bigCrash, collisionVehicle ); + } + } + + // + // Play the collision sound if we've got a player free + // + rAssert( collisionData->mIntensity >= 0.0f ); + rAssert( collisionData->mIntensity <= 1.0f ); + + // + // Check whether the colliding entities are really far apart. If so, + // then the player has hit a wall or something, so don't bother averaging + // the distances between them, just play at the avatar position + // + if( otherEntity != NULL ) + { + pVehicle->GetPosition( &avatarPosition ); + otherEntity->GetPosition( &diffVector ); + diffVector -= avatarPosition; + + if( diffVector.MagnitudeSqr() > POSITIONAL_COLLISION_MAX_DISTANCE_SQR ) + { + vectorPtr = &avatarPosition; + } + } + + // + // Play the sounds in free players (or kill something + // if they're all in use) + // + if( vehicleCrashName != NULL ) + { + startCollisionPlayer( vehicleCrashName, collisionData->collObjA, collisionData->collObjB, vectorPtr ); + } + if( nonVehicleCrashName != NULL ) + { + startCollisionPlayer( nonVehicleCrashName, collisionData->collObjA, collisionData->collObjB, vectorPtr ); + + // + // Stinky fire hydrant hack. + // + if( hydrantHit ) + { + rAssert( hydrantObject != NULL ); + startCollisionPlayer( s_hydrantSprayName, hydrantObject, NULL, NULL ); + } + } +} + +//============================================================================= +// SoundFXGameplayLogic::handleObjectKick +//============================================================================= +// Description: Play a sound for an object that gets kicked +// +// Parameters: collObject - kicked object +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::handleObjectKick( CollisionEntityDSG* collObject ) +{ + CollisionAttributes* attributes; + const char* soundName; + + attributes = collObject->GetCollisionAttributes(); + if( attributes != NULL ) + { + soundName = attributes->GetSound(); + + // + // Cheat: don't play this positionally because I'd have to work around + // the double-play filter because of the stinky fire hydrant. Hope + // that's okay + // + playSFXSound( soundName, true ); + + // + // More hydrant hack + // + if( attributes->GetBreakable() == BreakablesEnum::eHydrantBreaking ) + { + startCollisionPlayer( s_hydrantSprayName, collObject, NULL, NULL ); + } + } +} + +//============================================================================= +// SoundFXGameplayLogic::collisionPairMatches +//============================================================================= +// Description: Check for match between parameters and collision pair at +// given index +// +// Parameters: index - index into m_collisionSource that we're matching against +// firstObj, secondObj - collision objects to match with +// +// Return: true if matching, false otherwise +// +//============================================================================= +bool SoundFXGameplayLogic::collisionPairMatches( int index, void* firstObj, void* secondObj ) +{ + PositionalSFXPlayer* storedPair; + + rAssert( index < s_numCollisionSounds ); + + storedPair = &(m_collisionSounds[index]); + + return( ( ( storedPair->collObjA == firstObj ) && + ( storedPair->collObjB == secondObj ) ) + + || + + ( ( storedPair->collObjA == secondObj ) && + ( storedPair->collObjB == firstObj ) ) ); +} + +//============================================================================= +// SoundFXGameplayLogic::startCollisionPlayer +//============================================================================= +// Description: Find a free positional sound player (or stop one if necessary) +// and play the sound and store the collision pair +// +// Parameters: soundName - name of sound resource to play +// objA - first colliding object +// objB - second colliding object +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::startCollisionPlayer( const char* soundName, + CollisionEntityDSG* objA, + CollisionEntityDSG* objB, + rmt::Vector* positionPtr ) +{ + int index; + rmt::Vector posnA; + rmt::Vector posnB; + rmt::Vector average; + rmt::Vector vectorToListener; + radSoundVector rsListenerPosition; + rmt::Vector listenerPosition; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + IRadSoundHalListener* theListener; + + + // + // Look for a free player first + // + for( index = 0; index < s_numCollisionSounds; index++ ) + { + if( !(m_collisionSounds[index].soundPlayer.IsInUse()) ) + { + break; + } + } + + // + // If all are in use, stop one. Arbitrarily, use the one + // at the start of the list, since it's most likely to have + // been playing the longest. No need to get cute. + // + if( index >= s_numCollisionSounds ) + { + index = 0; + m_collisionSounds[0].soundPlayer.Stop(); + + rWarningMsg( false, "Collision sound dropped for lack of players\n" ); + } + + // + // Store the collision pair + // + m_collisionSounds[index].collObjA = objA; + m_collisionSounds[index].collObjB = objB; + + // + // Play the sound halfway between the positions of the + // colliding objects (if two objects are used) + // + if( positionPtr != NULL ) + { + average = *positionPtr; + } + else if( objA == NULL ) + { + objB->GetPosition( &average ); + } + else if( objB == NULL ) + { + objA->GetPosition( &average ); + } + else + { + objA->GetPosition( &posnA ); + objB->GetPosition( &posnB ); + average.Add( posnA, posnB ); + average *= 0.5f; + } + + if( GetGameplayManager()->IsSuperSprint() ) + { + // + // Hoo boy. Big stinky hack coming up. The problem is that sounds are + // positional, and the camera is a very long ways away in the bonus game. + // Fudge the position to make the collisions suitably close. + // + theListener = ::radSoundHalListenerGet( ); + rAssert( theListener != NULL ); + + theListener->GetPosition( &rsListenerPosition ); + listenerPosition.Set( rsListenerPosition.m_x, rsListenerPosition.m_y, rsListenerPosition.m_z ); + vectorToListener = average - listenerPosition; + vectorToListener.Scale( 0.01f, 0.01f, 0.01f ); + average = listenerPosition + vectorToListener; + } + + if( m_collisionMinMax == NULL ) + { + // + // Lazy initialization. Grab the positional characteristics for collision sounds + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( "collision_sounds" ); + rAssert( nameSpaceObj != NULL ); + m_collisionMinMax = reinterpret_cast<positionalSoundSettings*>(nameSpaceObj); + } + + m_collisionSounds[index].soundPlayer.SetPosition( average.x, average.y, average.z ); + m_collisionSounds[index].soundPlayer.SetParameters( m_collisionMinMax ); + m_collisionSounds[index].soundPlayer.PlaySound( soundName ); + m_collisionSounds[index].soundPlayer.SetTrim( 1.0f ); +} + +//============================================================================= +// SoundFXGameplayLogic::handleFootstepEvent +//============================================================================= +// Description: Plays appropriate footstep sound +// +// Parameters: walkingCharacter - character doing the walking +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::handleFootstepEvent( Character* walkingCharacter ) +{ + eTerrainType terrain; + bool isInterior; + globalSettings* clipNameObj; + const char* name = NULL; + + rAssert( walkingCharacter != NULL ); + walkingCharacter->GetTerrainType( terrain, isInterior ); + + // + // Get the parameter object for this positional sound. + // + clipNameObj = getGlobalSettings(); + + if( isInterior ) + { + // + // Use the road clip indoors + // + name = clipNameObj->GetFootstepRoadClipName(); + } + else if( terrain == TT_Metal ) + { + name = clipNameObj->GetFootstepMetalClipName(); + } + else if( terrain == TT_Wood ) + { + name = clipNameObj->GetFootstepWoodClipName(); + } + else + { + name = clipNameObj->GetFootstepRoadClipName(); + } + + rAssert( name != NULL ); + + playSFXSound( name, true ); +} + +//============================================================================= +// SoundFXGameplayLogic::handleSwitchEvent +//============================================================================= +// Description: Play clicking sound for big red switches +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::handleSwitchEvent() +{ + playSFXSound( "switch", false ); +} + +//============================================================================= +// SoundFXGameplayLogic::handleCollection +//============================================================================= +// Description: Play the appropriate sound for picking up a collectible +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::handleCollection() +{ + RenderEnums::LevelEnum level; + int mission; + const char* soundName = NULL; + + // + // We don't have a way to identify collectible types, so select a + // sound resource based on our level/mission + // + level = GetGameplayManager()->GetCurrentLevelIndex(); + mission = GetGameplayManager()->GetCurrentMissionIndex(); + switch( level ) + { + case RenderEnums::L1 : + soundName = "level_1_pickup_sfx"; + break; + case RenderEnums::L2 : + if( mission == 6 ) + { + soundName = "monkey_collect"; + } + else + { + soundName = "level_2_pickup_sfx"; + } + break; + case RenderEnums::L3 : + soundName = "level_3_pickup_sfx"; + break; + case RenderEnums::L4 : + soundName = "level_4_pickup_sfx"; + break; + case RenderEnums::L5 : + soundName = "level_5_pickup_sfx"; + break; + case RenderEnums::L6 : + soundName = "level_6_pickup_sfx"; + break; + case RenderEnums::L7 : + soundName = "nuclear_waste_collect"; + break; + default: + break; + } + + rAssertMsg( ( soundName != NULL ), "Collection without sound effect, tell Esan\n" ); + + playSFXSound( soundName, true ); +} + +//============================================================================= +// SoundFXGameplayLogic::playPositionalSound +//============================================================================= +// Description: Plays a positional sound in the world when a positional +// (a.k.a. script) trigger is hit +// +// Parameters: locator - locator for the sound +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::playPositionalSound( ScriptLocator* locator ) +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + positionalSoundSettings* parameters; + float diceRoll; + float probability; + int i; + rmt::Vector locatorPosition; + + rAssert( locator != NULL ); + + // + // Get the parameter object for this positional sound. + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( locator->GetKey() ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj ); + + if( !(locator->GetPlayerEntered()) ) + { + // + // Player is exiting volume, not entering. If we're playing a sound + // already, stop it + // + for( i = 0; i < s_numPositionalSounds; i++ ) + { + if( m_positionalSounds[i].IsInUse() + && ( m_positionalSounds[i].GetParameters() == parameters ) ) + { + m_positionalSounds[i].Stop(); + break; + } + } + } + else + { + probability = parameters->GetPlaybackProbability(); + if( probability < 1.0f ) + { + // + // Random play + // + diceRoll = (static_cast<float>( rand() % 100 )) / 100.0f; + if( diceRoll >= probability ) + { + return; + } + } + + // + // Find a player and play + // + for( i = 0; i < s_numPositionalSounds; i++ ) + { + if( !(m_positionalSounds[i].IsInUse()) ) + { + locator->GetLocation( &locatorPosition ); + m_positionalSounds[i].SetPosition( locatorPosition.x, + locatorPosition.y, + locatorPosition.z ); + m_positionalSounds[i].SetParameters( parameters ); + + // + // Don't buffer, to save IOP + // + + m_positionalSounds[i].PlaySound( parameters->GetClipName(), NULL ); + break; + } + } + } + } + else + { + rDebugString( "Couldn't play missing positional sound" ); + } +} + +void SoundFXGameplayLogic::playCarDoorSound( EventEnum eventType, Character* playerCharacter ) +{ + Vehicle* car; + carSoundParameters* parameters; + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + const char* clipName; + + // + // Get name of clip + // + car = playerCharacter->GetTargetVehicle(); + if( car != NULL ) + { + // + // Get the car sound parameter object for this vehicle. + // + // IMPORTANT: We assume that the object in the namespace has the same + // name as the one in the vehicle object, which comes from the loading + // script for that car, I think. + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + nameSpaceObj = nameSpace->GetInstance( car->GetName() ); + if( nameSpaceObj != NULL ) + { + parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj ); + + if( eventType == EVENT_GETINTOVEHICLE_START ) + { + clipName = parameters->GetCarDoorOpenClipName(); + } + else + { + clipName = parameters->GetCarDoorCloseClipName(); + } + + if( clipName != NULL ) + { + playSFXSound( clipName, false ); + } + } + else + { + rDebugString( "Couldn't find car door sound\n" ); + } + } +} + +//============================================================================= +// SoundFXGameplayLogic::playCoinCollectSound +//============================================================================= +// Description: Play next coin collect sound in sequence +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXGameplayLogic::playCoinCollectSound() +{ + float pitch; + unsigned int numPitches; + globalSettings* pitchSequence = getGlobalSettings(); + + pitch = pitchSequence->GetCoinPitch( m_coinCounter ); + playSFXSound( "coin_collect_01", true, false, NULL, 1.0f, pitch ); + + numPitches = pitchSequence->GetNumCoinPitches(); + if( numPitches > 0 ) + { + m_coinCounter = ( m_coinCounter + 1 ) % numPitches; + } +} diff --git a/game/code/sound/soundfx/soundfxgameplaylogic.h b/game/code/sound/soundfx/soundfxgameplaylogic.h new file mode 100644 index 0000000..cb1e5f9 --- /dev/null +++ b/game/code/sound/soundfx/soundfxgameplaylogic.h @@ -0,0 +1,130 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxgameplaylogic.h +// +// Description: Declaration for the SoundFXGameplayLogic class, which handles +// the translation of events into sound effects for the game +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDFXGAMEPLAYLOGIC_H +#define SOUNDFXGAMEPLAYLOGIC_H + +//======================================== +// Nested Includes +//======================================== +#include <radmath/radmath.hpp> + +#include <sound/soundfx/soundfxlogic.h> +#include <sound/positionalsoundplayer.h> +#include <events/eventenum.h> + +//======================================== +// Forward References +//======================================== + +class SoundCollisionData; +class Character; +class ScriptLocator; +class CollisionEntityDSG; +class globalSettings; + +//============================================================================= +// +// Synopsis: PositionalSFXPlayer +// +// Associated stuff required to play a positional collision sound +// +//============================================================================= +struct PositionalSFXPlayer +{ + PositionalSoundPlayer soundPlayer; + void* collObjA; + void* collObjB; +}; + +//============================================================================= +// +// Synopsis: SoundFXGameplayLogic +// +//============================================================================= + +class SoundFXGameplayLogic : public SoundFXLogic +{ + public: + SoundFXGameplayLogic(); + virtual ~SoundFXGameplayLogic(); + + void RegisterEventListeners(); + + virtual void HandleEvent( EventEnum id, void* pEventData ); + + // + // Override callback to trigger tutorial events + // + void OnPlaybackComplete(); + + // + // Virtual function to clean up positional sounds + // + void Cleanup(); + + private: + //Prevent wasteful constructor creation. + SoundFXGameplayLogic( const SoundFXGameplayLogic& original ); + SoundFXGameplayLogic& operator=( const SoundFXGameplayLogic& rhs ); + + globalSettings* getGlobalSettings(); + + void handleCollisionEvent( SoundCollisionData* collisionData ); + void handleFootstepEvent( Character* walkingCharacter ); + void handleSwitchEvent(); + void handleCollection(); + void handleObjectKick( CollisionEntityDSG* collObject ); + void playPositionalSound( ScriptLocator* locator ); + void playCarDoorSound( EventEnum eventType, Character* playerCharacter ); + void playCoinCollectSound(); + void startCollisionPlayer( const char* soundName, + CollisionEntityDSG* objA, + CollisionEntityDSG* objB, + rmt::Vector* positionPtr ); + + bool collisionPairMatches( int index, void* firstObj, void* secondObj ); + + // Number of simultaneous positional sounds + static const int s_numPositionalSounds = 3; + + // + // Positional sound objects + // + PositionalSoundPlayer m_positionalSounds[s_numPositionalSounds]; + + // Number of simultaneous collision sounds + static const int s_numCollisionSounds = 6; + + // + // Positional collision sounds + // + PositionalSFXPlayer m_collisionSounds[s_numCollisionSounds]; + + positionalSoundSettings* m_collisionMinMax; + + // + // Counter for cycling through ka-ching sounds + // + unsigned int m_coinCounter; + + // + // Timing for suspension ronks + // + unsigned int m_lastRonkTime; + + globalSettings* m_globalSettings; +}; + + +#endif // SOUNDFXGAMEPLAYLOGIC_H + diff --git a/game/code/sound/soundfx/soundfxlogic.cpp b/game/code/sound/soundfx/soundfxlogic.cpp new file mode 100644 index 0000000..798d061 --- /dev/null +++ b/game/code/sound/soundfx/soundfxlogic.cpp @@ -0,0 +1,322 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxlogic.cpp +// +// Description: Implement the SoundFXLogic class, which is an abstract +// base class for objects that translate events into sound effects +// in the different game states +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/soundfxlogic.h> + +#include <events/eventmanager.h> +#include <events/eventdata.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +struct CreditInfo +{ + int lineNumber; + radKey32 dialogName; +}; + +#ifdef PAL + const int PAL_OFFSET = +16; // due to additional VUG localization team +#else + const int PAL_OFFSET = 0; +#endif + +static CreditInfo s_creditDialogTable[] = +{ + { 7, ::radMakeKey32( "pubcredits" ) }, + { 55, ::radMakeKey32( "foxcredits" ) }, + { 178 + PAL_OFFSET, ::radMakeKey32( "radproducer" ) }, + { 190 + PAL_OFFSET, ::radMakeKey32( "radlead" ) }, + { 204 + PAL_OFFSET, ::radMakeKey32( "raddesign" ) }, + { 221 + PAL_OFFSET, ::radMakeKey32( "radworld" ) }, + { 230 + PAL_OFFSET, ::radMakeKey32( "radmodel" ) }, + { 235 + PAL_OFFSET, ::radMakeKey32( "radfx" ) }, + { 242 + PAL_OFFSET, ::radMakeKey32( "radfmvart" ) }, + { 251 + PAL_OFFSET, ::radMakeKey32( "radfeart" ) }, + { 260 + PAL_OFFSET, ::radMakeKey32( "radprog" ) }, + { 279 + PAL_OFFSET, ::radMakeKey32( "radtest" ) }, + { 289 + PAL_OFFSET, ::radMakeKey32( "radsound" ) } +}; + +static int s_creditDialogTableSize = sizeof( s_creditDialogTable ) / sizeof( CreditInfo ); + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundFXLogic::SoundFXLogic +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXLogic::SoundFXLogic() +{ + unsigned int i; + + for( i = 0; i < s_numSFXPlayers; i++ ) + { + m_soundPlayers[i].isKillable = true; + } +} + +//============================================================================== +// SoundFXLogic::~SoundFXLogic +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXLogic::~SoundFXLogic() +{ +} + +//============================================================================= +// SoundFXLogic::UnregisterEventListeners +//============================================================================= +// Description: Unregister all events with the Event Manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXLogic::UnregisterEventListeners() +{ + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// SoundFXLogic::GetAvailableSFXPlayer +//============================================================================= +// Description: Find an unused SFXPlayer. Failing that, find a killable +// SFXPlayer. Failing that, well, just fail. +// +// Parameters: index - Address of unsigned int, filled in with index of +// SFXPlayer if one is available and address is non-NULL, +// untouched otherwise +// +// Return: SFXPlayer pointer if one available, NULL otherwise +// +//============================================================================= +SFXPlayer* SoundFXLogic::GetAvailableSFXPlayer( unsigned int* index ) +{ + unsigned int i; + int lastKillable = -1; + + // + // First, look for free players + // + for( i = 0; i < s_numSFXPlayers; i++ ) + { + if( !m_soundPlayers[i].soundPlayer.IsInUse() ) + { + if( index != NULL ) + { + *index = i; + } + return( &(m_soundPlayers[i]) ); + } + else if( m_soundPlayers[i].isKillable ) + { + lastKillable = i; + } + } + + // + // If we get this far, all players are in use. Kill a player if we can + // + if( lastKillable != -1 ) + { + if( index != NULL ) + { + *index = lastKillable; + } + return( &(m_soundPlayers[lastKillable]) ); + } + else + { + return( NULL ); + } +} + +//============================================================================= +// SoundFXLogic::playSFXSound +//============================================================================= +// Description: Searches through the list of SFXPlayers for one that's free. +// If one isn't found, find one that's killable. If none of them +// are killable, eh, no sound. +// +// Parameters: resource - name of sound resource to play +// killable - true if sound can be killed prematurely, false otherwise +// useCallback - true if we set callback on playback completion, +// false otherwise +// index - to be filled in with index of SFXPlayer used if sound +// played and index is non-NULL +// trim - volume to play sound at +// pitch - pitch to play sound at +// +// Return: true if sound played, false otherwise +// +//============================================================================= +bool SoundFXLogic::playSFXSound( const char* resource, bool killable, + bool useCallback, unsigned int* index, + float trim, float pitch ) +{ + SFXPlayer* player; + bool success = false; + SimpsonsSoundPlayerCallback* callbackObj = NULL; + + // + // Get a player if possible + // + player = GetAvailableSFXPlayer( index ); + + if( player != NULL ) + { + if( player->soundPlayer.IsInUse() ) + { + player->soundPlayer.Stop(); + } + + if( useCallback ) + { + callbackObj = this; + } + player->soundPlayer.PlaySound( resource, callbackObj ); + player->soundPlayer.SetTrim( trim ); + player->soundPlayer.SetPitch( pitch ); + player->isKillable = killable; + + success = true; + } + else + { + rDebugString( "Dropped sound effect, no player available\n" ); + } + + return( success ); +} + +//============================================================================= +// SoundFXLogic::ServiceOncePerFrame +//============================================================================= +// Description: Does nothing. Subclasses with servicing requirements need +// to override this function +// +// Parameters: elapsedTime - time elapsed since last frame +// +// Return: void +// +//============================================================================= +void SoundFXLogic::ServiceOncePerFrame( unsigned int elapsedTime ) +{ +} + +//============================================================================= +// SoundFXLogic::OnSoundReady +//============================================================================= +// Description: Does nothing. Needed to pull this in to get OnPlaybackComplete +// from SimpsonsSoundPlayer. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXLogic::OnSoundReady() +{ +} + +//============================================================================= +// SoundFXLogic::OnPlaybackComplete +//============================================================================= +// Description: Does nothing. Subclasses with callback requirements need +// to override this virtual function. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXLogic::OnPlaybackComplete() +{ +} + +//============================================================================= +// SoundFXLogic::Cleanup +//============================================================================= +// Description: Does nothing. Subclasses with stuff to clean up override +// this virtual function. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXLogic::Cleanup() +{ +} + +//****************************************************************************** +// +// Protected Member Functions +// +//****************************************************************************** + +//============================================================================= +// SoundFXLogic::playCreditLine +//============================================================================= +// Description: Play a credits conversation +// +// Parameters: lineNumber - last scrolled line number in credits text +// +// Return: void +// +//============================================================================= +void SoundFXLogic::playCreditLine( int lineNumber ) +{ + int i; + DialogEventData data; + + for( i = 0; i < s_creditDialogTableSize; i++ ) + { + if( lineNumber == s_creditDialogTable[i].lineNumber ) + { + data.charUID1 = tEntity::MakeUID( "kang" ); + data.charUID2 = tEntity::MakeUID( "kodos" ); + data.dialogName = s_creditDialogTable[i].dialogName; + + GetEventManager()->TriggerEvent( EVENT_IN_GAMEPLAY_CONVERSATION, static_cast<void*>(&data) ); + } + } +} diff --git a/game/code/sound/soundfx/soundfxlogic.h b/game/code/sound/soundfx/soundfxlogic.h new file mode 100644 index 0000000..f5735f6 --- /dev/null +++ b/game/code/sound/soundfx/soundfxlogic.h @@ -0,0 +1,91 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxlogic.h +// +// Description: Declaration for the SoundFXLogic class, which is an abstract +// base class for objects that translate events into sound effects +// in the different game states +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDFXLOGIC_H +#define SOUNDFXLOGIC_H + +//======================================== +// Nested Includes +//======================================== +#include <events/eventlistener.h> +#include <sound/simpsonssoundplayer.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: SFXPlayer +// +// Structure that maps the sound player to a flag indicating whether +// we can kill the sound effect for something higher priority +// +//============================================================================= +struct SFXPlayer +{ + SimpsonsSoundPlayer soundPlayer; + bool isKillable; +}; + +//============================================================================= +// +// Synopsis: SoundFXLogic +// +//============================================================================= + +class SoundFXLogic : public EventListener, + public SimpsonsSoundPlayerCallback +{ + public: + SoundFXLogic(); + virtual ~SoundFXLogic(); + + virtual void RegisterEventListeners() = 0; + void UnregisterEventListeners(); + + SFXPlayer* GetAvailableSFXPlayer( unsigned int* index = NULL ); + + virtual void ServiceOncePerFrame( unsigned int elapsedTime ); + + virtual void Cleanup(); + + // + // SimpsonsSoundPlayerCallback functions + // + void OnSoundReady(); + virtual void OnPlaybackComplete(); + + protected: + // Number of SFXPlayers + static const unsigned int s_numSFXPlayers = 6; + + // + // Sound players + // + SFXPlayer m_soundPlayers[s_numSFXPlayers]; + + bool playSFXSound( const char* resource, bool killable, bool useCallback = false, + unsigned int* index = NULL, float trim = 1.0f, float pitch = 1.0f ); + + void playCreditLine( int lineNumber ); + + private: + //Prevent wasteful constructor creation. + SoundFXLogic( const SoundFXLogic& original ); + SoundFXLogic& operator=( const SoundFXLogic& rhs ); +}; + + +#endif // SOUNDFXLOGIC_H + diff --git a/game/code/sound/soundfx/soundfxpauselogic.cpp b/game/code/sound/soundfx/soundfxpauselogic.cpp new file mode 100644 index 0000000..961996a --- /dev/null +++ b/game/code/sound/soundfx/soundfxpauselogic.cpp @@ -0,0 +1,98 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxpauselogic.cpp +// +// Description: Implements the SoundFXPauseLogic class, which handles +// the translation of events into sound effects for the pause +// menu. +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/soundfxpauselogic.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundFXPauseLogic::SoundFXPauseLogic +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXPauseLogic::SoundFXPauseLogic() +{ +} + +//============================================================================== +// SoundFXPauseLogic::~SoundFXPauseLogic +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundFXPauseLogic::~SoundFXPauseLogic() +{ +} + +//============================================================================= +// SoundFXPauseLogic::RegisterEventListeners +//============================================================================= +// Description: Register as listener of sound effect events with Event Manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundFXPauseLogic::RegisterEventListeners() +{ +} + +//============================================================================= +// SoundFXPauseLogic::HandleEvent +//============================================================================= +// Description: Play sound effects in response to events +// +// Parameters: id - Sound effect event identifier +// pEventData - Currently unused +// +// Return: void +// +//============================================================================= +void SoundFXPauseLogic::HandleEvent( EventEnum id, void* pEventData ) +{ +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundfx/soundfxpauselogic.h b/game/code/sound/soundfx/soundfxpauselogic.h new file mode 100644 index 0000000..5cc861f --- /dev/null +++ b/game/code/sound/soundfx/soundfxpauselogic.h @@ -0,0 +1,50 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundfxpauselogic.h +// +// Description: Declaration for the SoundFXPauseLogic class, which handles +// the translation of events into sound effects for the pause +// menu. +// +// History: 31/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDFXPAUSELOGIC_H +#define SOUNDFXPAUSELOGIC_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/soundfxlogic.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: SoundFXPauseLogic +// +//============================================================================= + +class SoundFXPauseLogic : public SoundFXLogic +{ + public: + SoundFXPauseLogic(); + virtual ~SoundFXPauseLogic(); + + void RegisterEventListeners(); + + void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + SoundFXPauseLogic( const SoundFXPauseLogic& original ); + SoundFXPauseLogic& operator=( const SoundFXPauseLogic& rhs ); +}; + + +#endif // SOUNDFXPAUSELOGIC_H + diff --git a/game/code/sound/soundfx/win32reverbcontroller.cpp b/game/code/sound/soundfx/win32reverbcontroller.cpp new file mode 100644 index 0000000..8876d0d --- /dev/null +++ b/game/code/sound/soundfx/win32reverbcontroller.cpp @@ -0,0 +1,127 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: win32reverbcontroller.cpp +// +// Description: Implementation for the Win32ReverbController class, which provides +// the Windows-specific reverb control +// +// History: 03/25/2003 + Created -- Ziemek +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radsound_win32.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/win32reverbcontroller.h> + +#include <sound/soundfx/reverbsettings.h> + +#include <memory/srrmemory.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// Win32ReverbController::Win32ReverbController +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Win32ReverbController::Win32ReverbController() +{ + m_reverbInterface = ::radSoundHalEffectEAX2ReverbCreate( GMA_PERSISTENT ); + m_reverbInterface->AddRef(); + + registerReverbEffect( m_reverbInterface ); +} + +//============================================================================== +// Win32ReverbController::~Win32ReverbController +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Win32ReverbController::~Win32ReverbController() +{ + m_reverbInterface->Release(); + m_reverbInterface = NULL; +} + +//============================================================================= +// Win32ReverbController::SetReverbOn +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Win32ReverbController::SetReverbOn( reverbSettings* settings ) +{ + if( settings != NULL ) + { + rReleaseString( "Settings not null\n" ); + SetReverbGain( settings->GetGain() ); + m_reverbInterface->SetRoom( settings->GetXboxRoom() ); + m_reverbInterface->SetRoomHF( settings->GetXboxRoomHF() ); + m_reverbInterface->SetRoomRolloffFactor( settings->GetXboxRoomRolloffFactor() ); + m_reverbInterface->SetDecayTime( settings->GetXboxDecayTime() ); + m_reverbInterface->SetDecayHFRatio( settings->GetXboxDecayHFRatio() ); + m_reverbInterface->SetReflections( settings->GetXboxReflections() ); + m_reverbInterface->SetReflectionsDelay( settings->GetXboxReflectionsDelay() ); + m_reverbInterface->SetReverb( settings->GetXboxReverb() ); + m_reverbInterface->SetReverbDelay( settings->GetXboxReverbDelay() ); + m_reverbInterface->SetEnvironmentDiffusion( settings->GetWinEnvironmentDiffusion() ); + m_reverbInterface->SetAirAbsorptionHF( settings->GetWinAirAbsorptionHF() ); + + prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(), + settings->GetFadeOutTime() ); + } +} + +//============================================================================= +// Win32ReverbController::SetReverbOff +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Win32ReverbController::SetReverbOff() +{ + startFadeOut(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundfx/win32reverbcontroller.h b/game/code/sound/soundfx/win32reverbcontroller.h new file mode 100644 index 0000000..055ae84 --- /dev/null +++ b/game/code/sound/soundfx/win32reverbcontroller.h @@ -0,0 +1,54 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: win32reverbcontroller.h +// +// Description: Declaration for the Win32ReverbController class, which provides +// the Windows-specific reverb control +// +// History: 03/25/2003 + Created -- Ziemek +// +//============================================================================= + +#ifndef WIN32REVERBCONTROLLER_H +#define WIN32REVERBCONTROLLER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/reverbcontroller.h> + +//======================================== +// Forward References +//======================================== +struct IRadSoundHalEffectEAX2Reverb; + +//============================================================================= +// +// Synopsis: Win32ReverbController +// +//============================================================================= + +class Win32ReverbController : public ReverbController +{ +public: + Win32ReverbController(); + virtual ~Win32ReverbController(); + + void SetReverbOn( reverbSettings* settings ); + void SetReverbOff(); + +private: + //Prevent wasteful constructor creation. + Win32ReverbController( const Win32ReverbController& original ); + Win32ReverbController& operator=( const Win32ReverbController& rhs ); + + // + // Radsound's Win32 reverb interface + // + IRadSoundHalEffectEAX2Reverb* m_reverbInterface; +}; + + +#endif // WIN32REVERBCONTROLLER_H + diff --git a/game/code/sound/soundfx/xboxreverbcontroller.cpp b/game/code/sound/soundfx/xboxreverbcontroller.cpp new file mode 100644 index 0000000..599aeee --- /dev/null +++ b/game/code/sound/soundfx/xboxreverbcontroller.cpp @@ -0,0 +1,127 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: xboxreverbcontroller.cpp +// +// Description: Implementation for the XboxReverbController class, which provides +// the Xbox-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radsound_xbox.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundfx/xboxreverbcontroller.h> + +#include <sound/soundfx/reverbsettings.h> + +#include <memory/srrmemory.h> + + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// XboxReverbController::XboxReverbController +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +XboxReverbController::XboxReverbController() +{ + m_reverbInterface = ::radSoundHalEffectI3DL2ReverbXBoxCreate( GMA_PERSISTENT ); + + registerReverbEffect( m_reverbInterface ); +} + +//============================================================================== +// XboxReverbController::~XboxReverbController +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +XboxReverbController::~XboxReverbController() +{ + m_reverbInterface->Release(); + m_reverbInterface = NULL; +} + +//============================================================================= +// XboxReverbController::SetReverbOn +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void XboxReverbController::SetReverbOn( reverbSettings* settings ) +{ + if( settings != NULL ) + { + rReleaseString( "Settings not null\n" ); + SetReverbGain( settings->GetGain() ); + m_reverbInterface->SetRoom( settings->GetXboxRoom() ); + m_reverbInterface->SetRoomHF( settings->GetXboxRoomHF() ); + m_reverbInterface->SetRoomRolloffFactor( settings->GetXboxRoomRolloffFactor() ); + m_reverbInterface->SetDecayTime( settings->GetXboxDecayTime() ); + m_reverbInterface->SetDecayHFRatio( settings->GetXboxDecayHFRatio() ); + m_reverbInterface->SetReflections( settings->GetXboxReflections() ); + m_reverbInterface->SetReflectionsDelay( settings->GetXboxReflectionsDelay() ); + m_reverbInterface->SetReverb( settings->GetXboxReverb() ); + m_reverbInterface->SetReverbDelay( settings->GetXboxReverbDelay() ); + m_reverbInterface->SetDiffusion( settings->GetXboxDiffusion() ); + m_reverbInterface->SetDensity( settings->GetXboxDensity() ); + m_reverbInterface->SetHFReference( settings->GetXboxHFReference() ); + + prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(), + settings->GetFadeOutTime() ); + } +} + +//============================================================================= +// XboxReverbController::SetReverbOff +//============================================================================= +// Description: Self-explanatory +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void XboxReverbController::SetReverbOff() +{ + startFadeOut(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundfx/xboxreverbcontroller.h b/game/code/sound/soundfx/xboxreverbcontroller.h new file mode 100644 index 0000000..5ef87f3 --- /dev/null +++ b/game/code/sound/soundfx/xboxreverbcontroller.h @@ -0,0 +1,54 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: xboxreverbcontroller.h +// +// Description: Declaration for the XboxReverbController class, which provides +// the Xbox-specific reverb control +// +// History: 10/28/2002 + Created -- Darren +// +//============================================================================= + +#ifndef XBOXREVERBCONTROLLER_H +#define XBOXREVERBCONTROLLER_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/soundfx/reverbcontroller.h> + +//======================================== +// Forward References +//======================================== +struct IRadSoundHalEffectI3DL2ReverbXBox; + +//============================================================================= +// +// Synopsis: XboxReverbController +// +//============================================================================= + +class XboxReverbController : public ReverbController +{ + public: + XboxReverbController(); + virtual ~XboxReverbController(); + + void SetReverbOn( reverbSettings* settings ); + void SetReverbOff(); + + private: + //Prevent wasteful constructor creation. + XboxReverbController( const XboxReverbController& original ); + XboxReverbController& operator=( const XboxReverbController& rhs ); + + // + // Radsound's Xbox reverb interface + // + IRadSoundHalEffectI3DL2ReverbXBox* m_reverbInterface; +}; + + +#endif // XBOXREVERBCONTROLLER_H + diff --git a/game/code/sound/soundloader.cpp b/game/code/sound/soundloader.cpp new file mode 100644 index 0000000..f0a2c64 --- /dev/null +++ b/game/code/sound/soundloader.cpp @@ -0,0 +1,601 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundloader.cpp +// +// Description: Implement SoundLoader class, which makes sure that sounds +// required in the game are allocated and resident in sound memory +// +// History: 26/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <stdio.h> + +#include <sound/soundloader.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundresourcemanager.h> + +#include <memory/srrmemory.h> +#include <loading/loadingmanager.h> +#include <worldsim/redbrick/vehicle.h> +#include <constants/vehicleenum.h> +#include <mission/gameplaymanager.h> +#include <events/eventmanager.h> +#include <worldsim/character/character.h> + +#include <radscript.hpp> + + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Names of sound clusters. Used so that the loading manager can tell us +// when it's our turn to loading something. Should correspond to SoundClusterName +// enumeration +// +static const char* s_clusterNames[] = +{ + "permanent", + "frontend", + "ingame", + "suburbs", + "downtown", + "seaside", + "level1", + "level2", + "level3", + "level4", + "level5", + "level6", + "level7", + "minigame", + "huh?", + "apu", + "bart", + "homer", + "lisa", + "marge", + "bart_v", + "apu_v", + "snake_v", + "homer_v", + "famil_v", + "gramp_v", + "cletu_v", + "wiggu_v", + "empty1", + "marge_v", + "empty2", + "empty3", + "smith_v", + "empty4", + "empty5", + "empty6", + "zombi_v", + "empty7", + "empty8", + "cVan", + "compactA", + "comic_v", + "skinn_v", + "cCola", + "cSedan", + "cPolice", + "cCellA", + "cCellB", + "cCellC", + "cCellD", + "minivanA_v", + "pickupA", + "taxiA_v", + "sportsA", + "sportsB", + "SUVA", + "wagonA", + "hbike_v", + "burns_v", + "honor_v", + "cArmor", + "cCurator", + "cHears", + "cKlimo", + "cLimo", + "cNerd", + "frink_v", + "cMilk", + "cDonut", + "bbman_v", + "bookb_v", + "carhom_v", + "elect_v", + "fone_v", + "gramR_v", + "moe_v", + "mrplo_v", + "otto_v", + "plowk_v", + "scorp_v", + "willi_v", + "sedanA", + "sedanB", + "cBlbart", + "cCube", + "cDuff", + "cNonup", + "lisa_v", + "krust_v", + "coffin", + "hallo", + "ship", + "witchcar", + "huska", + "atv_v", + "dune_v", + "hype_v", + "knigh_v", + "mono_v", + "oblit_v", + "rocke_v", + "ambul", + "burnsarm", + "fishtruc", + "garbage", + "icecream", + "istruck", + "nuctruck", + "pizza", + "schoolbu", + "votetruc", + "glastruc", + "cfire_v", + "cBone", + "redbrick" +}; + +static const int NumScriptNames = sizeof( s_clusterNames ) / sizeof( const char* ); + +// +// Indices of character namespaces for each level. +// +// TODO: I don't like these tables, there must be a more data-driven way to do +// this. +// 0 == Apu +// 1 == Bart +// 2 == Homer +// 3 == Lisa +// 4 == Marge +// +static unsigned int s_charNamespaceIndices[] = { 2, 1, 3, 4, 0, 1, 2, 0 }; + +static VehicleEnum::VehicleID s_carIndices[] = { VehicleEnum::FAMIL_V, + VehicleEnum::HONOR_V, + VehicleEnum::LISA_V, + VehicleEnum::MARGE_V, + VehicleEnum::APU_V, + VehicleEnum::BART_V, + VehicleEnum::HOMER_V, + VehicleEnum::FAMIL_V, +}; + +static unsigned int s_levelIndices[] = { 0, 1, 2, 0, 1, 2, 0, 0 }; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundLoader::SoundLoader +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundLoader::SoundLoader() : + m_currentCluster( SC_ALWAYS_LOADED ) +{ + unsigned int i; + SoundClusterName clusterIndex; + Sound::daSoundRenderingManager* renderingMgr = Sound::daSoundRenderingManagerGet(); + + for( i = 0; i < SC_MAX_CLUSTERS; i++ ) + { + m_clusterList[i] = NULL; + } + + for( clusterIndex = SC_ALWAYS_LOADED; + clusterIndex < SC_CHAR_APU; + clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) ) + { + m_clusterList[clusterIndex] = + new(GMA_PERSISTENT) SoundCluster( clusterIndex, + renderingMgr->GetSoundNamespace() ); + } + + for( clusterIndex = SC_CHAR_APU; + clusterIndex < SC_CAR_BASE; + clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) ) + { + m_clusterList[clusterIndex] = + new(GMA_PERSISTENT) SoundCluster( clusterIndex, + renderingMgr->GetCharacterNamespace( clusterIndex - SC_CHAR_APU ) ); + } + + for( clusterIndex = SC_CAR_BASE; + clusterIndex < SC_MAX_CLUSTERS; + clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) ) + { + m_clusterList[clusterIndex] = + new(GMA_PERSISTENT) SoundCluster( clusterIndex, + renderingMgr->GetSoundNamespace() ); + } + + // + // Register event listeners + // + GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START ); +} + +//============================================================================== +// SoundLoader::~SoundLoader +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundLoader::~SoundLoader() +{ + unsigned int i; + + for( i = 0; i < SC_MAX_CLUSTERS; i++ ) + { + if( m_clusterList[i] != NULL ) + { + m_clusterList[i]->Release(); + } + } + + GetEventManager()->RemoveAll( this ); +} + +//============================================================================= +// SoundLoader::LevelLoad +//============================================================================= +// Description: Loads the sound cluster for a particular level +// +// Parameters: RenderEnums::LevelEnum level - +// enumeration indicating the level whose sound cluster +// is to be loaded +// +// Return: void +// +//============================================================================= +void SoundLoader::LevelLoad( RenderEnums::LevelEnum level ) +{ + IRadNameSpace* charNamespace; + unsigned int levelNum; + + clusterUnload( SC_FRONTEND ); + + queueLoad( SC_INGAME ); + + if( GetGameplayManager()->IsSuperSprint() ) + { + queueLoad( SC_MINIGAME ); + } + else + { + rAssert( level <= RenderEnums::numLevels ); + levelNum = static_cast<unsigned int>(level); + + queueLoad( static_cast<SoundClusterName>( SC_LEVEL_SUBURBS + s_levelIndices[levelNum] ) ); + queueLoad( static_cast<SoundClusterName>( SC_CHAR_APU + s_charNamespaceIndices[levelNum] ) ); + queueLoad( static_cast<SoundClusterName>( SC_CAR_BASE + s_carIndices[levelNum] ) ); + queueLoad( static_cast<SoundClusterName>( SC_LEVEL1 + levelNum ) ); + + charNamespace = m_clusterList[SC_CHAR_APU + s_charNamespaceIndices[level]]->GetMyNamespace(); + rAssert( charNamespace != NULL ); + + // + // We need to do this for RadTuner, since we've got duplicate names and + // it won't necessarily find the correct character otherwise + // + charNamespace->MoveToFront(); + } +} + +//============================================================================= +// SoundLoader::LevelUnload +//============================================================================= +// Description: Unloads the sound cluster for a particular level +// +// Parameters: RenderEnums::LevelEnum level - +// enumeration indicating the level whose sound cluster +// is to be unloaded +// +// Return: void +// +//============================================================================= +void SoundLoader::LevelUnload( bool goingToFe ) +{ + SoundClusterName clusterIndex; + + // + // Unload everything that's not permanent + // + for( clusterIndex = SC_INGAME; + clusterIndex < SC_MAX_CLUSTERS; + clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) ) + { + if( m_clusterList[clusterIndex]->IsLoaded() ) + { + clusterUnload( clusterIndex ); + } + } + + if( goingToFe ) + { + queueLoad( SC_FRONTEND ); + } +} + +//============================================================================= +// SoundLoader::MissionLoad +//============================================================================= +// Description: Loads the sound cluster for a particular mission +// +// Parameters: RenderEnums::MissionEnum mission - +// enumeration indicating the mission whose sound cluster +// is to be loaded +// +// Return: void +// +//============================================================================= +void SoundLoader::MissionLoad( RenderEnums::MissionEnum mission ) +{ +} + +//============================================================================= +// SoundLoader::MissionUnload +//============================================================================= +// Description: Unloads the sound cluster for a particular mission +// +// Parameters: RenderEnums::MissionEnum mission - +// enumeration indicating the mission whose sound cluster +// is to be unloaded +// +// Return: void +// +//============================================================================= +void SoundLoader::MissionUnload( RenderEnums::MissionEnum mission ) +{ +} + +void SoundLoader::LoadCarSound( Vehicle* theCar, bool unloadOtherCars ) +{ + rAssert( theCar ); + + SoundClusterName clusterIndex; + SoundClusterName newCarCluster = static_cast<SoundClusterName>(SC_CAR_BASE + theCar->mVehicleID); + bool validCar = ( SC_CAR_BASE + theCar->mVehicleID ) < NumScriptNames; + + rAssertMsg( validCar, "A new vehicle has been added that the sound system does not have a script for. Tell Esan.\n" ); + + // + // Have we loaded this car already? + // + if( !validCar || m_clusterList[newCarCluster]->IsLoaded() ) + { + return; + } + + // + // Unload the existing car sound + // + if( unloadOtherCars ) + { + for( clusterIndex = SC_CAR_BASE; + clusterIndex < SC_MAX_CLUSTERS; + clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) ) + { + if( m_clusterList[clusterIndex]->IsLoaded() ) + { + clusterUnload( clusterIndex ); + } + } + } + + // + // Load the new car + // + queueLoad( newCarCluster ); +} + +//============================================================================= +// SoundLoader::IsSoundLoaded +//============================================================================= +// Description: Indicate whether a particular sound resource has been loaded +// +// Parameters: soundKey - hashed name of the sound resource to look for +// +// Return: true if loaded, falsed otherwise +// +//============================================================================= +bool SoundLoader::IsSoundLoaded( Sound::daResourceKey soundKey ) +{ + unsigned int i; + + for( i = 0; i < SC_MAX_CLUSTERS; i++ ) + { + if( ( m_clusterList[i] != NULL ) && ( m_clusterList[i]->ContainsResource( soundKey ) ) ) + { + return( m_clusterList[i]->IsLoaded() ); + } + } + + return( false ); +} + +//============================================================================= +// SoundLoader::LoadClusterByName +//============================================================================= +// Description: Given a cluster name from the loading manager, load the +// desired cluster +// +// Parameters: clusterName - text name for the cluster +// callbackObj - loading file handler to notify on completion +// +// Return: true if cluster already loaded, false otherwise +// +//============================================================================= +bool SoundLoader::LoadClusterByName( const char* clusterName, SoundFileHandler* callbackObj ) +{ + const char* shortName; + unsigned int i; + + // Strip out the "sound:" prefix + rAssert( strlen( clusterName ) > 6 ); + shortName = &(clusterName[6]); + + // + // Find the matching cluster name + // + for( i = 0; i < SC_MAX_CLUSTERS; i++ ) + { + if( strcmp( shortName, s_clusterNames[i] ) == 0 ) + { + return( clusterLoad( static_cast<SoundClusterName>( i ), callbackObj ) ); + } + } + + // + // If we get here, cluster not found + // + rAssert( false ); + return( false ); +} + +//============================================================================= +// SoundLoader::HandleEvent +//============================================================================= +// Description: Comment +// +// Parameters: ( EventEnum id, void* pEventData ) +// +// Return: void +// +//============================================================================= +void SoundLoader::HandleEvent( EventEnum id, void* pEventData ) +{ + Character* theCharacter; + Vehicle* theCar; + + switch( id ) + { + case EVENT_GETINTOVEHICLE_START: + // + // Make sure we've got the correct car sound for this vehicle + // + theCharacter = static_cast<Character*>(pEventData); + rAssert( theCharacter != NULL ); + theCar = theCharacter->GetTargetVehicle(); + rAssert( theCar != NULL ); + + LoadCarSound( theCar, true ); + break; + + default: + rAssert( false ); + break; + } +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// SoundLoader::queueLoad +//============================================================================= +// Description: Queue a cluster load with the loading manager +// +// Parameters: cluster - name of cluster to queue +// +// Return: void +// +//============================================================================= +void SoundLoader::queueLoad( SoundClusterName cluster ) +{ + char fakeFilename[50]; + + // + // Create a pseudo filename that we'll give to the loading manager. + // Content doesn't really matter, we'll just throw it out when the + // loading manager passes it back + // + if( cluster >= NumScriptNames ) + { + // + // Just load Bart for now + // + cluster = SC_CAR_BASE; + } + + sprintf( fakeFilename, "sound:%s", s_clusterNames[cluster] ); + GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, fakeFilename, GMA_LEVEL_AUDIO ); +} + +//============================================================================= +// SoundLoader::clusterLoad +//============================================================================= +// Description: Find the specified sound cluster and direct it to load sounds +// +// Parameters: SoundClusterName name - specifies which cluster to load +// +// Return: false if cluster not yet loaded, true otherwise +// +//============================================================================= +bool SoundLoader::clusterLoad( SoundClusterName name, SoundFileHandler* callbackObj ) +{ + bool loaded; + + rAssert( name < SC_MAX_CLUSTERS ); + + loaded = m_clusterList[name]->IsLoaded(); + if( !loaded ) + { + m_clusterList[name]->LoadSounds( callbackObj ); + } + + return( loaded ); +} + +void SoundLoader::clusterUnload( SoundClusterName name ) +{ + rAssert( name < SC_MAX_CLUSTERS ); + if( m_clusterList[name]->IsLoaded() ) + { + m_clusterList[name]->UnloadSounds(); + } +}
\ No newline at end of file diff --git a/game/code/sound/soundloader.h b/game/code/sound/soundloader.h new file mode 100644 index 0000000..fad45fe --- /dev/null +++ b/game/code/sound/soundloader.h @@ -0,0 +1,98 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundloader.h +// +// Description: Declaration for the SoundLoader class, used to manage the loading +// and unloading of sound clips and streams. +// +// History: 25/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDLOADER_H +#define SOUNDLOADER_H + +//======================================== +// Nested Includes +//======================================== + +#include <render/Enums/RenderEnums.h> +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundclusternameenum.h> +#include <sound/soundcluster.h> +#include <events/eventlistener.h> + +//======================================== +// Forward References +//======================================== + +class SoundFileHandler; +class Vehicle; + +//============================================================================= +// +// Synopsis: SoundLoader class declaration +// +//============================================================================= + +class SoundLoader : public EventListener +{ + public: + SoundLoader(); + virtual ~SoundLoader(); + + bool LoadClusterByName( const char* clusterName, SoundFileHandler* callbackObj ); + + void LoadPermanentSounds() { queueLoad( SC_ALWAYS_LOADED ); } + + void LevelLoad( RenderEnums::LevelEnum level ); + void LevelUnload( bool goingToFe ); + + void MissionLoad( RenderEnums::MissionEnum mission ); + void MissionUnload( RenderEnums::MissionEnum mission ); + + void LoadFrontEnd() { queueLoad( SC_FRONTEND ); } + void UnloadFrontEnd() { clusterUnload( SC_FRONTEND ); } + + void LoadCarSound( Vehicle* theCar, bool unloadOtherCars ); + + bool IsSoundLoaded( Sound::daResourceKey soundKey ); + + void SetCurrentCluster( SoundClusterName cluster ) + { rAssert( cluster != SC_MAX_CLUSTERS ); m_currentCluster = cluster; } + bool AddResourceToCurrentCluster( const char* resourceName ) + { return( m_clusterList[m_currentCluster]->AddResource( resourceName ) ); } + + // + // EventListener functions + // + void HandleEvent( EventEnum id, void* pEventData ); + + private: + //Prevent wasteful constructor creation. + SoundLoader( const SoundLoader& original ); + SoundLoader& operator=( const SoundLoader& rhs ); + + // + // Queue a load with the loading manager + // + void queueLoad( SoundClusterName cluster ); + + bool clusterLoad( SoundClusterName name, SoundFileHandler* callbackObj = NULL ); + void clusterUnload( SoundClusterName name ); + + // + // List of clusters, each holding list of loadable sounds + // + SoundCluster* m_clusterList[SC_MAX_CLUSTERS]; + + // + // Cluster that any created sound resources will be added to + // + SoundClusterName m_currentCluster; +}; + + +#endif // SOUNDLOADER_H + diff --git a/game/code/sound/soundmanager.cpp b/game/code/sound/soundmanager.cpp new file mode 100644 index 0000000..5fea9cc --- /dev/null +++ b/game/code/sound/soundmanager.cpp @@ -0,0 +1,2193 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundmanager.cpp +// +// Description: Manager interface that the other game components use to +// interact with sound. +// +// History: 01/06/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <radfactory.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundmanager.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundnucleus.hpp> + +#include <sound/music/musicplayer.h> +#include <sound/dialog/dialogcoordinator.h> +#include <sound/dialog/dialogline.h> +#include <sound/tuning/globalsettings.h> +#include <sound/soundfx/soundeffectplayer.h> +#include <sound/soundfx/reverbsettings.h> +#include <sound/soundfx/positionalsoundsettings.h> +#include <sound/movingpositional/movingsoundmanager.h> +#include <sound/sounddebug/sounddebugdisplay.h> + +#include <loading/loadingmanager.h> +#include <loading/soundfilehandler.h> + +#include <memory/srrmemory.h> +#include <mission/gameplaymanager.h> +#include <events/eventmanager.h> +#include <data/gamedatamanager.h> +#include <interiors/interiormanager.h> +#include <gameflow/gameflow.h> +#include <worldsim/avatarmanager.h> + +#include <string.h> + +#ifdef RAD_GAMECUBE +#include <dolphin/os.h> +#endif + +#ifdef RAD_WIN32 +#include <data/config/gameconfigmanager.h> +#endif + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// Static pointer to instance of singleton. +SoundManager* SoundManager::spInstance = NULL; + +// +// Sound file extensions +// +const char* RADSCRIPT_TYPE_INFO_FILE = "typ"; +const char* RADSCRIPT_SCRIPT_FILE = "spt"; +const char* RADMUSIC_SCRIPT_FILE = "rms"; + +// +// Sound mode flags, necessary because it's too late in the project +// to turn the boolean used to save the sound mode into an enumeration. +// Doh. +// +static const int SOUND_MODE_STEREO_FLAG = 1; +static const int SOUND_MODE_SURROUND_FLAG = 1 << 1; + +static unsigned int gLastServiceTime; +static unsigned int gLastServiceOncePerFrameTime; + +static const unsigned int BAD_SERVICE_TIME = 100; +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundManager::CreateInstance +//============================================================================== +// +// Description: Creates the SoundManager. +// +// Parameters: None. +// +// Return: Pointer to the SoundManager. +// +// Constraints: This is a singleton so only one instance is allowed. +// +//============================================================================== +SoundManager* SoundManager::CreateInstance( bool muteSound, bool noMusic, + bool noEffects, bool noDialogue ) +{ + MEMTRACK_PUSH_GROUP( "Sound" ); + + rAssert( spInstance == NULL ); + + spInstance = new(GMA_PERSISTENT) SoundManager( muteSound, noMusic, noEffects, noDialogue ); + rAssert( spInstance ); + + spInstance->initialize(); + + MEMTRACK_POP_GROUP( "Sound" ); + + return spInstance; +} + +//============================================================================== +// SoundManager::GetInstance +//============================================================================== +// +// Description: - Access point for the SoundManager singleton. +// +// Parameters: None. +// +// Return: Pointer to the SoundManager. +// +// Constraints: This is a singleton so only one instance is allowed. +// +//============================================================================== +SoundManager* SoundManager::GetInstance() +{ + rAssert( spInstance != NULL ); + + return spInstance; +} + + +//============================================================================== +// SoundManager::DestroyInstance +//============================================================================== +// +// Description: Destroy the SoundManager. +// +// Parameters: None. +// +// Return: None. +// +//============================================================================== +void SoundManager::DestroyInstance() +{ + rAssert( spInstance != NULL ); + + delete( GMA_PERSISTENT, spInstance ); + spInstance = NULL; +} + +//============================================================================== +// SoundManager::Update +//============================================================================== +// +// Description: Update the sound renderer. This should be done as frequently +// as possible. +// +// Parameters: None. +// +// Return: None. +// +//============================================================================== +void SoundManager::Update() +{ + unsigned int now = radTimeGetMilliseconds( ); + unsigned int dif = now - gLastServiceTime; + + if ( dif > BAD_SERVICE_TIME ) + { + #ifndef RAD_DEBUG + rReleasePrintf( "\nAUDIO: Detected Service Lag:[%d]ms -- this could cause skipping\n\n", dif ); + #endif + } + + gLastServiceTime = now; + + if( m_isMuted ) + { + return; + } + + m_musicPlayer->Service(); + + rAssert( m_pSoundRenderMgr != NULL ); + m_pSoundRenderMgr->Service(); +} + +//============================================================================== +// SoundManager::UpdateOncePerFrame +//============================================================================== +// +// Description: Update the sound renderer's expensive, can-do-once-per-frame +// stuff (e.g. positional sound info). +// +// Parameters: None. +// +// Return: None. +// +//============================================================================== +void SoundManager::UpdateOncePerFrame( unsigned int elapsedTime, ContextEnum context, bool useContext, bool isPausedForErrors ) +{ + + unsigned int now = radTimeGetMilliseconds( ); + unsigned int dif = now - gLastServiceOncePerFrameTime; + + if ( dif > BAD_SERVICE_TIME ) + { + #ifndef RAD_DEBUG + rReleasePrintf( "\nAUDIO: Detected ServiceOpf Lag:[%d]ms -- this could cause skipping\n\n", dif ); + #endif + } + + gLastServiceOncePerFrameTime = now; + + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + m_pSoundRenderMgr->ServiceOncePerFrame( elapsedTime ); + m_soundFXPlayer->ServiceOncePerFrame( elapsedTime ); + m_movingSoundManager->ServiceOncePerFrame(); + + if( !isPausedForErrors ) + { + // + // If we've paused the players, don't update this thing because it could + // start up new sounds + // + m_avatarSoundPlayer.UpdateOncePerFrame( elapsedTime ); + } + + // + // No point in updating listener position more than once per frame + // + if( useContext ) + { + m_listener.Update( context ); + } + + // + // The dialog queue timer list isn't that time-sensitive, once per frame + // should be fine + // + if( m_dialogCoordinator != NULL ) + { + m_dialogCoordinator->ServiceOncePerFrame(); + } +} + +//============================================================================= +// SoundManager::HandleEvent +//============================================================================= +// Description: Handle any events that the underlying systems won't do +// during mute. +// +// Parameters: id - ID of event to be handled +// pEventData - user data for event +// +// Return: void +// +//============================================================================= +void SoundManager::HandleEvent( EventEnum id, void* pEventData ) +{ + switch( id ) + { + case EVENT_CONVERSATION_SKIP: + // + // We only get this in mute mode. Nothing to stop, signal that we're done + // + GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE ); + break; + case EVENT_CONVERSATION_START: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_LETTERBOX, NULL, false ); + break; + case EVENT_CONVERSATION_DONE: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_LETTERBOX, NULL, true ); + break; + case EVENT_GETINTOVEHICLE_END: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, true ); + break; + case EVENT_GETOUTOFVEHICLE_END: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false ); + break; + case EVENT_ENTER_INTERIOR_START: + case EVENT_EXIT_INTERIOR_START: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, false ); + break; + case EVENT_ENTER_INTERIOR_END: + case EVENT_EXIT_INTERIOR_END: + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true ); + break; + + case EVENT_MISSION_RESET: + // + // D'oh! Problem: if you fail a mission just as you're trying to go into an interior, you never + // get the enter/exit_interior_end event. However, we will be getting a mission reset in this + // case, so bring up the volume just in case. + // + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true ); + + // + // Fall through to case below + // + + case EVENT_CHARACTER_POS_RESET: + case EVENT_VEHICLE_DESTROYED_SYNC_SOUND: + if( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() ) + { + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, true ); + } + else + { + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false ); + } + break; + + case EVENT_FE_MENU_SELECT: + playStartupAcceptSound(); + break; + + case EVENT_FE_MENU_UPORDOWN: + playStartupScrollSound(); + break; + + default: + rAssertMsg( false, "Huh? SoundManager getting events it shouldn't\n" ); + break; + } +} + +//============================================================================= +// SoundManager::OnBootupStart +//============================================================================= +// Description: Called when bootup context started +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::OnBootupStart() +{ + if( m_isMuted ) + { + return; + } + + // + // Load the RadScript and RadMusic files + // + m_pSoundRenderMgr->QueueCementFileRegistration(); + m_musicPlayer->QueueRadmusicScriptLoad(); + m_pSoundRenderMgr->QueueRadscriptFileLoads(); + + // + // Load front end sounds + // + m_soundLoader->LoadFrontEnd(); +} + +//============================================================================= +// SoundManager::OnBootupComplete +//============================================================================= +// Description: Initialize the dialog system once the scripts are known to +// have been loaded +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::OnBootupComplete() +{ + if( m_isMuted ) + { + return; + } + + dumpStartupSounds(); + + // Set up the tuner + m_pSoundRenderMgr->GetTuner()->PostScriptLoadInitialize(); + + // + // If the sound levels haven't been auto-loaded, set them now + // + if( !( GetGameDataManager()->IsGameLoaded() ) ) + { + ResetData(); + } + else + { + // + // Hack! If we've loaded sound volumes before we had the scripts loaded, + // then we wouldn't have been able to calculate the ambience volumes properly. + // Fix those up now. + // + SetAmbienceVolume( GetCalculatedAmbienceVolume() ); + } +} + +//============================================================================= +// SoundManager::QueueLevelSoundLoads +//============================================================================= +// Description: Prepare to load level-related sound through the loading manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::QueueLevelSoundLoads() +{ + RenderEnums::LevelEnum level; + + if( m_isMuted ) + { + return; + } + + level = GetGameplayManager()->GetCurrentLevelIndex(); + m_soundLoader->LevelLoad( level ); + m_musicPlayer->QueueMusicLevelLoad( level ); +} + +//============================================================================= +// SoundManager::ResetDucking +//============================================================================= +// Description: Bring all the volume levels back up +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::ResetDucking() +{ + if( m_isMuted ) + { + return; + } + m_pSoundRenderMgr->GetTuner()->ResetDuck(); +} + +//============================================================================= +// SoundManager::OnFrontEndStart +//============================================================================= +// Description: To be called when front end starts, so we can do initialization +// stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::OnFrontEndStart() +{ + if( m_isMuted ) + { + return; + } + + // + // Start loading the permanent, always-in-memory stuff + // + m_soundLoader->LoadPermanentSounds(); + + m_musicPlayer->OnFrontEndStart(); + m_soundFXPlayer->OnFrontEndStart(); +} + +//============================================================================= +// SoundManager::OnFrontEndEnd +//============================================================================= +// Description: To be called when front end ends, so we can do destruction +// stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::OnFrontEndEnd() +{ + if( m_isMuted ) + { + return; + } + + m_musicPlayer->OnFrontEndFinish(); +} + +//============================================================================= +// SoundManager::OnGameplayStart +//============================================================================= +// Description: To be called when gameplay starts, so we can do initialization +// stuff +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::OnGameplayStart() +{ + bool playerInCar; + + if( m_isMuted ) + { + return; + } + + // + // Pass on start notice + // + playerInCar = m_avatarSoundPlayer.OnBeginGameplay(); + m_musicPlayer->OnGameplayStart( playerInCar ); + m_soundFXPlayer->OnGameplayStart(); + + // + // Assume we're starting gameplay on foot, except for minigame. Start the ducking + // + if( GetGameplayManager()->IsSuperSprint() ) + { + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MINIGAME, NULL, false ); + } + else + { + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false ); + } +} + +//============================================================================= +// SoundManager::OnGameplayEnd +//============================================================================= +// Description: To be called when gameplay ends, so we can do destruction +// stuff +// +// Parameters: goingToFE - indicates whether we're exiting gameplay to go +// to the front end (as opposed to loading a different +// level) +// +// Return: void +// +//============================================================================= +void SoundManager::OnGameplayEnd( bool goingToFE ) +{ + if( m_isMuted ) + { + return; + } + + m_avatarSoundPlayer.OnEndGameplay(); + m_musicPlayer->OnGameplayFinish(); + m_dialogCoordinator->OnGameplayEnd(); + m_soundFXPlayer->OnGameplayEnd(); + + m_soundLoader->LevelUnload( goingToFE ); + m_musicPlayer->UnloadRadmusicScript(); + + // + // Set up for the front end + // + if( goingToFE ) + { + m_musicPlayer->QueueMusicLevelLoad( RenderEnums::L3 ); + } +} + +//============================================================================= +// SoundManager::OnPauseStart +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnPauseStart() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_PAUSE, NULL, false ); + + m_musicPlayer->OnPauseStart(); + m_dialogCoordinator->OnPauseStart(); + m_soundFXPlayer->OnPauseStart(); + m_NISPlayer->PauseAllNISPlayers(); + + GetEventManager()->TriggerEvent( EVENT_FE_PAUSE_MENU_START ); +} + +//============================================================================= +// SoundManager::OnPauseEnd +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnPauseEnd() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_PAUSE, NULL, true ); + + m_musicPlayer->OnPauseEnd(); + m_dialogCoordinator->OnPauseEnd(); + m_soundFXPlayer->OnPauseEnd(); + m_NISPlayer->ContinueAllNISPlayers(); + + GetEventManager()->TriggerEvent( EVENT_FE_PAUSE_MENU_END ); +} + +//============================================================================= +// SoundManager::OnStoreScreenStart +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnStoreScreenStart( bool playMusic ) +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_STORE, NULL, false ); + + if( playMusic ) + { + m_musicPlayer->OnStoreStart(); + } +} + +//============================================================================= +// SoundManager::OnStoreScreenEnd +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnStoreScreenEnd() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_STORE, NULL, true ); + + m_musicPlayer->OnStoreEnd(); +} + +//============================================================================= +// SoundManager::DuckEverythingButMusicBegin +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::DuckEverythingButMusicBegin( bool playMuzak ) +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, false ); + + if( playMuzak ) + { + m_musicPlayer->OnStoreStart(); + } +} + +//============================================================================= +// SoundManager::DuckEverythingButMusicEnd +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::DuckEverythingButMusicEnd( bool playMuzak ) +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true ); + + if( playMuzak ) + { + m_musicPlayer->OnStoreEnd(); + } +} + +//============================================================================= +// SoundManager::OnMissionBriefingStart +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnMissionBriefingStart() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MISSION, NULL, false ); + m_NISPlayer->PauseAllNISPlayers(); +} + +//============================================================================= +// SoundManager::OnMissionBriefingEnd +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::OnMissionBriefingEnd() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MISSION, NULL, true ); + m_NISPlayer->ContinueAllNISPlayers(); +} + +//============================================================================= +// SoundManager::DuckForInGameCredits +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::DuckForInGameCredits() +{ + if( m_isMuted ) + { + return; + } + + m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_CREDITS, NULL, false ); +} + +//============================================================================= +// SoundManager::LoadSoundFile +//============================================================================= +// Description: Pass on the file loading directive to the appropriate subsystem, +// with callback object for the sake of the loading manager +// +// Parameters: filename - name of file to load +// callbackObj - callback into loading system when complete +// +// Return: void +// +//============================================================================= +void SoundManager::LoadSoundFile( const char* filename, SoundFileHandler* callbackObj ) +{ + char fileExtension[4]; + int length = strlen( filename ); + + // + // Determine the appropriate sound subsystem by the file extension + // + rAssert( length > 4 ); + if( filename[length - 4] == '.' ) + { + strcpy( fileExtension, &(filename[strlen(filename) - 3]) ); + + if( strcmp( fileExtension, RADSCRIPT_TYPE_INFO_FILE ) == 0 ) + { + m_pSoundRenderMgr->LoadTypeInfoFile( filename, callbackObj ); + } + else if( strcmp( fileExtension, RADSCRIPT_SCRIPT_FILE ) == 0 ) + { + m_pSoundRenderMgr->LoadScriptFile( filename, callbackObj ); + } + else if( strcmp( fileExtension, RADMUSIC_SCRIPT_FILE ) == 0 ) + { + m_musicPlayer->LoadRadmusicScript( filename, callbackObj ); + } + else + { + // + // Oops, don't recognize that type + // + rAssert( false ); + } + } + else + { + // + // No file extension, must be a sound cluster name + // + if( m_soundLoader->LoadClusterByName( filename, callbackObj ) ) + { + // + // LoadClusterByName indicated that the cluster was already loaded, + // so call the callback + // + callbackObj->LoadCompleted(); + } + } +} + +//============================================================================= +// SoundManager::LoadCarSound +//============================================================================= +// Description: Load car sound. Duh. +// +// Parameters: theCar - vehicle whose sound is to be loaded +// +// Return: void +// +//============================================================================= +void SoundManager::LoadCarSound( Vehicle* theCar, bool unloadOtherCars ) +{ + if( m_isMuted ) + { + return; + } + + m_soundLoader->LoadCarSound( theCar, unloadOtherCars ); +} + +//============================================================================= +// SoundManager::GetMasterVolume +//============================================================================= +// Description: Get master volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetMasterVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + + return( m_pSoundRenderMgr->GetTuner()->GetMasterVolume() ); +} + +//============================================================================= +// SoundManager::SetMasterVolume +//============================================================================= +// Description: Set master volume +// +// Parameters: volume - new master volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetMasterVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetMasterVolume( volume ); + + // + // Stinky special cases. This must be fixed. + // + /*if ( m_musicPlayer ) + { + m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMusicVolume() ); + m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() ); + }*/ +} + +//============================================================================= +// SoundManager::GetSfxVolume +//============================================================================= +// Description: Get sound effect volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetSfxVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + + return( m_pSoundRenderMgr->GetTuner()->GetSfxVolume() ); +} + +//============================================================================= +// SoundManager::SetSfxVolume +//============================================================================= +// Description: Set sound effect volume +// +// Parameters: volume - new sound effect volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetSfxVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetSfxVolume( volume ); +} + +//============================================================================= +// SoundManager::GetCarVolume +//============================================================================= +// Description: Get sound effect volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetCarVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + + return( m_pSoundRenderMgr->GetTuner()->GetCarVolume() ); +} + +//============================================================================= +// SoundManager::SetCarVolume +//============================================================================= +// Description: Set sound effect volume +// +// Parameters: volume - new sound effect volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetCarVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetCarVolume( volume ); +} + +//============================================================================= +// SoundManager::GetMusicVolume +//============================================================================= +// Description: Get music volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetMusicVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + + //rAssert( m_musicPlayer->GetVolume() == m_pSoundRenderMgr->GetTuner()->GetMusicVolume() ); + + return( m_pSoundRenderMgr->GetTuner()->GetMusicVolume() ); +} + +//============================================================================= +// SoundManager::SetMusicVolume +//============================================================================= +// Description: Set music volume +// +// Parameters: volume - new music volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetMusicVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetMusicVolume( volume ); + + // + // Sadly, we have to deal with the music player outside of the tuner. The + // daSound layer doesn't do our interactive music, so volume is controlled + // separately. + // + //rAssert( m_musicPlayer != NULL ); + //m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() ); +} + +//============================================================================= +// SoundManager::SetMusicVolumeWithoutTuner +//============================================================================= +// Description: Set music volume without affecting the tuner's internally +// stored volume setting +// +// Parameters: volume - new music volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetMusicVolumeWithoutTuner( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_musicPlayer != NULL ); + m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() ); +} + +//============================================================================= +// SoundManager::GetAmbienceVolume +//============================================================================= +// Description: Get ambience volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetAmbienceVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + +#ifdef RAD_WIN32 + m_musicPlayer->SetAmbienceVolume( m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() ); +#endif + + rAssert( m_musicPlayer->GetAmbienceVolume() == m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() ); + + return( m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() ); +} + +//============================================================================= +// SoundManager::SetAmbienceVolume +//============================================================================= +// Description: Set ambience volume +// +// Parameters: volume - new music volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetAmbienceVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetAmbienceVolume( volume ); + + // + // Sadly, we have to deal with the ambience player outside of the tuner. The + // daSound layer doesn't do our interactive music, so volume is controlled + // separately. + // + rAssert( m_musicPlayer != NULL ); + m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() ); +} + +//============================================================================= +// SoundManager::SetAmbienceVolumeWithoutTuner +//============================================================================= +// Description: Set ambience volume without affecting the tuner's internally +// stored volume setting +// +// Parameters: volume - new music volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetAmbienceVolumeWithoutTuner( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_musicPlayer != NULL ); + m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() ); +} + +//============================================================================= +// SoundManager::GetCalculatedAmbienceVolume +//============================================================================= +// Description: Hack!! Used to figure out what the ambience volume should +// be based on the sfx volume, to tie it to the effect slider. +// +// Parameters: None +// +// Return: float +// +//============================================================================= +float SoundManager::GetCalculatedAmbienceVolume() +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + globalSettings* settings; + float ratio; + float newVolume; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + if(!nameSpace) + { + return( -1.0f ); + } + nameSpaceObj = nameSpace->GetInstance( "tuner" ); + if(!nameSpaceObj) + { + return( -1.0f ); + } + + settings = static_cast<globalSettings*>( nameSpaceObj ); + ratio = GetSfxVolume() / settings->GetSfxVolume(); + + newVolume = ratio * settings->GetAmbienceVolume(); + if( newVolume < 0.0f ) + { + newVolume = 0.0f; + } + else if( newVolume > 1.0f ) + { + newVolume = 1.0f; + } + + return( newVolume ); +} + +//============================================================================= +// SoundManager::GetDialogueVolume +//============================================================================= +// Description: Get dialogue volume +// +// Parameters: None +// +// Return: Volume setting from 0.0f to 1.0f +// +//============================================================================= +float SoundManager::GetDialogueVolume() +{ + if( m_isMuted ) + { + return( 0.0f ); + } + + rAssert( m_pSoundRenderMgr != NULL ); + + return( m_pSoundRenderMgr->GetTuner()->GetDialogueVolume() ); +} + +//============================================================================= +// SoundManager::DebugRender +//============================================================================= +// Description: Display the sound debug info +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::DebugRender() +{ + m_debugDisplay->Render(); +} + +//============================================================================= +// SoundManager::SetDialogueVolume +//============================================================================= +// Description: Set dialogue volume +// +// Parameters: volume - new dialog volume +// +// Return: void +// +//============================================================================= +void SoundManager::SetDialogueVolume( float volume ) +{ + if( m_isMuted ) + { + return; + } + + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->GetTuner()->SetDialogueVolume( volume ); +} + +//============================================================================= +// SoundManager::PlayCarOptionMenuStinger +//============================================================================= +// Description: Play option menu sound for car slider +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::PlayCarOptionMenuStinger() +{ + if( m_isMuted ) + { + return; + } + + m_soundFXPlayer->PlayCarOptionStinger( GetCarVolume() ); +} + +//============================================================================= +// SoundManager::PlayDialogueOptionMenuStinger +//============================================================================= +// Description: Play option menu sound for dialogue slider +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::PlayDialogueOptionMenuStinger() +{ + if( m_isMuted ) + { + return; + } + + m_soundFXPlayer->PlayDialogOptionStinger( GetDialogueVolume() ); +} + +//============================================================================= +// SoundManager::PlayMusicOptionMenuStinger +//============================================================================= +// Description: Play option menu sound for music slider +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::PlayMusicOptionMenuStinger() +{ + // + // Music is special. We're already playing music in the front end, + // so we don't need to play a stinger there. We need it in game, + // though. + // + if( m_isMuted || ( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ) ) + { + return; + } + + m_soundFXPlayer->PlayMusicOptionStinger( GetMusicVolume() ); +} + +//============================================================================= +// SoundManager::PlaySfxOptionMenuStinger +//============================================================================= +// Description: Play option menu sound for sfx slider +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::PlaySfxOptionMenuStinger() +{ + if( m_isMuted ) + { + return; + } + + m_soundFXPlayer->PlaySfxOptionStinger( GetSfxVolume() ); +} + +void SoundManager::LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback ) +{ + if( m_isMuted ) + { + return; + } + + m_NISPlayer->LoadNISSound( NISSoundID, callback ); +} + +void SoundManager::PlayNISSound( radKey32 NISSoundID, rmt::Box3D* boundingBox, NISSoundPlaybackCompleteCallback* callback ) +{ + if( m_isMuted ) + { + return; + } + + m_NISPlayer->PlayNISSound( NISSoundID, boundingBox, callback ); +} + +void SoundManager::StopAndDumpNISSound( radKey32 NISSoundID ) +{ + if( m_isMuted ) + { + return; + } + + m_NISPlayer->StopAndDumpNISSound( NISSoundID ); +} + +//============================================================================= +// SoundManager::StopForMovie +//============================================================================= +// Description: To be called when we want to stop the action for an FMV +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::StopForMovie() +{ + if ( !m_stoppedForMovie ) + { + if( m_isMuted ) + { + return; + } + + // + // Shut the works down + // + m_musicPlayer->StopForMovie(); + GetInteriorManager()->UnloadGagSounds(); + m_pSoundRenderMgr->GetPlayerManager()->PausePlayers(); + m_stoppedForMovie = true; + } +} + +//============================================================================= +// SoundManager::ResumeAfterMovie +//============================================================================= +// Description: Call this to start everything up again after an FMV +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::ResumeAfterMovie() +{ + if ( m_stoppedForMovie ) + { + if( m_isMuted ) + { + return; + } + + m_musicPlayer->ResumeAfterMovie(); + m_pSoundRenderMgr->GetPlayerManager()->ContinuePlayers(); + m_stoppedForMovie = false; + } +} + +//============================================================================= +// SoundManager::IsStoppedForMovie +//============================================================================= + +bool SoundManager::IsStoppedForMovie() +{ + if( m_isMuted ) + { + return true; + } + else + { + return ( m_stoppedForMovie + && m_musicPlayer->IsStoppedForMovie() + && m_pSoundRenderMgr->GetPlayerManager()->AreAllPlayersStopped() ); + } +} + +//============================================================================= +// SoundManager::MuteNISPlayers +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::MuteNISPlayers() +{ + m_pSoundRenderMgr->GetTuner()->MuteNIS(); +} + +//============================================================================= +// SoundManager::UnmuteNISPlayers +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void SoundManager::UnmuteNISPlayers() +{ + m_pSoundRenderMgr->GetTuner()->UnmuteNIS(); +} + +//============================================================================= +// SoundManager::RestartSupersprintMusic +//============================================================================= +// Description: Give the supersprint music a kick +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::RestartSupersprintMusic() +{ + m_musicPlayer->RestartSupersprintMusic(); +} + +//============================================================================= +// SoundManager::GetBeatValue +//============================================================================= +// Description: Pass on the beat from the music player +// +// Parameters: None +// +// Return: float from 0.0f to 4.0f +// +//============================================================================= +float SoundManager::GetBeatValue() +{ + rAssert( m_musicPlayer != NULL ); + + return( m_musicPlayer->GetBeatValue() ); +} + +//============================================================================= +// SoundManager::IsFoodCharacter +//============================================================================= +// Description: Determine whether this character gets Askfood lines or +// Idlereply +// +// Parameters: theGuy - ambient character being talked to +// +// Return: true for Askfood, false for Idlereply +// +//============================================================================= +bool SoundManager::IsFoodCharacter( Character* theGuy ) +{ + return( DialogLine::IsFoodCharacter( theGuy ) ); +} + +//============================================================================= +// SoundManager::SetDialogueLanguage +//============================================================================= +// Description: Switch the current cement file for dialogue to match the +// given language. +// +// Parameters: language - new language to use +// +// Return: void +// +//============================================================================= +void SoundManager::SetDialogueLanguage( Scrooby::XLLanguage language ) +{ + // TODO: Commented out to get PAL working for alpha. It's basically a no-op anyway until we get localized dialogue. --jdy + m_pSoundRenderMgr->SetLanguage( language ); +} + +//============================================================================= +// SoundManager::LoadData +//============================================================================= +// Description: Load sound settings (from saved game file). +// +// Parameters: +// +// Return: void +// +//============================================================================= +void SoundManager::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes ) +{ + SoundSettings soundSettings; + SoundMode loadedSoundMode; + float calculatedAmbienceVolume; + +#ifdef RAD_WIN32 // temp + return; +#endif + + memcpy( &soundSettings, dataBuffer, sizeof( soundSettings ) ); + + this->SetMusicVolume( soundSettings.musicVolume ); + this->SetSfxVolume( soundSettings.sfxVolume ); + this->SetCarVolume( soundSettings.carVolume ); + + calculatedAmbienceVolume = GetCalculatedAmbienceVolume(); + // + // If the volume returned is less than zero, we haven't initialized + // the scripts yet. This will get taken care of in OnBootupComplete(), + // then. + // + if( calculatedAmbienceVolume >= 0.0f ) + { + SetAmbienceVolume( GetCalculatedAmbienceVolume() ); + } + + // + // Hack! Hack! Hack! Hack! + // + // This is horrible. I'm not allowed to change the save structure, because we're + // too close to final. I can't cast that bool to an enumeration, because the PS2 + // changes its size in release. The Xbox compiler won't let me set bit flags on + // booleans. So, I'm going to use the dialogue volume to signal stereo/mono by + // adding 100 to the volume to signal mono. I feel dirty. + // + if( soundSettings.dialogVolume >= 100.0f ) + { + this->SetDialogueVolume( soundSettings.dialogVolume - 100.0f ); + loadedSoundMode = SOUND_MONO; + } + else + { + this->SetDialogueVolume( soundSettings.dialogVolume ); + if( soundSettings.isSurround ) + { + loadedSoundMode = SOUND_SURROUND; + } + else + { + loadedSoundMode = SOUND_STEREO; + } + } + +#ifdef RAD_GAMECUBE + // + // GameCube's IPL needs to override the loaded settings, sadly + // + u32 GCSoundMode = OSGetSoundMode(); + if( GCSoundMode == OS_SOUND_MODE_MONO ) + { + // + // IPL says mono, we go mono + // + loadedSoundMode = SOUND_MONO; + } + else + { + if( loadedSoundMode == SOUND_MONO ) + { + // + // IPL says stereo, we go stereo. Since the saved game had said + // mono, we need to choose a sub-sound mode. We'll go with ProLogic. + // + loadedSoundMode = SOUND_SURROUND; + } + } + +#endif + + this->SetSoundMode( loadedSoundMode ); +} + +//============================================================================= +// SoundManager::SaveData +//============================================================================= +// Description: Save sound settings (to saved game file). +// +// Parameters: +// +// Return: void +// +//============================================================================= +void SoundManager::SaveData( GameDataByte* dataBuffer, unsigned int numBytes ) +{ + SoundMode mode; + SoundSettings soundSettings; + + soundSettings.musicVolume = this->GetMusicVolume(); + soundSettings.sfxVolume = this->GetSfxVolume(); + soundSettings.carVolume = this->GetCarVolume(); + soundSettings.dialogVolume = this->GetDialogueVolume(); + + mode = this->GetSoundMode(); + if( mode == SOUND_MONO ) + { + // + // Horrible stinky hack explained in LoadData() + // + soundSettings.dialogVolume += 100.0f; + soundSettings.isSurround = false; + } + else if( mode == SOUND_STEREO ) + { + soundSettings.isSurround = false; + } + else + { + soundSettings.isSurround = true; + } + + memcpy( dataBuffer, &soundSettings, sizeof( soundSettings ) ); +} + +//============================================================================= +// SoundManager::ResetData +//============================================================================= +// Description: Reset sound settings to defaults. +// +// Parameters: +// +// Return: void +// +//============================================================================= +void SoundManager::ResetData() +{ + if( GetGameDataManager()->IsGameLoaded() ) + { + // since sound settings can be changed in the FE, we should + // never reset to defaults if a game is already loaded + // + return; + } + + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + globalSettings* settings; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( "tuner" ); + rAssert( nameSpaceObj != NULL ); + + settings = static_cast<globalSettings*>( nameSpaceObj ); + + SetSfxVolume( settings->GetSfxVolume() ); + SetMusicVolume( settings->GetMusicVolume() ); + SetAmbienceVolume( settings->GetAmbienceVolume() ); + SetDialogueVolume( settings->GetDialogueVolume() ); + SetCarVolume( settings->GetCarVolume() ); + + // + // Sound mode. For GameCube, get it from the IPL (Initial Program + // Loader, the thingy you get when you start up a GameCube without + // a disc). For PS2 and Xbox, default to stereo (RadSound ignores + // this stuff for Xbox anyway, since these settings are supposed + // to be changed from the dashboard). + // +#ifdef RAD_GAMECUBE + u32 GCSoundMode = OSGetSoundMode(); + if( GCSoundMode == OS_SOUND_MODE_MONO ) + { + SetSoundMode( SOUND_MONO ); + } + else + { + SetSoundMode( SOUND_SURROUND ); + } +#else + SetSoundMode( SOUND_SURROUND ); +#endif +} + +#ifdef RAD_WIN32 +//============================================================================= +// SoundManager::GetConfigName +//============================================================================= +// Description: Returns the name of the sound manager's config +// +// Parameters: n/a +// +// Returns: +// +//============================================================================= + +const char* SoundManager::GetConfigName() const +{ + return "Sound"; +} + +//============================================================================= +// SoundManager::GetNumProperties +//============================================================================= +// Description: Returns the number of config properties +// +// Parameters: n/a +// +// Returns: +// +//============================================================================= + +int SoundManager::GetNumProperties() const +{ + return 5; +} + +//============================================================================= +// SoundManager::LoadDefaults +//============================================================================= +// Description: Loads the default configuration for the sound manager. +// +// Parameters: n/a +// +// Returns: +// +//============================================================================= + +void SoundManager::LoadDefaults() +{ + IRadNameSpace* nameSpace; + IRefCount* nameSpaceObj; + globalSettings* settings; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + nameSpaceObj = nameSpace->GetInstance( "tuner" ); + rAssert( nameSpaceObj != NULL ); + + settings = static_cast<globalSettings*>( nameSpaceObj ); + + SetSfxVolume( settings->GetSfxVolume() ); + SetMusicVolume( settings->GetMusicVolume() ); + SetAmbienceVolume( settings->GetAmbienceVolume() ); + SetDialogueVolume( settings->GetDialogueVolume() ); + SetCarVolume( settings->GetCarVolume() ); +} + +//============================================================================= +// SoundManager::LoadConfig +//============================================================================= +// Description: Loads the manager's configuration +// +// Parameters: n/a +// +// Returns: +// +//============================================================================= + +void SoundManager::LoadConfig( ConfigString& config ) +{ + char property[ ConfigString::MaxLength ]; + char value[ ConfigString::MaxLength ]; + + while ( config.ReadProperty( property, value ) ) + { + if( _stricmp( property, "sfx" ) == 0 ) + { + float val = (float) atof( value ); + if( val >= 0 && val <= 1 ) + { + SetSfxVolume( val ); + } + } + else if( _stricmp( property, "music" ) == 0 ) + { + float val = (float) atof( value ); + if( val >= 0 && val <= 1 ) + { + SetMusicVolume( val ); + } + } + else if( _stricmp( property, "ambience" ) == 0 ) + { + float val = (float) atof( value ); + if( val >= 0 && val <= 1 ) + { + SetAmbienceVolume( val ); + } + } + else if( _stricmp( property, "dialogue" ) == 0 ) + { + float val = (float) atof( value ); + if( val >= 0 && val <= 1 ) + { + SetDialogueVolume( val ); + } + } + else if( _stricmp( property, "car" ) == 0 ) + { + float val = (float) atof( value ); + if( val >= 0 && val <= 1 ) + { + SetCarVolume( val ); + } + } + } +} + +//============================================================================= +// SoundManager::SaveConfig +//============================================================================= +// Description: Saves the manager's configuration to the config string. +// +// Parameters: config string to save to +// +// Returns: +// +//============================================================================= + +void SoundManager::SaveConfig( ConfigString& config ) +{ + char value[ 32 ]; + + sprintf( value, "%f", GetSfxVolume() ); + config.WriteProperty( "sfx", value ); + sprintf( value, "%f", GetMusicVolume() ); + config.WriteProperty( "music", value ); + sprintf( value, "%f", GetAmbienceVolume() ); + config.WriteProperty( "ambience", value ); + sprintf( value, "%f", GetDialogueVolume() ); + config.WriteProperty( "dialogue", value ); + sprintf( value, "%f", GetCarVolume() ); + config.WriteProperty( "car", value ); +} +#endif // RAD_WIN32 + +void SoundManager::SetSoundMode( SoundMode mode ) +{ + radSoundOutputMode radSoundMode; + + m_soundMode = mode; + + if( m_soundMode == SOUND_MONO ) + { + radSoundMode = radSoundOutputMode_Mono; + } + else if( m_soundMode == SOUND_STEREO ) + { + radSoundMode = radSoundOutputMode_Stereo; + } + else + { + radSoundMode = radSoundOutputMode_Surround; + } + + ::radSoundHalSystemGet()->SetOutputMode( radSoundMode ); +} + +SoundMode SoundManager::GetSoundMode() +{ + return( m_soundMode ); +} + +//****************************************************************************** +// +// Protected Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundManager::SoundManager +//============================================================================== +// +// Description: Constructor. +// +// Parameters: +// +// Return: N/A +// +//============================================================================== +SoundManager::SoundManager( bool noSound, bool noMusic, + bool noEffects, bool noDialogue ) : + m_soundLoader( NULL ), + m_musicPlayer( NULL ), + m_soundFXPlayer( NULL ), + m_NISPlayer( NULL ), + m_movingSoundManager( NULL ), + m_isMuted( noSound ), + m_noMusic( noMusic ), + m_noEffects( noEffects ), + m_noDialogue( noDialogue ), + m_stoppedForMovie( false ), + m_selectSoundClip( NULL ), + m_scrollSoundClip( NULL ), + m_selectSoundClipPlayer( NULL ), + m_scrollSoundClipPlayer( NULL ), + m_soundMode( SOUND_STEREO ) +{ + m_dialogCoordinator = NULL; + + Sound::daSoundRenderingManagerCreate( GMA_AUDIO_PERSISTENT ); + + if( !m_isMuted ) + { + m_pSoundRenderMgr = Sound::daSoundRenderingManagerGet(); + rAssert( m_pSoundRenderMgr != NULL ); + + m_pSoundRenderMgr->Initialize(); + } + + GetGameDataManager()->RegisterGameData( this, sizeof( SoundSettings ), "Sound Manager" ); + + prepareStartupSounds(); +} + +//============================================================================== +// SoundManager::~SoundManager +//============================================================================== +// +// Description: Destructor. first attempt. +// +// Parameters: N/A +// +// Return: N/A +// +//============================================================================== +SoundManager::~SoundManager() +{ + GetEventManager()->RemoveAll( this ); + + if( m_isMuted ) + { + return; + } + + delete( GMA_PERSISTENT, m_soundFXPlayer); + delete( GMA_PERSISTENT, m_movingSoundManager); + delete( GMA_PERSISTENT, m_NISPlayer); + delete( GMA_PERSISTENT, m_dialogCoordinator); + delete( GMA_PERSISTENT, m_musicPlayer); + delete( GMA_PERSISTENT, m_soundLoader); + + Sound::daSoundRenderingManagerTerminate(); + + delete( GMA_PERSISTENT, m_debugDisplay); +} + +//============================================================================= +// SoundManager::initialize +//============================================================================= +// Description: Do some class member initialization tasks that we can't really +// do in the constructor. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundManager::initialize() +{ + if( m_isMuted ) + { + // + // We need to intercept dialog skip events, since the dialog coordinator + // isn't going to do it if we're muted + // + GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP ); + return; + } + else + { + // + // Set up a few tuner-related events + // + GetEventManager()->AddListener( this, EVENT_CONVERSATION_START ); + GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE ); + GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END ); + GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_START ); + GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_END ); + GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START ); + GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_END ); + GetEventManager()->AddListener( this, EVENT_MISSION_RESET ); + GetEventManager()->AddListener( this, EVENT_CHARACTER_POS_RESET ); + GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_SYNC_SOUND ); + } + + m_debugDisplay = new( GMA_PERSISTENT ) SoundDebugDisplay( m_pSoundRenderMgr ); + m_soundLoader = new( GMA_PERSISTENT ) SoundLoader(); + m_musicPlayer = new( GMA_PERSISTENT ) MusicPlayer( *(m_pSoundRenderMgr->GetTuner()) ); + m_dialogCoordinator = new( GMA_PERSISTENT ) DialogCoordinator( m_pSoundRenderMgr->GetSoundNamespace() ); + m_NISPlayer = new( GMA_PERSISTENT ) NISSoundPlayer(); + m_movingSoundManager = new( GMA_PERSISTENT ) MovingSoundManager(); + m_soundFXPlayer = new( GMA_PERSISTENT ) SoundEffectPlayer(); + m_avatarSoundPlayer.Initialize(); + m_listener.Initialize( *m_pSoundRenderMgr ); + + // + // Apply the non-global mute options + // + if( m_noMusic ) + { + m_pSoundRenderMgr->GetTuner()->SetMusicVolume( 0.0f ); + } + if( m_noEffects ) + { + m_pSoundRenderMgr->GetTuner()->SetSfxVolume( 0.0f ); + } + if( m_noDialogue ) + { + m_pSoundRenderMgr->GetTuner()->SetDialogueVolume( 0.0f ); + } + + // + // Register a factory for creating the global settings object + // + ::radFactoryRegister( "globalSettings", (radFactoryOutParamProc*) ::GlobalSettingsObjCreate ); + ::radFactoryRegister( "reverbSettings", (radFactoryOutParamProc*) ::ReverbSettingsObjCreate ); + ::radFactoryRegister( "positionalSoundSettings", (radFactoryOutParamProc*) ::PositionalSettingsObjCreate ); + +#ifdef RAD_WIN32 + // + // Register with the game config manager + // + GetGameConfigManager()->RegisterConfig( this ); +#endif +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +void SoundManager::prepareStartupSounds() +{ + // + // Go direct to RadSound to load two sounds that we can + // play for bootcheck screens + // + IRadSoundRsdFileDataSource* selectFileDataSource = + radSoundRsdFileDataSourceCreate( GMA_DEFAULT ); + selectFileDataSource->AddRef(); + selectFileDataSource->InitializeFromFileName( "sound/accept.rsd", + false, + 0, + IRadSoundHalAudioFormat::Frames, + Sound::SoundNucleusGetClipFileAudioFormat() ); + m_selectSoundClip = radSoundClipCreate( GMA_DEFAULT ); + m_selectSoundClip->AddRef(); + m_selectSoundClip->Initialize( + selectFileDataSource, + ::radSoundHalSystemGet()->GetRootMemoryRegion(), + false, + "sound/accept.rsd" ); + selectFileDataSource->Release( ); + + m_selectSoundClipPlayer = radSoundClipPlayerCreate( GMA_DEFAULT ); + m_selectSoundClipPlayer->AddRef(); + + IRadSoundRsdFileDataSource* scrollFileDataSource = + radSoundRsdFileDataSourceCreate( GMA_DEFAULT ); + scrollFileDataSource->AddRef(); + scrollFileDataSource->InitializeFromFileName( "sound/scroll.rsd", + false, + 0, + IRadSoundHalAudioFormat::Frames, + Sound::SoundNucleusGetClipFileAudioFormat() ); + m_scrollSoundClip = radSoundClipCreate( GMA_DEFAULT ); + m_scrollSoundClip->AddRef(); + m_scrollSoundClip->Initialize( + scrollFileDataSource, + ::radSoundHalSystemGet()->GetRootMemoryRegion(), + false, + "sound/scroll.rsd" ); + scrollFileDataSource->Release( ); + + m_scrollSoundClipPlayer = radSoundClipPlayerCreate( GMA_DEFAULT ); + m_scrollSoundClipPlayer->AddRef(); + + // + // Starting listening for the select/scroll events + // + GetEventManager()->AddListener( this, EVENT_FE_MENU_SELECT ); + GetEventManager()->AddListener( this, EVENT_FE_MENU_UPORDOWN ); +} + +void SoundManager::dumpStartupSounds() +{ + m_selectSoundClip->Release(); + m_selectSoundClip = NULL; + + m_selectSoundClipPlayer->Release(); + m_selectSoundClipPlayer = NULL; + + m_scrollSoundClip->Release(); + m_scrollSoundClip = NULL; + + m_scrollSoundClipPlayer->Release(); + m_scrollSoundClipPlayer = NULL; + + GetEventManager()->RemoveListener( this, EVENT_FE_MENU_SELECT ); + GetEventManager()->RemoveListener( this, EVENT_FE_MENU_UPORDOWN ); +} + +void SoundManager::playStartupAcceptSound() +{ + if( m_selectSoundClip->GetState() == IRadSoundClip::Initialized ) + { + m_selectSoundClipPlayer->SetClip( m_selectSoundClip ); + m_selectSoundClipPlayer->Play(); + } +} + +void SoundManager::playStartupScrollSound() +{ + if( m_scrollSoundClip->GetState() == IRadSoundClip::Initialized ) + { + m_scrollSoundClipPlayer->SetClip( m_scrollSoundClip ); + m_scrollSoundClipPlayer->Play(); + } +}
\ No newline at end of file diff --git a/game/code/sound/soundmanager.h b/game/code/sound/soundmanager.h new file mode 100644 index 0000000..40f65da --- /dev/null +++ b/game/code/sound/soundmanager.h @@ -0,0 +1,377 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundmanager.h +// +// Description: Manager interface that the other game components use to +// interact with sound. +// +// History: 01/06/2002 + Created -- Darren +// +//============================================================================= + +#ifndef _SOUNDMANAGER_H +#define _SOUNDMANAGER_H + +#ifndef RAD_RELEASE +#define SOUNDDEBUG_ENABLED +#endif + + +// +// Debug display macros +// +#ifndef SOUNDDEBUG_ENABLED + +#define SOUNDDEBUG_RENDER() + +#else + +#define SOUNDDEBUG_RENDER() GetSoundManager()->DebugRender() + +#endif + +//======================================== +// Nested Includes +//======================================== + +#include <enums.h> + +#include <sound/soundloader.h> +#include <sound/avatar/avatarsoundplayer.h> +#include <sound/listener.h> +#include <sound/nis/nissoundplayer.h> + +#include <data/gamedata.h> +#include <events/eventlistener.h> +#include <contexts/contextenum.h> + +#ifdef RAD_WIN32 +#include <data/config/gameconfig.h> +#endif + +//======================================== +// Forward References +//======================================== + +class MusicPlayer; +class DialogCoordinator; +class SoundDebugDisplay; +class MovingSoundManager; +class Vehicle; +class Character; +class SoundEffectPlayer; +struct IRadSoundClip; +struct IRadSoundClipPlayer; + +//============================================================================= +// +// Synopsis: NIS loading and playback completion callbacks +// +//============================================================================= + +struct NISSoundLoadedCallback +{ + virtual void NISSoundLoaded() = 0; +}; + +struct NISSoundPlaybackCompleteCallback +{ + virtual void NISSoundPlaybackComplete() = 0; +}; + +enum SoundMode +{ + SOUND_MONO, + SOUND_STEREO, + SOUND_SURROUND +}; + +//============================================================================= +// +// Synopsis: SoundManager class declaration +// +//============================================================================= + +class SoundManager : public EventListener, + #ifdef RAD_WIN32 + public GameConfigHandler, //ziemek: this is ugly..doh + #endif + public GameDataHandler +{ + public: + // + // Singleton accessors + // + static SoundManager* CreateInstance( bool muteSound, bool noMusic, + bool noEffects, bool noDialogue ); + static SoundManager* GetInstance(); + static void DestroyInstance(); + + // + // EventListener interface + // + void HandleEvent( EventEnum id, void* pEventData ); + + // + // All-purpose sound file loading. Coordinates the sound system + // with the loading system + // + void LoadSoundFile( const char* filename, SoundFileHandler* callbackObj ); + + // + // Update routines. + // + void Update(); + // This one is for expensive stuff like positional sound calculations + // that we can get away with doing once per frame + void UpdateOncePerFrame( unsigned int elapsedTime, + ContextEnum context, + bool useContext = true, + bool isPausedForErrors = false ); + + // Prepare to load level sounds + void QueueLevelSoundLoads(); + + // Load the sounds associated with a car + void LoadCarSound( Vehicle* theCar, bool unloadOtherCars ); + + // Called when bootup context starts and ends + void OnBootupStart(); + void OnBootupComplete(); + + // Called when front end starts and ends + void OnFrontEndStart(); + void OnFrontEndEnd(); + + // Called when gameplay starts and ends + void OnGameplayStart(); + void OnGameplayEnd( bool goingToFE ); + + // Called when pause menu starts and ends (ends going back to gameplay, + // not loading) + void OnPauseStart(); + void OnPauseEnd(); + + // Called for pseudo-pause stuff like clothing and phone booth + // + void OnStoreScreenStart( bool playMusic = true ); + void OnStoreScreenEnd(); + + // Called when we want to kill everything but music (e.g. loading, + // minigame standings) + void DuckEverythingButMusicBegin( bool playMuzak = false ); + void DuckEverythingButMusicEnd( bool playMuzak = false ); + + // Called when mission briefing screen starts and ends + void OnMissionBriefingStart(); + void OnMissionBriefingEnd(); + + // Called when in-game credits start + // + void DuckForInGameCredits(); + + // Call these before and after FMVs + void StopForMovie(); + void ResumeAfterMovie(); + bool IsStoppedForMovie(); + + // Hack! Need to mute gags during conversations + void MuteNISPlayers(); + void UnmuteNISPlayers(); + + // + // Supersprint + // + void RestartSupersprintMusic(); + + // Surround sound control + void SetSoundMode( SoundMode mode ); + SoundMode GetSoundMode(); + + // Function for getting that funky beat. Values from 0.0f to 4.0f, assuming + // that Marc doesn't write any waltzes + float GetBeatValue(); + + // Special case dialog handling + static bool IsFoodCharacter( Character* theGuy ); + + // + // Volume controls. Values range from 0.0f to 1.0f + // + void SetMasterVolume( float volume ); + float GetMasterVolume(); + + void SetSfxVolume( float volume ); + float GetSfxVolume(); + + void SetCarVolume( float volume ); + float GetCarVolume(); + + void SetMusicVolume( float volume ); + float GetMusicVolume(); + + void SetAmbienceVolume( float volume ); + float GetAmbienceVolume(); + + void SetDialogueVolume( float volume ); + float GetDialogueVolume(); + + float GetCalculatedAmbienceVolume(); + + // + // Option menu stinger stuff + // + void PlayCarOptionMenuStinger(); + void PlayDialogueOptionMenuStinger(); + void PlayMusicOptionMenuStinger(); + void PlaySfxOptionMenuStinger(); + + // + // Ducking stuff + // + void ResetDucking(); + + // + // NIS Interface + // + void LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback = NULL ); + void PlayNISSound( radKey32 NISSoundID, rmt::Box3D* boundingBox, NISSoundPlaybackCompleteCallback* callback = NULL ); + void StopAndDumpNISSound( radKey32 NISSoundID ); + + // + // Language interface + // + void SetDialogueLanguage( Scrooby::XLLanguage language ); + + // + // Sound debug functions + // + void DebugRender(); + + // + // TODO: move these functions, they're not intended for use outside + // of the sound system + // + SoundLoader* GetSoundLoader() { return( m_soundLoader ); } + SoundDebugDisplay* GetDebugDisplay() { return( m_debugDisplay ); } + + // + // This should NOT be called outside the sound system. Unfortunately, + // to keep things clean, what I should do is split the MusicPlayer class + // and move a low-level controller into the soundrenderer layer. I don't + // have time for this. Things to do for the next round. + // + void SetMusicVolumeWithoutTuner( float volume ); + void SetAmbienceVolumeWithoutTuner( float volume ); + + // Implements GameDataHandler + // + virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes ); + virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes ); + virtual void ResetData(); + + #ifdef RAD_WIN32 + // Implementation of the GameConfigHandler interface + virtual const char* GetConfigName() const; + virtual int GetNumProperties() const; + virtual void LoadDefaults(); + virtual void LoadConfig( ConfigString& config ); + virtual void SaveConfig( ConfigString& config ); + #endif + + DialogCoordinator* m_dialogCoordinator; + + protected: + // + // Hide the SoundManager constructor and destructor so everyone + // is forced to use singleton accessors + // + SoundManager( bool noSound, bool noMusic, bool noEffects, bool noDialogue ); + ~SoundManager(); + + void initialize(); + + private: + //Prevent wasteful constructor creation. + SoundManager( const SoundManager& original ); + SoundManager& operator=( const SoundManager& rhs ); + + // + // Hack! + // + void prepareStartupSounds(); + void playStartupAcceptSound(); + void playStartupScrollSound(); + void dumpStartupSounds(); + + // Pointer to the one and only instance of this singleton. + static SoundManager* spInstance; + + struct SoundSettings + { + float musicVolume; + float sfxVolume; + float carVolume; + float dialogVolume; + bool isSurround; + }; + + // Sound loading subsystem + SoundLoader* m_soundLoader; + + // Avatar sound subsystem + AvatarSoundPlayer m_avatarSoundPlayer; + + // Music player subsystem + MusicPlayer* m_musicPlayer; + + // Sound effect subsystem + SoundEffectPlayer* m_soundFXPlayer; + + // Dialog subsystem + + + // NIS subsystem + NISSoundPlayer* m_NISPlayer; + + // RadSound listener update + Listener m_listener; + + // AI Vehicle sound subsystem + MovingSoundManager* m_movingSoundManager; + + // Sound debug display subsystem + SoundDebugDisplay* m_debugDisplay; + + // Mute options + bool m_isMuted; + + bool m_noMusic; + bool m_noEffects; + bool m_noDialogue; + + // Pointer to sound rendering interface + Sound::daSoundRenderingManager* m_pSoundRenderMgr; + + // [ps] avoid hammering on pause. + bool m_stoppedForMovie; + + // + // Hack for stinky pre-script-loading menu sounds + // + IRadSoundClip* m_selectSoundClip; + IRadSoundClip* m_scrollSoundClip; + + IRadSoundClipPlayer* m_selectSoundClipPlayer; + IRadSoundClipPlayer* m_scrollSoundClipPlayer; + + + SoundMode m_soundMode; +}; + +// A little syntactic sugar for getting at this singleton. +inline SoundManager* GetSoundManager() { return( SoundManager::GetInstance() ); } + +#endif //SOUNDMANAGER_H + diff --git a/game/code/sound/soundrenderer/allsoundrenderer.cpp b/game/code/sound/soundrenderer/allsoundrenderer.cpp new file mode 100644 index 0000000..f2315f3 --- /dev/null +++ b/game/code/sound/soundrenderer/allsoundrenderer.cpp @@ -0,0 +1,14 @@ +#include <sound/soundrenderer/dasoundplayer.cpp> +#include <sound/soundrenderer/fader.cpp> +#include <sound/soundrenderer/musicsoundplayer.cpp> +#include <sound/soundrenderer/soundnucleus.cpp> +#include <sound/soundrenderer/playermanager.cpp> +#include <sound/soundrenderer/soundallocatedresource.cpp> +#include <sound/soundrenderer/sounddynaload.cpp> +#include <sound/soundrenderer/soundrenderingmanager.cpp> +#include <sound/soundrenderer/soundresource.cpp> +#include <sound/soundrenderer/soundresourcemanager.cpp> +#include <sound/soundrenderer/soundtuner.cpp> +#include <sound/soundrenderer/wireplayers.cpp> +#include <sound/soundrenderer/wiresystem.cpp> +#include <sound/soundrenderer/tunerdebugpage.cpp> diff --git a/game/code/sound/soundrenderer/dasoundgroup.h b/game/code/sound/soundrenderer/dasoundgroup.h new file mode 100644 index 0000000..884b049 --- /dev/null +++ b/game/code/sound/soundrenderer/dasoundgroup.h @@ -0,0 +1,100 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: dasoundgroup.hpp +// +// Subsystem: Dark Angel - Sound Grouping +// +// Description: Contains definitions, enumerations, and interfaces for a +// Dark Angel sound group. +// +// Revisions: +// + Created October 18, 2001 -- breimer +// +//============================================================================= + +#ifndef _DASOUNDGROUP_HPP +#define _DASOUNDGROUP_HPP + +//============================================================================= +// Included Files +//============================================================================= + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Typedefs and Enumerations +//============================================================================= + +// +// The list of possible sound groups +// + +enum daSoundGroup { + // BASIC WIRING GROUPS + CARSOUND, + NIS, + + NUM_BASIC_SOUND_GROUPS, + + // SYSTEM WIRING GROUPS + // Warning: Do not modify + DIALOGUE = NUM_BASIC_SOUND_GROUPS, + DIALOGUE_TUNE, + DUCKABLE, + MASTER, + MASTER_TUNE, + MUSIC, + MUSIC_TUNE, + AMBIENCE, + AMBIENCE_TUNE, + SOUND_EFFECTS, + SOUND_EFFECTS_TUNE, + OPTIONS_MENU_STINGERS, + + NUM_SOUND_GROUPS +}; + +enum DuckSituations +{ + DUCK_FULL_FADE, + + DUCK_SIT_PAUSE, + DUCK_SIT_MISSION, + DUCK_SIT_LETTERBOX, + DUCK_SIT_DIALOG, + DUCK_SIT_STORE, + DUCK_SIT_ONFOOT, + DUCK_SIT_MINIGAME, + DUCK_SIT_JUST_MUSIC, + DUCK_SIT_CREDITS, + + NUM_DUCK_SITUATIONS +}; + +enum DuckVolumes +{ + DUCK_SFX, + DUCK_CAR, + DUCK_MUSIC, + DUCK_DIALOG, + DUCK_AMBIENCE, + + NUM_DUCK_VOLUMES +}; + +// +// A structure for holding a collection of volumes +// +struct DuckVolumeSet +{ + float duckVolume[NUM_DUCK_VOLUMES]; +}; + +} // Namespace +#endif //_DASOUNDGROUP_HPP + diff --git a/game/code/sound/soundrenderer/dasoundplayer.cpp b/game/code/sound/soundrenderer/dasoundplayer.cpp new file mode 100644 index 0000000..0220f47 --- /dev/null +++ b/game/code/sound/soundrenderer/dasoundplayer.cpp @@ -0,0 +1,1191 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: dasoundplayer.cpp +// +// Subsystem: Dark Angel - Sound players +// +// Description: Implements the a Dark Angel sound player +// +// Revisions: +// + Created October 16, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radlinkedclass.hpp> + +#include <radsound.hpp> +#include <radsoundmath.hpp> + +#include <sound/soundrenderer/soundsystem.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/soundplayer.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundnucleus.hpp> + +#include <main/commandlineoptions.h> + +//============================================================================= +// Static Variables (outside namespace) +//============================================================================= + + +// +// Initialially the player list is empty +// +Sound::daSoundPlayerBase* radLinkedClass< Sound::daSoundPlayerBase >::s_pLinkedClassHead = NULL; +Sound::daSoundPlayerBase* radLinkedClass< Sound::daSoundPlayerBase >::s_pLinkedClassTail = NULL; +Sound::daSoundClipStreamPlayer* radLinkedClass< Sound::daSoundClipStreamPlayer >::s_pLinkedClassHead = NULL; +Sound::daSoundClipStreamPlayer* radLinkedClass< Sound::daSoundClipStreamPlayer >::s_pLinkedClassTail = NULL; + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Debug Information +//============================================================================= + +// +// Use this if you want to debug the sound player +// +#ifndef FINAL +#ifndef NDEBUG +#define DASOUNDPLAYER_DEBUG +#ifdef DASOUNDPLAYER_DEBUG + +// Show sound state changes +static bool sg_ShowSoundStates = false; + +#endif //DASOUNDPLAYER_DEBUG +#endif //NDEBUG +#endif //FINAL + +//============================================================================= +// Definitions Macros and Constants +//============================================================================= + +// +// These are the minimum ranges that the given variables must range +// over to warrant a randomization calculation +// +const float DASOUND_MINFLOATVARYINGRANGE = 0.0001f; + +//============================================================================= +// Local functions +//============================================================================= + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundClipStreamPlayer Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundClipStreamPlayer::daSoundClipStreamPlayer +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundClipStreamPlayer::daSoundClipStreamPlayer( ) + : + m_Trim( 1.0f ), + m_GroupTrim( 1.0f ), + m_FaderGroupTrim( 1.0f ), + m_MasterTrim( 1.0f ), + m_State( State_DeCued ), + m_CueingState( CueingState_Null ), + m_CaptureCount( 0 ), + m_PauseCount( 0 ), + m_pAllocatedResource( NULL ), + m_AllocResInstanceID( 0 ), + m_pResource( NULL ), + m_pStateChangeCallback( NULL ), + m_pStateChangeCallbackUserData( NULL ), + m_Type( IDaSoundResource::UNKNOWN ) +{ + m_IsPositional = false; + m_pPositionalGroup = radSoundHalPositionalGroupCreate( GetThisAllocator( ) ); + m_pPositionalGroup->AddRef( ); + + m_CurrentTrim = 0.0f; + m_StoppingTrim = 1.0f; + m_VaryingTrim = 1.0f; + + m_Pitch = 1.0f; + m_VaryingPitch = 1.0f; + m_CurrentPitch = 1.0f; +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::~daSoundClipStreamPlayer +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundClipStreamPlayer::~daSoundClipStreamPlayer( ) +{ + if ( IDaSoundResource::CLIP == m_Type ) + { + m_ClipInfo.m_pClipPlayer->Stop( ); + m_ClipInfo.m_pClipPlayer->SetClip( NULL ); + m_ClipInfo.m_pClipPlayer->Release( ); + } + else + { + if ( m_StreamInfo.m_pResources != NULL ) + { + Sound::SoundNucleusUnCaptureStreamerResources( m_StreamInfo.m_pResources ); + m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( NULL, NULL ); + } + + if ( m_StreamInfo.m_pRsdFileDataSource != NULL ) + { + m_StreamInfo.m_pRsdFileDataSource->Release( ); + } + } + + if ( m_pResource != NULL ) + { + m_pResource->Release( ); + } + + if ( m_pPositionalGroup ) + { + m_pPositionalGroup->Release( ); + } + + if ( m_pAllocatedResource ) + { + m_pAllocatedResource->Release( ); + } + + if ( m_pResource != NULL ) + { + m_pResource->Release( ); + } +} + +void daSoundClipStreamPlayer::UpdateClip( void ) +{ + m_ClipInfo.m_pClipPlayer->SetTrim( m_CurrentTrim ); + m_ClipInfo.m_pClipPlayer->SetPitch( m_CurrentPitch ); + + IRadSoundClipPlayer * pCp = m_ClipInfo.m_pClipPlayer; + IRadSoundClipPlayer::State state = pCp->GetState( ); + + switch( m_State ) + { + case State_DeCued: + { + rAssert( IRadSoundClipPlayer::NoClip == state ); + break; + } + case State_CuedPlay: + case State_Cueing: + { + switch( m_CueingState ) + { + case CueingState_Null: + { + rAssert( false ); + break; + } + case CueingState_Resource: + { + rAssert( IRadSoundClipPlayer::NoClip == state ); + + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + if ( pFileInstance->GetState( ) == daSoundFileInstance::Loaded ) + { + HookUpAndCuePlayer( ); + } + + break; + } + case CueingState_Player: + { + m_CueingState = CueingState_Cued; + break; + } + case CueingState_Cued: + { + rAssert( IRadSoundClipPlayer::Stopped == state ); + + if ( State_CuedPlay == m_State ) + { + if( 0 == m_PauseCount ) + { + pCp->Play( ); + } + + m_CaptureCount++; + m_State = State_Playing; + } + else + { + m_State = State_Cued; + } + + if ( m_pStateChangeCallback ) + { + m_pStateChangeCallback->OnSoundReady( m_pStateChangeCallbackUserData ); + } + + m_CueingState = CueingState_Null; + + break; + } + } + break; + } + case State_Cued: + { + switch( state ) + { + case IRadSoundClipPlayer::Stopped: + break; + case IRadSoundClipPlayer::NoClip: + case IRadSoundClipPlayer::Queueing: + case IRadSoundClipPlayer::QueuedPlay: + case IRadSoundClipPlayer::Playing: + default: + rAssert( false ); + } + break; + } + case State_Playing: + { + if ( m_StoppingTrim <= 1.0f ) + { + m_StoppingTrim += radSoundVolumeChangeThreshold; + + if ( m_StoppingTrim >= 1.0f ) + { + m_StoppingTrim = 1.0f; + } + } + switch( state ) + { + case IRadSoundClipPlayer::NoClip: + case IRadSoundClipPlayer::Queueing: + case IRadSoundClipPlayer::QueuedPlay: + rAssert( false ); + break; + case IRadSoundClipPlayer::Playing: + rAssert( 0 == m_PauseCount ); + break; + case IRadSoundClipPlayer::Stopped: + if( m_PauseCount == 0 ) + { + m_State = State_Done; + UnCapture( ); + + if ( m_pStateChangeCallback ) + { + m_pStateChangeCallback->OnSoundDone( m_pStateChangeCallbackUserData ); + } + } + break; + default: + break; + } + break; + } + case State_Stopping: + { + if ( m_CurrentTrim <= 0.0f ) + { + m_ClipInfo.m_pClipPlayer->Stop( ); + m_State = State_Cued; + m_StoppingTrim = 1.0f; + UnCapture( ); + } + else + { + m_StoppingTrim -= radSoundVolumeChangeThreshold; + + if ( m_StoppingTrim <= 0.0f ) + { + m_StoppingTrim = 0.0f; + } + } + + break; + } + + case State_Done: + { + rAssert( IRadSoundClipPlayer::NoClip == state ); + break; + } + default: + { + rAssert( false ); + } + } +} + +void daSoundClipStreamPlayer::UpdateStream( void ) +{ + IRadSoundStreamPlayer::State state; + + if ( NULL != m_StreamInfo.m_pResources ) + { + m_StreamInfo.m_pResources->m_pStreamPlayer->SetTrim( m_CurrentTrim ); + m_StreamInfo.m_pResources->m_pStreamPlayer->SetPitch( m_CurrentPitch ); + state = m_StreamInfo.m_pResources->m_pStreamPlayer->GetState( ); + } + else + { + state = IRadSoundStreamPlayer::NoSource; + } + + switch( m_State ) + { + case State_DeCued: + { + rAssert( IRadSoundStreamPlayer::NoSource == state ); + break; + } + case State_CuedPlay: + case State_Cueing: + { + switch( m_CueingState ) + { + case CueingState_Resource: + { + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + + if ( pFileInstance->GetState( ) == daSoundFileInstance::Loaded ) + { + if ( NULL == m_StreamInfo.m_pRsdFileDataSource ) + { + m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID )->CreateFileDataSource( & m_StreamInfo.m_pRsdFileDataSource ); + } + else if ( IRadSoundRsdFileDataSource::Initialized == m_StreamInfo.m_pRsdFileDataSource->GetState( ) ) + { + /* rDebugPrintf( "DASOUND: Playing Streamed File: [%s], format: [%d], channels: [%d]\n", + pFileInstance->GetFileName( ), + m_StreamInfo.m_pRsdFileDataSource->GetFormat( )->GetEncoding( ), + m_StreamInfo.m_pRsdFileDataSource->GetFormat( )->GetNumberOfChannels( ) ); */ + + HookUpAndCuePlayer( ); + } + } + + break; + } + case CueingState_Player: + { + switch( state ) + { + case IRadSoundStreamPlayer::Queueing: + { + break; + } + case IRadSoundStreamPlayer::Paused: + { + + if ( m_pStateChangeCallback ) + { + m_pStateChangeCallback->OnSoundReady( m_pStateChangeCallbackUserData ); + } + + m_CueingState = CueingState_Cued; + break; + } + case IRadSoundStreamPlayer::NoSource: + case IRadSoundStreamPlayer::Playing: + case IRadSoundStreamPlayer::QueuedPlay: + default: + rAssert( false ); + } + + break; + } + case CueingState_Cued: + { + if ( State_Cueing == m_State ) + { + m_State = State_Cued; + } + else + { + if ( 0 == m_PauseCount ) + { + m_StreamInfo.m_pResources->m_pStreamPlayer->Play( ); + } + m_CaptureCount++; + m_State = State_Playing; + } + + m_CueingState = CueingState_Null; + break; + } + default: + { + rAssert( false ); + } + } + break; + } + case State_Cued: + { + rAssert( IRadSoundStreamPlayer::Paused == state ); + break; + } + case State_Playing: + { + m_StoppingTrim += radSoundVolumeChangeThreshold; + + if ( m_StoppingTrim >= 1.0f ) + { + m_StoppingTrim = 1.0f; + } + + switch( state ) + { + case IRadSoundStreamPlayer::Queueing: + rAssert(false); + break; + case IRadSoundStreamPlayer::Paused: + rAssert( 0 < m_PauseCount ); + break; + case IRadSoundStreamPlayer::NoSource: + m_State = State_Done; + UnCapture( ); // Internal + if ( m_pStateChangeCallback ) + { + m_pStateChangeCallback->OnSoundDone( m_pStateChangeCallbackUserData ); + } + break; + case IRadSoundStreamPlayer::Playing: + rAssert( 0 == m_PauseCount ); + break; + case IRadSoundStreamPlayer::QueuedPlay: + default: + rAssert( false ); + } + + break; + + } + case State_Stopping: + { + if ( m_CurrentTrim <= 0.0f ) + { + m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( ); + m_State = State_Cued; + m_StoppingTrim = 1.0f; + UnCapture( ); + } + else + { + m_StoppingTrim -= radSoundVolumeChangeThreshold; + + if ( m_StoppingTrim <= 0.0f ) + { + m_StoppingTrim = 0.0f; + } + } + + break; + } + case State_Done: + { + rAssert( IRadSoundStreamPlayer::NoSource == state ); + break; + } + default: + { + rAssert( false ); + } + } + +} + +void daSoundClipStreamPlayer::ServiceOncePerFrame( void ) +{ + // + // Update the state information. This now requires polling the player. + // + + State prevState; + CueingState prevCueingState; + + do + { + + CalculateCurrentTrim( ); + CalculateCurrentPitch( ); + + prevState = m_State; + prevCueingState = m_CueingState; + + switch( m_Type ) + { + case IDaSoundResource::CLIP: + UpdateClip( ); + break; + case IDaSoundResource::STREAM: + UpdateStream( ); + break; + default: + rAssert( 0 ); + break; + } + } + while( prevState != m_State || prevCueingState != m_CueingState ); +} + +void daSoundClipStreamPlayer::InitializeAsClipPlayer( void ) +{ + m_Type = IDaSoundResource::CLIP; + + m_ClipInfo.m_pClipPlayer = radSoundClipPlayerCreate( GetThisAllocator( ) ); + m_ClipInfo.m_pClipPlayer->AddRef( ); + +#ifndef RAD_WIN32 + // Prepare the player to listen for spacial effects + for( unsigned int i = 0; i < ::radSoundHalSystemGet( )->GetNumAuxSends( ); i++ ) + { + m_ClipInfo.m_pClipPlayer->SetAuxMode( i, radSoundAuxMode_PostFader ); + m_ClipInfo.m_pClipPlayer->SetAuxGain( i, 1.0f ); + } +#endif +} + +void daSoundClipStreamPlayer::InitializeAsStreamPlayer( void ) +{ + m_Type = IDaSoundResource::STREAM; + + m_StreamInfo.m_pResources = NULL; + m_StreamInfo.m_pRsdFileDataSource = NULL; +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::CapturePlayer +//============================================================================= +// Description: Capture the sound player +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::Capture( + IDaSoundResource* pResource, + bool isPositional ) +{ + rAssert( pResource != NULL ); + rAssert( State_DeCued == m_State ); + + m_IsPositional = isPositional; + + ++m_CaptureCount; + + // A bit of a hack, but the resource should be connected only at this time + rAssert( m_CaptureCount == 1 ); + + rAssert( m_pAllocatedResource == NULL ); + rAssert( m_pResource == NULL ); + + m_pResource = pResource; + m_pResource->CaptureResource( ); + + // + // Get an allocated resource + // + m_pAllocatedResource = daSoundResourceManager::GetInstance( )->FindAllocatedResource( m_pResource ); + + rAssertMsg( m_pAllocatedResource != NULL, "Resource not properly allocated" ); + + m_pAllocatedResource->AddRef( ); + + // Choose an instance to play... + m_AllocResInstanceID = m_pAllocatedResource->ChooseNextInstance( ); + + // + // Check the type of resource + // + + rAssert( m_Type == m_pResource->GetType( ) ); + + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + rAssert( pFileInstance != NULL ); + + if( pFileInstance->GetType( ) == IDaSoundResource::UNKNOWN ) + { + rAssert( false); // when does this happen ? -Th + m_State = State_DeCued; + } + else + { + // + // Start initializing + // + m_State = State_Cueing; + m_CueingState = CueingState_Resource; + } + + // Reset state. + + m_Pitch = 1.0f; + m_Trim = 1.0f; + + CalculateNewVaryingPitch( ); + CalculateNewVaryingTrim( ); +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::IsCaptured +//============================================================================= +// Description: Returns true if the player is already captured +// +//----------------------------------------------------------------------------- + +bool daSoundClipStreamPlayer::IsCaptured( void ) +{ + return( m_CaptureCount > 0 ); +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::ReleasePlayer +//============================================================================= +// Description: Releases a player. Automattically detatches any +// resources when it is no longer captured. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::UnCapture( void ) +{ + unsigned int i = 0; + + // + // I don't think this assertion is valid anymore. If a player is cued but not + // playing, then Stop() will release the resource, but the player isn't + // captured yet and this call shouldn't do anything -- DE + // + //rAssert( IsCaptured( ) ); + if( m_CaptureCount > 0 ) + { + --m_CaptureCount; + + if( 0 == m_CaptureCount ) + { + rAssert( NULL == m_pStateChangeCallback ); + rAssert( m_pResource != NULL ); + rAssert( m_pAllocatedResource->GetResource( ) == m_pResource ); + rAssert( m_Type == m_pResource->GetType( ) ); + + // Detatch the real player + switch( m_Type ) + { + case IDaSoundResource::CLIP: + { + rAssert( m_ClipInfo.m_pClipPlayer != NULL ); + + // Detach the clip + m_ClipInfo.m_pClipPlayer->Stop( ); + m_ClipInfo.m_pClipPlayer->SetClip( NULL ); + + break; + } + case IDaSoundResource::STREAM: + { + if ( m_StreamInfo.m_pResources != NULL ) + { + m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( NULL, NULL ); + + // Detach the stream + m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( ); + m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource( NULL ); + + if ( m_StreamInfo.m_pResources->m_pBufferedDataSource != NULL ) + { + m_StreamInfo.m_pResources->m_pBufferedDataSource->SetInputDataSource( NULL ); + } + + SoundNucleusUnCaptureStreamerResources( m_StreamInfo.m_pResources ); + m_StreamInfo.m_pResources = NULL; + } + + if ( m_StreamInfo.m_pRsdFileDataSource != NULL ) + { + m_StreamInfo.m_pRsdFileDataSource->Release( ); + m_StreamInfo.m_pRsdFileDataSource = NULL; + } + + break; + } + default: + rAssert( 0 ); + break; + } + + // Release the allocated resource + m_pAllocatedResource->Release( ); + m_pAllocatedResource = NULL; + m_pResource->ReleaseResource( ); + m_pResource = NULL; + + m_State = State_DeCued; + m_CueingState = CueingState_Null; + } + } +} + +//============================================================================= +// daSoundClipStreamPlayer::ChangeTrim +//============================================================================= +// Description: Change the trim setting for a sound group, or for the master +// volume +// +// Parameters: groupName - name of group to change +// newTrim - trim value +// +// Return: void +// +//============================================================================= +void daSoundClipStreamPlayer::ChangeTrim( daSoundGroup groupName, float newTrim ) +{ + daSoundGroup myGroup = GetSoundGroup(); + + if ( myGroup == groupName ) + { + SetGroupTrim(newTrim); + } + else if ( groupName == MASTER ) + { + SetMasterTrim(newTrim); + } + else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) ) + { + SetGroupTrim(newTrim); + } +} + +//============================================================================= +// daSoundClipStreamPlayer::ChangeFaderTrim +//============================================================================= +// Description: Change the trim setting for a sound group, or for the master +// volume +// +// Parameters: groupName - name of group to change +// newTrim - trim value +// +// Return: void +// +//============================================================================= +void daSoundClipStreamPlayer::ChangeFaderTrim( daSoundGroup groupName, float newTrim ) +{ + daSoundGroup myGroup = GetSoundGroup(); + + // + // ChangeTrim should be used for master volume settings + // + rAssert( groupName != MASTER ); + + if ( myGroup == groupName ) + { + SetFaderGroupTrim(newTrim); + } + else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) ) + { + SetFaderGroupTrim(newTrim); + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::GetSoundGroup +//============================================================================= +// Description: Get the sound group that this player belongs to. This is +// the sound group that its currently active sound is a part of. +// +// Returns: Returns the appropriate sound group. +// +//----------------------------------------------------------------------------- + +daSoundGroup daSoundClipStreamPlayer::GetSoundGroup( void ) +{ + daSoundGroup soundGroup = Sound::MASTER; + if( m_pAllocatedResource != NULL ) + { + rAssert( m_pAllocatedResource->GetResource( ) != NULL ); + soundGroup = m_pAllocatedResource->GetResource( )->GetSoundGroup( ); + } + return soundGroup; +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::RegisterSoundPlayerStateCallback +//============================================================================= +// Description: Register a sound player state callback +// +// Notes: Currently, only one callback is supported. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::RegisterSoundPlayerStateCallback +( + IDaSoundPlayerState* pCallback, + void* pUserData +) +{ + rAssertMsg( m_pStateChangeCallback == NULL, "Currently, only one callback is allowed" ); + m_pStateChangeCallback = pCallback; + m_pStateChangeCallbackUserData = pUserData; + if( m_pStateChangeCallback != NULL ) + { + m_pStateChangeCallback->AddRef( ); + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::UnregisterSoundPlayerStateCallback +//============================================================================= +// Description: Unregister a sound player state callback +// +// Notes: Currently, only one callback is supported. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::UnregisterSoundPlayerStateCallback +( + IDaSoundPlayerState* pCallback, + void* pUserData +) +{ + rAssert( pCallback == m_pStateChangeCallback ); + if( m_pStateChangeCallback != NULL ) + { + m_pStateChangeCallback->Release( ); + m_pStateChangeCallback = NULL; + m_pStateChangeCallbackUserData = NULL; + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::GetPlaybackTimeInSamples +//============================================================================= +// Description: Get the samples that have elapsed since playback started. +// +//----------------------------------------------------------------------------- + +unsigned int daSoundClipStreamPlayer::GetPlaybackTimeInSamples( void ) +{ + // Is this synchronized with play / pause? + unsigned int elapsedtime = 0; + if( IsCaptured( ) ) + { + // Update the positional information + IRadSoundPlayer* pPlayer = NULL; + switch( m_Type ) + { + case IDaSoundResource::CLIP: + { + pPlayer = static_cast< IRadSoundPlayer* > + ( + m_ClipInfo.m_pClipPlayer + ); + break; + } + case IDaSoundResource::STREAM: + { + pPlayer = static_cast< IRadSoundPlayer* > + ( + m_StreamInfo.m_pResources->m_pStreamPlayer + ); + break; + } + default: + rAssert( 0 ); + break; + } + if( pPlayer != NULL ) + { + elapsedtime = pPlayer->GetPlaybackTimeInSamples( ); + //elapsedtime = ::radSoundHalSystemGet( )->GetReferenceClock( ); + } + } + + // Return the time + return( elapsedtime ); +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::Play +//============================================================================= +// Description: Play the active sound resource. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::Play( void ) +{ + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + rAssert( pFileInstance != NULL ); + + #ifndef RAD_RELEASE + if ( daSoundFileInstance::Loaded != pFileInstance->GetState( ) ) + { + char fileName[ 256 ]; + pFileInstance->GetFileName( fileName, 256 ); + + rTunePrintf( + "\nAUDIO: Controlling code is not paying attention to latency: [%s]\n\n", fileName ); + } + #endif + + switch ( m_State ) + { + case State_Cueing: + { + m_State = State_CuedPlay; + break; + } + case State_Cued: + { + m_StoppingTrim = 1.0f; + + // fall through + } + case State_Stopping: + { + m_State = State_CuedPlay; + m_CueingState = CueingState_Cued; + break; + } + default: + { + rAssert( false ); + break; + } + } +} + + +void daSoundClipStreamPlayer::OnStitch( IRadSoundHalDataSource ** ppHds , unsigned int frameCount, void * pUserData ) +{ + bool loop = m_pAllocatedResource->GetResource( )->GetLooping( ); + + if ( loop || NULL != m_StreamInfo.m_pRsdFileDataSource ) + { + if ( NULL != m_StreamInfo.m_pRsdFileDataSource ) + { + // pass on ref count + + *ppHds = m_StreamInfo.m_pRsdFileDataSource; + m_StreamInfo.m_pRsdFileDataSource = NULL; + } + else + { + ref< IRadSoundRsdFileDataSource > refFds; + m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID )->CreateFileDataSource( & refFds ); + + *ppHds = refFds; + (*ppHds)->AddRef( ); + } + //rDebugPrintf( "DASOUND: Player: [0x%x] Stitching: [%s]\n", this, refFds->GetName( ) ); + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::Pause +//============================================================================= +// Description: Pause the active sound resource. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::Pause( void ) +{ + m_PauseCount++; + + if ( State_Playing == m_State ) + { + switch( m_Type ) + { + case IDaSoundResource::CLIP: + { + m_ClipInfo.m_pClipPlayer->Stop( ); + break; + } + case IDaSoundResource::STREAM: + { + m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( ); + break; + } + default: + { + rAssert( false ); + } + } + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::Continue +//============================================================================= +// Description: Continue the active sound resource. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::Continue( void ) +{ + rAssert( m_PauseCount > 0 ); + + m_PauseCount--; + + if ( 0 == m_PauseCount ) + { + if ( State_Playing == m_State ) + { + switch( m_Type ) + { + case IDaSoundResource::CLIP: + { + m_ClipInfo.m_pClipPlayer->Play( ); + break; + } + case IDaSoundResource::STREAM: + { + m_StreamInfo.m_pResources->m_pStreamPlayer->Play( ); + break; + } + default: + { + rAssert( false ); + } + } + } + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::UberContinue +//============================================================================= +// Description: Continue the active sound resource, no matter how many times +// it was previously paused +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::UberContinue( void ) +{ + if( IsPaused() ) + { + if( m_PauseCount > 1 ) + { + m_PauseCount = 1; + } + Continue(); + } +} + +//============================================================================= +// Function: daSoundClipStreamPlayer::Stop +//============================================================================= +// Description: Stop the active sound resource. +// +//----------------------------------------------------------------------------- + +void daSoundClipStreamPlayer::Stop( void ) +{ + if ( State_CuedPlay == m_State ) + { + m_State = State_Cueing; + } + else if ( State_Playing == m_State ) + { + m_State = State_Stopping; + } +} + +void daSoundClipStreamPlayer::HookUpAndCuePlayer( void ) +{ + rAssert( State_Cueing == m_State || State_CuedPlay == m_State ); + rAssert( CueingState_Resource == m_CueingState ); + + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + rAssert( pFileInstance != NULL ); + + // + // Initialize the group trims + // + daSoundGroup myGroup = m_pResource->GetSoundGroup(); + SetMasterTrim( daSoundRenderingManagerGet()->GetTuner()->GetMasterVolume() ); + SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( myGroup ) ); + SetFaderGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetFaderGroupTrim( myGroup ) ); + + switch( m_Type ) + { + case IDaSoundResource::CLIP: + { + IRadSoundClip * pClip = pFileInstance->GetSoundClip( ); + rAssert( NULL != pClip ); + + // Attach the clip to the player + m_ClipInfo.m_pClipPlayer->SetClip( pClip ); + m_ClipInfo.m_pClipPlayer->SetPositionalGroup( m_IsPositional ? m_pPositionalGroup : NULL ); + + break; + } + + case IDaSoundResource::STREAM: + { + m_StreamInfo.m_pResources = SoundNucleusCaptureStreamerResources( + m_StreamInfo.m_pRsdFileDataSource->GetFormat( ) ); + + rAssert( m_StreamInfo.m_pResources != NULL ); + + m_StreamInfo.m_pResources->m_pStreamPlayer->SetPositionalGroup( m_IsPositional ? m_pPositionalGroup : NULL ); + + m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( this, NULL ); + m_StreamInfo.m_pResources->m_pStitchedDataSource->ResetAudioFormat( m_StreamInfo.m_pRsdFileDataSource->GetFormat( ) ); // GCN ADPCM HACK + m_StreamInfo.m_pResources->m_pStitchedDataSource->Reset( ); + + if ( NULL != m_StreamInfo.m_pResources->m_pBufferedDataSource ) + { + m_StreamInfo.m_pResources->m_pBufferedDataSource->SetInputDataSource( m_StreamInfo.m_pResources->m_pStitchedDataSource ); + m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource( m_StreamInfo.m_pResources->m_pBufferedDataSource ); + } + else + { + m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource( + m_StreamInfo.m_pResources->m_pStitchedDataSource ); + } + + + break; + } + default: + rAssert( 0 ); + break; + } + + m_CueingState = CueingState_Player; +} + +const void daSoundClipStreamPlayer::GetFileName( char * pBuf, unsigned int max ) +{ + rAssert( m_pAllocatedResource ); + + daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID ); + + return pFileInstance->GetFileName( pBuf, max ); + +} + +} // Sound Namespace +
\ No newline at end of file diff --git a/game/code/sound/soundrenderer/fader.cpp b/game/code/sound/soundrenderer/fader.cpp new file mode 100644 index 0000000..125fc9e --- /dev/null +++ b/game/code/sound/soundrenderer/fader.cpp @@ -0,0 +1,483 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: fader.cpp +// +// Description: Implementation for Fader class which brings down volume on +// associated multi-input knob. Replaces IRadSoundFade +// objects from radsound/util. +// +// History: 13/08/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== +#include <float.h> +#include <radmath/radmath.hpp> + +//======================================== +// Project Includes +//======================================== +#include <sound/soundrenderer/fader.h> + +#include <sound/soundrenderer/playermanager.h> +#include <sound/soundrenderer/idasoundtuner.h> + +using namespace Sound; + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +Fader* Fader::s_faderUpdateList = NULL; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// Fader::Fader +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Fader::Fader( globalSettings* duckSettings, + DuckSituations situation, + daSoundPlayerManager& playerMgr, + IDaSoundTuner& tuner ) : + m_duckSituation( situation ), + m_playerManager( playerMgr ), + m_tuner( tuner ) +{ + m_Time = 750; + m_In = true; + m_State = FadedIn; + m_callback = NULL; + m_nextUpdatableFader = NULL; + + ReinitializeFader( duckSettings ); +} + +//============================================================================== +// Fader::~Fader +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +Fader::~Fader() +{ + if( s_faderUpdateList != NULL ) + { + removeFromUpdateList(); + } +} + +//======================================================================== +// Fader::SetTime +//======================================================================== + +void Fader::SetTime( unsigned int milliseconds ) +{ + m_Time = milliseconds; +} + +//======================================================================== +// Fader::GetTime +//======================================================================== + +unsigned int Fader::GetTime( void ) +{ + return m_Time; +} + +//======================================================================== +// Fader::BroadCast +//======================================================================== + +void Fader::BroadCast( void ) +{ + m_playerManager.PlayerFaderVolumeChange( SOUND_EFFECTS, m_currentVolumes.duckVolume[DUCK_SFX] ); + m_playerManager.PlayerFaderVolumeChange( CARSOUND, m_currentVolumes.duckVolume[DUCK_CAR] ); + m_playerManager.PlayerFaderVolumeChange( DIALOGUE, m_currentVolumes.duckVolume[DUCK_DIALOG] ); + m_playerManager.PlayerFaderVolumeChange( MUSIC, m_currentVolumes.duckVolume[DUCK_MUSIC] ); + m_playerManager.PlayerFaderVolumeChange( AMBIENCE, m_currentVolumes.duckVolume[DUCK_AMBIENCE] ); + + m_tuner.SetFaderGroupTrim( DUCK_SFX, m_currentVolumes.duckVolume[DUCK_SFX] ); + m_tuner.SetFaderGroupTrim( DUCK_CAR, m_currentVolumes.duckVolume[DUCK_CAR] ); + m_tuner.SetFaderGroupTrim( DUCK_DIALOG, m_currentVolumes.duckVolume[DUCK_DIALOG] ); + m_tuner.SetFaderGroupTrim( DUCK_MUSIC, m_currentVolumes.duckVolume[DUCK_MUSIC] ); + m_tuner.SetFaderGroupTrim( DUCK_AMBIENCE, m_currentVolumes.duckVolume[DUCK_AMBIENCE] ); +} + +//======================================================================== +// Fader::Fade +//======================================================================== + +void Fader::Fade( bool in, DuckVolumeSet* initialVolumes, DuckVolumeSet* targetVolumes ) +{ + unsigned int i; + + m_In = in; + + // + // Set current values to whatever we're fading from and target values + // to whatever we're fading to + // + if( initialVolumes != NULL ) + { + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + m_currentVolumes.duckVolume[i] = initialVolumes->duckVolume[i]; + } + } + else + { + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + if( m_In ) + { + m_currentVolumes.duckVolume[i] = m_globalDuckSettings.duckVolume[i]; + } + else + { + m_currentVolumes.duckVolume[i] = 1.0f; + } + } + } + + if( targetVolumes != NULL ) + { + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + m_targetVolumes.duckVolume[i] = targetVolumes->duckVolume[i]; + } + } + else + { + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + if( m_In ) + { + m_targetVolumes.duckVolume[i] = 1.0f; + } + else + { + m_targetVolumes.duckVolume[i] = m_globalDuckSettings.duckVolume[i]; + } + } + } + + // + // Now calculate the time steps + // + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + if ( m_Time == 0 ) + { + // avoid divide by zero + + m_stepValues[i] = FLT_MAX / 10.0f; // Some arbitrarily large number + } + else + { + m_stepValues[i] = ( rmt::Fabs( m_currentVolumes.duckVolume[i] - m_targetVolumes.duckVolume[i] ) ) / ( m_Time ); + } + } + + setState( ); + + addToUpdateList( ); +} + +//======================================================================== +// Fader::RegisterStateCallback +//======================================================================== + +void Fader::RegisterStateCallback( FaderStateChangeCallback* callback ) +{ + // + // I'm assuming only one callback is set at a time + // + rAssert( m_callback == NULL ); + m_callback = callback; +} + +//======================================================================== +// Fader::UnRegisterStateCallback +//======================================================================== + +void Fader::UnRegisterStateCallback( FaderStateChangeCallback* callback ) +{ + // + // Accept the callback as a parameter to test my assumption that + // we only set one at a time + // + rAssert( m_callback == callback ); + + m_callback = NULL; +} + +//======================================================================== +// Fader::GetState +//======================================================================== + +Fader::State Fader::GetState( void ) +{ + unsigned int i; + float targetValue; + State currentState = m_In ? FadedIn : FadedOut; + + // + // Test each of the fading volumes. If one of them hasn't hit + // the target yet, we're still fading + // + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + targetValue = m_In ? 1.0f : m_globalDuckSettings.duckVolume[i]; + + if( m_currentVolumes.duckVolume[i] != targetValue ) + { + currentState = m_In ? FadingIn : FadingOut; + break; + } + } + + return( currentState ); +} + +//======================================================================== +// Fader::OnTimerDone +//======================================================================== + +void Fader::Update( unsigned int elapsed ) +{ + unsigned int i; + float stepValue; + bool allTargetsHit = true; + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + stepValue = m_stepValues[i] * elapsed; // adjust for game chug. + + if ( stepValue >= radSoundVolumeChangeThreshold ) + { + stepValue = radSoundVolumeChangeThreshold; + } + + if ( m_currentVolumes.duckVolume[i] < m_targetVolumes.duckVolume[i] ) + { + m_currentVolumes.duckVolume[i] += stepValue; + + if ( m_currentVolumes.duckVolume[i] >= m_targetVolumes.duckVolume[i] ) + { + m_currentVolumes.duckVolume[i] = m_targetVolumes.duckVolume[i]; + } + else + { + allTargetsHit = false; + } + } + else if ( m_currentVolumes.duckVolume[i] > m_targetVolumes.duckVolume[i] ) + { + m_currentVolumes.duckVolume[i] -= stepValue; + + if ( m_currentVolumes.duckVolume[i] <= m_targetVolumes.duckVolume[i] ) + { + m_currentVolumes.duckVolume[i] = m_targetVolumes.duckVolume[i]; + } + else + { + allTargetsHit = false; + } + } + + } + + BroadCast(); + + if ( allTargetsHit ) + { + removeFromUpdateList( ); + + setState( ); + } +} + +//============================================================================= +// Fader::UpdateAllFaders +//============================================================================= +// Description: Run through the fader list and update everything +// +// Parameters: elapsedTime - number of elapsed msecs since last call +// +// Return: void +// +//============================================================================= +void Fader::UpdateAllFaders( unsigned int elapsedTime ) +{ + Fader* currFader; + + currFader = s_faderUpdateList; + while( currFader != NULL ) + { + currFader->Update( elapsedTime ); + currFader = currFader->m_nextUpdatableFader; + } +} + +//============================================================================= +// Fader::ReinitializeFader +//============================================================================= +// Description: Get the ducking parameters from the global settings object +// +// Parameters: ( globalSettings* settingObj ) +// +// Return: void +// +//============================================================================= +void Fader::ReinitializeFader( globalSettings* settingObj ) +{ + unsigned int i; + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + if( settingObj == NULL ) + { + m_globalDuckSettings.duckVolume[i] = 0.0f; + } + else + { + m_globalDuckSettings.duckVolume[i] = + settingObj->GetDuckVolume( m_duckSituation, static_cast<Sound::DuckVolumes>(i) ); + } + } +} + +//============================================================================= +// Fader::Stop +//============================================================================= +// Description: Fader is being interrupted, get it out of the update list +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void Fader::Stop() +{ + removeFromUpdateList(); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//======================================================================== +// Fader::setState +//======================================================================== + +void Fader::setState( void ) +{ + + if ( GetState( ) != m_State ) + { + m_State = GetState( ); + + if( m_callback != NULL ) + { + m_callback->OnStateChange( m_State ); + } + } +} + +//======================================================================== +// Fader::addToUpdateList +//======================================================================== +void Fader::addToUpdateList() +{ + if( !faderInUpdateList() ) + { + // + // Order doesn't matter, add it to the head of the list + // + m_nextUpdatableFader = s_faderUpdateList; + s_faderUpdateList = this; + } +} + +//======================================================================== +// Fader::removeFromUpdateList +//======================================================================== +void Fader::removeFromUpdateList() +{ + Fader* currentFader; + + // + // Search for position in list. The list is usually one or two faders, + // I think, so don't bother with double-linked lists. + // + if( s_faderUpdateList == this ) + { + s_faderUpdateList = m_nextUpdatableFader; + } + else + { + currentFader = s_faderUpdateList; + while( ( currentFader != NULL ) + && ( currentFader->m_nextUpdatableFader != this ) ) + { + currentFader = currentFader->m_nextUpdatableFader; + } + + if( currentFader != NULL ) + { + currentFader->m_nextUpdatableFader = m_nextUpdatableFader; + } + } + + m_nextUpdatableFader = NULL; +} + +//============================================================================= +// Fader::faderInUpdateList +//============================================================================= +// Description: Indicate whether this fader is currently in the update list +// +// Parameters: None +// +// Return: true if in list, false otherwise +// +//============================================================================= +bool Fader::faderInUpdateList() +{ + Fader* currFader; + + currFader = s_faderUpdateList; + while( currFader != NULL ) + { + if( currFader == this ) + { + return( true ); + } + + currFader = currFader->m_nextUpdatableFader; + } + + return( false ); +} diff --git a/game/code/sound/soundrenderer/fader.h b/game/code/sound/soundrenderer/fader.h new file mode 100644 index 0000000..492080d --- /dev/null +++ b/game/code/sound/soundrenderer/fader.h @@ -0,0 +1,146 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: fader.h +// +// Description: Declaration for Fader class which brings down volume on +// associated multi-input knob. Replaces IRadSoundFade +// objects from radsound/util. +// +// History: 13/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef FADER_H +#define FADER_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> + +#include <sound/soundrenderer/dasoundgroup.h> +#include <sound/tuning/globalsettings.h> + +//======================================== +// Forward References +//======================================== + +struct FaderStateChangeCallback; + +namespace Sound +{ + class daSoundPlayerManager; + struct IDaSoundTuner; +} + +//============================================================================= +// +// Synopsis: Fader +// +//============================================================================= + +class Fader : public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "Fader" ); + + Fader( globalSettings* duckSettings, + Sound::DuckSituations situation, + Sound::daSoundPlayerManager& playerMgr, + Sound::IDaSoundTuner& tuner ); + ~Fader(); + + void Update( unsigned int elapsed ); + static void UpdateAllFaders( unsigned int elapsedTime ); + + void Stop(); + + void SetTime( unsigned int milliseconds ); + unsigned int GetTime( void ); + + void Fade( bool in, Sound::DuckVolumeSet* initialVolumes = NULL, Sound::DuckVolumeSet* targetVolumes = NULL ); + + float GetCurrentVolume( Sound::DuckVolumes volumeKnob ) { return( m_currentVolumes.duckVolume[volumeKnob] ); } + float GetTargetSettings( Sound::DuckVolumes volumeKnob ) { return( m_globalDuckSettings.duckVolume[volumeKnob] ); } + + enum State { FadedIn, FadedOut, FadingIn, FadingOut }; + + void RegisterStateCallback( FaderStateChangeCallback* callback ); + void UnRegisterStateCallback( FaderStateChangeCallback* callback ); + + State GetState( void ); + + void BroadCast( void ); + + Sound::DuckSituations GetSituation( void ) { return( m_duckSituation ); } + + void ReinitializeFader( globalSettings* settingObj ); + + private: + //Prevent wasteful constructor creation. + Fader(); + Fader( const Fader& original ); + Fader& operator=( const Fader& rhs ); + + void setState(); + + // + // Add and remove fader from the update list. We should only update + // the fader if it's not on its target value yet. + // + void addToUpdateList(); + void removeFromUpdateList(); + bool faderInUpdateList(); + + unsigned int m_Time; + bool m_In; + State m_State; + + Sound::DuckVolumeSet m_currentVolumes; + float m_stepValues[Sound::NUM_DUCK_VOLUMES]; + Sound::DuckVolumeSet m_targetVolumes; + Sound::DuckVolumeSet m_globalDuckSettings; + + Sound::DuckSituations m_duckSituation; + + // + // Callback object for state change notification. Was a list of objects + // in radSoundFade + // + FaderStateChangeCallback* m_callback; + + // + // Static fader list, used for updates + // + static Fader* s_faderUpdateList; + + // + // Pointer used to hold place in update list + // + Fader* m_nextUpdatableFader; + + // + // Player manager, used to actually do the fading + // + Sound::daSoundPlayerManager& m_playerManager; + + // + // Tuner, stores the results + // + Sound::IDaSoundTuner& m_tuner; +}; + +//============================================================================= +// +// Synopsis: FaderStateChangeCallback +// +//============================================================================= +struct FaderStateChangeCallback +{ + virtual void OnStateChange( Fader::State newState ); +}; + + +#endif // FADER_H + diff --git a/game/code/sound/soundrenderer/idasoundresource.h b/game/code/sound/soundrenderer/idasoundresource.h new file mode 100644 index 0000000..f6564db --- /dev/null +++ b/game/code/sound/soundrenderer/idasoundresource.h @@ -0,0 +1,172 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: idasoundresource.hpp +// +// Subsystem: Dark Angel - Sound resources +// +// Description: Defines the interface for a sound resource +// +// WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// IF THIS FILE IS MODIFIED THE TYPE INFO FILE MUST BE REGENERATED +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Revisions: +// + Created October 3, 2001 -- aking +// +//============================================================================= + +#ifndef _IDASOUNDRESOURCE_HPP +#define _IDASOUNDRESOURCE_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> + +#include <sound/soundrenderer/dasoundgroup.h> + +//============================================================================= +// Definitions and macros +//============================================================================= + +// +// This is the maximum number of file variations that can exist in a resource. +// +#define DASound_MaxNumSoundResourceFiles 7 + +//============================================================================= +// Prototypes +//============================================================================= + +struct IDaSoundResource; + +//============================================================================= +// Typedefs and enumerations +//============================================================================= + +// +// A procedure used to modify a sound resource +// +typedef void daSoundResourceProc( IDaSoundResource* pRes, void* pUserData ); + +//============================================================================= +// Interface Definitions +//============================================================================= + +// +// IDaSoundResourceData contains sound resource data. +// +struct IDaSoundResourceData : public IRefCount +{ + // SCRIPTED FEATURES THAT CAN BE TUNED IN REAL TIME + + // + // Add files to the resource + // + virtual void AddFilename + ( + const char* newFileName, + float trim + ) = 0; + + // + // Set the pitch variation + // + virtual void SetPitchRange + ( + float minPitch, + float maxPitch + ) = 0; + + // + // Set the trim variation + // + virtual void SetTrimRange + ( + float minTrim, + float maxTrim + ) = 0; + + // + // Set the trim to one value + // + virtual void SetTrim( float trim ) = 0; + + // SCRIPTED FEATURES THAT CAN NOT BE TUNED IN REAL TIME + + // Is this a streaming sound resource? + virtual void SetStreaming( bool streaming ) = 0; + + // Is this a looping sound resource? + virtual void SetLooping( bool looping ) = 0; + + // SCRIPTED FEATURES AVAILABLE ONLY FOR TUNERS + + // Play the resource + virtual void Play( void ) = 0; +}; + +// +// IDaSoundResource is based on an IDaSoundResourceData. It adds functionality +// to get parameters, and to capture/release resources. +// +struct IDaSoundResource : public IDaSoundResourceData +{ + // A resource file description + struct daSoundResourceFileDesc + { + char* m_Filename; + float m_Trim; + int m_TableIndex; + }; + + // + // Monitor files in the resource + // + virtual unsigned int GetNumFiles( void ) = 0; + + virtual void GetFileNameAt( unsigned int index, char* buffer, unsigned int max ) = 0; + virtual void GetFileKeyAt( unsigned int index, char* buffer, unsigned int max ) = 0; + // + // Get parameters set in IDaSoundResourceData + // + virtual void GetPitchRange + ( + float* pMinPitch, + float* pMaxPitch + ) = 0; + virtual void GetTrimRange + ( + float* pMinTrim, + float* pMaxTrim + ) = 0; + virtual bool GetLooping( void ) = 0; + + // + // Find out what kind of resource this is + // + enum Type { + UNKNOWN, + CLIP, + STREAM + }; + virtual Type GetType( void ) = 0; + + // + // Modify the sound group that this resource belongs to + // + virtual void SetSoundGroup( Sound::daSoundGroup soundGroup ) = 0; + virtual Sound::daSoundGroup GetSoundGroup( void ) = 0; + + // + // Capture and release the resource + // + virtual void CaptureResource( void ) = 0; + virtual bool IsCaptured( void ) = 0; + virtual void ReleaseResource( void ) = 0; +}; + +//}; //Sound Namespace +#endif // _IDASOUNDRESOURCE_HPP diff --git a/game/code/sound/soundrenderer/idasoundtuner.h b/game/code/sound/soundrenderer/idasoundtuner.h new file mode 100644 index 0000000..c537af2 --- /dev/null +++ b/game/code/sound/soundrenderer/idasoundtuner.h @@ -0,0 +1,205 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: idasoundtuner.hpp +// +// Subsystem: Dark Angel - Sound Tuner System +// +// Description: Description of the DA sound tuner interface +// +// Revisions: +// + Created October 4, 2001 -- breimer +// +//============================================================================= + +#ifndef _IDASOUNDTUNER_HPP +#define _IDASOUNDTUNER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> + +#include <sound/soundrenderer/dasoundgroup.h> +#include <sound/soundrenderer/soundsystem.h> + +#include <sound/tuning/globalsettings.h> + +//============================================================================= +// Global namespace forward declarations +//============================================================================= + +struct IRadSoundMultiInputKnob; +class Fader; + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +struct IDaSoundWiring; +struct IDaSoundTuner; +struct IDaSoundFadeState; + +//============================================================================= +// Interfaces +//============================================================================= + +// +// Wire together various sound groups that take the form of multi-input +// knobs. +// +struct IDaSoundWiring : public IRefCount +{ + // + // Wire a sound group to a path + // + virtual void WirePath + ( + daSoundGroup soundGroup, + const char* path + ) = 0; + + // + // Initialize the sound groups + // + virtual void WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup ) = 0; +}; + +// +// The sound tuner is responsible for managing global settings and various +// "wirings" of the sound system. It allows the generation of tuner instances +// for controlling various game parameters, and it manages common global +// sound settings (like ducking, master volume, pause/continue/cancel, +// mono/stereo). +// +struct IDaSoundTuner : public IDaSoundWiring +{ + // + // Initialize and perform all wiring. This will lock down the resources + // if they have not been locked already. + // + virtual void Initialize( void ) = 0; + + // + // Create faders after we've got scripts to latch them to + // + virtual void PostScriptLoadInitialize() = 0; + + // + // Service the tuner + // + virtual void ServiceOncePerFrame( unsigned int elapsedTime ) = 0; + + // + // Modify the sound output mode + // + enum SoundOutputMode + { + MONO, + STEREO, + SURROUND + }; + virtual void SetSoundOutputMode + ( + SoundOutputMode outputMode + ) = 0; + virtual SoundOutputMode GetSoundOutputMode( void ) = 0; + + // SPECIALIZED SOUND SETTINGS FOR COMMON CONTROL + + // + // Duck sounds + // + virtual void ActivateDuck( IDaSoundFadeState* pObject, + void* pUserData, + bool fadeIn ) = 0; + + virtual void ActivateSituationalDuck( IDaSoundFadeState* pObject, + DuckSituations situation, + void* pUserData, + bool fadeIn ) = 0; + virtual void ResetDuck() = 0; + + // + // Common volume controls + // + virtual void SetMasterVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetMasterVolume( void ) = 0; + + virtual void SetDialogueVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetDialogueVolume( void ) = 0; + + virtual void SetMusicVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetMusicVolume( void ) = 0; + + virtual void SetAmbienceVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetAmbienceVolume( void ) = 0; + + virtual void SetSfxVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetSfxVolume( void ) = 0; + + virtual void SetCarVolume( daTrimValue volume ) = 0; + virtual daTrimValue GetCarVolume( void ) = 0; + + virtual daTrimValue GetGroupTrim( daSoundGroup group ) = 0; + virtual daTrimValue GetFaderGroupTrim( daSoundGroup group ) = 0; + + virtual void MuteNIS() = 0; + virtual void UnmuteNIS() = 0; + + virtual void SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim ) = 0; + + // GENERAL SOUND SETTINGS + + // + // Fade a sound group + // + virtual void FadeSounds( IDaSoundFadeState* pObject, + void* pUserData, + Fader* pFader, + bool fadeIn, + DuckVolumeSet* initialVolumes = NULL ) = 0; + + + // + // Sound group info + // + virtual bool IsSlaveGroup( daSoundGroup slave, daSoundGroup master ) = 0; + +}; + +//============================================================================= +// Public functions +//============================================================================= + +// +// Do the wiring of the sound system +// +void daSoundTunerWireSystem +( + IDaSoundWiring* pWiring +); + +//============================================================================= +// Factory functions +//============================================================================= + +// +// Create the tuner manager +// +void daSoundTunerCreate +( + IDaSoundTuner** ppTuner, + radMemoryAllocator allocator +); + +} // Sound Namespace +#endif //_IDASOUNDTUNER_HPP + diff --git a/game/code/sound/soundrenderer/musicsoundplayer.cpp b/game/code/sound/soundrenderer/musicsoundplayer.cpp new file mode 100644 index 0000000..365b8ef --- /dev/null +++ b/game/code/sound/soundrenderer/musicsoundplayer.cpp @@ -0,0 +1,300 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: musicsoundplayer.cpp +// +// Subsystem: Dark Angel - Sound players +// +// Description: Implements the a Dark Angel sound player +// +// Revisions: +// + Created October 16, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <sound/soundrenderer/musicsoundplayer.h> + +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/idasoundtuner.h> + +#include <sound/soundmanager.h> + +//============================================================================= +// Static Variables (outside namespace) +//============================================================================= + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Debug Information +//============================================================================= + +//============================================================================= +// Definitions Macros and Constants +//============================================================================= + +//============================================================================= +// Local functions +//============================================================================= + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// MusicSoundPlayer Implementation +//============================================================================= + +//============================================================================= +// Function: MusicSoundPlayer::MusicSoundPlayer +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +MusicSoundPlayer::MusicSoundPlayer( ) + : + m_isMusic( true ), + m_PlayerTrim( 1.0f ), + m_ResourceTrim( 1.0f ), + m_ExternalTrim( 1.0f ), + m_GroupTrim( 1.0f ), + m_FaderGroupTrim( 1.0f ), + m_MasterTrim( 1.0f ) +{ +} + +//============================================================================= +// Function: MusicSoundPlayer::~MusicSoundPlayer +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +MusicSoundPlayer::~MusicSoundPlayer( ) +{ +} + +//============================================================================= +// Function: MusicSoundPlayer::Initialize +//============================================================================= +// Description: Initialize the sound player to determine whether it will +// set the trim for music or ambience +// +//----------------------------------------------------------------------------- +void MusicSoundPlayer::Initialize( bool isMusic ) +{ + m_isMusic = isMusic; + + // + // Set the group trim + // + if( m_isMusic ) + { + SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( Sound::MUSIC ) ); + } + else + { + SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( Sound::AMBIENCE ) ); + } +} + +//============================================================================= +// MusicSoundPlayer::GetSoundGroup +//============================================================================= +// Description: Unlike regular sound players, our group is determined by +// the music/ambience specification from initialization, not +// by an associated sound resource +// +// Parameters: None +// +// Return: sound group (either MUSIC or AMBIENCE) +// +//============================================================================= +daSoundGroup MusicSoundPlayer::GetSoundGroup() +{ + if( m_isMusic ) + { + return( Sound::MUSIC ); + } + else + { + return( Sound::AMBIENCE ); + } +} + +//============================================================================= +// MusicSoundPlayer::SetExternalTrim +//============================================================================= +// Description: Set arbitrary player trim. May not be needed. +// +// Parameters: newTrim - new trim value +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::SetExternalTrim( float newTrim ) +{ + m_ExternalTrim = newTrim; + m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim; + + ResetMusicTrim(); +} + +//============================================================================= +// MusicSoundPlayer::SetGroupTrim +//============================================================================= +// Description: Set sound group trim +// +// Parameters: newTrim - new trim setting +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::SetGroupTrim( float newTrim ) +{ + m_GroupTrim = newTrim; + m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim; + + ResetMusicTrim(); +} + +//============================================================================= +// MusicSoundPlayer::SetFaderGroupTrim +//============================================================================= +// Description: Set sound group fader trim +// +// Parameters: newTrim - new trim setting +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::SetFaderGroupTrim( float newTrim ) +{ + m_FaderGroupTrim = newTrim; + m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim; + + ResetMusicTrim(); +} + +//============================================================================= +// MusicSoundPlayer::SetMasterTrim +//============================================================================= +// Description: Set master trim. Duh. +// +// Parameters: newTrim - new trim setting +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::SetMasterTrim( float newTrim ) +{ + m_MasterTrim = newTrim; + m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim; + + ResetMusicTrim(); +} + +// +// TODO: revisit trim functions below. They're identical to the one in the base +// class, should be virtual. +// + +//============================================================================= +// MusicSoundPlayer::ChangeTrim +//============================================================================= +// Description: Change the trim setting for a sound group, or for the master +// volume +// +// Parameters: groupName - name of group to change +// newTrim - trim value +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::ChangeTrim( daSoundGroup groupName, float newTrim ) +{ + daSoundGroup myGroup = GetSoundGroup(); + + if ( myGroup == groupName ) + { + SetGroupTrim(newTrim); + } + else if ( groupName == MASTER ) + { + SetMasterTrim(newTrim); + } + else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) ) + { + SetGroupTrim(newTrim); + } +} + +//============================================================================= +// MusicSoundPlayer::ChangeFaderTrim +//============================================================================= +// Description: Change the trim setting for a sound group, or for the master +// volume +// +// Parameters: groupName - name of group to change +// newTrim - trim value +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::ChangeFaderTrim( daSoundGroup groupName, float newTrim ) +{ + daSoundGroup myGroup = GetSoundGroup(); + + // + // Shouldn't change master volume here, use ChangeTrim + // + rAssert( groupName != MASTER ); + + if ( myGroup == groupName ) + { + SetFaderGroupTrim(newTrim); + } + else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) ) + { + SetFaderGroupTrim(newTrim); + } +} + + +//============================================================================= +// Private functions +//============================================================================= + +//============================================================================= +// MusicSoundPlayer::resetMusicTrim +//============================================================================= +// Description: Update the music player with the given trim setting +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void MusicSoundPlayer::ResetMusicTrim() +{ + if( m_isMusic ) + { + GetSoundManager()->SetMusicVolumeWithoutTuner( m_PlayerTrim ); + } + else + { + GetSoundManager()->SetAmbienceVolumeWithoutTuner( m_PlayerTrim ); + } +} + +} // Sound Namespace + diff --git a/game/code/sound/soundrenderer/musicsoundplayer.h b/game/code/sound/soundrenderer/musicsoundplayer.h new file mode 100644 index 0000000..15cedb3 --- /dev/null +++ b/game/code/sound/soundrenderer/musicsoundplayer.h @@ -0,0 +1,112 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: musicsoundplayer.hpp +// +// Subsystem: Dark Angel - Sound players +// +// Description: Used to hack in volume control for RadMusic, even though +// it doesn't go through the DA system +// +// Revisions: +// + Hacked 5 Mar 03 -- Esan +// +//============================================================================= + +#ifndef _MUSICSOUNDPLAYER_HPP +#define _MUSICSOUNDPLAYER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <sound/soundrenderer/soundplayer.h> + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +//============================================================================= +// Forward declarations +//============================================================================= + +//============================================================================= +// Typdefs and enumerations +//============================================================================= + +//============================================================================= +// Class Declarations +//============================================================================= + +// +// This contains a DA player instance. +// +class MusicSoundPlayer : public daSoundPlayerBase +{ +public: + // + // Constructor and destructor + // + MusicSoundPlayer ( ); + virtual ~MusicSoundPlayer ( ); + + void Initialize ( bool isMusic ); + + // Suspend the player if it should be playing, but no one can hear it + void Suspend ( void ); + + void SetExternalTrim( float newTrim ); + void SetGroupTrim( float newTrim ); + void SetFaderGroupTrim( float newTrim ); + void SetMasterTrim( float newTrim ); + + daSoundGroup GetSoundGroup ( void ); + + unsigned int GetPlaybackTimeInSamples ( void ); + + // daSoundPlayerBase + + virtual void ServiceOncePerFrame( void ) { } + virtual bool IsCaptured ( void ) { return true; } + virtual void Pause ( void ) { } + virtual bool IsPaused( void ) { return false; } + virtual void Continue ( void ) { } + virtual void UberContinue( void ) { } + virtual void Stop ( void ) { } + virtual void SetPitch( float pitch ) { } + + virtual void ChangeTrim( daSoundGroup groupName, float newTrim ); + virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim ); + + +private: + void ResetMusicTrim( ); + + bool m_isMusic; + + // + // Trim values + // + float m_PlayerTrim; + float m_ResourceTrim; + float m_ExternalTrim; + float m_GroupTrim; + float m_FaderGroupTrim; + float m_MasterTrim; + +}; + + +//============================================================================= +// Public Functions +//============================================================================= + +} // Sound Namespace +#endif //_MUSICSOUNDPLAYER_HPP + diff --git a/game/code/sound/soundrenderer/playermanager.cpp b/game/code/sound/soundrenderer/playermanager.cpp new file mode 100644 index 0000000..9a92b77 --- /dev/null +++ b/game/code/sound/soundrenderer/playermanager.cpp @@ -0,0 +1,989 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: playermagaer.cpp +// +// Subsystem: Dark Angel - Player Manager System +// +// Description: Implementation of the DA sound player manager +// +// Revisions: +// + Created October 16, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radobjectlist.hpp> +#include <raddebug.hpp> +#include <radlinkedclass.hpp> +#include <radnamespace.hpp> +#include <radsound_hal.hpp> + +#include <memory/srrmemory.h> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/dasoundgroup.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/playermanager.h> +#include <sound/soundrenderer/soundplayer.h> +#include <sound/soundrenderer/musicsoundplayer.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/fader.h> +#include <sound/soundrenderer/soundconstants.h> +#include <sound/soundrenderer/soundnucleus.hpp> + +#include <pddi/pddi.hpp> +#include <p3d/utility.hpp> +#include <p3d/p3dtypes.hpp> + +#include <radobjectbtree.hpp> + + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Debug Information +//============================================================================= + +daSoundPlayerManager * daSoundPlayerManager::s_pInstance = 0; + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundAsyncFadeCallback Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundAsyncFadeCallback::daSoundAsyncFadeCallback +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundAsyncFadeCallback::daSoundAsyncFadeCallback( ) +: +radObject( ), +m_Action( 0 ), +m_pPlayerManager( NULL ), +m_pCallback( NULL ), +m_pUserData( NULL ) +{ + +} + +//============================================================================= +// Function: daSoundAsyncFadeCallback::~daSoundAsyncFadeCallback +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundAsyncFadeCallback::~daSoundAsyncFadeCallback( ) +{ + + if( m_pCallback != NULL ) + { + m_pCallback->Release( ); + } + if( m_pPlayerManager != NULL ) + { + m_pPlayerManager->Release( ); + } +} + +//============================================================================= +// Function: daSoundAsyncFadeCallback::SetPlayerManager +//============================================================================= +// Description: Set the player manager +// +//----------------------------------------------------------------------------- + +void daSoundAsyncFadeCallback::SetPlayerManager +( + daSoundPlayerManager* pPlayerManager +) +{ + rAssert( m_pPlayerManager == NULL ); + rAssert( pPlayerManager != NULL ); + + m_pPlayerManager = pPlayerManager; + m_pPlayerManager->AddRef( ); +} + +//============================================================================= +// Function: daSoundAsyncFadeCallback::GetPlayerManager +//============================================================================= +// Description: Get the player manager +// +//----------------------------------------------------------------------------- + +daSoundPlayerManager* daSoundAsyncFadeCallback::GetPlayerManager( void ) +{ + return m_pPlayerManager; +} + +//============================================================================= +// Function: daSoundAsyncFadeCallback::SetCallback +//============================================================================= +// Description: Set the fade callback +// +//----------------------------------------------------------------------------- + +void daSoundAsyncFadeCallback::SetCallback +( + IDaSoundFadeState* pCallback, + void* pUserData +) +{ + rAssert( m_pCallback == NULL ); + m_pCallback = pCallback; + m_pUserData = pUserData; + + if( m_pCallback != NULL ) + { + m_pCallback->AddRef( ); + } +} + +//============================================================================= +// Function: daSoundAsyncFadeCallback::GetCallback +//============================================================================= +// Description: Get the fade callback +// +//----------------------------------------------------------------------------- + +void daSoundAsyncFadeCallback::GetCallback( IDaSoundFadeState** ppCallback, void** ppUserData ) +{ + rAssert( ppCallback != NULL ); + rAssert( ppUserData != NULL ); + + *ppCallback = m_pCallback; + *ppUserData = m_pUserData; +} + + +//============================================================================= +// daSoundPlayerManager Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundPlayerManager::daSoundPlayerManager +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundClipStreamPlayer * gClipPlayerArray[ SOUND_NUM_CLIP_PLAYERS ]; +daSoundClipStreamPlayer * gStreamPlayerArray [ SOUND_NUM_STREAM_PLAYERS ]; + +daSoundPlayerManager::daSoundPlayerManager( ) + : + radRefCount( 0 ) +{ + m_pIngameFadeIn = NULL; + m_pIngameFadeOut = NULL; + m_pMusicPlayer = NULL; + m_pAmbiencePlayer = NULL; + m_Initialized = false; + + s_pInstance = this; +} + +//============================================================================= +// Function: daSoundPlayerManager::~daSoundPlayerManager +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundPlayerManager::~daSoundPlayerManager( ) +{ + s_pInstance = NULL; + // Release our faders + if( m_pIngameFadeOut != NULL ) + { + m_pIngameFadeOut->Release(); + m_pIngameFadeOut = NULL; + } + if( m_pIngameFadeIn != NULL ) + { + m_pIngameFadeIn->Release(); + m_pIngameFadeIn = NULL; + } + + // Release music players + if( m_pMusicPlayer != NULL ) + { + m_pMusicPlayer->Release(); + m_pMusicPlayer = NULL; + } + if( m_pAmbiencePlayer != NULL ) + { + m_pAmbiencePlayer->Release(); + m_pAmbiencePlayer = NULL; + } + + for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c ++ ) + { + gClipPlayerArray[ c ]->Release( ); + } + + for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s ++ ) + { + gStreamPlayerArray[ s ]->Release( ); + } +} + +//============================================================================= +// daSoundPlayerManager::UglyHackPostInitialize +//============================================================================= +// Description: Comment +// +// Parameters: ( IDaSoundTuner* pTuner ) +// +// Return: void +// +//============================================================================= +void daSoundPlayerManager::UglyHackPostInitialize( IDaSoundTuner* pTuner ) +{ + // Ingame faders + m_pIngameFadeIn = new( GMA_PERSISTENT ) Fader( NULL, DUCK_FULL_FADE, *this, *pTuner ); + rAssert( m_pIngameFadeIn != NULL ); + + m_pIngameFadeOut = new( GMA_PERSISTENT ) Fader( NULL, DUCK_FULL_FADE, *this, *pTuner ); + rAssert( m_pIngameFadeOut != NULL ); +} + +//============================================================================= +// Function: daSoundPlayerManager::GetObjectSize +//============================================================================= +// Description: Get the size of the sound player object +//----------------------------------------------------------------------------- + +unsigned int daSoundPlayerManager::GetObjectSize( void ) +{ + unsigned int thisSize = sizeof( *this ); + return thisSize; +} + +unsigned int daSoundPlayerManager::GetNumUsedClipPlayers() +{ + unsigned int playerCount = 0; + + for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c ++ ) + { + if ( gClipPlayerArray[ c ]->IsCaptured( ) ) + { + playerCount++; + } + } + + return( playerCount ); +} + +unsigned int daSoundPlayerManager::GetNumUsedStreamPlayers() +{ + unsigned int playerCount = 0; + + for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s ++ ) + { + if ( gStreamPlayerArray[ s ]->IsCaptured( ) ) + { + playerCount++; + } + } + + return playerCount; +} + +//============================================================================= +// Function: daSoundPlayerManager::Initialize +//============================================================================= +// Description: Initialize the player manager +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::Initialize( void ) +{ + // The stream players are tuned from associated radscripts + + IRadSoundClipPlayer* pClipPlayer = NULL; + IRadSoundStreamPlayer* pStreamPlayer = NULL; + IRadSoundStitchedDataSource* pStitchedDataSource = NULL; + + // CLIP PLAYERS /////////////////////////////////////////////////////////// + + for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c++ ) + { + gClipPlayerArray[ c ] = new (GetThisAllocator( ) ) daSoundClipStreamPlayer( ); + gClipPlayerArray[ c ]->AddRef( ); + gClipPlayerArray[ c ]->InitializeAsClipPlayer( ); + } + + // STREAM PLAYERS ///////////////////////////////////////////////////////// + + for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s++ ) + { + + gStreamPlayerArray[ s ] = new ( GetThisAllocator() ) daSoundClipStreamPlayer; + gStreamPlayerArray[ s ]->AddRef( ); + gStreamPlayerArray[ s ]->InitializeAsStreamPlayer( ); + } + + // + // Make a couple of fake players for controlling music and + // ambience trim + // + + // Create a music and an ambience sound player + m_pMusicPlayer = new( GetThisAllocator() ) MusicSoundPlayer; + m_pMusicPlayer->AddRef( ); + m_pMusicPlayer->Initialize( true ); + + m_pAmbiencePlayer = new( GetThisAllocator() ) MusicSoundPlayer; + m_pAmbiencePlayer->AddRef( ); + m_pAmbiencePlayer->Initialize( false ); + + m_Initialized = true; + +} + +//============================================================================= +// Function: daSoundPlayerManager::ServiceOncePerFrame +//============================================================================= +// Description: Service once per frame +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::ServiceOncePerFrame( void ) +{ + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + pPlayer->ServiceOncePerFrame( ); + pPlayer = pPlayer->GetLinkedClassNext( ); + } +} + +//============================================================================= +// Function: daSoundPlayerManager::CaptureFreePlayer +//============================================================================= +// Description: This function finds and captures a sound player +// and connects the given resource to it. +// +// Parameters: ppPlayer - (out) the captured player or NULL if one can't be +// found +// pResource - a pointer to a sound resource +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::CaptureFreePlayer +( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource, + bool positional +) +{ + rAssert( ppPlayer != NULL ); + rAssert( pResource != NULL ); + + // Find out where to look + unsigned int i = 0; + bool playerFound = false; + daSoundGroup soundGroup = pResource->GetSoundGroup( ); + IDaSoundResource::Type resourceType = pResource->GetType( ); + + switch( pResource->GetType( ) ) + { + case IDaSoundResource::CLIP: + { + // Look in the clip player list + playerFound = FindFreeClipPlayer( ppPlayer, pResource ); + break; + } + case IDaSoundResource::STREAM: + { + // Look in the stream player list + playerFound = FindFreeStreamPlayer( ppPlayer, pResource ); + break; + } + default: + { + rAssert( 0 ); + break; + } + } + if( playerFound ) + { + // Capture it + (*ppPlayer)->Capture( pResource, positional ); + } + else + { + (*ppPlayer) = NULL; + } + +} + +bool daSoundPlayerManager::FindFreePlayer( + daSoundClipStreamPlayer** ppPlayerArray, + unsigned int numPlayers, + daSoundClipStreamPlayer ** ppPlayer ) +{ + *ppPlayer = 0; + + for( unsigned int c = 0; c < numPlayers; c ++ ) + { + if ( false == ppPlayerArray[ c ]->IsCaptured( ) ) + { + *ppPlayer = ppPlayerArray[ c ]; + break; + } + } + + return NULL != *ppPlayer; +} + + +//============================================================================= +// Function: daSoundPlayerManager::FindFreeClipPlayer +//============================================================================= +// Description: Find a free clip player +// +//----------------------------------------------------------------------------- + +bool daSoundPlayerManager::FindFreeClipPlayer( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource +) +{ + return FindFreePlayer( gClipPlayerArray, SOUND_NUM_CLIP_PLAYERS, ppPlayer ); +} + +//============================================================================= +// Function: daSoundPlayerManager::FindFreeStreamPlayer +//============================================================================= +// Description: Find a free stream player +// +//----------------------------------------------------------------------------- + +bool daSoundPlayerManager::FindFreeStreamPlayer( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource ) +{ + return FindFreePlayer( gStreamPlayerArray, SOUND_NUM_STREAM_PLAYERS, ppPlayer ); +} + +//============================================================================= +// Function: daSoundPlayerManager::PausePlayers +//============================================================================= +// Description: Pause the sound players +// +// Parameters: stackFrame - the stack frame that a player is associated with. +// If NULL, all players pause. +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::PausePlayers +( +) +{ + // + // Pause all players + // + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + pPlayer->Pause( ); + + // Move on + pPlayer = pPlayer->GetLinkedClassNext( ); + } +} + +//============================================================================= +// Function: daSoundPlayerManager::PausePlayersWithFade +//============================================================================= +// Description: Fade out the players and then pause +// +// Parameters: see PausePlayers +// pCallback - the asynchronous callback to call when done +// pUserData - the user data to pass into the callback +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::PausePlayersWithFade +( + IDaSoundFadeState* pCallback, + void* pUserData +) +{ + // Create the fade info + daSoundAsyncFadeCallback* pFadeInfo = new( GMA_TEMP ) daSoundAsyncFadeCallback( ); + rAssert( pFadeInfo != NULL ); + pFadeInfo->SetPlayerManager( this ); + pFadeInfo->SetAction( OnFade_PausePlayers ); + pFadeInfo->SetCallback( pCallback, pUserData ); + + // Start fading the sounds + rAssert( m_pIngameFadeOut != NULL ); + Sound::daSoundRenderingManagerGet( )->GetTuner( )->FadeSounds + ( + this, + pFadeInfo, + m_pIngameFadeOut, + false + ); +} + +//============================================================================= +// Function: daSoundPlayerManager::ContinuePlayers +//============================================================================= +// Description: Continue the sound players +// +// Parameters: stackFrame - the stack frame that a player is associated with. +// If NULL, all players continue. +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::ContinuePlayers +( +) +{ + // + // Continue all players + // + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + if( pPlayer->IsPaused() ) + { + pPlayer->Continue( ); + } + + // Move on + pPlayer = pPlayer->GetLinkedClassNext( ); + } +} + +//============================================================================= +// Function: daSoundPlayerManager::ContinuePlayersWithFade +//============================================================================= +// Description: Continue the players and then fade in. +// +// Parameters: see PausePlayers +// pCallback - the asynchronous callback to call when done +// pUserData - the user data to pass into the callback +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::ContinuePlayersWithFade +( + IDaSoundFadeState* pCallback, + void* pUserData +) +{ + // Create the fade info + daSoundAsyncFadeCallback* pFadeInfo = + new( GMA_TEMP ) daSoundAsyncFadeCallback( ); + rAssert( pFadeInfo != NULL ); + pFadeInfo->SetPlayerManager( this ); + pFadeInfo->SetAction( OnFade_ContinuePlayers ); + pFadeInfo->SetCallback( pCallback, pUserData ); + + // Continue the players + ContinuePlayers(); + + // Start fading the sounds + rAssert( m_pIngameFadeIn != NULL ); + Sound::daSoundRenderingManagerGet( )->GetTuner( )->FadeSounds + ( + this, + pFadeInfo, + m_pIngameFadeIn, + true + ); +} + +//============================================================================= +// Function: daSoundPlayerManager::CancelPlayers +//============================================================================= +// Description: Stop the sound players +// +// Parameters: stackFrame - the stack frame that a player is associated with. +// If NULL, all players stop. +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::CancelPlayers +( +) +{ + // + // Stop all players + // + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + if( pPlayer->IsCaptured( ) ) + { + // Stop the player + pPlayer->Stop( ); + } + + // Move on + pPlayer = pPlayer->GetLinkedClassNext( ); + } + + // + // Since this command may be called by the sound manager destructor, + // we must make sure that we service the sound system at least one + // more time so that we can actually stop all the sounds. + // + ::radSoundHalSystemGet( )->Service( ); + ::radSoundHalSystemGet( )->ServiceOncePerFrame( ); +} + +//============================================================================= +// daSoundPlayerManager::AreAllPlayersStopped +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: bool +// +//============================================================================= +bool daSoundPlayerManager::AreAllPlayersStopped() +{ + unsigned int c; + daSoundClipStreamPlayer::State playerState; + + for( c = 0; c < SOUND_NUM_CLIP_PLAYERS; c++ ) + { + if( gClipPlayerArray[c] != NULL ) + { + playerState = gClipPlayerArray[c]->GetState(); + + if( ( playerState == daSoundClipStreamPlayer::State_Cueing ) + || ( playerState == daSoundClipStreamPlayer::State_CuedPlay ) + || ( playerState == daSoundClipStreamPlayer::State_Playing ) ) + { + if( !(gClipPlayerArray[c]->IsPaused()) ) + { + return( false ); + } + } + } + } + + for( c = 0; c < SOUND_NUM_STREAM_PLAYERS; c ++ ) + { + if( gStreamPlayerArray[c] != NULL ) + { + playerState = gStreamPlayerArray[c]->GetState(); + + if( ( playerState == daSoundClipStreamPlayer::State_Cueing ) + || ( playerState == daSoundClipStreamPlayer::State_CuedPlay ) + || ( playerState == daSoundClipStreamPlayer::State_Playing ) + || ( playerState == daSoundClipStreamPlayer::State_Stopping ) ) + { + if( !(gStreamPlayerArray[c]->IsPaused()) ) + { + return( false ); + } + } + } + } + + return( true ); +} + +//============================================================================= +// Function: daSoundPlayerManager::PlayerVolumeChange +//============================================================================= +// Description: updates all of the players with the new volume value +// +//----------------------------------------------------------------------------- +void daSoundPlayerManager::PlayerVolumeChange( daSoundGroup groupName, float trim ) +{ + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + pPlayer->ChangeTrim(groupName,trim); + + // Move on + pPlayer = pPlayer->GetLinkedClassNext( ); + } +} + +//============================================================================= +// Function: daSoundPlayerManager::PlayerFaderVolumeChange +//============================================================================= +// Description: updates all of the players with the new fader volume value +// +//----------------------------------------------------------------------------- +void daSoundPlayerManager::PlayerFaderVolumeChange( daSoundGroup groupName, float trim ) +{ + daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( ); + while( pPlayer != NULL ) + { + pPlayer->ChangeFaderTrim(groupName,trim); + + // Move on + pPlayer = pPlayer->GetLinkedClassNext( ); + } +} + +//============================================================================= +// Function: daSoundPlayerManager::OnFadeDone +//============================================================================= +// Description: React when the fade is done +// +// Parameters: pUserData - a void* to a daSoundAsyncFadeCallback +// +//----------------------------------------------------------------------------- + +void daSoundPlayerManager::OnFadeDone( void* pUserData ) +{ + daSoundAsyncFadeCallback* pFadeInfo = + reinterpret_cast< daSoundAsyncFadeCallback* >( pUserData ); + rAssert( pFadeInfo != NULL ); + + // Perform the appropriate action + switch( pFadeInfo->GetAction( ) ) + { + case OnFade_PausePlayers: + { + PausePlayers(); + break; + } + case OnFade_ContinuePlayers: + { + break; + } + case OnFade_CancelPlayers: + { + CancelPlayers(); + Sound::daSoundRenderingManagerGet( )-> + GetTuner( )-> + SetMasterVolume( 1.0f ); + break; + } + default: + { + rAssert( 0 ); + break; + } + } + + // Call the callback + IDaSoundFadeState* pCallback = NULL; + void* pData = NULL; + pFadeInfo->GetCallback( &pCallback, &pData ); + + if( pCallback != NULL ) + { + pCallback->OnFadeDone( pData ); + } + + // Delete the fade info + delete pFadeInfo; +} + +void TrimFileName( char * pS, int len ) +{ + int sl = strlen( pS ); + + char * pStart; + char * pEnd; + + pEnd = pS + sl - 4; + pStart = pEnd - len; + + if ( pStart < pS ) + { + pStart = pS; + } + + if ( pEnd < pStart ) + { + pEnd = pS + 1; + } + + int chars = pEnd - pStart; + + ::memcpy( pS, pStart, chars); + pS[ chars ] = 0; +} + +void RenderPlayer( daSoundClipStreamPlayer * pPlayer, int row, int col ) +{ + char buf[ 256 ]; + + if ( false == pPlayer->IsCaptured( ) ) + { + sprintf( buf, "free" ); + } + else + { + float fDistToListener; + char sDistToListener[ 64 ]; + char sFileName[ 64 ]; + char sMaxDistance[ 64 ]; + + pPlayer->GetFileName( sFileName, 64 ); + + TrimFileName( sFileName, 8 ); + + IRadSoundHalPositionalGroup * pPosGroup = pPlayer->GetPositionalGroup( ); + + if ( pPosGroup ) + { + radSoundVector listenerPos; + radSoundVector position; + + float minDist; + float maxDist; + + radSoundHalListenerGet( )->GetPosition( & listenerPos ); + + pPosGroup->GetPosition( & position ); + pPosGroup->GetMinMaxDistance( & minDist, & maxDist ); + + fDistToListener = listenerPos.GetDistanceBetween( position ); + + sprintf( sDistToListener, "%.2f", fDistToListener ); + sprintf( sMaxDistance, "%.2f", maxDist ); + } + else + { + strcpy( sDistToListener, "--" ); + strcpy( sMaxDistance, "--" ); + } + + + // gClipPlayerArray[ c ]-> + sprintf( buf, "[%s](%s)[%d][%s][%s]", + sFileName, + pPlayer->IsPaused( ) ? "-" : "*", + pPlayer->GetState( ), + sDistToListener, + sMaxDistance ); + } + + p3d::pddi->DrawString( buf, 40 + col * 320 + , 36 + row * 16, pddiColour( 255, 255, 0 ) ); + +} + +void daSoundPlayerManager::Render( void ) +{ + if( m_Initialized ) + { + int col = 0; + int row = 0; + + unsigned int freeMem; + unsigned int numObjects; + unsigned int largestBlock; + unsigned int size; + + radSoundHalSystemGet( )->GetRootMemoryRegion( )->GetStats( + & freeMem, + & numObjects, + & largestBlock, + true ); + + size = radSoundHalSystemGet( )->GetRootMemoryRegion( )->GetSize( ); + + char memStr[ 256 ]; + sprintf( + memStr, + "Usd %dK Fre %dK Lrg %dK Objs: %d", + ( size - freeMem ) / 1024, + freeMem / 1024, + largestBlock / 1024, + numObjects ); + + p3d::pddi->DrawString( memStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) ); + + row++; + + unsigned int usedBTreeNodes = radObjectBTree::GetNumAllocatedNodes( ); + unsigned int nodeSize = sizeof( radObjectBTreeNode ); + + sprintf( memStr, + "BTree Nodes: [0x%x], size: [0x%x]", + usedBTreeNodes, + nodeSize ); + + p3d::pddi->DrawString( memStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) ); + + row++; + + char listenerStr[ 128 ]; + radSoundVector lp; + radSoundVector lv; + radSoundHalListenerGet( )->GetPosition( & lp ); + radSoundHalListenerGet( )->GetVelocity( & lv ); + + sprintf( listenerStr, "Pos:[%.2f][%.2f][%.2f] Vel:[%.2f][%.2f][%.2f]\n", + lp.m_x, lp.m_y, lp.m_z, lv.m_x, lv.m_y, lv.m_z ); + + p3d::pddi->DrawString( listenerStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) ); + + row++; + + for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS / 2; c ++ ) + { + RenderPlayer( gClipPlayerArray[ c ], row, col ); + + col++; + + if ( col >= 2 ) + { + row++; + col = 0; + } + } + + row++; + + for( unsigned int c = 0; c < SOUND_NUM_STREAM_PLAYERS; c ++ ) + { + RenderPlayer( gStreamPlayerArray[ c ], row, col ); + + col++; + + if ( col >= 2 ) + { + row++; + col = 0; + } + } + } +} + +} // Sound Namespace + diff --git a/game/code/sound/soundrenderer/playermanager.h b/game/code/sound/soundrenderer/playermanager.h new file mode 100644 index 0000000..09fa4eb --- /dev/null +++ b/game/code/sound/soundrenderer/playermanager.h @@ -0,0 +1,185 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: playermanager.h +// +// Subsystem: Dark Angel - Player Manager System +// +// Description: Description of the DA sound player manager +// +// Revisions: +// + Created October 16, 2001 -- breimer +// +//============================================================================= + +#ifndef _PLAYERMANAGER_HPP +#define _PLAYERMANAGER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/musicsoundplayer.h> +#include <radsound.hpp> + +//============================================================================= +// Global namespace forward declarations +//============================================================================= + +struct IRadObjectList; + +class Fader; + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundPlayerGroupWiring; +class daSoundPlayerManager; + +//============================================================================= +// Forward declarations +//============================================================================= + +class daSoundClipStreamPlayer; + +//============================================================================= +// Class Declarations +//============================================================================= + +// +// This class is created for our asynchronous fades +// + +class daSoundAsyncFadeCallback : public radObject +{ +public: + IMPLEMENT_BASEOBJECT( "daSoundAsyncFadeCallback" ) + + daSoundAsyncFadeCallback( ); + virtual ~daSoundAsyncFadeCallback( ); + + void SetAction( int action ) { m_Action = action; } + int GetAction( void ) { return m_Action; } + + void SetPlayerManager( daSoundPlayerManager* pPlayerManager ); + daSoundPlayerManager* GetPlayerManager( void ); + + void SetCallback( IDaSoundFadeState* pCallback, void* pUserData ); + void GetCallback( IDaSoundFadeState** ppCallback, void** ppUserData ); + +private: + + int m_Action; + daSoundPlayerManager* m_pPlayerManager; + IDaSoundFadeState* m_pCallback; + void* m_pUserData; +}; + +// +// The player manager is responsible for creating and managing daSoundClipStreamPlayer +// objects. These objects allow the user to play sound resources. +// +class daSoundPlayerManager : public IDaSoundFadeState, + public radRefCount +{ +public: + IMPLEMENT_REFCOUNTED_NOSIZE( "daSoundPlayerManager" ); + + // + // Constructor and destructor + // + daSoundPlayerManager( ); + virtual ~daSoundPlayerManager( ); + + inline daSoundPlayerManager * GetInstance( void ); + + bool FindFreeClipPlayer( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource ); + bool FindFreeStreamPlayer( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource ); + + unsigned int GetNumUsedClipPlayers(); + unsigned int GetNumUsedStreamPlayers(); + void Initialize( void ); + void UglyHackPostInitialize( IDaSoundTuner* pTuner ); + void ServiceOncePerFrame( void ); + unsigned int GetObjectSize( void ); + void CaptureFreePlayer( + daSoundClipStreamPlayer** ppPlayer, + IDaSoundResource* pResource, + bool positional ); + + void PausePlayers ( ); + void PausePlayersWithFade( + IDaSoundFadeState* pCallback, + void* pUserData ); + + void ContinuePlayers ( ); + + void ContinuePlayersWithFade( + IDaSoundFadeState* pCallback, + void* pUserData ); + + void CancelPlayers ( ); + + bool AreAllPlayersStopped(); + + // + // Volume controls + // + void PlayerVolumeChange( daSoundGroup soundGroup, daTrimValue trim ); + void PlayerFaderVolumeChange( daSoundGroup soundGroup, daTrimValue trim ); + + void Render( void ); + +protected: + // When a fade is done go here and call our callback + enum FadeTypesEnum { + OnFade_PausePlayers, + OnFade_ContinuePlayers, + OnFade_CancelPlayers + }; + void OnFadeDone( void* pUserData ); + +private: + + bool FindFreePlayer( daSoundClipStreamPlayer** ppPlayerArray, unsigned int numPlayers, daSoundClipStreamPlayer ** ppPlayer ); + + MusicSoundPlayer* m_pMusicPlayer; + MusicSoundPlayer* m_pAmbiencePlayer; + + // + // The ingame faders + // + Fader* m_pIngameFadeIn; + Fader* m_pIngameFadeOut; + + bool m_Initialized; + + static daSoundPlayerManager * s_pInstance; + +}; + +inline daSoundPlayerManager * daSoundPlayerManager::GetInstance( void ) +{ + return s_pInstance; +} + +} // Sound Namespace +#endif //_PLAYERMANAGER_HPP + + diff --git a/game/code/sound/soundrenderer/soundallocatedresource.cpp b/game/code/sound/soundrenderer/soundallocatedresource.cpp new file mode 100644 index 0000000..e6678ca --- /dev/null +++ b/game/code/sound/soundrenderer/soundallocatedresource.cpp @@ -0,0 +1,429 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundallocatedresource.cpp +// +// Subsystem: Dark Angel - Sound Resource Management System +// +// Description: Implementation of an allocated sound resource +// +// Revisions: +// + Created October 18, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radsound.hpp> +#include <raddebugwatch.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundnucleus.hpp> + +#include <memory/srrmemory.h> + +//============================================================================= +// Static Variables (outside namespace) +//============================================================================= + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Static Variables (inside namespace) +//============================================================================= + +//============================================================================= +// Constants +//============================================================================= + +#ifndef RAD_RELEASE + +#ifdef RAD_DEBUG +static bool s_displayMemoryInfo = true; +#else +static bool s_displayMemoryInfo = false; +#endif + +static bool s_isInitialized = false; +static int s_memoryRemaining = 0; + +#endif // RAD_RELEASE + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundAllocatedResource Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundFileInstance::daSoundFileInstance +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundFileInstance::daSoundFileInstance( + IDaSoundResource* pResource, + unsigned int fileIndex ) +{ + rAssert( pResource != NULL ); + + m_RefCount = 0; + m_State = UnLoaded; + m_pSoundClip = NULL; + + m_State = UnLoaded; + + m_FileIndex = fileIndex; + + m_pResource = pResource; + m_pResource->AddRef( ); + + // Set the type of resource + + IDaSoundResource::Type type = pResource->GetType( ); + + if( type == IDaSoundResource::CLIP ) + { + m_Type = IDaSoundResource::CLIP; + } + else + { + rAssert( type == IDaSoundResource::STREAM ); + m_Type = IDaSoundResource::STREAM; + } + +#ifndef RAD_RELEASE + if( !s_isInitialized ) + { + radDbgWatchAddBoolean( &s_displayMemoryInfo, "Show Loading Spew", "Sound Info", 0, 0 ); + + s_isInitialized = true; + } +#endif +} + +//============================================================================= +// Function: daSoundFileInstance::~daSoundFileInstance +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundFileInstance::~daSoundFileInstance( ) +{ + m_pResource->Release( ); + + rAssert( m_State == UnLoaded ); + rAssert( NULL == m_pSoundClip ); + +} + +//============================================================================= +// Function: daSoundFileInstance::CreateFileDataSource +//============================================================================= +// Description: Get the sound stream. +// +// Returns: Returns the sound stream if it is allocated for this resource, +// or NULL if it is not. +// +//----------------------------------------------------------------------------- + +void daSoundFileInstance::CreateFileDataSource( + IRadSoundRsdFileDataSource** ppFds ) +{ + rAssert( GetType( ) == IDaSoundResource::STREAM ); + rAssert( Loaded == m_State ); + + char fileName[ 256 ]; + m_pResource->GetFileKeyAt( m_FileIndex, fileName, 256 ); + + *ppFds = radSoundRsdFileDataSourceCreate( GMA_AUDIO_PERSISTENT ); + (*ppFds)->AddRef( ); + + (*ppFds)->InitializeFromFileName( + fileName, + true, + 0, + IRadSoundHalAudioFormat::Milliseconds, + SoundNucleusGetStreamFileAudioFormat( ) ); + +} + +//============================================================================= +// Function: daSoundFileInstance::OnDynaLoadObjectCreate +//============================================================================= +// Description: Called when this object is being created (by dynamic loading) +// +//----------------------------------------------------------------------------- + +void daSoundFileInstance::Load( IRadSoundHalMemoryRegion* pRegion ) +{ + rAssert( m_State == UnLoaded ); + + rAssert( m_Type == IDaSoundResource::UNKNOWN || m_pResource != NULL ); + + // Create each type of object + if( m_Type == IDaSoundResource::CLIP ) + { + char fileName[ 256 ]; + m_pResource->GetFileKeyAt( m_FileIndex, fileName, 256 ); + + SoundNucleusLoadClip( fileName, m_pResource->GetLooping( ) ); + } + + m_State = Loading; +} + +//============================================================================= +// Function: daSoundFileInstance::OnDynaLoadObjectCreate +//============================================================================= +// Description: Is this object ready and stable? +// +//----------------------------------------------------------------------------- + +bool daSoundFileInstance::UpdateLoading( void ) +{ + rAssert( Loading == m_State ); + + if( m_Type == IDaSoundResource::CLIP ) + { + if ( SoundNucleusIsClipLoaded( ) ) + { + SoundNucleusFinishClipLoad( & m_pSoundClip ); + m_State = Loaded; + } + } + else + { + m_State = Loaded; + } + + return Loaded == m_State; +} + +//============================================================================= +// Function: daSoundFileInstance::OnDynaLoadObjectDestroy +//============================================================================= +// Description: Destroy this dynamically loading object +// +//----------------------------------------------------------------------------- + +void daSoundFileInstance::UnLoad( void ) +{ + rAssert( m_State == Loaded || Loading == m_State ); + + if ( m_Type == IDaSoundResource::CLIP ) + { + if ( Loading == m_State ) + { + SoundNucleusCancelClipLoad( ); + } + else if ( Loaded == m_State ) + { + rAssert( m_pSoundClip != NULL ); + m_pSoundClip->Release( ); + m_pSoundClip = NULL; + } + } + + m_State = UnLoaded; +} + +//============================================================================= +// daSoundAllocatedResource Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundAllocatedResource::daSoundAllocatedResource +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundAllocatedResource::daSoundAllocatedResource( ) + : + m_RefCount( 0 ) +{ + unsigned int i = 0; + + for( i = 0; i < DASound_MaxNumSoundResourceFiles; i++ ) + { + m_pFileInstance[ i ] = NULL; + m_pDynaLoadRegion[ i ] = NULL; + } +} + +//============================================================================= +// Function: daSoundAllocatedResource::~daSoundAllocatedResource +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundAllocatedResource::~daSoundAllocatedResource( void ) +{ + if( GetResource( ) != NULL ) + { + unsigned int i = 0; + unsigned int j = 0; + for( i = 0; i < DASound_MaxNumSoundResourceFiles; i++ ) + { + // Release the dynamic loading region + if( m_pDynaLoadRegion[ i ] != NULL ) + { + for( j = 0; j < m_pDynaLoadRegion[ i ]->GetNumSlots( ); j++ ) + { + m_pDynaLoadRegion[ i ]->SwapInObject( j, NULL ); + } + m_pDynaLoadRegion[ i ]->Release( ); + m_pDynaLoadRegion[ i ] = NULL; + } + + // Release file instance + if( m_pFileInstance[ i ] != NULL ) + { + m_pFileInstance[ i ]->Release( ); + m_pFileInstance[ i ] = NULL; + } + } + + // Release the resource + m_pResource->Release( ); + } + +} + +//============================================================================= +// Function: daSoundAllocatedResource::Initialize +//============================================================================= +// Description: Intialize the allocated resource by giving it a normal +// resource. +// +// Parameters: pResource - the resource for this instance to associate with +// index - the index of the resource file to use +// +//----------------------------------------------------------------------------- + +void daSoundAllocatedResource::Initialize +( + IDaSoundResource* pResource +) +{ + rAssert( pResource != NULL ); + + m_pResource = pResource; + m_pResource->AddRef( ); + + // Look in the resource manager's tree of allocated resources + // If the file is already loaded, just addref the object + Sound::daSoundResourceManager* pResManager = Sound::daSoundResourceManager::GetInstance( ); + + // Load and initialize the files; + + unsigned int numFiles = m_pResource->GetNumFiles( ); + + for( unsigned int i = 0; i < numFiles; i++ ) + { + +#ifdef RAD_XBOX + daSoundFileInstance* pFileInstance = new( GMA_XBOX_SOUND_MEMORY ) daSoundFileInstance( m_pResource, i ); +#else + daSoundFileInstance* pFileInstance = new( GMA_AUDIO_PERSISTENT ) daSoundFileInstance( m_pResource, i ); +#endif + pFileInstance->AddRef( ); + + // + // Generate a dyna load region for this object + // + + unsigned int size = 0; + daSoundDynaLoadRegion* pRegion = NULL; + m_pDynaLoadRegion[ i ] = Sound::daSoundRenderingManagerGet( )-> + GetDynaLoadManager( )-> + CreateRegion + ( + ::radSoundHalSystemGet( )->GetRootMemoryRegion( ), + size, + 1 + ); + rAssert( m_pDynaLoadRegion[ i ] != NULL ); + m_pDynaLoadRegion[ i ]->AddRef( ); + m_pDynaLoadRegion[ i ]->SwapInObject( 0, pFileInstance ); + + // Set the file instance internally + // (The AddRef is just copied) + + m_pFileInstance[ i ] = pFileInstance; + } +} + +//============================================================================= +// Function: daSoundAllocatedResource::ChooseNextInstance +//============================================================================= +// Description: Choose the next instance to play in this allocated resource +// +// Notes: +// The resource automatically keeps track of what files +// are being used. It will try not to let anything repeat +// based on the following rules: +// (1) IF ( numfiles <= DASound_NumLastPlayedFilesToRemember ) THEN +// Choose a file randomly +// (2) IF ( numfiles == DASound_NumLastPlayedFilesToRemember + 1 ) THEN +// Choose randomly out of all files except the last one +// used. +// (2) ELSE +// Don't choose one of the DASound_NumLastPlayedFilesToRemember +// last files, but choose randomly out of the rest +// +//----------------------------------------------------------------------------- + +unsigned int daSoundAllocatedResource::ChooseNextInstance( void ) +{ + rAssert( GetResource( ) != NULL ); + + // Get the number of resource files + unsigned int numResourceFiles = GetResource( )->GetNumFiles( ); + rAssert( numResourceFiles > 0 ); + + // Return the chosen file + return( rand( ) % numResourceFiles ); +} + +//============================================================================= +// Function: daSoundAllocatedResource::GetFileInstance +//============================================================================= +// Description: Get the file instance at the given index +// +// Returns: Returns a pointer to the file instance +// +//----------------------------------------------------------------------------- + +daSoundFileInstance* daSoundAllocatedResource::GetFileInstance +( + unsigned int index +) +{ + rAssert( GetResource( ) != NULL ); + rAssert( index < GetResource( )->GetNumFiles( ) ); + + return m_pFileInstance[ index ]; +} + +} // Sound Namespace + diff --git a/game/code/sound/soundrenderer/soundallocatedresource.h b/game/code/sound/soundrenderer/soundallocatedresource.h new file mode 100644 index 0000000..599e4ca --- /dev/null +++ b/game/code/sound/soundrenderer/soundallocatedresource.h @@ -0,0 +1,249 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundallocatedresource.hpp +// +// Subsystem: Dark Angel - Sound Resource Management System +// +// Description: Description of an allocated sound resource +// +// Revisions: +// + Created October 19, 2001 -- breimer +// +//============================================================================= + +#ifndef _SOUNDALLOCATEDRESOURCE_HPP +#define _SOUNDALLOCATEDRESOURCE_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radlinkedclass.hpp> +#include <radsound_hal.hpp> +#include <radsound.hpp> + +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/sounddynaload.h> + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundAllocatedResource; + +class daSoundFileInstance +{ +public: + + inline void * operator new ( size_t size, radMemoryAllocator allocator ); + inline void operator delete( void * pMem ); + + inline void AddRef( void ); + inline void Release( void ); + + // Constructor and destructor + daSoundFileInstance( IDaSoundResource* pResource, unsigned int fileIndex ); + ~daSoundFileInstance( ); + + void Load( IRadSoundHalMemoryRegion* pRegion ); + bool UpdateLoading( void ); + void UnLoad( void ); + + inline IDaSoundResource::Type GetType( void ); + + inline IRadSoundClip* GetSoundClip( void ); + void CreateFileDataSource( IRadSoundRsdFileDataSource** ); + + // Internal state + enum State { Loading, Loaded, UnLoaded }; + + inline State GetState( void ); + + inline void GetFileName( char * pFileName, unsigned int maxChars ); + +protected: + +private: + + unsigned int m_FileIndex; + unsigned int m_RefCount; + + // Store the resource + IDaSoundResource * m_pResource; + + // Store the actual allocated resource based on the attached + IDaSoundResource::Type m_Type; + + IRadSoundClip* m_pSoundClip; + + State m_State; +}; + +// +// The resource library stores a giant array of resources, and provides helpfull +// ways to get at the information. Notices that there is no +// way to remove a resource file from this library. +// +class daSoundAllocatedResource : public IRefCount +{ + +public: + + inline void AddRef( void ); + inline void Release( void ); + + inline void * operator new ( size_t size, radMemoryAllocator allocator ); + inline void operator delete( void * pMem ); + + // + // Constructor and destructor + // + daSoundAllocatedResource( ); + ~daSoundAllocatedResource( ); + + // + // IDaSoundAllocatedResource + // + void Initialize( IDaSoundResource* pResource ); + inline IDaSoundResource* GetResource( void ); + unsigned int ChooseNextInstance( void ); + + daSoundFileInstance* GetFileInstance( unsigned int index ); + +protected: + void ApplyResourceSettings_Internal( unsigned int index ); + +private: + // Store the attached resource + IDaSoundResource* m_pResource; + + unsigned int m_RefCount; + + // Store the file instances + daSoundFileInstance* m_pFileInstance[ DASound_MaxNumSoundResourceFiles ]; + + // Dynamic loading region + daSoundDynaLoadRegion* m_pDynaLoadRegion[ DASound_MaxNumSoundResourceFiles ]; +}; + +//============================================================================= +// Function: daSoundFileInstance::GetType +//============================================================================= +// Description: Get the type of the allocated res +// +//----------------------------------------------------------------------------- + +inline IDaSoundResource::Type daSoundFileInstance::GetType( void ) +{ + return m_Type; +} + + +//============================================================================= +// Function: daSoundFileInstance::GetSoundClip +//============================================================================= +// Description: Get the sound clip. +// +// Returns: Returns the sound clip if it is allocated for this resource, +// or NULL if it is not. +// +//----------------------------------------------------------------------------- + +inline IRadSoundClip* daSoundFileInstance::GetSoundClip( void ) +{ + rAssert( IDaSoundResource::CLIP == GetType( ) ); + rAssert( Loaded == m_State ); + + return m_pSoundClip; +} + + +//============================================================================= +// Function: daSoundAllocatedResource::GetResource +//============================================================================= +// Description: Get the resource data from the allocated resource +// +//----------------------------------------------------------------------------- + +IDaSoundResource* daSoundAllocatedResource::GetResource( void ) +{ + return m_pResource; +} + +inline daSoundFileInstance::State daSoundFileInstance::GetState( void ) +{ + return m_State; +} + +inline void daSoundFileInstance::GetFileName( char * pBuffer, unsigned int max ) +{ + m_pResource->GetFileKeyAt( m_FileIndex, pBuffer, max ); +} + + +inline void daSoundFileInstance::AddRef( void ) +{ + m_RefCount++; +} + +inline void daSoundFileInstance::Release( void ) +{ + rAssert( m_RefCount > 0 ); + + m_RefCount--; + + if( 0 == m_RefCount ) + { + delete this; + } +} + + +inline void * daSoundFileInstance::operator new ( size_t size, radMemoryAllocator allocator ) +{ + return radMemoryAlloc( allocator, size ); +} + +inline void daSoundFileInstance::operator delete( void * pMem ) +{ + return radMemoryFree( pMem ); +} + +inline void daSoundAllocatedResource::AddRef( void ) +{ + m_RefCount++; +} + +inline void daSoundAllocatedResource::Release( void ) +{ + rAssert( m_RefCount > 0 ); + + m_RefCount--; + + if( 0 == m_RefCount ) + { + delete this; + } +} + +inline void * daSoundAllocatedResource::operator new ( size_t size, radMemoryAllocator allocator ) +{ + return radMemoryAlloc( allocator, size ); +} + +inline void daSoundAllocatedResource::operator delete( void * pMem ) +{ + return radMemoryFree( pMem ); +} + +} // Sound Namespace +#endif //_SOUNDALLOCATEDRESOURCE_HPP + diff --git a/game/code/sound/soundrenderer/soundconstants.h b/game/code/sound/soundrenderer/soundconstants.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/game/code/sound/soundrenderer/soundconstants.h diff --git a/game/code/sound/soundrenderer/sounddynaload.cpp b/game/code/sound/soundrenderer/sounddynaload.cpp new file mode 100644 index 0000000..8a5f7b4 --- /dev/null +++ b/game/code/sound/soundrenderer/sounddynaload.cpp @@ -0,0 +1,954 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddynaload.cpp +// +// Subsystem: Dark Angel - Dynamic Loading System +// +// Description: Implementation of the DA Dynamic Sound Loading System +// +// Revisions: +// + Created: Novemeber 22, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radkey.hpp> +#include <radsound.hpp> + +#include <sound/soundrenderer/sounddynaload.h> +#include <sound/soundrenderer/soundallocatedresource.h> + +//============================================================================= +// Static Variables (outside namespace) +//============================================================================= + +Sound::daSoundDynaLoadRegion* radLinkedClass< Sound::daSoundDynaLoadRegion >::s_pLinkedClassHead = NULL; +Sound::daSoundDynaLoadRegion* radLinkedClass< Sound::daSoundDynaLoadRegion >::s_pLinkedClassTail = NULL; + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Debug Information +//============================================================================= + +// +// Use this if you want to debug the sound player +// +#ifndef FINAL +#ifndef NDEBUG +#define DASOUNDDYNALOAD_DEBUG +#ifdef DASOUNDDYNALOAD_DEBUG + +// Show the creation and destruction of dynamic loading regions +static bool sg_ShowDynaLoadRegionCreation = false; + +#endif //DASOUNDDYNALOAD_DEBUG +#endif //NDEBUG +#endif //FINAL + +//============================================================================= +// Static Variables +//============================================================================= + +daSoundDynaLoadManager* daSoundDynaLoadManager::s_pSingleton = NULL; +daSoundDynaLoadRegion* daSoundDynaLoadRegion::s_pActiveRegion = NULL; +unsigned int daSoundDynaLoadRegion::s_ActiveSlot = 0; +unsigned int daSoundDynaLoadRegion::s_GlobalPendingSwapCount = 0; + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundFileInstance; +class daSoundDynaLoadRegion; + +//============================================================================= +// Class Implementation +//============================================================================= + +//============================================================================= +// daSoundDynaLoadRegion Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundDynaLoadRegion::daSoundDynaLoadRegion +//============================================================================= +// Description: Constructor. +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadRegion::daSoundDynaLoadRegion( ) +: +radRefCount( 0 ), +m_IsInitialized( false ), +m_NumSlots( 0 ), +m_SlotSize( 0 ), +m_ppSlot( NULL ), +m_ppSlotObjects( NULL ), +m_ppPendingSwapObjects( NULL ), +m_PendingSwapCount( 0 ) +{ + // +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::~daSoundDynaLoadRegion +//============================================================================= +// Description: Destructor. +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadRegion::~daSoundDynaLoadRegion( ) +{ + // Destroy everything + Destroy( ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::~daSoundDynaLoadRegion +//============================================================================= +// Description: Create the region. If the size of slots is zero, do +// not preallocate the region memory. +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::Create +( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots, + unsigned int numslots +) +{ + rAssert( pMemRegion != NULL ); + rAssert( !m_IsInitialized ); + rAssert( !ArePendingSwapsRegistered( ) ); + + // Set the region information + m_SlotSize = sizeofslots; + m_NumSlots = numslots; + + // Create the slots + m_ppSlot = reinterpret_cast< IRadSoundHalMemoryRegion** > + ( + ::radMemoryAlloc + ( + GetThisAllocator( ), + sizeof( IRadSoundHalMemoryRegion* ) * numslots + ) + ); + rAssert( m_ppSlot ); + + unsigned int i = 0; + for( i = 0; i < numslots; i++ ) + { + // If the slot size is zero, just use the main memory + if( SharedMemoryRegions( ) ) + { + m_ppSlot[ i ] = pMemRegion; + m_ppSlot[ i ]->AddRef( ); + } + else + { + // + // ESAN TODO: Investigate the magic number 32 below... + // + m_ppSlot[ i ] = pMemRegion->CreateChildRegion( m_SlotSize, 32, "Sound Memory Region object" ); + if( m_ppSlot[ i ] == NULL ) + { + // If this occurs, there in the check for free space. This may + // occur if the slot size is not aligned in the same way as + // sound memory. + rDebugString( "Out of sound memory allocating region\n" ); + rAssert( m_ppSlot[ i ] != NULL ); + } + else + { + m_ppSlot[ i ]->AddRef( ); + } + } + rAssert( m_ppSlot[ i ] != NULL ); + rAssert( m_ppSlot[ i ]->GetSize( ) >= m_SlotSize ); + } + + // Create the slot objects and pending slot objects + m_ppSlotObjects = reinterpret_cast< daSoundFileInstance** > + ( + ::radMemoryAlloc + ( + GetThisAllocator( ), + sizeof( daSoundFileInstance* ) * numslots + ) + ); + m_ppPendingSwapObjects = reinterpret_cast< daSoundFileInstance** > + ( + ::radMemoryAlloc + ( + GetThisAllocator( ), + sizeof( daSoundFileInstance* ) * numslots + ) + ); + for( i = 0; i < numslots; i++ ) + { + m_ppSlotObjects[ i ] = NULL; + m_ppPendingSwapObjects[ i ] = NULL; + } + + m_IsInitialized = true; +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::Destroy +//============================================================================= +// Description: Destroy the region. +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::Destroy( void ) +{ + if( m_IsInitialized ) + { + // + // Swap out all the objects + // + unsigned int i = 0; + for( i = 0; i < m_NumSlots; i++ ) + { + SwapInObject( i, NULL ); + } + + // Destroy the pending swap objects + if( m_ppPendingSwapObjects != NULL ) + { + ::radMemoryFree( GetThisAllocator( ), m_ppPendingSwapObjects ); + m_ppPendingSwapObjects = NULL; + } + + // Destroy the memory objects + if( m_ppSlotObjects != NULL ) + { + ::radMemoryFree( GetThisAllocator( ), m_ppSlotObjects ); + m_ppSlotObjects = NULL; + } + + // Destroy the memory regions + if( m_ppSlot != NULL ) + { + for( i = 0; i < m_NumSlots; i++ ) + { + rAssert( m_ppSlot[ i ] != NULL ); + m_ppSlot[ i ]->Release( ); + m_ppSlot[ i ] = NULL; + } + ::radMemoryFree( GetThisAllocator( ), m_ppSlot ); + m_ppSlot = NULL; + } + + // Make sure we are not the active swap + ClearActiveSwap( ); + } +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::ServiceOncePerFrame +//============================================================================= +// Description: Service the sound system once per frame. +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::ServiceOncePerFrame( void ) +{ + // Service any active swaps + if( s_pActiveRegion == this ) + { + if( GetSlotState( s_ActiveSlot ) != Initializing ) + { + ClearActiveSwap( ); + } + } + else + { + // Make sure that any pending swaps take place + if( ArePendingSwapsRegistered( ) ) + { + unsigned int i = 0; + for( i = 0; i < GetNumSlots( ); i++ ) + { + daSoundFileInstance* pObject = GetPendingSwapObject( i ); + if( pObject != NULL ) + { + daSoundDynaLoadRegion::SlotState state = GetSlotState( i ); + if( state == Empty ) + { + PerformSwap( i ); + } + } + } + } + } +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SwapInObject +//============================================================================= +// Description: Swap in a sound object. +// +// Parameters: slot - the slot number to swap a sound into +// pObject - a dynamic loading object +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::SwapInObject +( + unsigned int slot, + daSoundFileInstance* pObject +) +{ + // Destroy any pending swap object + daSoundFileInstance* pOldObject = GetPendingSwapObject( slot ); + if( pOldObject != NULL ) + { + SetPendingSwapObject( slot, NULL ); + ClearActiveSwap( ); + } + + // Destroy the old object + pOldObject = GetSlotObject( slot ); + if( pOldObject != NULL ) + { + pOldObject->UnLoad( ); + SetSlotObject( slot, NULL ); + } + + // Set the pending swap object + SetPendingSwapObject( slot, pObject ); + + // The swap will occur asynchronously in ServiceOncePerFrame( ) +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetSlotState +//============================================================================= +// Description: Get the state of a slot +// +// Parameters: slot - the slot number to check +// +// Return: Returns the state of a slot +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadRegion::SlotState daSoundDynaLoadRegion::GetSlotState +( + unsigned int slot +) +{ + rAssert( m_IsInitialized ); + rAssert( m_ppSlot != NULL ); + rAssert( slot < m_NumSlots ); + rAssert( GetSlotMemoryRegion( slot ) != NULL ); + + bool slotHasObject = true; + bool slotHasAllocation = true; + + unsigned int numobjs = 0; + GetSlotMemoryRegion( slot )->GetStats( NULL, &numobjs, NULL, false ); + slotHasAllocation = ( ( numobjs != 0 ) && ( !SharedMemoryRegions( ) ) ); + + daSoundFileInstance* pObject = GetSlotObject( slot ); + // If we do not have our own memory regions, we assume that the object has already disapeared. + slotHasObject = ( pObject != NULL ); + + // Determine the state + daSoundDynaLoadRegion::SlotState state = Empty; + if( slotHasObject ) + { + rAssert( pObject != NULL ); + + if( pObject->UpdateLoading( ) ) + { + state = Ready; + } + else + { + state = Initializing; + } + } + else + { + if( slotHasAllocation ) + { + state = Destroying; + } + else + { + state = Empty; + } + } + + return state; +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetNumSlots +//============================================================================= +// Description: Get the number of slots in this region +// +// Return: Returns the number of slots +// +//----------------------------------------------------------------------------- + +unsigned int daSoundDynaLoadRegion::GetNumSlots( void ) +{ + rAssert( m_IsInitialized ); + return m_NumSlots; +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetSlotSize +//============================================================================= +// Description: Get the size of the slots in this dynaload region +// +// Return: Returns the size of the slots +// +//----------------------------------------------------------------------------- + +unsigned int daSoundDynaLoadRegion::GetSlotSize( void ) +{ + rAssert( m_IsInitialized ); + return m_SlotSize; +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SharedMemoryRegions +//============================================================================= +// Description: Are the memory regions shared, or does each slot have its own? +// +// Return: Returns true if the memory regions are shared. +// +//----------------------------------------------------------------------------- + +bool daSoundDynaLoadRegion::SharedMemoryRegions( void ) +{ + return( m_SlotSize == 0 ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::PerformSwap +//============================================================================= +// Description: Do an actual swap into a memory slot. The slot +// should be already verified as empty. +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::PerformSwap +( + unsigned int slot +) +{ + bool result = SetActiveSwap( slot ); + if( result ) + { + rAssert( GetSlotState( slot ) == Empty ); + + // Swap in the object + daSoundFileInstance* pObject = GetPendingSwapObject( slot ); + rAssert( pObject != NULL ); + pObject->AddRef( ); + SetSlotObject( slot, pObject ); + SetPendingSwapObject( slot, NULL ); + + // Tell it to create itself + IRadSoundHalMemoryRegion* pRegion = GetSlotMemoryRegion( slot ); + rAssert( pRegion != NULL ); + pObject->Load( pRegion ); + + pObject->Release( ); + } +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetSlotMemoryRegion +//============================================================================= +// Description: Get the slot memory region +// +// Return: Returns the size of the slots +// +//----------------------------------------------------------------------------- + +IRadSoundHalMemoryRegion* daSoundDynaLoadRegion::GetSlotMemoryRegion +( + unsigned int slot +) +{ + rAssert( m_IsInitialized ); + rAssert( m_ppSlot != NULL ); + rAssert( slot < m_NumSlots ); + + return( m_ppSlot[ slot ] ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SetSlotObject +//============================================================================= +// Description: Set a slot's object +// +// Parameters: slot - the slot whose object is to be set +// pObject - the object to place in the slot +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::SetSlotObject +( + unsigned int slot, + daSoundFileInstance* pObject +) +{ + SetObject_Internal( m_ppSlotObjects, slot, pObject ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetSlotObject +//============================================================================= +// Description: Get a slot's object +// +// Return: Returns the sound object +// +//----------------------------------------------------------------------------- + +daSoundFileInstance* daSoundDynaLoadRegion::GetSlotObject +( + unsigned int slot +) +{ + return GetObject_Internal( m_ppSlotObjects, slot ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SetPendingSwapObject +//============================================================================= +// Description: Set a pending swap ovject for a given slot +// +// Parameters: slot - the slot whose object is to be set +// pObject - the object to place in the slot +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::SetPendingSwapObject +( + unsigned int slot, + daSoundFileInstance* pObject +) +{ + if( GetPendingSwapObject( slot ) != NULL ) + { + --m_PendingSwapCount; + --s_GlobalPendingSwapCount; + } + if( pObject != NULL ) + { + ++m_PendingSwapCount; + ++s_GlobalPendingSwapCount; + } + SetObject_Internal( m_ppPendingSwapObjects, slot, pObject ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::ArePendingSwapsRegistered +//============================================================================= +// Description: Returns true if there are still pending swaps registered +// +//----------------------------------------------------------------------------- + +bool daSoundDynaLoadRegion::ArePendingSwapsRegistered( void ) +{ + return( m_PendingSwapCount > 0 ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::ArePendingSwapsRegistered +//============================================================================= +// Description: Returns true if there are still pending swaps registered +// +//----------------------------------------------------------------------------- + +unsigned int daSoundDynaLoadRegion::GetNumPendingSwaps( void ) +{ + return( s_GlobalPendingSwapCount ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetPendingSwapObject +//============================================================================= +// Description: Get a pending swap object for a given slot +// +// Return: Returns the sound object +// +//----------------------------------------------------------------------------- + +daSoundFileInstance* daSoundDynaLoadRegion::GetPendingSwapObject +( + unsigned int slot +) +{ + return GetObject_Internal( m_ppPendingSwapObjects, slot ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SetObject_Internal +//============================================================================= +// Description: Set an object in an object array +// +// Parameters: ppObjects - the object array +// slot - the slot whose object is to be set +// pObject - the object to place in the slot +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::SetObject_Internal +( + daSoundFileInstance** ppObjects, + unsigned int slot, + daSoundFileInstance* pObject +) +{ + rAssert( m_IsInitialized ); + rAssert( ppObjects != NULL ); + rAssert( slot < m_NumSlots ); + + // Under the current usage of this class, we cannot set a slot object + // to anything but NULL if an object already exists. + daSoundFileInstance* pOldObject = GetObject_Internal + ( + ppObjects, + slot + ); + rAssert( (pObject == NULL ) || ( pOldObject == NULL ) ); + if( pOldObject != pObject ) + { + // Out with the old + if( pOldObject != NULL ) + { + pOldObject->Release( ); + pOldObject = NULL; + } + + // In with the new + ppObjects[ slot ] = pObject; + rAssert( GetObject_Internal( ppObjects, slot ) == pObject ); + if( ppObjects[ slot ] != NULL ) + { + ppObjects[ slot ]->AddRef( ); + } + } +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::GetObject_Internal +//============================================================================= +// Description: Get an object in an object array +// +// Return: Returns the sound object +// +//----------------------------------------------------------------------------- + +daSoundFileInstance* daSoundDynaLoadRegion::GetObject_Internal +( + daSoundFileInstance** ppObjects, + unsigned int slot +) +{ + rAssert( m_IsInitialized ); + rAssert( ppObjects != NULL ); + rAssert( slot < m_NumSlots ); + + return( ppObjects[ slot ] ); +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::ClearActiveSwap +//============================================================================= +// Description: Clear the active swap region +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadRegion::ClearActiveSwap( void ) +{ + if( s_pActiveRegion == this ) + { + s_pActiveRegion = NULL; + } +} + +//============================================================================= +// Function: daSoundDynaLoadRegion::SetActiveSwap +//============================================================================= +// Description: Set the active swap to this object. This is used +// to help serialize dynamic loading. +// +// Returns: Returns true if the active swap region has been set to +// this. +// +//----------------------------------------------------------------------------- + +bool daSoundDynaLoadRegion::SetActiveSwap( unsigned int slot ) +{ + if( s_pActiveRegion == NULL ) + { + s_pActiveRegion = this; + s_ActiveSlot = slot; + + return true; + } + return false; +} + + +//============================================================================= +// daSoundDynaLoadManager Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundDynaLoadManager::daSoundDynaLoadManager +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadManager::daSoundDynaLoadManager( ) + : + radRefCount( 0 ), + m_pCompletionCallback( NULL ), + m_pCompletionUserData( NULL ) +{ + // Set the singleton + rAssert( s_pSingleton == NULL ); + s_pSingleton = this; +} + +//============================================================================= +// Function: daSoundDynaLoadManager:~daSoundDynaLoadManager +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadManager::~daSoundDynaLoadManager( ) +{ + // Remove the singleton + rAssert( s_pSingleton != NULL ); + s_pSingleton = NULL; + + // Assert that there is no pending completion callback + rAssert( m_pCompletionCallback == NULL ); +} + +//============================================================================= +// Function: daSoundDynaLoadManager::GetInstance +//============================================================================= +// Description: Get the singleton instance of the load manager +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadManager* daSoundDynaLoadManager::GetInstance( void ) +{ + return s_pSingleton; +} + +//============================================================================= +// Function: daSoundDynaLoadManager::ServiceOncePerFrame +//============================================================================= +// Description: Service the load manager. +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadManager::ServiceOncePerFrame( void ) +{ + IDaSoundDynaLoadCompletionCallback* callback; + + // Service each of the regions + daSoundDynaLoadRegion* pDynaLoadRegion = + daSoundDynaLoadRegion::GetLinkedClassHead( ); + while( pDynaLoadRegion != NULL ) + { + pDynaLoadRegion->ServiceOncePerFrame( ); + pDynaLoadRegion = pDynaLoadRegion->GetLinkedClassNext( ); + } + + // Call any completion callbacks + if + ( + ( m_pCompletionCallback != NULL ) && + ( daSoundDynaLoadRegion::GetNumPendingSwaps( ) == 0 ) + ) + { + // + // Store the callback separately before using, since the callback + // may lead to another sound load + // + callback = m_pCompletionCallback; + m_pCompletionCallback = NULL; + m_pCompletionUserData = NULL; + + callback->OnDynaLoadOperationsComplete + ( + m_pCompletionUserData + ); + + callback->Release(); + } +} + +//============================================================================= +// Function: daSoundDynaLoadManager::CreateRegion +//============================================================================= +// Description: Create a dynamic loading region +// +// Parameters: pMemRegion - the sound region to divide up so that +// the given slots may be created. +// sizeofslots - the size of the dynamic loading slots +// numslots - the number of dynamic loading slots +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadRegion* daSoundDynaLoadManager::CreateRegion +( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots, + unsigned int numslots +) +{ + rAssert( pMemRegion != NULL ); +#ifdef DASOUNDDYNALOAD_DEBUG + if( sg_ShowDynaLoadRegionCreation ) + { + rReleasePrintf + ( + "CreateRegion( %#x, sizeofslots=%u, numslots=%u )\n", + pMemRegion, + sizeofslots, + numslots, + 0 + ); + } +#endif //DASOUNDDYNALOAD_DEBUG + + // Make sure the new region fits into memory + unsigned int freeSpace = 0; + pMemRegion->GetStats( NULL, NULL, &freeSpace, false ); + daSoundDynaLoadRegion* pDynaRegion = NULL; + if( freeSpace >= sizeofslots * numslots ) + { + // Create the memory region + pDynaRegion = new( GetThisAllocator( ) ) daSoundDynaLoadRegion( ); + rAssert( pDynaRegion != NULL ); + pDynaRegion->Create( pMemRegion, sizeofslots, numslots ); + } + else + { + // Out of memory! + // If this occurs during the game, your loading stratagies must be + // reconsidered. If it happens while setting up sounds for + // the game then the choices of resident sound files must + // be looked at, or more sound memory should be allocated. + rDebugString( "Out of sound memory trying to create sound region\n" ); + rAssert( 0 ); + } + + return pDynaRegion; +} + +//============================================================================= +// Function: daSoundDynaLoadManager::CreateRegionFromTotalSpace +//============================================================================= +// Description: Create a dynamic loading region when given a total amount of +// space. +// +// Parameters: pMemRegion - the sound region to divide up so that +// the given slots may be created. +// sizeofslots - the size of the dynamic loading slots +// +// Note: The actual amount of space used will be aligned down by the +// size of slots. +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadRegion* daSoundDynaLoadManager::CreateRegionFromTotalSpace +( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots +) +{ + rAssert( pMemRegion != NULL ); + + // How much free space is there? + unsigned int freeSpace = 0; + pMemRegion->GetStats( NULL, NULL, &freeSpace, false ); + + // How many slots can we make + unsigned int numslots = freeSpace / sizeofslots; + + // Make the slots + daSoundDynaLoadRegion* pDynaRegion = NULL; + if( numslots > 0 ) + { + pDynaRegion = CreateRegion( pMemRegion, sizeofslots, numslots ); + } + + return pDynaRegion; +} + +//============================================================================= +// Function: daSoundDynaLoadManager::AddCompletionCallback +//============================================================================= +// Description: Add a completion callback +// +//----------------------------------------------------------------------------- + +void daSoundDynaLoadManager::AddCompletionCallback +( + IDaSoundDynaLoadCompletionCallback* pCallback, + void* pUserData +) +{ + if( m_pCompletionCallback != NULL ) + { + rDebugString( "Cannot add a completion callback while one is\n" ); + rDebugString( "pending using current sounddynamic loading manager.\n" ); + rAssert( 0 ); + + m_pCompletionCallback->Release( ); + m_pCompletionCallback = NULL; + } + + m_pCompletionCallback = pCallback; + m_pCompletionUserData = pUserData; + + if( m_pCompletionCallback != NULL ) + { + m_pCompletionCallback->AddRef( ); + } +} + +//============================================================================= +// Function: daSoundDynaLoadManager::GetNumPendingSwaps +//============================================================================= +// Description: Returns the number of load regions in line to +// be loaded with new data +// +//----------------------------------------------------------------------------- + +unsigned int daSoundDynaLoadManager::GetNumPendingSwaps( void ) +{ + return daSoundDynaLoadRegion::GetNumPendingSwaps( ); +} + +} // Sound Namespace diff --git a/game/code/sound/soundrenderer/sounddynaload.h b/game/code/sound/soundrenderer/sounddynaload.h new file mode 100644 index 0000000..ef24357 --- /dev/null +++ b/game/code/sound/soundrenderer/sounddynaload.h @@ -0,0 +1,199 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: sounddynaload.hpp +// +// Subsystem: Dark Angel - Dynamic Loading System +// +// Description: Description of the DA Dynamic Sound Loading System +// +// Revisions: +// + Created: Novemeber 22, 2001 -- breimer +// +//============================================================================= + +#ifndef _SOUNDDYNALOAD_HPP +#define _SOUNDDYNALOAD_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <radsound.hpp> + +//============================================================================= +// Namespace +//============================================================================= + + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundDynaLoadRegion; +class daSoundDynaLoadManager; +class daSoundFileInstance; + +//============================================================================= +// Class Declarations +//============================================================================= + +struct IDaSoundDynaLoadCompletionCallback : public IRefCount +{ + virtual void OnDynaLoadOperationsComplete( void* pUserData ) = 0; +}; + +// +// A dynamic load region +// +class daSoundDynaLoadRegion : public radLinkedClass< daSoundDynaLoadRegion >, + public radRefCount +{ +public: + + enum SlotState { + Empty, + Initializing, + Ready, + Destroying + }; + + IMPLEMENT_REFCOUNTED( "daSoundDynaLoadRegion" ); + daSoundDynaLoadRegion( ); + virtual ~daSoundDynaLoadRegion( ); + + // Create and destroy the region + void Create + ( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots, + unsigned int numslots + ); + void Destroy( void ); + void ServiceOncePerFrame( void ); + + // Track if there are any pending swaps registered. + static unsigned int GetNumPendingSwaps( void ); + + // + // daSoundDynaLoadRegion + // + void SwapInObject + ( + unsigned int slot, + daSoundFileInstance* pObject + ); + daSoundDynaLoadRegion::SlotState GetSlotState + ( + unsigned int slot + ); + unsigned int GetNumSlots( void ); + unsigned int GetSlotSize( void ); + +protected: + bool SharedMemoryRegions( void ); + + void PerformSwap( unsigned int slot ); + + IRadSoundHalMemoryRegion* GetSlotMemoryRegion( unsigned int slot ); + + void SetSlotObject( unsigned int slot, daSoundFileInstance* pObject ); + daSoundFileInstance* GetSlotObject( unsigned int slot ); + + void SetPendingSwapObject( unsigned int slot, daSoundFileInstance * pObject ); + bool ArePendingSwapsRegistered( void ); + daSoundFileInstance* GetPendingSwapObject( unsigned int slot ); + + void SetObject_Internal + ( + daSoundFileInstance** ppObjects, + unsigned int slot, + daSoundFileInstance* pObject + ); + daSoundFileInstance* GetObject_Internal + ( + daSoundFileInstance** ppObjects, + unsigned int slot + ); + + void ClearActiveSwap( void ); + bool SetActiveSwap( unsigned int slot ); + +private: + bool m_IsInitialized; + + // The active swap + static daSoundDynaLoadRegion* s_pActiveRegion; + static unsigned int s_ActiveSlot; + + // Region information + unsigned int m_NumSlots; + unsigned int m_SlotSize; + + // Allocated regions + IRadSoundHalMemoryRegion** m_ppSlot; + daSoundFileInstance** m_ppSlotObjects; + daSoundFileInstance** m_ppPendingSwapObjects; + + // Count of pending swap operations + unsigned int m_PendingSwapCount; + static unsigned int s_GlobalPendingSwapCount; +}; + +// +// Dynamic loading system interface +// +class daSoundDynaLoadManager : public radRefCount +{ +public: + IMPLEMENT_REFCOUNTED( "daSoundDynaLoadManager" ); + daSoundDynaLoadManager( ); + virtual ~daSoundDynaLoadManager( ); + + static daSoundDynaLoadManager* GetInstance( void ); + + // + // daSoundDynaLoadRegion + // + virtual void ServiceOncePerFrame( void ); + + daSoundDynaLoadRegion* CreateRegion + ( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots, + unsigned int numslots + ); + daSoundDynaLoadRegion* CreateRegionFromTotalSpace + ( + IRadSoundHalMemoryRegion* pMemRegion, + unsigned int sizeofslots + ); + void AddCompletionCallback + ( + IDaSoundDynaLoadCompletionCallback* pCallback, + void* pUserData + ); + unsigned int GetNumPendingSwaps( void ); + +protected: +private: + // + // Store this class as a singleton + // + static daSoundDynaLoadManager* s_pSingleton; + + // + // Store a completion callback + // + IDaSoundDynaLoadCompletionCallback* m_pCompletionCallback; + void* m_pCompletionUserData; +}; + +} // Sound Namespace +#endif //_SOUNDDYNALOAD_HPP + diff --git a/game/code/sound/soundrenderer/soundnucleus.cpp b/game/code/sound/soundrenderer/soundnucleus.cpp new file mode 100644 index 0000000..2c56488 --- /dev/null +++ b/game/code/sound/soundrenderer/soundnucleus.cpp @@ -0,0 +1,594 @@ +#include <sound/soundrenderer/soundnucleus.hpp> +#include <main/commandlineoptions.h> +#include <memory/srrmemory.h> +#include <radmusic/radmusic.hpp> +#include <memory/srrmemory.h> + +namespace Sound +{ + +const Encoding gPcmEncoding = { IRadSoundHalAudioFormat::PCM, 1, 1 }; +const Encoding gPcmBEncoding = { IRadSoundHalAudioFormat::PCM_BIGENDIAN, 1, 1 }; +const Encoding gVagEncoding = { IRadSoundHalAudioFormat::VAG, 2, 7 }; +const Encoding gGcAdpcmEncoding = { IRadSoundHalAudioFormat::GCNADPCM, 2, 7 }; +const Encoding gRadAdpcmEncoding = { IRadSoundHalAudioFormat::RadicalAdpcm, 5, 16 }; +const Encoding gXAdpcmEncoding = { IRadSoundHalAudioFormat::XBOXADPCM, 36, 128 }; + +const unsigned int NUM_AUX_SENDS = 1; + +const unsigned int MUSIC_NUM_STREAM_PLAYERS = 4; +const unsigned int MUSIC_NUM_CLIP_PLAYERS = 2; +const unsigned int MUSIC_NUM_CHANNELS = 2; +const unsigned int MUSIC_SAMPLING_RATE = 24000; + +const unsigned int TOTAL_PS2_FREE_UNCOMPRESSED_CLIP_BYTES = ( 1624 * 1024 * 7 ) / 2; + +#if defined RAD_GAMECUBE + + const int ARAM_SILENT_BUFFER_SIZE = 1280; + const int ARAM_USER_START = 0x4000; + const int ARAM_RESERVED_BINK_MEMORY = 0x25800; + const int GAMECUBE_SOUND_MEMORY_AVAILABLE = + ( 1024 * 1024 * 10 ) - ARAM_SILENT_BUFFER_SIZE - ARAM_USER_START - ARAM_RESERVED_BINK_MEMORY; + + const int PLAYBACK_RATE = 32000; + + AudioFormat gCompressedStreamAudioFormat = { 1, & gGcAdpcmEncoding, 24000 }; + AudioFormat gUnCompressedStreamAudioFormat = { 1, & gPcmBEncoding, 24000 }; + AudioFormat gClipFileAudioFormat = { 1, & gPcmBEncoding, 24000 }; + AudioFormat gMusicAudioFormat = { 2, & gPcmBEncoding, 24000 }; + + const unsigned int STREAM_BUFFER_SIZE_MS = 6000; + const bool STREAM_USE_BUFFERED_DATA_SOURCES = false; + const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 0; + const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local; + + const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 0; + const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local; + +#elif defined RAD_XBOX || defined RAD_WIN32 + + const int PLAYBACK_RATE = 0; + +#ifdef PAL + AudioFormat gCompressedStreamAudioFormat = { 1, & gXAdpcmEncoding, 24000 }; +#else + AudioFormat gCompressedStreamAudioFormat = { 1, & gPcmEncoding, 24000 }; +#endif + AudioFormat gUnCompressedStreamAudioFormat = { 1, & gPcmEncoding, 24000 }; + AudioFormat gClipFileAudioFormat = { 1, & gPcmEncoding, 24000 }; + AudioFormat gMusicAudioFormat = { 2, & gPcmEncoding, 24000 }; + + const unsigned int STREAM_BUFFER_SIZE_MS = 6000; + const bool STREAM_USE_BUFFERED_DATA_SOURCES = false; + const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 0; + const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local; + + const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 0; + const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local; + + +#elif defined RAD_PS2 + + const int PLAYBACK_RATE = 0; + + AudioFormat gCompressedStreamAudioFormat = { 1, & gVagEncoding, 24000 }; + AudioFormat gUnCompressedStreamAudioFormat = { 1, & gVagEncoding, 24000 }; + AudioFormat gClipFileAudioFormat = { 1, & gVagEncoding, 24000 }; + AudioFormat gMusicAudioFormat = { 2, & gVagEncoding, 24000 }; + + const unsigned int STREAM_BUFFER_SIZE_MS = 1000; + const bool STREAM_USE_BUFFERED_DATA_SOURCES = true; + const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 4100; + const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Iop; + + const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 5000; + const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Ee; + +#endif + +enum ClipLoadState +{ + ClipLoadState_Idle, + ClipLoadState_InitFile, + ClipLoadState_FillingBuffer, + ClipLoadState_LoadingClip, + ClipLoadState_Done +}; + + +struct ClipLoadInfo +{ + IRadSoundRsdFileDataSource * pFds; + IRadSoundClip * pClip; + IRadSoundBufferedDataSource * pBds; + ClipLoadState state; + bool looping; +} gClipLoadInfo; + +StreamerResources gStreamers[ SOUND_NUM_STREAM_PLAYERS ] = +{ + { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, +#ifdef RAD_XBOX + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }, +#endif + { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 } +}; + + +void CreateAudioFormat( AudioFormat * pAf, radMemoryAllocator alloc ) +{ + pAf->m_pAudioFormat = ::radSoundHalAudioFormatCreate( alloc ); + pAf->m_pAudioFormat->AddRef( ); + + pAf->m_pAudioFormat->Initialize( + pAf->m_pEncoding->m_Encoding, + NULL, + pAf->m_SamplingRate, + pAf->m_Channels, + 16 ); +} + +void DestroyAudioFormat( AudioFormat * pAf ) +{ + pAf->m_pAudioFormat->Release( ); + pAf->m_pAudioFormat = NULL; +} + +void CreateStreamerResources( StreamerResources * pSi, radMemoryAllocator alloc ) +{ + pSi->m_pStreamPlayer = ::radSoundStreamPlayerCreate( alloc ); + pSi->m_pStreamPlayer->AddRef( ); + + pSi->m_pStreamPlayer->Initialize( + pSi->m_pAudioFormat->m_pAudioFormat, + STREAM_BUFFER_SIZE_MS, + IRadSoundHalAudioFormat::Milliseconds, + ::radSoundHalSystemGet( )->GetRootMemoryRegion( ), + "Sound Stream Player" ); + + pSi->m_pStitchedDataSource = ::radSoundStitchedDataSourceCreate( alloc ); + pSi->m_pStitchedDataSource->AddRef( ); + pSi->m_pStitchedDataSource->InitializeFromAudioFormat( pSi->m_pAudioFormat->m_pAudioFormat ); + + if ( STREAM_USE_BUFFERED_DATA_SOURCES && ( false == CommandLineOptions::Get( CLO_FIREWIRE ) ) ) + { + pSi->m_pBufferedDataSource = radSoundBufferedDataSourceCreate( alloc ); + pSi->m_pBufferedDataSource->AddRef( ); + + pSi->m_pBufferedDataSource->Initialize( + STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE, + radMemorySpaceGetAllocator( STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE, RADMEMORY_ALLOC_DEFAULT ), // This is IOP Memory + STREAM_BUFFERED_DATA_SOURCE_SIZE_MS, + IRadSoundHalAudioFormat::Milliseconds, + pSi->m_pAudioFormat->m_pAudioFormat, + "DaSound Buffered DataSource" ); + + } + else + { + pSi->m_pBufferedDataSource = NULL; + } +} + +void DestroyStreamerResources( StreamerResources* pSi ) +{ + // Its a stream player + pSi->m_pStreamPlayer->Stop( ); + pSi->m_pStreamPlayer->SetDataSource( NULL ); + + if( pSi->m_pBufferedDataSource ) + { + pSi->m_pBufferedDataSource->Release( ); + pSi->m_pBufferedDataSource = NULL; + } + + // Release the player + pSi->m_pStreamPlayer->Release( ); + pSi->m_pStreamPlayer = NULL; + + + pSi->m_pStitchedDataSource->Release( ); + pSi->m_pStitchedDataSource = NULL; +} + +unsigned int CalculateStreamerSize( unsigned int ms, AudioFormat * pAf ) +{ + unsigned int sizeInFrames = pAf->m_pAudioFormat->MillisecondsToFrames( ms ); + + unsigned int optimalFrameMultiple = + pAf->m_pAudioFormat->BytesToFrames( radSoundHalDataSourceReadMultipleGet( ) ); + + // Our buffer must be at least as big as two optimal reads. + + sizeInFrames = radMemoryRoundUp( sizeInFrames, optimalFrameMultiple * 2 ); + + sizeInFrames = ::radSoundHalBufferCalculateMemorySize( IRadSoundHalAudioFormat::Frames, + sizeInFrames, IRadSoundHalAudioFormat::Frames, pAf->m_pAudioFormat ); + + unsigned int sizeInBytes = pAf->m_pAudioFormat->FramesToBytes( sizeInFrames ); + + return sizeInBytes; +} + +void SoundNucleusInitialize( radMemoryAllocator alloc ) +{ + +#if defined( RAD_PS2 ) || defined( RAD_GAMECUBE ) || defined( RAD_WIN32 ) + ::radSoundHalSystemInitialize( alloc ); +#else + ::radSoundHalSystemInitialize( GMA_XBOX_SOUND_MEMORY ); +#endif + + CreateAudioFormat( & gCompressedStreamAudioFormat, alloc ); + CreateAudioFormat( & gUnCompressedStreamAudioFormat, alloc ); + CreateAudioFormat( & gClipFileAudioFormat, alloc ); + CreateAudioFormat( & gMusicAudioFormat, alloc ); + + + + // + // ESAN TODO: Investigate the magic number 150 below... + // + + unsigned int totalStreamSoundMemoryNeeded = 0; + unsigned int totalStreamBufferMemoryNeeded = 0; + unsigned int totalClipBufferMemoryNeeded = 0; + + if ( CLIP_BUFFERED_DATA_SOURCE_SIZE_MS > 0 ) + { + gClipLoadInfo.pBds = radSoundBufferedDataSourceCreate( GMA_AUDIO_PERSISTENT ); + gClipLoadInfo.pBds->AddRef( ); + gClipLoadInfo.pBds->Initialize( + CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE, + radMemorySpaceGetAllocator( + CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE, + GMA_AUDIO_PERSISTENT ), + CLIP_BUFFERED_DATA_SOURCE_SIZE_MS, + IRadSoundHalAudioFormat::Milliseconds, + SoundNucleusGetClipFileAudioFormat( ), + "Clip Bds" ); + + gClipLoadInfo.pBds->SetLowWaterMark( 1.0f ); + + totalClipBufferMemoryNeeded = + SoundNucleusGetClipFileAudioFormat( )->MillisecondsToBytes( + CLIP_BUFFERED_DATA_SOURCE_SIZE_MS ); + } + + + for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ ) + { + StreamerResources* pSi = gStreamers + i; + + unsigned int streamSoundMemoryNeeded = CalculateStreamerSize( + STREAM_BUFFER_SIZE_MS, + pSi->m_pAudioFormat ); + + totalStreamSoundMemoryNeeded += streamSoundMemoryNeeded; + + unsigned int streamBufferMemoryNeeded = CalculateStreamerSize( + STREAM_BUFFERED_DATA_SOURCE_SIZE_MS, + pSi->m_pAudioFormat ); + + totalStreamBufferMemoryNeeded += streamBufferMemoryNeeded; + + rTunePrintf( "AUDIO: Predicting SOUND streamer will allocate: Sound: [0x%x] Buffer:[0x%x]\n", + streamSoundMemoryNeeded, + streamBufferMemoryNeeded ); + } + + for( unsigned int i = 0; i < MUSIC_NUM_STREAM_PLAYERS; i ++ ) + { + unsigned int streamSoundMemoryNeeded = CalculateStreamerSize( + STREAM_BUFFER_SIZE_MS, + & gMusicAudioFormat ); + + totalStreamSoundMemoryNeeded += streamSoundMemoryNeeded; + + unsigned int streamBufferMemoryNeeded = CalculateStreamerSize( + STREAM_BUFFERED_DATA_SOURCE_SIZE_MS, + & gMusicAudioFormat ); + + totalStreamBufferMemoryNeeded += streamBufferMemoryNeeded; + + rTunePrintf( "AUDIO: Predicting MUSIC streamer will allocate: Sound: [0x%x] Buffer:[0x%x]\n", + streamSoundMemoryNeeded, + streamBufferMemoryNeeded ); + + } + + unsigned int totalClipMemoryNeeded = + ( TOTAL_PS2_FREE_UNCOMPRESSED_CLIP_BYTES * gClipFileAudioFormat.m_pEncoding->m_CompressionNumerator ) / + gClipFileAudioFormat.m_pEncoding->m_CompressionDenominator; + + rTunePrintf( + "AUDIO: Sound Memory totals: Stream: [0x%x] Clip: [0x%x], Total: [0x%x]\n", + totalStreamSoundMemoryNeeded, + totalClipMemoryNeeded, + totalStreamSoundMemoryNeeded + totalClipMemoryNeeded ); + + #ifdef RAD_GAMECUBE + rAssert( + ( totalStreamSoundMemoryNeeded + totalClipMemoryNeeded ) <= + GAMECUBE_SOUND_MEMORY_AVAILABLE ); + #endif + + rTunePrintf( + "AUDIO: Sound Buffered Stream Memory Stream: [0x%x], Clip: [0x%x] Total:\n", + totalStreamBufferMemoryNeeded, + totalClipBufferMemoryNeeded, + totalStreamBufferMemoryNeeded + totalClipBufferMemoryNeeded ); + + IRadSoundHalSystem::SystemDescription desc; + + desc.m_MaxRootAllocations = 170; + desc.m_NumAuxSends = NUM_AUX_SENDS; +#ifdef RAD_WIN32 + desc.m_SamplingRate = 24000; +#endif + +#ifndef RAD_PS2 + desc.m_ReservedSoundMemory = totalStreamSoundMemoryNeeded + totalClipMemoryNeeded; +#endif + +#ifdef RAD_GAMECUBE + desc.m_EffectsAllocator = GMA_AUDIO_PERSISTENT; +#endif + + ::radSoundHalSystemGet( )->Initialize( desc ); + //::radSoundHalSystemGet( )->SetOutputMode( radSoundOutputMode_Surround ); + + for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ ) + { + gStreamers[ i ].index = i; + CreateStreamerResources( gStreamers + i, alloc ); + } + + radmusic::stream_graph_description sgDesc[ MUSIC_NUM_STREAM_PLAYERS ]; + + for( unsigned int i = 0; i < MUSIC_NUM_STREAM_PLAYERS; i ++ ) + { + sgDesc[ i ].buffered_data_source_size_in_ms = STREAM_BUFFERED_DATA_SOURCE_SIZE_MS; + sgDesc[ i ].buffered_data_source_space = STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE; + sgDesc[ i ].channels = MUSIC_NUM_CHANNELS; + sgDesc[ i ].sampling_rate = MUSIC_SAMPLING_RATE; + sgDesc[ i ].stream_buffer_size_in_ms = STREAM_BUFFER_SIZE_MS; + sgDesc[ i ].use_buffered_data_source = CommandLineOptions::Get( CLO_FIREWIRE ) ? false : STREAM_USE_BUFFERED_DATA_SOURCES; + } + + radmusic::initialize( sgDesc, MUSIC_NUM_STREAM_PLAYERS, MUSIC_NUM_CLIP_PLAYERS, GMA_MUSIC ); + radmusic::register_radload_loaders( ); + +} + +void SoundNucleusTerminate( void ) +{ + rAssert( ClipLoadState_Idle == gClipLoadInfo.state ); + + if ( gClipLoadInfo.pBds != NULL ) + { + gClipLoadInfo.pBds->Release( ); + } + + for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ ) + { + DestroyStreamerResources( gStreamers + i ); + } + + DestroyAudioFormat( & gCompressedStreamAudioFormat ); + DestroyAudioFormat( & gUnCompressedStreamAudioFormat ); + DestroyAudioFormat( & gClipFileAudioFormat ); + DestroyAudioFormat( & gMusicAudioFormat ); + + radmusic::terminate( ); + + // Shutdown our related systems + ::radSoundHalSystemTerminate( ); + +} + +IRadSoundHalAudioFormat * SoundNucleusGetStreamFileAudioFormat( void ) +{ +#if defined( RAD_GAMECUBE ) || ( defined( RAD_XBOX ) && defined( PAL ) ) + return NULL; + #else + return gUnCompressedStreamAudioFormat.m_pAudioFormat; + #endif + +} + +IRadSoundHalAudioFormat * SoundNucleusGetClipFileAudioFormat( void ) +{ + return gClipFileAudioFormat.m_pAudioFormat; +} + +StreamerResources* SoundNucleusCaptureStreamerResources( IRadSoundHalAudioFormat * pAf ) +{ + for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ ) + { + StreamerResources * pSi = + gStreamers + i; + + if ( ! pSi->m_IsCaptured ) + { + if ( pSi->m_pAudioFormat->m_pAudioFormat->Matches( pAf ) ) + { + pSi->m_IsCaptured = true; + return pSi; + } + } + } + + rTuneAssertMsg( false, "Out of streamers of desired format" ); + + return NULL; +} + +void SoundNucleusUnCaptureStreamerResources( StreamerResources * pSi ) +{ + rAssert( gStreamers[ pSi->index ].m_IsCaptured == true ); + gStreamers[ pSi->index ].m_IsCaptured = false; +} + +void SoundNucleusLoadClip( const char * pFileName, bool looping ) +{ + rAssert( ClipLoadState_Idle == gClipLoadInfo.state ); + + gClipLoadInfo.pFds = radSoundRsdFileDataSourceCreate( GMA_AUDIO_PERSISTENT ); + gClipLoadInfo.pFds->AddRef( ); + gClipLoadInfo.looping = looping; + + + // Initialize the file data source iwth our file + // + gClipLoadInfo.pFds->InitializeFromFileName( + pFileName, + false, + 0, + IRadSoundHalAudioFormat::Frames, + SoundNucleusGetClipFileAudioFormat( ) ); + + gClipLoadInfo.state = ClipLoadState_InitFile; +} + +bool SoundNucleusIsClipLoaded( void ) +{ + rAssert( ClipLoadState_Idle != gClipLoadInfo.state ); + + return ClipLoadState_Done == gClipLoadInfo.state; +} + +void SoundNucleusFinishClipLoad( IRadSoundClip ** ppClip ) +{ + rAssert( ClipLoadState_Done == gClipLoadInfo.state ); + + *ppClip = gClipLoadInfo.pClip; + gClipLoadInfo.pClip = NULL; + + gClipLoadInfo.pFds->Release( ); + gClipLoadInfo.pFds = NULL; + + if ( NULL != gClipLoadInfo.pBds ) + { + gClipLoadInfo.pBds->SetInputDataSource( 0 ); + } + + gClipLoadInfo.state = ClipLoadState_Idle; +} + +void SoundNucleusCancelClipLoad( void ) +{ + rAssert( ClipLoadState_Idle != gClipLoadInfo.state ); + + if( NULL != gClipLoadInfo.pClip ) + { + gClipLoadInfo.pClip->Release( ); + gClipLoadInfo.pClip = NULL; + } + + if( NULL != gClipLoadInfo.pFds ) + { + gClipLoadInfo.pFds->Release( ); + gClipLoadInfo.pFds = NULL; + } + + if ( NULL != gClipLoadInfo.pBds ) + { + gClipLoadInfo.pBds->SetInputDataSource( 0 ); + } + + gClipLoadInfo.state = ClipLoadState_Idle; + +} + + +void SoundNucleusServiceClipLoad( void ) +{ + ClipLoadState oldState; + + do + { + oldState = gClipLoadInfo.state; + + switch ( gClipLoadInfo.state ) + { + case ClipLoadState_Idle: + { + break; + } + case ClipLoadState_InitFile: + { + if ( IRadSoundHalDataSource::Initialized == gClipLoadInfo.pFds->GetState( ) ) + { + if ( gClipLoadInfo.pBds != NULL ) + { + unsigned int ms = + gClipLoadInfo.pBds->GetFormat( )->FramesToMilliseconds( + gClipLoadInfo.pFds->GetRemainingFrames( ) ); + + rTuneAssert( ms < CLIP_BUFFERED_DATA_SOURCE_SIZE_MS ); + + gClipLoadInfo.pBds->SetInputDataSource( gClipLoadInfo.pFds ); + + gClipLoadInfo.state = ClipLoadState_FillingBuffer; + } + else + { + gClipLoadInfo.pClip = radSoundClipCreate( GMA_AUDIO_PERSISTENT ); + gClipLoadInfo.pClip->AddRef( ); + gClipLoadInfo.pClip->Initialize( + gClipLoadInfo.pFds, + radSoundHalSystemGet( )->GetRootMemoryRegion( ), + gClipLoadInfo.looping, + "Clip" ); + + gClipLoadInfo.state = ClipLoadState_LoadingClip; + } + } + + break; + } + case ClipLoadState_FillingBuffer: + { + if ( gClipLoadInfo.pBds->IsBufferFull( ) ) + { + gClipLoadInfo.pClip = radSoundClipCreate( GMA_AUDIO_PERSISTENT ); + gClipLoadInfo.pClip->AddRef( ); + gClipLoadInfo.pClip->Initialize( + gClipLoadInfo.pBds, + radSoundHalSystemGet( )->GetRootMemoryRegion( ), + gClipLoadInfo.looping, + "Clip" ); + + gClipLoadInfo.state = ClipLoadState_LoadingClip; + } + + break; + } + case ClipLoadState_LoadingClip: + { + if ( IRadSoundClip::Initialized == gClipLoadInfo.pClip->GetState( ) ) + { + gClipLoadInfo.state = ClipLoadState_Done; + } + + break; + } + case ClipLoadState_Done: + { + break; + } + } + } + while( oldState != gClipLoadInfo.state ); +} + +}
\ No newline at end of file diff --git a/game/code/sound/soundrenderer/soundnucleus.hpp b/game/code/sound/soundrenderer/soundnucleus.hpp new file mode 100644 index 0000000..dc028e3 --- /dev/null +++ b/game/code/sound/soundrenderer/soundnucleus.hpp @@ -0,0 +1,70 @@ + +#ifndef SOUNDNUCLEUS_HPP +#define SOUNDNUCLEUS_HPP + +#include <radmemory.hpp> +#include <radsound.hpp> + +namespace Sound +{ +#ifdef RAD_XBOX + const unsigned int SOUND_NUM_STREAM_PLAYERS = 10; +#else + const unsigned int SOUND_NUM_STREAM_PLAYERS = 9; +#endif + const unsigned int SOUND_NUM_CLIP_PLAYERS = 64; + +struct Encoding +{ + IRadSoundHalAudioFormat::Encoding m_Encoding; + unsigned int m_CompressionNumerator; + unsigned int m_CompressionDenominator; +}; + +struct AudioFormat +{ + unsigned int m_Channels; + const Encoding * m_pEncoding; + unsigned int m_SamplingRate; + IRadSoundHalAudioFormat * m_pAudioFormat; +}; + +struct StreamerResources +{ + IRadSoundStreamPlayer * m_pStreamPlayer; + IRadSoundBufferedDataSource * m_pBufferedDataSource; + IRadSoundStitchedDataSource * m_pStitchedDataSource; + AudioFormat * m_pAudioFormat; + bool m_IsCaptured; + unsigned int index; +}; + +void SoundNucleusInitialize( radMemoryAllocator alloc ); +void SoundNucleusTerminate( void ); + +IRadSoundHalAudioFormat * SoundNucleusGetStreamFileAudioFormat( void ); +IRadSoundHalAudioFormat * SoundNucleusGetClipFileAudioFormat( void ); + +StreamerResources * SoundNucleusCaptureStreamerResources( IRadSoundHalAudioFormat * pAf ); +void SoundNucleusUnCaptureStreamerResources( StreamerResources * pSi ); + +void SoundNucleusLoadClip( const char * pFileName, bool looping ); +bool SoundNucleusIsClipLoaded( void ); +void SoundNucleusCancelClipLoad( void ); +void SoundNucleusFinishClipLoad( IRadSoundClip ** ppClip ); +void SoundNucleusServiceClipLoad( void ); + +} + +#endif + + + + + + + + + + + diff --git a/game/code/sound/soundrenderer/soundplayer.h b/game/code/sound/soundrenderer/soundplayer.h new file mode 100644 index 0000000..78538d3 --- /dev/null +++ b/game/code/sound/soundrenderer/soundplayer.h @@ -0,0 +1,468 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: dasoundplayer.hpp +// +// Subsystem: Dark Angel - Sound players +// +// Description: Defines the a Dark Angel sound player +// +// Revisions: +// + Created October 16, 2001 -- breimer +// +//============================================================================= + +#ifndef _DASOUNDPLAYER_HPP +#define _DASOUNDPLAYER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <radsound.hpp> + +//============================================================================= +// Forward declarations +//============================================================================= + +struct IRadObjectBTree; + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundClipStreamPlayer; +class daSoundTuner; +struct IDaSoundPlayerState; +struct StreamerResources; + +//============================================================================= +// Forward declarations +//============================================================================= + +class daSoundAllocatedResource; + +//============================================================================= +// Typdefs and enumerations +//============================================================================= + +const float radSoundPitchChangeThreshold = 0.1f; + +//============================================================================= +// Class Declarations +//============================================================================= + +class daSoundPlayerBase : + public IRefCount, + public radLinkedClass< daSoundPlayerBase >, + public radRefCount +{ + public: + + inline daSoundPlayerBase( ) : radRefCount( 0 ) { } + virtual ~daSoundPlayerBase( ) { } + + IMPLEMENT_REFCOUNTED( "daSoundPlayerBase" ); + + virtual void ServiceOncePerFrame ( void ) = 0; + virtual bool IsCaptured ( void ) = 0; + virtual void Pause ( void ) = 0; + virtual void Continue ( void ) = 0; + virtual void UberContinue( void ) = 0; + virtual void Stop ( void ) = 0; + virtual void SetPitch( float pitch ) = 0; + virtual bool IsPaused( void ) = 0; + + virtual void ChangeTrim( daSoundGroup groupName, float newTrim ) = 0; + virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim ) = 0; +}; + +// +// This contains a Dark Angel player instance. +// +class daSoundClipStreamPlayer + : + public daSoundPlayerBase, + public IRadSoundStitchCallback + +{ +public: + + // + // Constructor and destructor + // + daSoundClipStreamPlayer( void ); + virtual ~daSoundClipStreamPlayer ( void ); + + // daSoundPlayerBase + + virtual void ServiceOncePerFrame ( void ); + virtual bool IsCaptured ( void ); + virtual void Pause ( void ); + virtual void Continue ( void ); + virtual void UberContinue( void ); + virtual void Stop ( void ); + virtual void SetPitch( float pitch ); + + virtual void ChangeTrim( daSoundGroup groupName, float newTrim ); + virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim ); + + bool IsPaused( void ); + + // + // Sound player states + // + + enum State { + State_DeCued, + State_Cueing, + State_Cued, + State_CuedPlay, + State_Playing, + State_Stopping, + State_Done + }; + + // + // Get and state this player's state + // + + inline State GetState ( void ); + + // + // Initialize with some form of player + // + void InitializeAsClipPlayer( void ); + void InitializeAsStreamPlayer( void ); + + void Capture( + IDaSoundResource* pResource, + bool isPositional ); + + inline IRadSoundHalPositionalGroup* GetPositionalGroup( void ); + + void Play( void ); + void UnCapture ( void ); + + inline void SetPositionAndVelocity( + const radSoundVector * pPosition, + const radSoundVector * pVelocity ); + + inline void SetMinMaxDistance( float min, float max ); + + inline void SetExternalTrim( float newTrim ); + inline void SetGroupTrim( float newTrim ); + inline void SetFaderGroupTrim( float newTrim ); + inline void SetMasterTrim( float newTrim ); + + inline IDaSoundResource::Type GetPlayerType ( void ); + daSoundGroup GetSoundGroup ( void ); + + void RegisterSoundPlayerStateCallback + ( + IDaSoundPlayerState* pCallback, + void* pUserData + ); + void UnregisterSoundPlayerStateCallback + ( + IDaSoundPlayerState* pCallback, + void* pUserData + ); + + unsigned int GetPlaybackTimeInSamples ( void ); + + void OnStitch( IRadSoundHalDataSource **, unsigned int frameCount, void * pUserData ); + + const void GetFileName( char * pBuf, unsigned int max ); + +private: + + enum CueingState + { + CueingState_Null, + CueingState_Resource, + CueingState_Player, + CueingState_Cued + }; + + inline void CalculateCurrentTrim( void ); + inline void CalculateCurrentPitch( void ); + void HookUpAndCuePlayer( void ); + + void UpdateClip( void ); + void UpdateStream( void ); + + // + // Get the randomized trim and pitch settings based on a + // min/max variance of the current resource + // + inline void CalculateNewVaryingTrim ( void ); + inline void CalculateNewVaryingPitch ( void ); + + // + // Trim values + // + float m_Trim; + float m_GroupTrim; + float m_FaderGroupTrim; + float m_MasterTrim; + float m_StoppingTrim; + float m_VaryingTrim; + float m_CurrentTrim; // floating target + + float m_VaryingPitch; + float m_Pitch; + float m_CurrentPitch; // floating target + + // + // Sound player state + // + State m_State; + CueingState m_CueingState; + + // + // Hold a capture counter + // + unsigned int m_CaptureCount; + + // + // Hold a pause counter + // + unsigned int m_PauseCount; + + // + // Store the allocated and normal resource + // + daSoundAllocatedResource* m_pAllocatedResource; + unsigned int m_AllocResInstanceID; + IDaSoundResource* m_pResource; + + // + // The positional group that this player uses + // + IRadSoundHalPositionalGroup * m_pPositionalGroup; + bool m_IsPositional; + + // + // Currently, only one callback is allowed at a time + // + IDaSoundPlayerState* m_pStateChangeCallback; + void* m_pStateChangeCallbackUserData; + + // + // Store the various types of players based on the connected resource + // + union + { + struct + { + IRadSoundClipPlayer* m_pClipPlayer; + } m_ClipInfo; + + struct + { + StreamerResources* m_pResources; + IRadSoundRsdFileDataSource* m_pRsdFileDataSource; + } m_StreamInfo; + }; + + IDaSoundResource::Type m_Type; +}; + + +inline IDaSoundResource::Type daSoundClipStreamPlayer::GetPlayerType( void ) +{ + return m_Type; +} + +inline void daSoundClipStreamPlayer::SetExternalTrim( float newTrim ) +{ + radSoundVerifyAnalogVolume( newTrim ); + + m_Trim = newTrim; + +} + +inline void daSoundClipStreamPlayer::SetGroupTrim( float newTrim ) +{ + radSoundVerifyAnalogVolume( newTrim ); + + m_GroupTrim = newTrim; + +} + +inline void daSoundClipStreamPlayer::SetFaderGroupTrim( float newTrim ) +{ + radSoundVerifyAnalogVolume( newTrim ); + + m_FaderGroupTrim = newTrim; +} + +inline void daSoundClipStreamPlayer::SetMasterTrim( float newTrim ) +{ + radSoundVerifyAnalogVolume( newTrim ); + + m_MasterTrim = newTrim; +} + +inline void daSoundClipStreamPlayer::CalculateCurrentTrim( void) +{ + float oldTrim = m_CurrentTrim; + + float newTrim = + m_VaryingTrim * + m_StoppingTrim * + m_Trim * + m_GroupTrim * + m_FaderGroupTrim * + m_MasterTrim; + + if ( State_Playing == m_State || State_Stopping == m_State ) + { + if ( newTrim >= oldTrim ) + { + float dif = newTrim - oldTrim; + + if ( dif >= radSoundVolumeChangeThreshold ) + { + newTrim = oldTrim + radSoundVolumeChangeThreshold; + } + } + else + { + float dif = oldTrim - newTrim; + + if ( dif >= radSoundVolumeChangeThreshold ) + { + newTrim = oldTrim - radSoundVolumeChangeThreshold; + } + } + } + + m_CurrentTrim = newTrim; + + radSoundVerifyAnalogVolume( m_CurrentTrim ); +} + +inline void daSoundClipStreamPlayer::CalculateCurrentPitch( void ) +{ + float oldPitch = m_CurrentPitch; + + float newPitch = m_VaryingPitch * m_Pitch; + + if ( m_State == State_Playing || m_State == State_Stopping ) + { + if ( newPitch > oldPitch ) + { + float dif = newPitch - oldPitch; + + if( dif > radSoundPitchChangeThreshold ) + { + newPitch = oldPitch + radSoundVolumeChangeThreshold; + } + } + else + { + float dif = oldPitch - newPitch; + + if( dif > radSoundPitchChangeThreshold ) + { + newPitch = oldPitch - radSoundVolumeChangeThreshold; + } + } + } + + m_CurrentPitch = newPitch; + + radSoundVerifyAnalogPitch( m_CurrentPitch ); +} + +inline void daSoundClipStreamPlayer::SetPitch( float pitch ) +{ + if ( pitch <= 0.0f ) + { + rDebugPrintf( "AUDIO: Error, pitch set to: [%f]\n", pitch ); + + pitch = 0.0f; + } + + // radSoundVerifyAnalogPitch( pitch ); + + m_Pitch = pitch; +} + +inline void daSoundClipStreamPlayer::CalculateNewVaryingTrim( void ) +{ + float minTrim; + float maxTrim; + + m_pResource->GetTrimRange( &minTrim, &maxTrim ); + + m_VaryingTrim = ::radSoundRandMinMax( minTrim, maxTrim ); + + radSoundVerifyAnalogVolume( m_VaryingTrim ); +} + + +inline void daSoundClipStreamPlayer::CalculateNewVaryingPitch( void ) +{ + float minPitch; + float maxPitch; + + m_pResource->GetPitchRange( &minPitch, &maxPitch ); + + m_VaryingPitch = ::radSoundRandMinMax( minPitch, maxPitch ); + + radSoundVerifyAnalogPitch( m_VaryingPitch ); +} + +inline daSoundClipStreamPlayer::State daSoundClipStreamPlayer::GetState( void ) +{ + return m_State; +} + +inline void daSoundClipStreamPlayer::SetPositionAndVelocity( + const radSoundVector * pPosition, + const radSoundVector * pVelocity ) +{ + m_pPositionalGroup->SetPosition( (radSoundVector*) pPosition ); + m_pPositionalGroup->SetVelocity( (radSoundVector*) pVelocity ); +} + +inline void daSoundClipStreamPlayer::SetMinMaxDistance( float min, float max ) +{ + m_pPositionalGroup->SetMinMaxDistance( min, max ); +} + +inline bool daSoundClipStreamPlayer::IsPaused( void ) +{ + return m_PauseCount != 0; +} + + +inline IRadSoundHalPositionalGroup * daSoundClipStreamPlayer::GetPositionalGroup( void ) +{ + if ( m_IsPositional ) + { + return m_pPositionalGroup; + } + + return NULL; +} + + +} // Sound Namespace +#endif //_DASOUNDPLAYER_HPP + diff --git a/game/code/sound/soundrenderer/soundrenderingmanager.cpp b/game/code/sound/soundrenderer/soundrenderingmanager.cpp new file mode 100644 index 0000000..167e871 --- /dev/null +++ b/game/code/sound/soundrenderer/soundrenderingmanager.cpp @@ -0,0 +1,1545 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundrenderingmanager.cpp +// +// Subsystem: Dark Angel - Sound Manager System +// +// Description: Implementation of the sound manager +// +// Revisions: +// + Created October 2, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radfile.hpp> + + +#include <radmusic/radmusic.hpp> +#include <radsound.hpp> + +#include <radobjectlist.hpp> +#include <radscript.hpp> +#include <radtypeinfo.hpp> +#include <radtypeinfoutil.hpp> +#include <radfactory.hpp> + +#include <memory/srrmemory.h> +#include <loading/loadingmanager.h> +#include <loading/soundfilehandler.h> + +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundresource.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/playermanager.h> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <sound/soundrenderer/soundconstants.h> +#include <sound/soundclusternameenum.h> +#include <sound/soundmanager.h> +#include <sound/soundrenderer/soundnucleus.hpp> +#include <sound/dialog/dialogcoordinator.h> +#include <radobjectbtree.hpp> +#include <radmemorymonitor.hpp> + +#include <pddi/pddi.hpp> +#include <p3d/utility.hpp> +#include <p3d/p3dtypes.hpp> + +//============================================================================= +// Namespace +//============================================================================= + +//#define AUDIO_FILE_PERFORMANCE_LOG +//#define AUDIO_FILE_PERFORMANCE_SPEW + +bool gDaSoundStats = false; +bool gTuneSound = false; + +namespace Sound { + +const short MAX_BTREE_NODES = 5100; + +radObjectBTreeNode* gpBTreeNodePool = 0; + +//============================================================================= +// Debug Information +//============================================================================= + +// +// Use this if you want to debug the sound tuner +// +#ifndef FINAL +#ifndef NDEBUG + +// This is the type of debug information to output +#define DASOUNDMANAGER_OUTPUTDEBUGINFO +#ifdef DASOUNDMANAGER_OUTPUTDEBUGINFO +//static DebugInfoType sg_DebugInfoType = DEBUG_RESOURCEINFO; +#endif //DASOUNDMANAGER_OUTPUTDEBUGINFO + +#endif //NDEBUG +#endif //FINAL + + +//============================================================================= +// Macros and Defines +//============================================================================= + +// +// THe sound memory size +// + +// +// Some directories +// +#define SOUND_ROOT_DIR "sound" +#define SCRIPT_DIR SOUND_ROOT_DIR"\\scripts" +#define TYPEINFO_DIR SOUND_ROOT_DIR"\\typ" + +unsigned int gTotalMicrosecondsWastedParsingScripts = 0; + +//============================================================================= +// Constants +//============================================================================= + +// +// Custom memory management should not be handled by the sound system! +// +radMemoryAllocator DAMEMORY_ALLOC_SOUND = RADMEMORY_ALLOC_DEFAULT; + +// +// The object list page size for the sound objects +// +static const unsigned int SoundObjectsListPageSize = 32; + +// +// Flag used for file callback +// +static const bool LastScriptTrue = true; +static const bool LastScriptFalse = false; + +//============================================================================= +// Static Variables +//============================================================================= + +// +// The sound manager singleton class +// +daSoundRenderingManager* daSoundRenderingManager::s_Singleton = NULL; + + +//============================================================================= +// Local data +//============================================================================= + +// +// The name of our sound resource namespace +// +static const char s_SoundNamespace[ ] = "SoundGlobals"; + +// +// The name of our tuning object namespace +// +static const char s_TuningNamespace[] = "TuningGlobals"; + +// +// Base name for our character namespaces +// +static const char s_CharacterNamespace[] = "CharSounds"; + +// +// The name of the listener object +// +//static const char s_TheListenerName[ ] = "TheListener"; + +// +// Dialog cement file names +// +#if defined( RAD_XBOX ) + +static const unsigned int s_NumDialogCementFiles = 3; + +static const char s_EnglishDialogue[] = "dialog0?.rcf"; +static const char s_FrenchDialogue[] = "dialogf0?.rcf"; +static const char s_GermanDialogue[] = "dialogg0?.rcf"; +static const char s_SpanishDialogue[] = "dialogs0?.rcf"; +static const char s_ItalianDialogue[] = "dialogi0?.rcf"; + +#else + +static const unsigned int s_NumDialogCementFiles = 1; + +static const char s_EnglishDialogue[] = "dialog.rcf"; +static const char s_FrenchDialogue[] = "dialogf.rcf"; +static const char s_GermanDialogue[] = "dialogg.rcf"; +static const char s_SpanishDialogue[] = "dialogs.rcf"; +static const char s_ItalianDialogue[] = "dialogi.rcf"; + +#endif + +// +// Cement libraries associated with the Dark Angel sound system +// +struct DaSoundCementLibraryData +{ + const char* m_LibraryIDString; + const char* m_Filename; + unsigned int m_CacheSize; +}; + +// +// Type info +// +struct DaSoundTypeinfoData +{ + const char* m_Filename; +}; +static DaSoundTypeinfoData s_DaSoundTypeInfo = +{ + TYPEINFO_DIR"\\srrtypes.typ" +}; + +// +// Radscripts associated wtih the DA sound system +// +struct DaSoundScriptData +{ + const char* m_Filename; +}; + +// +// IMPORTANT CHANGE: The last script files listed will have its contents go +// into the tuning namespace, not the sound namespace. This is to ensure +// that everything in the sound namespace is a soundResourceData object +// to allow me to search for dialog resources without an illegal downcast +// (stinky downcasts, hate 'em). +// +static DaSoundScriptData s_DaSoundScripts[ ] = +{ + // Character sound scripts + { "\\Apu.spt" }, + { "\\Bart.spt" }, + { "\\Homer.spt" }, + { "\\Lisa.spt" }, + { "\\Marge.spt" }, + + // Level scripts + { "\\suburbs.spt" }, + { "\\downtown.spt" }, + { "\\seaside.spt" }, + { "\\level1.spt" }, + { "\\level2.spt" }, + { "\\level3.spt" }, + { "\\level4.spt" }, + { "\\level5.spt" }, + { "\\level6.spt" }, + { "\\level7.spt" }, + { "\\minigame.spt" }, + + // Sound effect resources + { "\\frontend.spt" }, + { "\\collide.spt" }, + { "\\carsound.spt" }, + { "\\World.spt" }, + { "\\positionalSounds.spt" }, + { "\\interactive_props.spt" }, + + // Dialog + { "\\dialog.spt" }, + { "\\nis.spt" }, + + // Cars + { "\\bart_v.spt" }, + { "\\apu_v.spt" }, + { "\\snake_v.spt" }, + { "\\homer_v.spt" }, + { "\\famil_v.spt" }, + { "\\gramp_v.spt" }, + { "\\cletu_v.spt" }, + { "\\wiggu_v.spt" }, + { "\\empty.spt" }, + { "\\marge_v.spt" }, + { "\\empty.spt" }, + { "\\empty.spt" }, + { "\\smith_v.spt" }, + { "\\empty.spt" }, + { "\\empty.spt" }, + { "\\empty.spt" }, + { "\\zombi_v.spt" }, + { "\\empty.spt" }, + { "\\empty.spt" }, + { "\\cVan.spt" }, + { "\\compactA.spt" }, + { "\\comic_v.spt" }, + { "\\skinn_v.spt" }, + { "\\cCola.spt" }, + { "\\cSedan.spt" }, + { "\\cPolice.spt" }, + { "\\cCellA.spt" }, + { "\\cCellB.spt" }, + { "\\cCellC.spt" }, + { "\\cCellD.spt" }, + { "\\minivanA_v.spt" }, + { "\\pickupA.spt" }, + { "\\taxiA_v.spt" }, + { "\\sportsA.spt" }, + { "\\sportsB.spt" }, + { "\\SUVA.spt" }, + { "\\wagonA.spt" }, + { "\\hbike_v.spt" }, + { "\\burns_v.spt" }, + { "\\honor_v.spt" }, + { "\\cArmor.spt" }, + { "\\cCurator.spt" }, + { "\\cHears.spt" }, + { "\\cKlimo.spt" }, + { "\\cLimo.spt" }, + { "\\cNerd.spt" }, + { "\\frink_v.spt" }, + { "\\cMilk.spt" }, + { "\\cDonut.spt" }, + { "\\bbman_v.spt" }, + { "\\bookb_v.spt" }, + { "\\carhom_v.spt" }, + { "\\elect_v.spt" }, + { "\\fone_v.spt" }, + { "\\gramR_v.spt" }, + { "\\moe_v.spt" }, + { "\\mrplo_v.spt" }, + { "\\otto_v.spt" }, + { "\\plowk_v.spt" }, + { "\\scorp_v.spt" }, + { "\\willi_v.spt" }, + { "\\sedanA.spt" }, + { "\\sedanB.spt" }, + { "\\cBlbart.spt" }, + { "\\cCube.spt" }, + { "\\cDuff.spt" }, + { "\\cNonup.spt" }, + { "\\lisa_v.spt" }, + { "\\krust_v.spt" }, + { "\\coffin.spt" }, + { "\\hallo.spt" }, + { "\\ship.spt" }, + { "\\witchcar.spt" }, + { "\\huska.spt" }, + { "\\atv_v.spt" }, + { "\\dune_v.spt" }, + { "\\hype_v.spt" }, + { "\\knigh_v.spt" }, + { "\\mono_v.spt" }, + { "\\oblit_v.spt" }, + { "\\rocke_v.spt" }, + { "\\ambul.spt" }, + { "\\burnsarm.spt" }, + { "\\fishtruc.spt" }, + { "\\garbage.spt" }, + { "\\icecream.spt" }, + { "\\istruck.spt" }, + { "\\nuctruck.spt" }, + { "\\pizza.spt" }, + { "\\schoolbu.spt" }, + { "\\votetruc.spt" }, + { "\\glastruc.spt" }, + { "\\cfire_v.spt" }, + { "\\cBone.spt" }, + { "\\redbrick.spt" }, + + // Tuning + { "\\car_tune.spt" }, + { "\\positionalSettings.spt" }, + { "\\global.spt" } +}; + +const unsigned int NumSoundScripts = sizeof( s_DaSoundScripts ) / + sizeof( DaSoundScriptData ); + +const unsigned int NUM_TUNING_SCRIPTS = 3; +const unsigned int NUM_CHARACTER_SCRIPTS = 5; +const unsigned int INTERACTIVE_PROPS_SCRIPT_POSITION = 21; +const unsigned int DIALOGUE_SCRIPT_POSITION = 22; +const unsigned int NIS_SCRIPT_POSITION = 23; + +// +// Array mapping scripts to sound clusters that their sounds should +// be loaded/unloaded with. +// +// IMPORTANT: this array needs to be maintained to match the scripts +// in s_DaSoundScripts up to the first car script. After that, we +// do it programatically (I don't think that's actually a word). +// +static SoundClusterName s_ScriptClusters[] = +{ + SC_CHAR_APU, + SC_CHAR_BART, + SC_CHAR_HOMER, + SC_CHAR_LISA, + SC_CHAR_MARGE, + SC_LEVEL_SUBURBS, + SC_LEVEL_DOWNTOWN, + SC_LEVEL_SEASIDE, + SC_LEVEL1, + SC_LEVEL2, + SC_LEVEL3, + SC_LEVEL4, + SC_LEVEL5, + SC_LEVEL6, + SC_LEVEL7, + SC_MINIGAME, + SC_ALWAYS_LOADED, + SC_ALWAYS_LOADED, + SC_ALWAYS_LOADED, + SC_ALWAYS_LOADED, + SC_INGAME, + SC_INGAME, + SC_NEVER_LOADED, + SC_NEVER_LOADED, +}; +const unsigned int NumClusterNames = sizeof( s_ScriptClusters ) / + sizeof( SoundClusterName ); + + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundRenderingManager Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundRenderingManager::daSoundRenderingManager +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundRenderingManager::daSoundRenderingManager( ) + : + radRefCount( 0 ), + m_pScript( NULL ), + m_IsInitialized( false ), + m_pResourceNameSpace( NULL ), + m_pTuningNameSpace( NULL ), + m_pDynaLoadManager( NULL ), + m_pTuner( NULL ), + m_pResourceManager( NULL ), + m_pPlayerManager( NULL ), + m_scriptLoadCount( 0 ), + m_currentLanguage( DIALOGUE_LANGUAGE_ENGLISH ), + m_languageSelected( false ) +{ + unsigned int i; + + // Create the singleton + rAssert( s_Singleton == NULL ); + s_Singleton = this; + + for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ ) + { + m_pCharacterNameSpace[i] = NULL; + } + + for( i = 0; i < NUM_SOUND_CEMENT_FILES; i++ ) + { + m_soundCementFileHandles[i] = 0; + } + + m_LastPerformanceEventTime = radTimeGetMilliseconds( ); + ::RadSoundSetFilePerformanceCallback( & daSoundRenderingManager::FilePerformanceEvent ); + + radDbgWatchAddBoolean( + & gDaSoundStats, + "DaSound Stats", + "Sound" ); +} + +//============================================================================= +// Function: daSoundRenderingManager::~daSoundRenderingManager +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundRenderingManager::~daSoundRenderingManager( ) +{ + + radDbgWatchDelete( & gDaSoundStats ); + ::RadSoundSetFilePerformanceCallback( NULL ); + + rAssert( !m_IsInitialized ); + + // Clear the singleton + s_Singleton = NULL; +} + +//============================================================================= +// Function: daSoundRenderingManager::GetInstance +//============================================================================= +// Description: Get the singleton instance of this class +// +//----------------------------------------------------------------------------- + +daSoundRenderingManager* daSoundRenderingManager::GetInstance( void ) +{ + return s_Singleton; +} + +//============================================================================= +// Function: daSoundRenderingManager::Initialize +//============================================================================= +// Description: Initialize the sound manager with the stuff that we don't +// need to do if we're muted. +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManager::Initialize +( + void +) +{ + unsigned int i; + char nameBuffer[50]; + int nameLength; + + // + // Create namespaces + // + m_pResourceNameSpace = ::radNameSpaceCreate( GetThisAllocator( ) ); + m_pResourceNameSpace->AddRef( ); + m_pResourceNameSpace->SetName( s_SoundNamespace ); + + m_pTuningNameSpace = ::radNameSpaceCreate( GetThisAllocator( ) ); + m_pTuningNameSpace->AddRef( ); + m_pTuningNameSpace->SetName( s_TuningNamespace ); + + strcpy( nameBuffer, s_CharacterNamespace ); + nameLength = strlen( s_CharacterNamespace ); + for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ ) + { + m_pCharacterNameSpace[i] = ::radNameSpaceCreate( GetThisAllocator() ); + rAssert( m_pCharacterNameSpace[i] != NULL ); + + m_pCharacterNameSpace[i]->AddRef(); + + // + // Create a unique name + // + nameBuffer[nameLength] = static_cast<char>( '0' + i ); + nameBuffer[nameLength+1] = '\0'; + m_pCharacterNameSpace[i]->SetName( nameBuffer ); + } + + // + // Spawn other elements of the sound system (some of these depend on the namespace) + // + m_pDynaLoadManager = new ( GetThisAllocator( ) ) daSoundDynaLoadManager( ); + m_pDynaLoadManager->AddRef( ); + m_pResourceManager = new ( GetThisAllocator( ) ) daSoundResourceManager( ); + m_pResourceManager->AddRef( ); + m_pPlayerManager = new ( GetThisAllocator( ) ) daSoundPlayerManager( ); + m_pPlayerManager->AddRef( ); + + Sound::daSoundTunerCreate ( &m_pTuner, GetThisAllocator( ) ); + + m_pPlayerManager->UglyHackPostInitialize( m_pTuner ); + + // + // Register some factories and some objects. Some of these object use + // the spawned systems. + // + ::radFactoryRegister( + "daSoundResourceData", + (radFactoryProc*) daSoundResourceManager::CreateResourceData ); + rAssert( GetSoundNamespace( ) != NULL ); + rAssert( GetTuningNamespace() != NULL ); +} + +//============================================================================= +// Function: daSoundRenderingManager::IsInitialized +//============================================================================= +// Description: Use this to pull for the end of initialization +// +//----------------------------------------------------------------------------- + +bool daSoundRenderingManager::IsInitialized( void ) +{ + return m_IsInitialized; +} + +//============================================================================= +// Function: daSoundRenderingManager::Terminate +//============================================================================= +// Description: Terminate the sound manager +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManager::Terminate( void ) +{ + unsigned int i; + + // Stop all the sounds in the game + GetPlayerManager( )->CancelPlayers( ); + + // Release the namespaces, must do this before killing resource manager + // some of the reference counting is not per-object. + + m_pResourceNameSpace->Release( ); + m_pTuningNameSpace->Release(); + + for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ ) + { + m_pCharacterNameSpace[i]->Release(); + } + + // Destroy the sound systems created by the initalize + if( IsInitialized( ) ) + { + // + // Detach the various systems spawned on creation + // + if( m_pPlayerManager != NULL ) + { + m_pPlayerManager->Release( ); + m_pPlayerManager = NULL; + } + if( m_pResourceManager != NULL ) + { + m_pResourceManager->Release( ); + m_pResourceManager = NULL; + } + if( m_pTuner != NULL ) + { + m_pTuner->Release( ); + m_pTuner = NULL; + } + if( m_pDynaLoadManager != NULL ) + { + m_pDynaLoadManager->Release( ); + m_pDynaLoadManager = NULL; + } + +#ifndef FINAL + if ( gTuneSound ) + { + ::radRemoteScriptTerminate( ); + } +#endif + rAssert( m_pScript == NULL ); + ::radScriptTerminate( ); + + // + // Release the cement file + // + for( i = 0; i < NUM_SOUND_CEMENT_FILES; i++ ) + { + GetLoadingManager()->UnregisterCementLibrary( m_soundCementFileHandles[i] ); + } + + // Uninitialize + m_IsInitialized = false; + } +} + +//============================================================================= +// Function: daSoundRenderingManager::Service +//============================================================================= +// Description: Service the Dark Angel sound system. This call should be made +// as often as possible. +// +// Parameters: none +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManager::Service( void ) +{ + // + // Service the radsound system + // + ::radSoundHalSystemGet( )->Service( ); + SoundNucleusServiceClipLoad( ); +} + +//============================================================================= +// Function: daSoundRenderingManager::ServiceOncePerFrame +//============================================================================= +// Description: Service the Dark Angel sound system. This call should be made +// no more than once per frame or else performance may suffer. +// +// Parameters: none +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManager::ServiceOncePerFrame( unsigned int elapsedTime ) +{ + // + // Service the radsound system + // + ::radSoundHalSystemGet( )->ServiceOncePerFrame( ); + + // + // Do nothing else until we're fully initialized + // + if( !IsInitialized( ) ) + { + return; + } + + // Service the dynamic loading system + if( GetDynaLoadManager( ) != NULL ) + { + GetDynaLoadManager( )->ServiceOncePerFrame( ); + } + + // Service the tuner + if( GetTuner( ) != NULL ) + { + GetTuner( )->ServiceOncePerFrame( elapsedTime ); + } + + // Service the player manager + if( GetPlayerManager( ) != NULL ) + { + GetPlayerManager( )->ServiceOncePerFrame( ); + } +} + +//============================================================================= +// daSoundRenderingManager::QueueCementFileRegistration +//============================================================================= +// Description: Set up the cement file registration with the loading manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::QueueCementFileRegistration() +{ + HeapMgr()->PushHeap (GMA_AUDIO_PERSISTENT); + + // + // Queue requests for sound cement file registration + // + int i = s_NumDialogCementFiles; + + if( !m_languageSelected ) + { + registerDialogueCementFiles( s_EnglishDialogue ); + m_languageSelected = true; + } + +#ifdef RAD_XBOX + // + // Register the music rcfs -- no localization needed. + // + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music00.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music01.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music02.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music03.rcf" ); +#else + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music.rcf" ); +#endif + + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "carsound.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "ambience.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "nis.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "soundfx.rcf" ); + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "scripts.rcf" ); + + HeapMgr()->PopHeap (GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// daSoundRenderingManager::SwitchDialogueCementFile +//============================================================================= +// Description: Switch the current language to the given language. +// +// Parameters: language - new language to use +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::SetLanguage( Scrooby::XLLanguage language ) +{ + unsigned int i; + const char* cementFileName = s_EnglishDialogue; + DialogueLanguage oldLanguage = m_currentLanguage; + + switch( language ) + { + case Scrooby::XL_ENGLISH: + cementFileName = s_EnglishDialogue; + m_currentLanguage = DIALOGUE_LANGUAGE_ENGLISH; + break; + + //************************************************************************** + // TEMPORARY: all dialog is English until we actually get localized dialogue + //************************************************************************** + case Scrooby::XL_FRENCH: + cementFileName = s_FrenchDialogue; + m_currentLanguage = DIALOGUE_LANGUAGE_FRENCH; + break; + + case Scrooby::XL_GERMAN: + cementFileName = s_GermanDialogue; + m_currentLanguage = DIALOGUE_LANGUAGE_GERMAN; + break; + + case Scrooby::XL_SPANISH: + cementFileName = s_SpanishDialogue; + m_currentLanguage = DIALOGUE_LANGUAGE_SPANISH; + break; + + default: + rAssertMsg( false, "Language not supported by sound system" ); + break; + } + + if( m_currentLanguage == oldLanguage ) + { + // + // Nothing needs to be done + // + return; + } + + for( i = 0; i < s_NumDialogCementFiles; i++ ) + { + GetLoadingManager()->UnregisterCementLibrary( m_soundCementFileHandles[i] ); + } + + registerDialogueCementFiles( cementFileName ); + + m_languageSelected = true; +} + +//============================================================================= +// daSoundRenderingManager::QueueRadscriptFileLoads +//============================================================================= +// Description: Set up the RadScript file loads with the loading manager +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::QueueRadscriptFileLoads() +{ + HeapMgr()->PushHeap (GMA_AUDIO_PERSISTENT); + + unsigned int i; + char filename[100]; + const char* scriptName; + + // + // Queue up the RadScript file loading requests + // + GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, s_DaSoundTypeInfo.m_Filename, GMA_AUDIO_PERSISTENT ); + + for( i = 0; i < NumSoundScripts; i++ ) + { + // + // Hack! + // + // Oops. Need to load the correct dialogue script depending on which language has been + // selected + // + if( i == DIALOGUE_SCRIPT_POSITION ) + { + switch( m_currentLanguage ) + { + case DIALOGUE_LANGUAGE_ENGLISH: + scriptName = s_DaSoundScripts[i].m_Filename; + break; + + case DIALOGUE_LANGUAGE_FRENCH: + scriptName = "\\dialogfr.spt"; + break; + + case DIALOGUE_LANGUAGE_GERMAN: + scriptName = "\\dialogge.spt"; + break; + + case DIALOGUE_LANGUAGE_SPANISH: + scriptName = "\\dialogsp.spt"; + break; + + default: + rAssert( false ); + scriptName = s_DaSoundScripts[i].m_Filename; + break; + } + } + else if( i == NIS_SCRIPT_POSITION ) + { + switch( m_currentLanguage ) + { + case DIALOGUE_LANGUAGE_ENGLISH: + scriptName = s_DaSoundScripts[i].m_Filename; + break; + + case DIALOGUE_LANGUAGE_FRENCH: + scriptName = "\\nisfr.spt"; + break; + + case DIALOGUE_LANGUAGE_GERMAN: + scriptName = "\\nisge.spt"; + break; + + case DIALOGUE_LANGUAGE_SPANISH: + scriptName = "\\nissp.spt"; + break; + + default: + rAssert( false ); + scriptName = s_DaSoundScripts[i].m_Filename; + break; + } + } + else if( i == INTERACTIVE_PROPS_SCRIPT_POSITION ) + { + switch( m_currentLanguage ) + { + case DIALOGUE_LANGUAGE_ENGLISH: + scriptName = s_DaSoundScripts[i].m_Filename; + break; + + case DIALOGUE_LANGUAGE_FRENCH: + scriptName = "\\interactive_propsfr.spt"; + break; + + case DIALOGUE_LANGUAGE_GERMAN: + scriptName = "\\interactive_propsge.spt"; + break; + + case DIALOGUE_LANGUAGE_SPANISH: + scriptName = "\\interactive_propssp.spt"; + break; + + default: + rAssert( false ); + scriptName = s_DaSoundScripts[i].m_Filename; + break; + } + } + else + { + scriptName = s_DaSoundScripts[i].m_Filename; + } + sprintf( filename, "%s%s", SCRIPT_DIR, scriptName ); + GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, filename, GMA_AUDIO_PERSISTENT ); + } + + HeapMgr()->PopHeap (GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// daSoundRenderingManager::LoadTypeInfoFile +//============================================================================= +// Description: Start the loading of the type info +// +// Parameters: filename - name of type info file +// fileHandler - completion callback object +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::LoadTypeInfoFile( const char* filename, SoundFileHandler* fileHandler ) +{ + m_soundFileHandler = fileHandler; + + IRadTypeInfoSystem* pTypeInfoSystem = ::radTypeInfoSystemGet( ); + rAssert( pTypeInfoSystem != NULL ); + pTypeInfoSystem->AddRef( ); + pTypeInfoSystem->LoadTypeInfo + ( + gTuneSound ? GMA_AUDIO_PERSISTENT : GMA_TEMP, + filename, + daSoundRenderingManager::TypeInfoComplete, + (void*)false + ); + pTypeInfoSystem->Release( ); +} + +//============================================================================= +// daSoundRenderingManager::LoadScriptFile +//============================================================================= +// Description: Start the loading of a RadScript script file +// +// Parameters: filename - name of script file +// fileHandler - completion callback object +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::LoadScriptFile( const char* filename, SoundFileHandler* fileHandler ) +{ + const bool* lastScript; + + m_soundFileHandler = fileHandler; + + // Create the script object, if it hasn't been done yet + if( m_pScript == NULL ) + { + m_pScript = ::radScriptCreateScript + ( + RADMEMORY_ALLOC_TEMP + ); + rAssert( m_pScript != NULL ); + m_pScript->AddRef( ); + } + + m_pScript->SetAllocator( GetThisAllocator( ) ); + + // + // Pass a flag indicating if this is the last script as user info + // + if( m_scriptLoadCount < NUM_CHARACTER_SCRIPTS ) + { + m_pScript->SetContext( GetCharacterNamespace( m_scriptLoadCount ) ); + } + else if( m_scriptLoadCount >= NumSoundScripts - NUM_TUNING_SCRIPTS ) + { + m_pScript->SetContext( GetTuningNamespace( ) ); + } + else + { + m_pScript->SetContext( GetSoundNamespace( ) ); + } + + if( m_scriptLoadCount == NumSoundScripts - 1 ) + { + lastScript = &LastScriptTrue; + } + else + { + lastScript = &LastScriptFalse; + } + + // Load the script + m_pScript->Load + ( + filename, + daSoundRenderingManager::ScriptComplete, + (void*)lastScript, + RADMEMORY_ALLOC_TEMP + ); + } + +//============================================================================= +// Function: daSoundRenderingManager::GetSoundNamespace +//============================================================================= +// Description: Get the sound resource namespace +// +// Parameters: none +// +// Returns: Returns a pointer to the sound resource namespace +// +//----------------------------------------------------------------------------- + +IRadNameSpace* daSoundRenderingManager::GetSoundNamespace( void ) +{ + return m_pResourceNameSpace; +} + +//============================================================================= +// Function: daSoundRenderingManager::GetTuningNamespace +//============================================================================= +// Description: Get the sound tuning namespace +// +// Parameters: none +// +// Returns: Returns a pointer to the sound tuning namespace +// +//----------------------------------------------------------------------------- + +IRadNameSpace* daSoundRenderingManager::GetTuningNamespace( void ) +{ + return m_pTuningNameSpace; +} + +//============================================================================= +// daSoundRenderingManager::GetCharacterNamespace +//============================================================================= +// Description: Get the specified character namespace +// +// Parameters: index - index into list of namespaces +// +// Return: Pointer to the desired namespace +// +//============================================================================= +IRadNameSpace* daSoundRenderingManager::GetCharacterNamespace( unsigned int index ) +{ + rAssert( index < NUM_CHARACTER_NAMESPACES ); + + return( m_pCharacterNameSpace[index] ); +} + +//============================================================================= +// Function: daSoundRenderingManager::GetTheListener +//============================================================================= +// Description: Get the listener +// +// Parameters: none +// +// Returns: Returns a pointer to the listener +// +//----------------------------------------------------------------------------- + +IRadSoundHalListener* daSoundRenderingManager::GetTheListener( void ) +{ + return ::radSoundHalListenerGet( ); +} + +//============================================================================= +// Function: daSoundRenderingManager::GetDynaLoadManager +//============================================================================= +// Description: Get the dynamic loading manager +// +// Parameters: none +// +// Returns: Returns a pointer to the dynamic loading manager +// +//----------------------------------------------------------------------------- + +daSoundDynaLoadManager* daSoundRenderingManager::GetDynaLoadManager( void ) +{ + return m_pDynaLoadManager; +} + +//============================================================================= +// Function: daSoundRenderingManager::GetDialogManager +//============================================================================= +// Description: Get the dialog manager +// +// Parameters: none +// +// Returns: Returns a pointer to the dialog manager +// +//----------------------------------------------------------------------------- + +/*IDaSoundDialogManager* daSoundRenderingManager::GetDialogManager( void ) +{ + return m_pDialogManager; +}*/ + +//============================================================================= +// Function: daSoundRenderingManager::GetTuner +//============================================================================= +// Description: Get the tuner +// +// Parameters: none +// +// Returns: Returns a pointer to the tuner +// +//----------------------------------------------------------------------------- + +IDaSoundTuner* daSoundRenderingManager::GetTuner( void ) +{ + return m_pTuner; +} + +//============================================================================= +// Function: daSoundRenderingManager::GetObjectDataLibrary +//============================================================================= +// Description: Get the object data library +// +// Parameters: none +// +// Returns: Returns a pointer to the object data library +// +//----------------------------------------------------------------------------- + +/*IDaSoundObjectDataLibrary* daSoundRenderingManager::GetObjectDataLibrary( void ) +{ + return m_pObjectDataLibrary; +}*/ + +//============================================================================= +// Function: daSoundRenderingManager::GetResourceManager +//============================================================================= +// Description: Get the resource manager +// +// Parameters: none +// +// Returns: Returns a pointer to the resource manager +// +//----------------------------------------------------------------------------- + +daSoundResourceManager* daSoundRenderingManager::GetResourceManager( void ) +{ + return m_pResourceManager; +} + +//============================================================================= +// Function: daSoundRenderingManager::GetPlayerManager +//============================================================================= +// Description: Get the player manager +// +// Parameters: none +// +// Returns: Returns a pointer to the player manager +// +//----------------------------------------------------------------------------- + +daSoundPlayerManager* daSoundRenderingManager::GetPlayerManager( void ) +{ + return m_pPlayerManager; +} + +//============================================================================= +// Function: daSoundRenderingManager::TypeInfoComplete +//============================================================================= +// Description: Asynchronous file load complete callback. Redundant, but +// RadScript requires a static function for callback +// +// Parameters: pUserData - some user data to pass on +// +// Note: This relies on the sound manager being a singleton +// +//----------------------------------------------------------------------------- +void daSoundRenderingManager::TypeInfoComplete( void* pUserData ) +{ + rAssert( s_Singleton != NULL ); + s_Singleton->ProcessTypeInfo( pUserData ); +} + +//============================================================================= +// daSoundRenderingManager::ScriptComplete +//============================================================================= +// Description: Asynchronous file load complete callback. Redundant, but +// RadScript requires a static function for callback +// +// Parameters: pUserData - some user data to pass on +// +// Note: This relies on the sound manager being a singleton +// +//============================================================================= +void daSoundRenderingManager::ScriptComplete( void* pUserData ) +{ + rAssert( s_Singleton != NULL ); + s_Singleton->ProcessScript( pUserData ); +} + +//============================================================================= +// daSoundRenderingManager::SoundObjectCreated +//============================================================================= +// Description: RadScript callback function called when an object is created. +// We use it to register the object for loading/unloading +// +// Parameters: objName - name of sound resource to load/unload +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::SoundObjectCreated( const char* objName, IRefCount* obj ) +{ + bool added; + rAssert( NULL != dynamic_cast< daSoundResourceData*>( obj ) ); + daSoundResourceData* resourceObj = static_cast<daSoundResourceData*>( obj ); + + // + // We only need to preload clips + // + if( resourceObj->GetStreaming() == false ) + { + added = GetSoundManager()->GetSoundLoader()->AddResourceToCurrentCluster( objName ); + rAssert( added ); + } + else + { + volatile int x = 4; + } +} + +//============================================================================= +// daSoundRenderingManager::ProcessTypeInfo +//============================================================================= +// Description: Asynchronous file load complete callback. +// +// Parameters: ( void* pUserData ) +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::ProcessTypeInfo( void* pUserData ) +{ + m_soundFileHandler->LoadCompleted(); +} + +//============================================================================= +// daSoundRenderingManager::ScriptComplete +//============================================================================= +// Description: Asynchronous script load callback +// +// Parameters: pUserData - some user data to pass on +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::ProcessScript( void* pUserData ) +{ + ScriptObjCreateCallback* callbackPtr = NULL; + const bool* lastScript = reinterpret_cast<const bool*>( pUserData ); + + rAssert( m_pScript != NULL ); + + // + // Tell the sound manager which sound cluster we'll associate + // with the resources in this script + // + if( m_scriptLoadCount < NumSoundScripts - NUM_TUNING_SCRIPTS ) + { + callbackPtr = SoundObjectCreated; + + if( m_scriptLoadCount < NumClusterNames ) + { + GetSoundManager()->GetSoundLoader()->SetCurrentCluster( s_ScriptClusters[m_scriptLoadCount] ); + } + else + { + GetSoundManager()->GetSoundLoader()->SetCurrentCluster( static_cast<SoundClusterName>(SC_CAR_BASE + m_scriptLoadCount - NumClusterNames) ); + } + } + + // Execute script file + + unsigned int start = radTimeGetMicroseconds( ); + m_pScript->Run( callbackPtr ); + unsigned int finished = radTimeGetMicroseconds( ); + unsigned int dif = finished - start; + + gTotalMicrosecondsWastedParsingScripts += dif; + + if ( dif > 1000 ) + { + //rTunePrintf( "\n\nAUDIO: Holy !@#$ script took [%d] ms to parse\n\n", dif / 1000 ); + } + + m_pScript->UnLoad( ); + + if( *lastScript ) + { + rReleasePrintf( "\n\nAUDIO: Wasted [%d] ms of load time parsing scripts\n\n", gTotalMicrosecondsWastedParsingScripts / 1000); + + // Free the script + rAssert( m_pScript != NULL ); + m_pScript->Release( ); + m_pScript = NULL; + + // + // At this point the sound resources should be finalized, lets lock them down + // and then intialize some systems. + // + + GetTuner( )->Initialize( ); + + // Initialize the dialog system + + SoundManager::GetInstance( )->m_dialogCoordinator->Initialize( ); + + Sound::daSoundResourceManager::GetInstance( )->SetResourceLockdown( true ); + + m_pPlayerManager->Initialize( ); + + // We are now fully initialized + m_IsInitialized = true; + + if ( ! gTuneSound ) + { + radScriptUnLoadAllTypeInfo( ); + } + } + + ++m_scriptLoadCount; + + m_soundFileHandler->LoadCompleted(); +} + + +//============================================================================= +// Public Functions +//============================================================================= + +//============================================================================= +// Function: ::daSoundRenderingManagerCreate +//============================================================================= +// Description: Create the sound manager +// +// Parameters: allocator - the memory allocator to use +// +// Preconditions: +// In order to manage memory fragmentation issues, and to +// allow a timely intialization of sound, this command should +// be executed before any frontend or ingame structures have +// been created. +// +// Postconditions: +// This will generate any singleton classes related to the Dark +// Angel sound system. It will register any appropriate +// cement files to give access to sound, and it will parse +// any sound scripts so that the sound system will be ready +// to start recieving sound events. +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManagerCreate( radMemoryAllocator allocator ) +{ + rAssert( daSoundRenderingManager::GetInstance( ) == NULL ); + + // Create the sound manager + daSoundRenderingManager* pSoundManager = new ( allocator ) daSoundRenderingManager( ); + rAssert( pSoundManager != NULL ); + pSoundManager->AddRef( ); + rAssert( pSoundManager == daSoundRenderingManager::GetInstance( ) ); + + rDebugChannelInitialize( GMA_MUSIC ); + //rDebugChannelEnable( radmusic::debug_channel ); + + // + // Initialize some usefull systems + // + ::radScriptInitialize( allocator ); +#ifndef FINAL + if( gTuneSound ) + { + ::radRemoteScriptInitialize( allocator ); + } +#endif + + gpBTreeNodePool = (radObjectBTreeNode*) radMemoryAlloc( + allocator, + MAX_BTREE_NODES * sizeof( radObjectBTreeNode ) ); + + ::memset( gpBTreeNodePool, 0, MAX_BTREE_NODES * sizeof( radObjectBTreeNode ) ); + + radMemoryMonitorIdentifyAllocation( gpBTreeNodePool, "Global BTree Node Pool" ); + + radObjectBTree::Initialize( gpBTreeNodePool, MAX_BTREE_NODES ); + // Initialize + + SoundNucleusInitialize( allocator ); +} + +//============================================================================= +// Function: ::daSoundRenderingManagerGet +//============================================================================= +// Description: Get a pointer to the sound manager singleton class +// +//----------------------------------------------------------------------------- + +daSoundRenderingManager* daSoundRenderingManagerGet( void ) +{ + rAssert( daSoundRenderingManager::GetInstance( ) != NULL ); + return + ( + static_cast< daSoundRenderingManager* >( daSoundRenderingManager::GetInstance( ) ) + ); +} + +void daSoundRenderingManager::FilePerformanceEvent( + bool start, + const char * pFile, + unsigned int bytes ) +{ + daSoundRenderingManager * pThis = daSoundRenderingManager::GetInstance( ); + + unsigned int now = radTimeGetMilliseconds( ); + + if ( start ) + { + pThis->m_LastPerformanceEventTime = now; + + if ( CommandLineOptions::Get( CLO_AUDIO_LOADING_SPEW ) ) + { + rTunePrintf( "<<START>> Async Loading: (Audio) %s Bytes: 0x%x\n", pFile, bytes ); + } + } + else + { + unsigned int dif = now - pThis->m_LastPerformanceEventTime; + + if ( CommandLineOptions::Get( CLO_AUDIO_LOADING_SPEW ) ) + { + rTunePrintf( "<< END >> Async Loading: (Audio) %s (%d msecs)\n", pFile, dif ); + } + } +} + +void daSoundRenderingManager::Render( void ) +{ + if ( gDaSoundStats ) + { + m_pPlayerManager->Render( ); + } +} + +//============================================================================= +// Function: ::daSoundRenderingManagerTerminate +//============================================================================= +// Description: Terminate the sound system +// +//----------------------------------------------------------------------------- + +void daSoundRenderingManagerTerminate( void ) +{ + rAssert( daSoundRenderingManager::GetInstance( ) != NULL ); + daSoundRenderingManager::GetInstance( )->Terminate( ); + daSoundRenderingManager::GetInstance( )->Release( ); + rAssert( daSoundRenderingManager::GetInstance( ) == NULL ); + + SoundNucleusTerminate( ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// daSoundRenderingManager::registerDialogueCementFiles +//============================================================================= +// Description: Comment +// +// Parameters: ( const char* cementFilename ) +// +// Return: void +// +//============================================================================= +void daSoundRenderingManager::registerDialogueCementFiles( const char* cementFilename ) +{ +#if defined( RAD_XBOX ) + char dialogNameBuffer[ 16 ]; + int i = 0; + + strcpy( dialogNameBuffer, cementFilename ); + char* numberPosition = strchr( dialogNameBuffer, '?' ); + for ( unsigned int j = 0; j < s_NumDialogCementFiles; j++ ) + { + *numberPosition = j + '0'; + m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( dialogNameBuffer ); + } +#else + rAssert( s_NumDialogCementFiles == 1 ); + m_soundCementFileHandles[0] = GetLoadingManager()->RegisterCementLibrary( cementFilename ); +#endif +} + +} // Sound Namespace + diff --git a/game/code/sound/soundrenderer/soundrenderingmanager.h b/game/code/sound/soundrenderer/soundrenderingmanager.h new file mode 100644 index 0000000..a77ac7c --- /dev/null +++ b/game/code/sound/soundrenderer/soundrenderingmanager.h @@ -0,0 +1,187 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundrenderingmanager.hpp +// +// Subsystem: Dark Angel - Sound Rendering Manager System +// +// Description: Description of the DA sound manager +// +// Revisions: +// + Created October 2, 2001 -- breimer +// +//============================================================================= + +#ifndef _SOUNDRENDERINGMANAGER_HPP +#define _SOUNDRENDERINGMANAGER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radfile.hpp> +#include <enums.h> +#include <radsound.hpp> +#include <radscript.hpp> + +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/playermanager.h> + +//============================================================================= +// Global namespace forward declarations +//============================================================================= + + +class SoundFileHandler; + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundRenderingManager; +class daSoundDynaLoadManager; + +//============================================================================= +// Class Declarations +//============================================================================= + +static const unsigned int NUM_CHARACTER_NAMESPACES = 5; + +// +// Language enumeration +// +enum DialogueLanguage +{ + DIALOGUE_LANGUAGE_ENGLISH, + DIALOGUE_LANGUAGE_FRENCH, + DIALOGUE_LANGUAGE_GERMAN, + DIALOGUE_LANGUAGE_SPANISH +}; + +// +// The sound manger +// +class daSoundRenderingManager : public radRefCount +{ +public: + IMPLEMENT_REFCOUNTED( "daSoundManager" ); + + // + // Constructor and destructor + // + daSoundRenderingManager( ); + virtual ~daSoundRenderingManager( ); + + // + // Get the singleton + // + static daSoundRenderingManager* GetInstance( void ); + + // + // Terminate + // + void Terminate( void ); + + void QueueCementFileRegistration(); + void QueueRadscriptFileLoads(); + void LoadTypeInfoFile( const char* filename, SoundFileHandler* fileHandler ); + void LoadScriptFile( const char* filename, SoundFileHandler* fileHandler ); + + void SetLanguage( Scrooby::XLLanguage language ); + + void ProcessTypeInfo( void* pUserData ); + void ProcessScript( void* pUserData ); + + // + // IDaSoundManager + // + void Initialize( void ); + bool IsInitialized( void ); + void Service( void ); + void ServiceOncePerFrame( unsigned int elapsedTime ); + void Render( void ); + + IRadNameSpace* GetSoundNamespace( void ); + IRadNameSpace* GetTuningNamespace( void ); + IRadNameSpace* GetCharacterNamespace( unsigned int index ); + IRadSoundHalListener* GetTheListener( void ); + + daSoundDynaLoadManager* GetDynaLoadManager( void ); + IDaSoundTuner* GetTuner( void ); + daSoundResourceManager* GetResourceManager( void ); + daSoundPlayerManager* GetPlayerManager( void ); + +protected: + + static void TypeInfoComplete( void* pUserData ); + static void ScriptComplete( void* pUserData ); + static void SoundObjectCreated( const char* objName, IRefCount* obj ); + +private: + + static void FilePerformanceEvent( bool start, const char * pFile, unsigned int bytes ); + + void registerDialogueCementFiles( const char* cementFilename ); + + // The singleton instance + static daSoundRenderingManager* s_Singleton; + + IRadScript* m_pScript; + bool m_IsInitialized; + + // + // Our namespaces + // + IRadNameSpace* m_pResourceNameSpace; + IRadNameSpace* m_pTuningNameSpace; + + IRadNameSpace* m_pCharacterNameSpace[NUM_CHARACTER_NAMESPACES]; + + // + // Store the various related systems + // + daSoundDynaLoadManager* m_pDynaLoadManager; + IDaSoundTuner* m_pTuner; + daSoundResourceManager* m_pResourceManager; + daSoundPlayerManager* m_pPlayerManager; + + // + // Loading system callback + // + SoundFileHandler* m_soundFileHandler; + + // + // Cement file handles, in case we want to release them + // +#ifdef RAD_XBOX + static const unsigned int NUM_SOUND_CEMENT_FILES = 12; +#else + static const unsigned int NUM_SOUND_CEMENT_FILES = 7; +#endif + unsigned int m_soundCementFileHandles[NUM_SOUND_CEMENT_FILES]; + + // + // Script loading count, so we can tell which namespace to put stuff in + // + unsigned int m_scriptLoadCount; + + unsigned int m_LastPerformanceEventTime; + + // + // Language + // + DialogueLanguage m_currentLanguage; + bool m_languageSelected; +}; + +} // Sound Namespace + +#endif //_SOUNDRENDERINGMANAGER_HPP diff --git a/game/code/sound/soundrenderer/soundresource.cpp b/game/code/sound/soundrenderer/soundresource.cpp new file mode 100644 index 0000000..ff39f81 --- /dev/null +++ b/game/code/sound/soundrenderer/soundresource.cpp @@ -0,0 +1,555 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundresource.cpp +// +// Subsystem: Dark Angel - Sound resources +// +// Description: Implements sound resources +// +// Modification History: +// + Created October 11, 2001 -- aking +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundplayer.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/playermanager.h> + +#include <sound/soundrenderer/soundresourcemanager.h> + +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundresource.h> + +#include <sound/soundmanager.h> +#include <memory/srrmemory.h> + +//============================================================================= +// Static Variables +//============================================================================= + +//============================================================================= +// daSoundResourceData Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundResourceData::daSoundResourceData +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + + +inline unsigned char vol_f_to_c( float f ) +{ + return (unsigned char) radSoundFloatToUInt( f * 255.0f ); +} + +inline float vol_c_to_f( unsigned char c ) +{ + return radSoundUIntToFloat( (unsigned int) c ) / 255.0f; +} + +inline unsigned short pitch_f_to_s( float f ) +{ + return (unsigned short) radSoundFloatToUInt( f * 6553.0f ); +} + +inline float pitch_s_to_f( unsigned short c ) +{ + return radSoundUIntToFloat( (unsigned short) c ) / 6553.0f; +} + +const int FLAG_LOOPING = 1 << 0; +const int FLAG_STREAMING = 1 << 1; + +daSoundResourceData::daSoundResourceData( ) +{ + m_NumFiles = 0; + + m_MinTrim = vol_f_to_c( 1.0f ); + m_MaxTrim = vol_f_to_c( 1.0f ); + m_MinPitch = pitch_f_to_s( 1.0f ); + m_MaxPitch = pitch_f_to_s( 1.0f ); + + m_SoundGroup = Sound::MASTER; + m_CaptureCount = 0; + + m_Flags = 0; + + m_pFileIds = 0; +} + +//============================================================================= +// Function: daSoundResourceData::~daSoundResourceData +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundResourceData::~daSoundResourceData( ) +{ + +} + +//============================================================================= +// Function: daSoundResourceData::AddFilename +//============================================================================= +// Description: Add a file to this resource +// +// Parameters: newFileName - the new file name +// trim - the trim for this file +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::AddFilename +( + const char* newFileName, + float trim +) +{ + rAssert( false == Sound::daSoundResourceManager::GetInstance( )->GetResourceLockdown( ) ); + + // very lazy indeed. + + if ( m_pFileIds == 0 ) + { + m_pFileIds = (FileId*) radMemoryAlloc( GMA_DEFAULT, sizeof( FileId ) * DASound_MaxNumSoundResourceFiles ); + } + + rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL ); + rAssert( m_NumFiles < DASound_MaxNumSoundResourceFiles ); + + m_pFileIds[ m_NumFiles ].m_pName = (char*) radMemoryAlloc( GMA_DEFAULT, strlen( newFileName ) + 1 ); + strcpy( m_pFileIds[ m_NumFiles ].m_pName, newFileName ); + m_NumFiles++; +} + + +//============================================================================= +// Function: daSoundResourceData::GetFilenameAt +//============================================================================= +// Description: Get the resource file at the given index +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::GetFileNameAt( unsigned int index, char* buffer, unsigned int max ) +{ + rTuneAssert( false == Sound::daSoundResourceManager::GetInstance( )->GetResourceLockdown( ) ); + rTuneAssert( index < m_NumFiles ); + + strcpy( buffer, m_pFileIds[ index ].m_pName ); +} + +void daSoundResourceData::GetFileKeyAt( unsigned int index, char* buffer, unsigned int max ) +{ + rTuneAssert( index < m_NumFiles ); + KeyToStringKey32( buffer, m_pFileIds[ index ].m_Key ); +} + +//============================================================================= +// Function: daSoundResourceData::SetPitchRange +//============================================================================= +// Description: Set the pitch range for this resource +// +// Parameters: minPitch - the minimum pitch value +// maxPitch - the maximum pitch value +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetPitchRange +( + Sound::daPitchValue minPitch, + Sound::daPitchValue maxPitch +) +{ + m_MinPitch = pitch_f_to_s( minPitch ); + m_MaxPitch = pitch_f_to_s( maxPitch ); +} + +//============================================================================= +// Function: daSoundResourceData::GetPitchRange +//============================================================================= +// Description: Get the pitch range for this resource +// +// Parameters: *MinPitch - (out )the minimum pitch value +// *MaxPitch - (out) the maximum pitch value +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::GetPitchRange +( + Sound::daPitchValue* pMinPitch, + Sound::daPitchValue* pMaxPitch +) +{ + if( pMinPitch != NULL ) + { + *pMinPitch = pitch_s_to_f( m_MinPitch ); + } + if( pMaxPitch != NULL ) + { + *pMaxPitch = pitch_s_to_f( m_MaxPitch ); + } +} + +//============================================================================= +// Function: daSoundResourceData::SetTrimRange +//============================================================================= +// Description: Set the trim range for this resource +// +// Parameters: minTrim - the minimum trim value +// maxTrim - the maximum trim value +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetTrimRange +( + float minTrim, + float maxTrim +) +{ + m_MinTrim = vol_f_to_c( minTrim ); + m_MaxTrim = vol_f_to_c( maxTrim ); +} + +//============================================================================= +// Function: daSoundResourceData::SetTrim +//============================================================================= +// Description: Set the trim for this resource +// +// Parameters: trim - the trim value +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetTrim +( + float trim +) +{ + m_MinTrim = vol_f_to_c( trim ); + m_MaxTrim = vol_f_to_c( trim ); +} + +//============================================================================= +// Function: daSoundResourceData::GetTrimRange +//============================================================================= +// Description: Get the trim range for this resource +// +// Parameters: *MinTrim - (out )the minimum trim value +// *MaxTrim - (out) the maximum trim value +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::GetTrimRange +( + float* pMinTrim, + float* pMaxTrim +) +{ + if( pMinTrim != NULL ) + { + *pMinTrim = vol_c_to_f( m_MinTrim ); + } + if( pMaxTrim != NULL ) + { + *pMaxTrim = vol_c_to_f( m_MaxTrim ); + } +} + +//============================================================================= +// Function: daSoundResourceData::SetStreaming +//============================================================================= +// Description: Set this as a streaming resource +// +// Parameters: streaming - should streaming be on or off +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetStreaming( bool streaming ) +{ + if( streaming ) + { + m_Flags |= FLAG_STREAMING; + } + else + { + m_Flags &= ~FLAG_STREAMING; + } + + if( IsCaptured( ) ) + { + rReleaseString + ( + "Streaming will not immediately " + "affect a captured resource\n" + ); + } +} + +//============================================================================= +// Function: daSoundResourceData::GetStreaming +//============================================================================= +// Description: Get whether or not this is a streaming resource +// +// Parameters: none +// +// Returns: Returns true if this resource streams, otherwise false +// +//----------------------------------------------------------------------------- + +bool daSoundResourceData::GetStreaming( void ) +{ + return ( m_Flags & FLAG_STREAMING ) != 0; +} + +//============================================================================= +// Function: daSoundResourceData::SetLooping +//============================================================================= +// Description: Set this as a looping resource +// +// Parameters: looping - should looping be on or off +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetLooping( bool looping ) +{ + if( looping ) + { + m_Flags |= FLAG_LOOPING; + } + else + { + m_Flags &= ~FLAG_LOOPING; + } + + if( IsCaptured( ) ) + { + rReleaseString + ( + "Looping will not immediately " + "affect a captured resource\n" + ); + } +} + +//============================================================================= +// Function: daSoundResourceData::GetLooping +//============================================================================= +// Description: Get whether or not this is a looping resource +// +// Parameters: none +// +// Returns: Returns true if this resource loops, otherwise false +// +//----------------------------------------------------------------------------- + +bool daSoundResourceData::GetLooping( void ) +{ + return ( m_Flags & FLAG_LOOPING ) != 0; +} + +//============================================================================= +// Function: daSoundResourceData::GetType +//============================================================================= +// Description: Find out what kind of resource this is +// +// Parameters: none +// +// Returns: Returns the type of resource +// +//----------------------------------------------------------------------------- + +IDaSoundResource::Type daSoundResourceData::GetType +( + void +) +{ + IDaSoundResource::Type resourceType = UNKNOWN; + + if( GetStreaming( ) ) + { + resourceType = STREAM; + } + else + { + resourceType = CLIP; + } + + return resourceType; +} + +//============================================================================= +// Function: daSoundResourceData::SetSoundGroup +//============================================================================= +// Description: Set the sound group that this resource belongs to +// +// Parameters: soundGroup - the sound group to associate with this resource +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::SetSoundGroup +( + Sound::daSoundGroup soundGroup +) +{ + m_SoundGroup = soundGroup; +} + +//============================================================================= +// Function: daSoundResourceData::GetSoundGroup +//============================================================================= +// Description: Get the sound group that this resource belongs to +// +// Parameters: none +// +// Returns: Returns the sound group that this resource belongs to. +// +//----------------------------------------------------------------------------- + +Sound::daSoundGroup daSoundResourceData::GetSoundGroup( void ) +{ + return static_cast<Sound::daSoundGroup>(m_SoundGroup); +} + +//============================================================================= +// Function: daSoundResourceData::CaptureResource +//============================================================================= +// Description: Capture this resource +// +// Parameters: none +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::CaptureResource( void ) +{ + rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL ); + bool wasCaptured = IsCaptured( ); + ++m_CaptureCount; + if( !wasCaptured ) + { + // Inform the resource manager + Sound::daSoundResourceManager::GetInstance( )-> + AllocateResource( this ); + } +} + +//============================================================================= +// Function: daSoundResourceData::IsCaptured +//============================================================================= +// Description: Is this resource captured? +// +// Parameters: none +// +// Returns: Returns true if the resource is captured, otherwise false. +// +//----------------------------------------------------------------------------- + +bool daSoundResourceData::IsCaptured( void ) +{ + return( m_CaptureCount > 0 ); +} + +//============================================================================= +// Function: daSoundResourceData::ReleaseResource +//============================================================================= +// Description: Release this resource +// +// Parameters: none +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::ReleaseResource( void ) +{ + rAssertMsg( m_CaptureCount > 0, "Cannot release uncaptured resource" ); + rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL ); + + --m_CaptureCount; + if( m_CaptureCount == 0 ) + { + // Inform the resource manager + Sound::daSoundResourceManager::GetInstance( )-> + DeallocateResource( this ); + } +} + +//============================================================================= +// Function: daSoundResourceData::ReleaseResource +//============================================================================= +// Description: Plays this resource +// +// Notes: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// THIS IS FOR COMPOSERS ONLY. DO NOT USE IT. +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +//----------------------------------------------------------------------------- + +void daSoundResourceData::Play( void ) +{ + if( Sound::daSoundRenderingManagerGet( )->GetResourceManager( )->GetResourceLockdown( ) ) + { + // Play it + // DO NOT USE THIS COMMAND IF YOU ARE NOT IN RADTUNER / SOUNDTUNER / etc. + Sound::daSoundClipStreamPlayer* pPlayer = NULL; + Sound::daSoundRenderingManagerGet( )->GetPlayerManager( )->CaptureFreePlayer + ( + &pPlayer, + this, + false ); + + if( pPlayer != NULL ) + { + pPlayer->Play( ); + } + } + else + { + // You shouldn't have left play commands in the composer script + rDebugString( "Can't play sounds before resources are locked down.\n" ); + } +} + +//============================================================================= +// Public functions +//============================================================================= + +void daSoundResourceData::AddRef( void ) +{ + //Sound::daSoundResourceManager::GetInstance( )->AddRef( ); +} + +void daSoundResourceData::Release( void ) +{ + //Sound::daSoundResourceManager::GetInstance( )->Release( ); +} + + + diff --git a/game/code/sound/soundrenderer/soundresource.h b/game/code/sound/soundrenderer/soundresource.h new file mode 100644 index 0000000..30abb0f --- /dev/null +++ b/game/code/sound/soundrenderer/soundresource.h @@ -0,0 +1,152 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundresource.hpp +// +// Subsystem: Dark Angel - Sound Resources +// +// Description: Defines sound resource +// +// Modification History: +// + Created October 11, 2001 -- aking +// +//============================================================================= + +#ifndef _SOUNDRESOURCE_HPP +#define _SOUNDRESOURCE_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/idasoundresource.h> + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundResourceData; + +//============================================================================= +// Class Declarations +//============================================================================= + +// +// This contains the type of resource. +// +class daSoundResourceData + : + public IDaSoundResource +{ +public: + + virtual void AddRef( void ); + virtual void Release( void ); + + // + // Constructor and destructor + // + daSoundResourceData( ); + virtual ~daSoundResourceData( ); + + // + // IDaSoundResourceData and IDaSoundResource + // + virtual void AddFilename + ( + const char* newFileName, + float trim + ); + + inline virtual unsigned int GetNumFiles( void ); + + virtual void GetFileNameAt( unsigned int index, char* buffer, unsigned int max ); + virtual void GetFileKeyAt( unsigned int index, char * buffer, unsigned int max ); + + virtual void SetPitchRange + ( + float minPitch, + float maxPitch + ); + virtual void GetPitchRange + ( + float* pMinPitch, + float* pMaxPitch + ); + + virtual void SetTrimRange + ( + float minTrim, + float maxTrim + ); + virtual void GetTrimRange + ( + float* pMinTrim, + float* pMaxTrim + ); + + virtual void SetTrim( float trim ); + + virtual void SetStreaming( bool streaming ); + virtual bool GetStreaming( void ); + + virtual void SetLooping( bool looping ); + virtual bool GetLooping( void ); + + virtual Type GetType( void ); + virtual void SetSoundGroup( Sound::daSoundGroup soundGroup ); + virtual Sound::daSoundGroup GetSoundGroup( void ); + + virtual void CaptureResource( void ); + virtual bool IsCaptured( void ); + virtual void ReleaseResource( void ); + + // COMPOSERS ONLY!! + virtual void Play( void ); + + // + // The pitch variation + // + short m_MinPitch; + short m_MaxPitch; + + // + // The sound files for this resource + // + + union FileId { + char * m_pName; + radKey32 m_Key; + }; + + FileId * m_pFileIds; + + + // + // The trim variation + // + unsigned char m_MinTrim; + unsigned char m_MaxTrim; + + unsigned char m_Flags; + + unsigned char m_NumFiles; + + // + // Hold a capture counter + // + unsigned char m_CaptureCount; + unsigned char m_SoundGroup; +}; + +inline unsigned int daSoundResourceData::GetNumFiles( void ) +{ + return m_NumFiles; +} + +#endif //_SOUNDRESOURCE_HPP + diff --git a/game/code/sound/soundrenderer/soundresourcemanager.cpp b/game/code/sound/soundrenderer/soundresourcemanager.cpp new file mode 100644 index 0000000..5fdc5f6 --- /dev/null +++ b/game/code/sound/soundrenderer/soundresourcemanager.cpp @@ -0,0 +1,527 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundresourcemanager.cpp +// +// Subsystem: Dark Angel - Sound Resource Manager +// +// Description: Implementation of the sound resource manager +// +// Revisions: +// + Created October 11, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radkey.hpp> +#include <radobjectbtree.hpp> +#include <radnamespace.hpp> +#include <radsound.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/soundresource.h> +#include <sound/soundrenderer/soundallocatedresource.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/soundrenderingmanager.h> + +#include <memory/srrmemory.h> + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Static Variables +//============================================================================= + +daSoundResourceManager* daSoundResourceManager::s_pSingleton = NULL; + +//============================================================================= +// Constants +//============================================================================= + +// +// The maximum number of sound resource files (not the resources, but the +// the actual files) +// +const unsigned int MaxNumResourceFiles = 512; + +#if defined( RAD_XBOX ) || defined( RAD_WIN32 ) + const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::PCM; +#elif defined RAD_PS2 + const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::VAG; +#elif defined RAD_GAMECUBE + const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::PCM_BIGENDIAN; +#else + Uh oh. +#endif + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundResourceManager Implementation +//============================================================================= + + +//============================================================================= +// Function: daSoundResourceManager::daSoundResourceManager +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundResourceManager::daSoundResourceManager( ) + : + radRefCount( 0 ), + m_pSoundNamespace( NULL ), + m_ResourceLockdown( false ), + m_secondaryNamespace( NULL ) + +{ + m_pFileIdMemory = 0; + + m_NumResourceDatas = 0; + + // Create the singleton + rAssert( s_pSingleton == NULL ); + s_pSingleton = this; + + // Remember the sound namespace + m_pSoundNamespace = Sound::daSoundRenderingManagerGet( )->GetSoundNamespace( ); + if( m_pSoundNamespace != NULL ) + { + m_pSoundNamespace->AddRef( ); + } + + m_xIOL_AllocatedResources = new ( GetThisAllocator( )) radObjectBTree( ); +} + +//============================================================================= +// Function: daSoundResourceManager::~daSoundResourceManager +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundResourceManager::~daSoundResourceManager( ) +{ + // Release the allocated sound resources + m_xIOL_AllocatedResources = NULL; + + // Release the sound namespace + if( m_pSoundNamespace != NULL ) + { + m_pSoundNamespace->Release( ); + m_pSoundNamespace = NULL; + } + + // Remove the singleton + rAssert( s_pSingleton != NULL ); + s_pSingleton = NULL; +} + +//============================================================================= +// Function: daSoundResourceManager::AllocateResource +//============================================================================= +// Description: Allocate a resource +// +// Parameters: pResource - the resource to allocate +// +//----------------------------------------------------------------------------- + +void daSoundResourceManager::AllocateResource +( + IDaSoundResource* pResource +) +{ + rAssert( pResource != NULL ); + + // + // Allocate this resource + // + radKey32 soundKey = (radKey32) pResource; + + // Find out if it already exists... + daSoundAllocatedResource* pAllocatedResource = + FindAllocatedResource( pResource ); + if( pAllocatedResource == NULL ) + { + pAllocatedResource = new ( GetThisAllocator( ) ) daSoundAllocatedResource; + pAllocatedResource->AddRef( ); + pAllocatedResource->Initialize( pResource ); + + // Add it to our allocated resource manager + m_xIOL_AllocatedResources->AddObject ( + soundKey, + pAllocatedResource + ); + + // Release our version + pAllocatedResource->Release( ); + } + + rAssert( pAllocatedResource != NULL ); +} + +//============================================================================= +// Function: daSoundResourceManager::DeallocateResource +//============================================================================= +// Description: Allocate a resource +// +// Parameters: pResource - the resource to allocate +// +//----------------------------------------------------------------------------- + +void daSoundResourceManager::DeallocateResource +( + IDaSoundResource* pResource +) +{ + rAssert( pResource != NULL ); + + // + // Deallocate the resource + // + radKey32 soundKey = (radKey32) pResource; + + bool result = m_xIOL_AllocatedResources->RemoveObject( soundKey ); + rAssertMsg( result, "Resource deallocated prematurely?" ); +} + +//============================================================================= +// Function: daSoundResourceManager::FindResource +//============================================================================= +// Description: Find a resource. +// +// Parameters: resourceName - the name of the resource to find +// +// Returns: Returns a pointer to the appropriate resource or NULL if none +// found. +// +//----------------------------------------------------------------------------- + +IDaSoundResource* daSoundResourceManager::FindResource +( + daResourceName resourceName +) +{ + IDaSoundResource* pResource; + + pResource = reinterpret_cast< IDaSoundResource* >( + m_pSoundNamespace->GetInstance( resourceName ) ); + + if( ( pResource == NULL ) && ( m_secondaryNamespace != NULL ) ) + { + // Search secondary namespace + pResource = reinterpret_cast< IDaSoundResource* > + ( + m_secondaryNamespace->GetInstance( resourceName ) + ); + } + + return pResource; +} + +//============================================================================= +// Function: daSoundResourceManager::FindResource +//============================================================================= +// Description: Find a resource by its key. +// +// Parameters: resourceKey - the key for the resource to find +// +// Returns: Returns a pointer to the appropriate resource or NULL if none +// found. +// +//----------------------------------------------------------------------------- + +IDaSoundResource* daSoundResourceManager::FindResource +( + daResourceKey resourceKey +) +{ + IDaSoundResource* pResource; + + pResource = reinterpret_cast< IDaSoundResource* > + ( + m_pSoundNamespace->GetInstance( resourceKey ) + ); + if( ( pResource == NULL ) && ( m_secondaryNamespace != NULL ) ) + { + // Search secondary namespace + pResource = reinterpret_cast< IDaSoundResource* > + ( + m_secondaryNamespace->GetInstance( resourceKey ) + ); + } + + return pResource; +} + +//============================================================================= +// Function: daSoundResourceManager::FindAllocatedResource +//============================================================================= +// Description: Find an allocated resource +// +// Parameters: pResource - the resource associated with an allocated resource +// +// Note: Calling this command on a given resource is not guarenteed +// to always return the same allocated resource. In fact, +// it randomly choose which of the possible file variations +// it should use. +// +//----------------------------------------------------------------------------- + +daSoundAllocatedResource* daSoundResourceManager::FindAllocatedResource +( + IDaSoundResource* pResource +) +{ + rAssert( pResource != NULL ); + + // Generate a key based on the resource + radKey32 soundKey = (radKey32) pResource; + + // Find the allocated resource for the resource + daSoundAllocatedResource* pAllocatedResource = + reinterpret_cast< daSoundAllocatedResource* > + ( + m_xIOL_AllocatedResources->FindObject( soundKey ) + ); + + return pAllocatedResource; +} + +//============================================================================= +// Function: daSoundResourceManager::SetResourceLockdown +//============================================================================= +// Description: Set the lockdown state for resources +// +// Notes: When the resources are locked down, no new resources +// can be created. This is so that the tuner's wiring +// of the sound system can successively map all available +// resources to sound groups. +// +//----------------------------------------------------------------------------- + +void FlipSlashes( char * pString ) +{ + while (*pString != 0 ) + { + if ( *pString == '/' ) + { + *pString = '\\'; + } + pString++; + } +} + +void daSoundResourceManager::SetResourceLockdown( bool lockdown ) +{ + // Currently we may only lock down the resources + rAssert( lockdown == true ); + m_ResourceLockdown = lockdown; + + unsigned int totalFiles = 0; + + for( unsigned int r = 0; r < m_NumResourceDatas; r ++ ) + { + daSoundResourceData * pRd = this->m_ResourceData + r; + + totalFiles += pRd->m_NumFiles; + } + + m_pFileIdMemory = (radKey32*) radMemoryAlloc( GMA_PERSISTENT, sizeof( radKey32 ) * totalFiles ); + + radKey32* pCurrent = m_pFileIdMemory; + + for( unsigned int r = 0; r < MAX_SOUND_DATA_RESOURCES; r ++ ) + { + daSoundResourceData * pRd = this->m_ResourceData + r; + + for( unsigned int f = 0; f < pRd->m_NumFiles; f ++ ) + { + FlipSlashes( pRd->m_pFileIds[ f ].m_pName ); + + pCurrent[ f ] = ::radMakeCaseInsensitiveKey32( pRd->m_pFileIds[ f ].m_pName ); + radMemoryFree( pRd->m_pFileIds[ f ].m_pName ); + } + + radMemoryFree( pRd->m_pFileIds ); + pRd->m_pFileIds = (daSoundResourceData::FileId*)pCurrent; + pCurrent += pRd->m_NumFiles; + } +} + +//============================================================================= +// Function: daSoundResourceManager::GetResourceLockdown +//============================================================================= +// Description: Get the lockdown state for resources +//----------------------------------------------------------------------------- + +bool daSoundResourceManager::GetResourceLockdown( void ) +{ + return m_ResourceLockdown; +} + +//============================================================================= +// Function: daSoundResourceManager::GetLargestFileSize +//============================================================================= +// Description: Get the state of a resource +// +// Parameters: pResource - the resource to get the state of +// +// Returns: Returns the state of the resource +// +//----------------------------------------------------------------------------- + +unsigned int daSoundResourceManager::GetLargestFileSize +( + IDaSoundResource* pResource +) +{ + return 0; +} + +//============================================================================= +// Function: daSoundResourceManager::GetTotalSize +//============================================================================= +// Description: Get the state of a resource +// +// Parameters: pResource - the resource to get the state of +// +// Returns: Returns the state of the resource +// +//----------------------------------------------------------------------------- + +unsigned int daSoundResourceManager::GetTotalSize +( + IDaSoundResource* pResource +) +{ + return 0; +} + +//============================================================================= +// Function: daSoundResourceManager::GetSoundFileSize +//============================================================================= +// Description: Get debug information from resource manager +// +// Parameters: pFile - the file to find the size of +// +//----------------------------------------------------------------------------- + +unsigned int daSoundResourceManager::GetSoundFileSize +( + const char* filename +) +{ + // TODO : Don't use this unless it is not synchronous + rDebugString( "BAD SOUND FUNCTION BEING USED\n" ); + unsigned int fileSize = 0; + IRadFile* pMyFile = NULL; + ::radFileOpen( &pMyFile, filename ); + rAssert( pMyFile != NULL ); + pMyFile->GetSizeAsync( &fileSize ); + pMyFile->WaitForCompletion( ); + pMyFile->Release( ); + return fileSize; +} + +//============================================================================= +// Function: daSoundResourceManager::GetNumResources +//============================================================================= +// Description: Get the number of resources +// +//----------------------------------------------------------------------------- + +unsigned int daSoundResourceManager::GetNumResourceDatas( void ) +{ + return m_NumResourceDatas; +} + +//============================================================================= +// Function: daSoundResourceManager::GetNumAllocatedResources +//============================================================================= +// Description: Get the number of allocated resources +// +//----------------------------------------------------------------------------- + +unsigned int daSoundResourceManager::GetNumAllocatedResources( void ) +{ + return m_xIOL_AllocatedResources->GetSize( ); +} + +//============================================================================= +// daSoundResourceManager::SetActiveResource +//============================================================================= +// Description: Set a secondary namespace for us to search for resources. +// NOTE: a more robust implementation would have us storing a list +// of these things, but I know for Simpsons that we'll only need +// one and I want to keep the searches fast +// +// Parameters: activeNamespace - secondary namespace +// +// Return: void +// +//============================================================================= +void daSoundResourceManager::SetActiveResource( IRadNameSpace* activeNamespace ) +{ + if( activeNamespace != m_pSoundNamespace ) + { + m_secondaryNamespace = activeNamespace; + } +} + +//============================================================================= +// daSoundResourceManager::ReleaseActiveResource +//============================================================================= +// Description: Lose our reference to the secondary namespace, probably because +// the in-game resources are being released. +// +// Parameters: inactiveNamespace - only used to make sure we aren't releasing +// the main sound namespace +// +// Return: void +// +//============================================================================= +void daSoundResourceManager::ReleaseActiveResource( IRadNameSpace* inactiveNamespace ) +{ + if( inactiveNamespace != m_pSoundNamespace ) + { + m_secondaryNamespace = NULL; + } +} + +daSoundResourceData * daSoundResourceManager::GetResourceDataAt( unsigned int i ) +{ + rAssert( i < m_NumResourceDatas ); + + return m_ResourceData + i; +} + +daSoundResourceData * daSoundResourceManager::CreateResourceData( void ) +{ + daSoundResourceManager * pThis = daSoundResourceManager::GetInstance( ); + + rAssert( false == pThis->m_ResourceLockdown ); + + rAssert( pThis->m_NumResourceDatas < MAX_SOUND_DATA_RESOURCES ); + + daSoundResourceData * pRd = pThis->m_ResourceData + pThis->m_NumResourceDatas; + + pThis->m_NumResourceDatas++; + + return pRd; +} + +} // Sound Namespace + diff --git a/game/code/sound/soundrenderer/soundresourcemanager.h b/game/code/sound/soundrenderer/soundresourcemanager.h new file mode 100644 index 0000000..3a290c1 --- /dev/null +++ b/game/code/sound/soundrenderer/soundresourcemanager.h @@ -0,0 +1,164 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundresourcemanager.hpp +// +// Subsystem: Dark Angel - Sound Resource Management System +// +// Description: Description of the DA sound resource manager +// +// Revisions: +// + Created October 11, 2001 -- breimer +// +//============================================================================= + +#ifndef _SOUNDRESOURCEMANAGER_HPP +#define _SOUNDRESOURCEMANAGER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> + +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundresource.h> + +//============================================================================= +// Global namespace forward declarations +//============================================================================= + +class radObjectBTree; + +//============================================================================= +// Namespace +//============================================================================= + +namespace Sound { + +const unsigned int MAX_SOUND_DATA_RESOURCES = 5000; + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundResourceManager; + +//============================================================================= +// Forward declarations +//============================================================================= + +class daSoundAllocatedResource; + +//============================================================================= +// Class Declarations +//============================================================================= + +// +// The sound player manager passes information into the various sound +// players. It also keeps track of these players, gives them to people +// who ask for them, and makes sure they do not cause to much trouble :) +// +class daSoundResourceManager : public IRefCount, + public radRefCount +{ +public: + IMPLEMENT_REFCOUNTED( "daSoundResourceManager" ); + + // + // Constructor and destructor + // + daSoundResourceManager( void ); + + virtual ~daSoundResourceManager( ); + + inline static daSoundResourceManager* GetInstance( void ); + + // Controlled by the resource data + void AllocateResource( IDaSoundResource* pResource ); + void DeallocateResource( IDaSoundResource* pResource ); + + // Resource lockdown + void SetResourceLockdown( bool lockdown ); + + // Allocated resources + daSoundAllocatedResource* FindAllocatedResource + ( + IDaSoundResource* pResource + ); + + // Get a sound file's size + unsigned int GetSoundFileSize + ( + const char* filename + ); + + // + // IDaSoundResourceManager + // + IDaSoundResource* FindResource( + daResourceName resourceName ); + + IDaSoundResource* FindResource( + daResourceKey resourceKey ); + + unsigned int GetLargestFileSize( IDaSoundResource* pResource ); + unsigned int GetTotalSize( IDaSoundResource* pResource ); + + + bool GetResourceLockdown( void ); + + void SetActiveResource( IRadNameSpace* activeNamespace ); + void ReleaseActiveResource( IRadNameSpace* inactiveNamespace ); + + unsigned int GetNumResourceDatas( void ); + daSoundResourceData* GetResourceDataAt( unsigned int ); + static daSoundResourceData* CreateResourceData( void ); + +protected: + // + // Calculate some debug info + // + + unsigned int GetNumAllocatedResources( ); + +private: + // This is a singleton + static daSoundResourceManager* s_pSingleton; + + // + // Store the sound namespace + // + IRadNameSpace* m_pSoundNamespace; + + // + // Store all allocated resources (referenced by the resource's + // address cast to a radkey) + // + ref< radObjectBTree > m_xIOL_AllocatedResources; + + // + // Are the resources locked down? + // + bool m_ResourceLockdown; + + // + // Store active secondary namespace + // + IRadNameSpace* m_secondaryNamespace; + + daSoundResourceData m_ResourceData[ MAX_SOUND_DATA_RESOURCES ]; + unsigned int m_NumResourceDatas; + + radKey32 * m_pFileIdMemory; +}; + +inline daSoundResourceManager* daSoundResourceManager::GetInstance( void ) +{ + return s_pSingleton; +} + +} // Sound Namespace +#endif //_SOUNDRESOURCEMANAGER_HPP + + diff --git a/game/code/sound/soundrenderer/soundsystem.h b/game/code/sound/soundrenderer/soundsystem.h new file mode 100644 index 0000000..b0557ff --- /dev/null +++ b/game/code/sound/soundrenderer/soundsystem.h @@ -0,0 +1,118 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundsystem.hpp +// +// Subsystem: Dark Angel - Sound System +// +// Description: General declarations relating to the Dark Angel sound system +// +// Notes: This is the only file that may be loaded by non sound related +// components of the game. +// +// Revisions: October 2, 2001 Creation BJR +// +//============================================================================= + +#ifndef _SOUNDSYSTEM_HPP +#define _SOUNDSYSTEM_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radkey.hpp> + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +class daSoundRenderingManager; + +//============================================================================= +// Constants, Definitions and Macros +//============================================================================= + +// The sound memory allocator +extern radMemoryAllocator DAMEMORY_ALLOC_SOUND; + +// One aux send channels will be used for spacial effects +#define DA_SPACIAL_EFFECT_AUX_SEND 0 +#define DA_MISC_AUX_SEND 1 + +//============================================================================= +// Prototypes +//============================================================================= + +struct IDaSoundPlayerState; +struct IDaSoundFadeState; +struct IDaSoundSoundState; + +//============================================================================= +// Typedefs and Enumerations +//============================================================================= + +// +// This is a sound resource name and key +// +typedef const char* daResourceName; +typedef radKey32 daResourceKey; + +// +// These are the sound values +// +typedef float daPitchValue; +typedef float daTrimValue; + +//============================================================================= +// Interfaces +//============================================================================= + +// +// Sound player state +// +struct IDaSoundPlayerState : public IRefCount +{ + virtual void OnSoundReady( void* pData ) = 0; + virtual void OnSoundDone( void* pData ) = 0; +}; + +// +// Fade state +// +struct IDaSoundFadeState : public IRefCount +{ + virtual void OnFadeDone( void* pData ) = 0; +}; + +// +// A sound object +// +struct IDaSoundObject : public IRefCount +{ + // Left intentionally blank +}; + +//============================================================================= +// Public Functions +//============================================================================= + +// +// Set some global sound flags +// +void daSoundSetSoundOn( bool soundOn ); + +// +// Work indirectly with the sound manager +// +void daSoundRenderingManagerCreate( radMemoryAllocator allocator ); +daSoundRenderingManager* daSoundRenderingManagerGet( void ); +void daSoundRenderingManagerTerminate( void ); + +} // Namespace +#endif //_SOUNDSYSTEM_HPP + diff --git a/game/code/sound/soundrenderer/soundtuner.cpp b/game/code/sound/soundrenderer/soundtuner.cpp new file mode 100644 index 0000000..7aaed29 --- /dev/null +++ b/game/code/sound/soundrenderer/soundtuner.cpp @@ -0,0 +1,1241 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundtuner.cpp +// +// Subsystem: Dark Angel - Sound Tuner System +// +// Description: Implementation of the sound tuner +// +// Revisions: +// + Created October 4, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> +#include <radnamespace.hpp> + +#include <radsound.hpp> +#include <radsound_hal.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/idasoundresource.h> +#include <sound/soundrenderer/soundrenderingmanager.h> +#include <sound/soundrenderer/soundresourcemanager.h> +#include <sound/soundrenderer/playermanager.h> +#include <sound/soundrenderer/soundtuner.h> + +#include <sound/soundmanager.h> + +#include <memory/srrmemory.h> + +//============================================================================= +// Static Variables (outside namespace) +//============================================================================= + +short Sound::daSoundTuner::s_groupWirings[NUM_SOUND_GROUPS]; + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Class Implementations +//============================================================================= + +//============================================================================= +// daSoundTuner_ActiveFadeInfo Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundTuner_ActiveFadeInfo::daSoundTuner_ActiveFadeInfo +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundTuner_ActiveFadeInfo::daSoundTuner_ActiveFadeInfo +( + Fader* pFader, + bool fadingIn, + IDaSoundFadeState* pDoneCallback, + void* pCallbackUserData, + DuckVolumeSet* initialVolumes, + DuckVolumeSet* targetVolumes +) + : + m_pFader( pFader ), + m_FadingIn( fadingIn ), + m_pDoneCallback( pDoneCallback ), + m_pCallbackUserData( pCallbackUserData ) +{ + // Reference count some items + rAssert( m_pFader != NULL ); + + m_pFader->AddRef( ); + if( m_pDoneCallback != NULL ) + { + m_pDoneCallback->AddRef( ); + } + + // Invoke the fader + m_pFader->Fade( m_FadingIn, initialVolumes, targetVolumes ); +} + + +//============================================================================= +// Function: daSoundTuner_ActiveFadeInfo::~daSoundTuner_ActiveFadeInfo +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundTuner_ActiveFadeInfo::~daSoundTuner_ActiveFadeInfo( ) +{ + // Release our objects + rAssert( m_pFader != NULL ); + + m_pFader->Stop(); + m_pFader->Release( ); + if( m_pDoneCallback != NULL ) + { + m_pDoneCallback->Release( ); + } +} + +//============================================================================= +// Function: daSoundTuner_ActiveFadeInfo::ProcessFader +//============================================================================= +// Description: Process a fader's state progress +// +// Returns: true if we're done with the fader and are ready for destruction, +// false otherwise +// +//----------------------------------------------------------------------------- + +bool daSoundTuner_ActiveFadeInfo::ProcessFader() +{ + // Is the fader done fading yet? + bool doneFading = false; + switch( m_pFader->GetState( ) ) + { + case Fader::FadedIn: + case Fader::FadedOut: + { + // Must be done (what ever it was) + doneFading = true; + break; + } + case Fader::FadingIn: + { + // If we're supposed to be fading out, we must be done + if( !m_FadingIn ) + { + doneFading = true; + } + break; + } + case Fader::FadingOut: + { + // If we're supposed to be fading in, we must be done + if( m_FadingIn ) + { + doneFading = true; + } + break; + } + default: + rAssert( 0 ); + break; + } + + // If done, disconnect it, destroy it, and call its callback + if( doneFading ) + { + // Remember the callback + IDaSoundFadeState* pCallback = m_pDoneCallback; + void* pUserData = m_pCallbackUserData; + if( pCallback != NULL ) + { + pCallback->AddRef( ); + } + + // Call the callback + if( pCallback != NULL ) + { + pCallback->OnFadeDone( pUserData ); + pCallback->Release( ); + } + } + + return( doneFading ); +} + +void daSoundTuner_ActiveFadeInfo::StoreCurrentVolumes( DuckVolumeSet& volumeSet ) +{ + unsigned int i; + + rAssert( m_pFader != NULL ); + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + volumeSet.duckVolume[i] = m_pFader->GetCurrentVolume( static_cast<DuckVolumes>(i) ); + } +} + +void daSoundTuner_ActiveFadeInfo::StoreTargetSettings( DuckVolumeSet& volumeSet ) +{ + unsigned int i; + + rAssert( m_pFader != NULL ); + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + volumeSet.duckVolume[i] = m_pFader->GetTargetSettings( static_cast<DuckVolumes>(i) ); + } +} + +//============================================================================= +// daSoundTuner Implementation +//============================================================================= + +//============================================================================= +// Function: daSoundTuner::daSoundTuner +//============================================================================= +// Description: Constructor +// +//----------------------------------------------------------------------------- + +daSoundTuner::daSoundTuner( ) + : + radRefCount( 0 ), + m_pDuckFade( NULL ), + m_MasterVolume( 1.0f ), + m_activeFadeInfo( NULL ), + m_NISTrim( 1.0f ) +{ + daSoundPlayerManager* playerMgr; + int i, j; + + // + // The tuner makes use of several fader objects. These objects may be + // customized by scripts, so they must be added to the sound namespace. + // + + // Duck fader + playerMgr = daSoundRenderingManagerGet()->GetPlayerManager(); + rAssert( playerMgr != NULL ); + + m_pDuckFade = new( GetThisAllocator() ) Fader( NULL, DUCK_FULL_FADE, *playerMgr, *this ); + rAssert( m_pDuckFade != NULL ); + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + m_situationFaders[i] = NULL; + } + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + m_userVolumes.duckVolume[i] = 1.0f; + m_finalDuckLevels.duckVolume[i] = 1.0f; + } + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + for( j = 0; j < NUM_DUCK_VOLUMES; j++ ) + { + m_duckLevels[i].duckVolume[j] = 1.0f; + } + } + + // + // Initialize the sound group wirings (IMPORTANT: this assumes that + // daSoundTuner is a singleton, we only want to do this once) + // + for( i = 0; i < NUM_SOUND_GROUPS; i++ ) + { + s_groupWirings[i] = ( 1 << i ) | ( 1 << MASTER ); + } +} + +//============================================================================= +// Function: daSoundTuner::~daSoundTuner +//============================================================================= +// Description: Destructor +// +//----------------------------------------------------------------------------- + +daSoundTuner::~daSoundTuner( ) +{ + int i; + + // Release our duck faders + if( m_pDuckFade != NULL ) + { + m_pDuckFade->Release( ); + m_pDuckFade = NULL; + } + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + if( m_situationFaders[i] != NULL ) + { + m_situationFaders[i]->Release( ); + m_situationFaders[i] = NULL; + } + } + + if( m_activeFadeInfo != NULL ) + { + delete m_activeFadeInfo; + } +} + +//============================================================================= +// Function: daSoundTuner::Initialize +//============================================================================= +// Description: Initialize the sound tuner. This can only be done once +// resources have been locked down. +// +// Parameters: outputMode - the output mode desired +// +// Returns: n/a +// +// Notes: This class does not support re-initialization +// +//----------------------------------------------------------------------------- + +void daSoundTuner::Initialize( void ) +{ + // Make sure resources are locked down + //rAssert( Sound::daSoundRenderingManagerGet( )->GetResourceManager( )->GetResourceLockdown( ) ); + + // Generate each of the wiring groups + Sound::daSoundTunerWireSystem( this ); +} + +//============================================================================= +// daSoundTuner::PostScriptLoadInitialize +//============================================================================= +// Description: Comment +// +// Parameters: () +// +// Return: void +// +//============================================================================= +void daSoundTuner::PostScriptLoadInitialize() +{ + IRadNameSpace* nameSpace; + globalSettings* settingsObj; + daSoundPlayerManager* playerMgr; + int i; + + playerMgr = daSoundRenderingManagerGet()->GetPlayerManager(); + rAssert( playerMgr != NULL ); + + // + // Get the globalSettings object for Fader use + // + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + settingsObj = reinterpret_cast<globalSettings*>( nameSpace->GetInstance( "tuner" ) ); + rAssert( settingsObj != NULL ); + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + m_situationFaders[i] = new( GetThisAllocator() ) Fader( settingsObj, + static_cast<DuckSituations>(i), + *playerMgr, + *this ); + rAssert( m_situationFaders[i] != NULL ); + } + +#ifdef SOUND_DEBUG_INFO_ENABLED + m_debugPage.LazyInitialization( 4, GetSoundManager()->GetDebugDisplay() ); +#endif +} + +//============================================================================= +// Function: daSoundTuner::ServiceOncePerFrame +//============================================================================= +// Description: Service the sound tuner. This should be done once per frame. +// +// Parameters: none +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundTuner::ServiceOncePerFrame( unsigned int elapsedTime ) +{ + bool faderDone; + + // + // Process the faders + // + Fader::UpdateAllFaders( elapsedTime ); + + // + // Process the fade info stuff that monitors the faders (hmmm, this is + // looking like a pretty lightweight class these days. Candidate for + // removal?) + // + if( m_activeFadeInfo != NULL ) + { + faderDone = m_activeFadeInfo->daSoundTuner_ActiveFadeInfo::ProcessFader(); + + if( faderDone ) + { + delete m_activeFadeInfo; + m_activeFadeInfo = NULL; + } + } + + serviceDebugInfo(); +} + +//============================================================================= +// Function: daSoundTuner::SetSoundOutputMode +//============================================================================= +// Description: Set the sound system output mode (stereo, mono, surround) +// +// Parameters: outputMode - the output mode desired +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetSoundOutputMode +( + IDaSoundTuner::SoundOutputMode outputMode +) +{ + radSoundOutputMode rsdOutputMode = radSoundOutputMode_Stereo; + if( outputMode == MONO ) + { + rsdOutputMode = radSoundOutputMode_Mono; + } + else if( outputMode == STEREO ) + { + rsdOutputMode = radSoundOutputMode_Stereo; + } + else if( outputMode == SURROUND ) + { + rsdOutputMode = radSoundOutputMode_Surround; + } + else + { + rAssertMsg( 0, "Invalid sound output mode" ); + } + + ::radSoundHalSystemGet( )->SetOutputMode( rsdOutputMode ); +} + +//============================================================================= +// Function: daSoundTuner::GetSoundOutputMode +//============================================================================= +// Description: Get the sound system output mode (stereo, mono, surround) +// +// Parameters: none +// +// Returns: Returns the current sound output mode +// +//----------------------------------------------------------------------------- + +IDaSoundTuner::SoundOutputMode daSoundTuner::GetSoundOutputMode +( + void +) +{ + IDaSoundTuner::SoundOutputMode outputMode = STEREO; + radSoundOutputMode rsdOutputMode = + ::radSoundHalSystemGet( )->GetOutputMode( ); + if( rsdOutputMode == radSoundOutputMode_Mono ) + { + outputMode = MONO; + } + else if( rsdOutputMode == radSoundOutputMode_Stereo ) + { + outputMode = STEREO; + } + else if( rsdOutputMode == radSoundOutputMode_Surround ) + { + outputMode = SURROUND; + } + else + { + rAssertMsg( 0, "Unrecognized sound output mode" ); + } + + return outputMode; +} + +//============================================================================= +// Function: daSoundTuner::ActivateDuck +//============================================================================= +// Description: Start/stop ducking of sounds +// +// Parameters: pObject - the object to receive fade state events +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundTuner::ActivateDuck +( + IDaSoundFadeState* pObject, + void* pUserData, + bool fadeIn +) +{ + activateDuckInternal( pObject, pUserData, fadeIn, m_pDuckFade ); +} + +//============================================================================= +// Function: daSoundTuner::StartSituationalDuck +//============================================================================= +// Description: Start ducking of sounds +// +// Parameters: pObject - the object to receive fade state events +// +// Returns: n/a +// +//----------------------------------------------------------------------------- + +void daSoundTuner::ActivateSituationalDuck( IDaSoundFadeState* pObject, + DuckSituations situation, + void* pUserData, + bool fadeIn ) +{ + activateDuckInternal( pObject, pUserData, fadeIn, m_situationFaders[situation] ); +} + +//============================================================================= +// daSoundTuner::ResetDuck +//============================================================================= +// Description: Stop all ducking +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void daSoundTuner::ResetDuck() +{ + unsigned int i, j; + + // + // Return all the duck values to max, then do a fade in + // + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + for( j = 0; j < NUM_DUCK_SITUATIONS; j++ ) + { + m_duckLevels[j].duckVolume[i] = 1.0f; + } + } + + activateDuckInternal( NULL, NULL, true, m_pDuckFade ); +} + +//============================================================================= +// Function: daSoundTuner::SetMasterVolume +//============================================================================= +// Description: Set the master volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetMasterVolume +( + daTrimValue volume +) +{ + m_MasterVolume = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( MASTER, m_MasterVolume ); +} + +//============================================================================= +// Function: daSoundTuner::GetMasterVolume +//============================================================================= +// Description: Get the master volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetMasterVolume( void ) +{ + return( m_MasterVolume ); +} + +//============================================================================= +// Function: daSoundTuner::SetDialogueVolume +//============================================================================= +// Description: Set the dialogue volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetDialogueVolume +( + daTrimValue volume +) +{ + m_userVolumes.duckVolume[DUCK_DIALOG] = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( DIALOGUE, volume ); +} + +//============================================================================= +// Function: daSoundTuner::GetDialogueVolume +//============================================================================= +// Description: Get the dialogue volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetDialogueVolume( void ) +{ + return( m_userVolumes.duckVolume[DUCK_DIALOG] ); +} + +//============================================================================= +// Function: daSoundTuner::SetMusicVolume +//============================================================================= +// Description: Set the music volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetMusicVolume +( + daTrimValue volume +) +{ + m_userVolumes.duckVolume[DUCK_MUSIC] = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( MUSIC, volume ); +} + +//============================================================================= +// Function: daSoundTuner::GetMusicVolume +//============================================================================= +// Description: Get the music volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetMusicVolume( void ) +{ + return( m_userVolumes.duckVolume[DUCK_MUSIC] ); +} + +//============================================================================= +// Function: daSoundTuner::SetAmbienceVolume +//============================================================================= +// Description: Set the music volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetAmbienceVolume +( + daTrimValue volume +) +{ + m_userVolumes.duckVolume[DUCK_AMBIENCE] = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( AMBIENCE, volume ); +} + +//============================================================================= +// Function: daSoundTuner::GetAmbienceVolume +//============================================================================= +// Description: Get the music volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetAmbienceVolume( void ) +{ + return( m_userVolumes.duckVolume[DUCK_AMBIENCE] ); +} + +//============================================================================= +// Function: daSoundTuner::SetSfxVolume +//============================================================================= +// Description: Set the sound effects volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetSfxVolume +( + daTrimValue volume +) +{ + m_userVolumes.duckVolume[DUCK_SFX] = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( SOUND_EFFECTS, volume ); +} + +//============================================================================= +// Function: daSoundTuner::GetSfxVolume +//============================================================================= +// Description: Get the sound effects volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetSfxVolume( void ) +{ + return( m_userVolumes.duckVolume[DUCK_SFX] ); +} + +//============================================================================= +// Function: daSoundTuner::SetCarVolume +//============================================================================= +// Description: Set the sound effects volume +// +//----------------------------------------------------------------------------- + +void daSoundTuner::SetCarVolume( daTrimValue volume ) +{ + m_userVolumes.duckVolume[DUCK_CAR] = volume; + + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( CARSOUND, volume ); +} + +//============================================================================= +// Function: daSoundTuner::GetCarVolume +//============================================================================= +// Description: Get the sound effects volume +// +//----------------------------------------------------------------------------- + +daTrimValue daSoundTuner::GetCarVolume( void ) +{ + return( m_userVolumes.duckVolume[DUCK_CAR] ); +} + +//============================================================================= +// Function: daSoundTuner::FadeSounds +//============================================================================= +// Description: Fade a particular group of sounds +// +// Parameters: pKnob - the knob to fade +// pObject - the fade state change object +// pUserData - user data for the state change callback +// pFader - a pointer to an actual fader to use +// fadeIn - true if we are fading in +// +//----------------------------------------------------------------------------- + +void daSoundTuner::FadeSounds +( + IDaSoundFadeState* pObject, + void* pUserData, + Fader* pFader, + bool fadeIn, + DuckVolumeSet* initialVolumes +) +{ + unsigned int i; + DuckVolumeSet currentVolumes; + DuckVolumeSet* initVolumePtr = initialVolumes; + DuckVolumeSet targetVolumes; + + rAssert( m_activeFadeInfo == NULL ); + rAssert( pFader != NULL ); + if( pFader == NULL ) + { + return; + } + + HeapMgr()->PushHeap( static_cast<GameMemoryAllocator>(GetThisAllocator()) ); + + // + // Store the intended targets for this fader. + // + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + if( fadeIn ) + { + m_duckLevels[pFader->GetSituation()].duckVolume[i] = 1.0f; + } + else + { + m_duckLevels[pFader->GetSituation()].duckVolume[i] = + pFader->GetTargetSettings( static_cast<Sound::DuckVolumes>(i) ); + } + } + + if( initVolumePtr == NULL ) + { + // + // Initial volumes aren't supplied, so use the current settings + // as we've recorded them here + // + initVolumePtr = ¤tVolumes; + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + currentVolumes.duckVolume[i] = m_userVolumes.duckVolume[i]; + } + } + + calculateDuckedVolumes( targetVolumes ); + + // Create the temporary fade object. It will destroy itself when done + m_activeFadeInfo = new daSoundTuner_ActiveFadeInfo + ( + pFader, + fadeIn, + pObject, + pUserData, + initialVolumes, + &targetVolumes + ); + + HeapMgr()->PopHeap( static_cast<GameMemoryAllocator>( GetThisAllocator() ) ); +} + + +//============================================================================= +// Function: daSoundTuner::WireKnobToPathHelper +//============================================================================= +// Description: Helper function to wire a knob to a path for a resource. +// This function is automattically called on every +// sound resource. If the path matches that of a file +// in the resource, the resource's sound group is automattically +// wired to the sound group specified by the user data. +// +// Notes: Trying to put a resource in more than one group will not work! +// If a resource contains files from more than one fundamental +// path and its wiring must reflect this, then a new +// allocation stratagy should be used. +// +//----------------------------------------------------------------------------- + +void daSoundTuner::WireKnobToPathHelper +( + IDaSoundResource* pRes, + void* pUserData +) +{ + char filenameBuffer[256]; + + // Get the wiring info + WirePathInfo* pInfo = (WirePathInfo*)pUserData; + + unsigned int i = 0; + for( i = 0; i < pRes->GetNumFiles( ); i++ ) + { + pRes->GetFileNameAt( i, filenameBuffer, 256 ); + unsigned int j = 0; + bool match = true; + for( j = 0; j < pInfo->m_PathLen; j++ ) + { + char a = filenameBuffer[j]; + char b = pInfo->m_Path[j]; + + if( a == '/' ) + { + a = '\\'; + } + if( b == '/' ) + { + b = '\\'; + } + if( a != b ) + { + // Notice that this supports the case when the length + // of the filename is less than the test path because, + // in that case, the '\0' character won't match + match = false; + break; + } + } + if( match ) + { + pRes->SetSoundGroup( pInfo->m_SoundGroup ); + break; + } + } +} + +//============================================================================= +// Function: daSoundTuner::WirePath +//============================================================================= +// Description: Wire a path to a particular sound group +// +//----------------------------------------------------------------------------- + +void daSoundTuner::WirePath +( + daSoundGroup soundGroup, + const char* path +) +{ + // Find all resource that are in this path and relate them to this knob + WirePathInfo wirePathInfo; + wirePathInfo.m_Path = path; + wirePathInfo.m_PathLen = strlen( wirePathInfo.m_Path ); + wirePathInfo.m_SoundGroup = soundGroup; + + unsigned int numResources = + daSoundResourceManager::GetInstance( )->GetNumResourceDatas( ); + + for( unsigned int r = 0; r < numResources; r ++ ) + { + WireKnobToPathHelper( + daSoundResourceManager::GetInstance( )->GetResourceDataAt( r ), + & wirePathInfo ); + } +} + +//============================================================================= +// daSoundTuner::WireGroup +//============================================================================= +// Description: Mark a sound group as changing in sync with the master group +// +// Parameters: slaveGroup - slave that changes with master group +// masterGroup - controller group +// +// Return: void +// +//============================================================================= +void daSoundTuner::WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup ) +{ + unsigned int i; + + for( i = 0; i < NUM_SOUND_GROUPS; i++ ) + { + if( s_groupWirings[i] & ( 1 << slaveGroup ) ) + { + s_groupWirings[i] |= 1 << masterGroup; + } + } +} + +//============================================================================= +// daSoundTuner::IsSlaveGroup +//============================================================================= +// Description: Indicates whether one group is slaved to another +// +// Parameters: slave - proposed slave sound group +// master - proposed master sound group +// +// Return: True if slave is affected by master, false otherwise +// +//============================================================================= +bool daSoundTuner::IsSlaveGroup( daSoundGroup slave, daSoundGroup master ) +{ + if( s_groupWirings[slave] & ( 1 << master ) ) + { + return( true ); + } + + return( false ); +} + +//============================================================================= +// daSoundTuner::GetGroupTrim +//============================================================================= +// Description: Get the trim associated with a particular sound group. +// Actually, our "group" is currently one of four settings, +// even though we break it down more in daSoundGroup for +// future expansion. +// +// Parameters: group - group that we want trim for +// +// Return: trim value for that group, or 1.0f (max) if it doesn't fit +// +//============================================================================= +daTrimValue daSoundTuner::GetGroupTrim( daSoundGroup group ) +{ + if( IsSlaveGroup( group, SOUND_EFFECTS ) ) + { + return( m_userVolumes.duckVolume[DUCK_SFX] ); + } + else if( IsSlaveGroup( group, MUSIC ) ) + { + return( m_userVolumes.duckVolume[DUCK_MUSIC] ); + } + else if( IsSlaveGroup( group, DIALOGUE ) ) + { + return( m_userVolumes.duckVolume[DUCK_DIALOG] ); + } + else if( IsSlaveGroup( group, AMBIENCE ) ) + { + return( m_userVolumes.duckVolume[DUCK_AMBIENCE] ); + } + else if( IsSlaveGroup( group, CARSOUND ) ) + { + return( m_userVolumes.duckVolume[DUCK_CAR] ); + } + else if( IsSlaveGroup( group, OPTIONS_MENU_STINGERS ) ) + { + // + // Special group for options menu, not affected by ducking + // + return( 1.0f ); + } + else + { + // + // None of the above. We shouldn't get here + // + rAssert( false ); + return( 1.0f ); + } +} + +//============================================================================= +// daSoundTuner::SetFaderGroupTrim +//============================================================================= +// Description: Comment +// +// Parameters: ( Sound::DuckVolumes group, daTrimValue trim ) +// +// Return: void +// +//============================================================================= +void daSoundTuner::SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim ) +{ + m_finalDuckLevels.duckVolume[group] = trim; +} + +//============================================================================= +// daSoundTuner::GetFaderGroupTrim +//============================================================================= +// Description: Get the fader trim associated with a particular sound group. +// Actually, our "group" is currently one of four settings, +// even though we break it down more in daSoundGroup for +// future expansion. +// +// Parameters: group - group that we want trim for +// +// Return: trim value for that group, or 1.0f (max) if it doesn't fit +// +//============================================================================= +daTrimValue daSoundTuner::GetFaderGroupTrim( daSoundGroup group ) +{ + if( IsSlaveGroup( group, SOUND_EFFECTS ) ) + { + return( m_finalDuckLevels.duckVolume[DUCK_SFX] ); + } + else if( IsSlaveGroup( group, MUSIC ) ) + { + return( m_finalDuckLevels.duckVolume[DUCK_MUSIC] ); + } + else if( IsSlaveGroup( group, DIALOGUE ) ) + { + return( m_finalDuckLevels.duckVolume[DUCK_DIALOG] ); + } + else if( IsSlaveGroup( group, AMBIENCE ) ) + { + return( m_finalDuckLevels.duckVolume[DUCK_AMBIENCE] ); + } + else if( IsSlaveGroup( group, CARSOUND ) ) + { + return( m_finalDuckLevels.duckVolume[DUCK_CAR] ); + } + else if( IsSlaveGroup( group, OPTIONS_MENU_STINGERS ) ) + { + // + // Special group for options menu, not affected by ducking + // + return( 1.0f ); + } + else + { + // + // None of the above. We shouldn't get here + // + rAssert( false ); + return( 1.0f ); + } +} + +void daSoundTuner::MuteNIS() +{ + m_NISTrim = GetGroupTrim( NIS ); + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( NIS, 0.0f ); +} + +void daSoundTuner::UnmuteNIS() +{ + daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( NIS, m_NISTrim ); +} + +//============================================================================= +// Private functions +//============================================================================= + +void daSoundTuner::activateDuckInternal( IDaSoundFadeState* pObject, + void* pUserData, + bool fadeOut, + Fader* faderObj ) +{ + DuckVolumeSet currentVolumes; + +#ifndef RAD_RELEASE + // + // Hack for fader tuning. The tuners usually only read the fader + // settings at startup, but we want them to pick up changes in radTuner + // on the fly. We'll make it refresh on each duck attempt in debug + // and tune builds. + // + refreshFaderSettings(); +#endif + + if( m_activeFadeInfo != NULL ) + { + // + // Get the current fader's settings and throw it on the stack + // + m_activeFadeInfo->StoreCurrentVolumes( currentVolumes ); + + // + // Now we can get rid of the old fader + // + delete m_activeFadeInfo; + m_activeFadeInfo = NULL; + } + else + { + calculateDuckedVolumes( currentVolumes ); + } + + // Use our duck fader... + FadeSounds( pObject, + pUserData, + faderObj, + fadeOut, + ¤tVolumes ); +} + +//============================================================================= +// daSoundTuner::calculateDuckedVolumes +//============================================================================= +// Description: Comment +// +// Parameters: ( DuckVolumeSet& volumes ) +// +// Return: void +// +//============================================================================= +void daSoundTuner::calculateDuckedVolumes( DuckVolumeSet& volumes ) +{ + unsigned int i, j; + + // + // Calculate target volumes + // + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + volumes.duckVolume[i] = 1.0f; + } + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + for( j = 0; j < NUM_DUCK_SITUATIONS; j++ ) + { + if( m_duckLevels[j].duckVolume[i] < volumes.duckVolume[i] ) + { + volumes.duckVolume[i] = m_duckLevels[j].duckVolume[i]; + } + } + } +} + +//============================================================================= +// daSoundTuner::refreshFaderSettings +//============================================================================= +// Description: Reset the duck settings for each fader +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void daSoundTuner::refreshFaderSettings() +{ + IRadNameSpace* nameSpace; + globalSettings* settingsObj; + unsigned int i; + + nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace(); + rAssert( nameSpace != NULL ); + + settingsObj = reinterpret_cast<globalSettings*>( nameSpace->GetInstance( "tuner" ) ); + rAssert( settingsObj != NULL ); + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + m_situationFaders[i]->ReinitializeFader( settingsObj ); + } +} + +//============================================================================= +// daSoundTuner::serviceDebugInfo +//============================================================================= +// Description: Send some debug stuff to be dumped on the screen +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void daSoundTuner::serviceDebugInfo() +{ +#ifdef SOUND_DEBUG_INFO_ENABLED + unsigned int i; + unsigned int j; + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + for( j = 0; j < NUM_DUCK_VOLUMES; j++ ) + { + m_debugPage.SetDuckLevel( static_cast<Sound::DuckSituations>(i), + static_cast<Sound::DuckVolumes>(j), + m_duckLevels[i].duckVolume[j] ); + } + } + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + m_debugPage.SetFinalDuckLevel( static_cast<Sound::DuckVolumes>(i), m_finalDuckLevels.duckVolume[i] ); + } + + for( i = 0; i < NUM_DUCK_VOLUMES; i++ ) + { + m_debugPage.SetUserVolume( static_cast<Sound::DuckVolumes>(i), m_userVolumes.duckVolume[i] ); + } +#endif +} + +//============================================================================= +// Factory functions +//============================================================================= + +//============================================================================= +// Function: daSoundTunerCreate +//============================================================================= +// Description: Create the tuner +// +//----------------------------------------------------------------------------- + +void daSoundTunerCreate +( + IDaSoundTuner** ppTuner, + radMemoryAllocator allocator +) +{ + rAssert( ppTuner != NULL ); + (*ppTuner) = new ( allocator ) daSoundTuner( ); + (*ppTuner)->AddRef( ); +} + +} // Sound Namespace diff --git a/game/code/sound/soundrenderer/soundtuner.h b/game/code/sound/soundrenderer/soundtuner.h new file mode 100644 index 0000000..93aa9f9 --- /dev/null +++ b/game/code/sound/soundrenderer/soundtuner.h @@ -0,0 +1,253 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: soundtuner.hpp +// +// Subsystem: Dark Angel - Sound Tuner System +// +// Description: Description of the DA sound tuner +// +// Revisions: +// + Created October 4, 2001 -- breimer +// +//============================================================================= + +#ifndef _SOUNDTUNER_HPP +#define _SOUNDTUNER_HPP + +//============================================================================= +// Included Files +//============================================================================= + +#include <raddebug.hpp> +#include <radlinkedclass.hpp> + +#include <sound/soundrenderer/dasoundgroup.h> + +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/fader.h> +#include <sound/soundrenderer/tunerdebugpage.h> + +//============================================================================= +// Global namespace forward declarations +//============================================================================= + +struct IDaSoundResource; + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Prototypes +//============================================================================= + +class daSoundTuner; + +//============================================================================= +// Forward declarations +//============================================================================= + +//============================================================================= +// Class Declarations +//============================================================================= + +// +// A fade state information structure stores necessary information about +// an active fade. +// +class daSoundTuner_ActiveFadeInfo +{ +public: + + // Constructor and destructor + daSoundTuner_ActiveFadeInfo + ( + Fader* pFader, + bool pFadingIn, + IDaSoundFadeState* pDoneCallback, + void* pCallbackUserData, + DuckVolumeSet* initialVolumes, + DuckVolumeSet* targetVolumes + ); + virtual ~daSoundTuner_ActiveFadeInfo( ); + + // Process the fader + bool ProcessFader(); + + void StoreCurrentVolumes( DuckVolumeSet& volumeSet ); + void StoreTargetSettings( DuckVolumeSet& volumeSet ); + + Sound::DuckSituations GetSituation() { return( m_pFader->GetSituation() ); } + +private: + // Store the raw data + Fader* m_pFader; + bool m_FadingIn; + IDaSoundFadeState* m_pDoneCallback; + void* m_pCallbackUserData; +}; + +// +// The sound tuner. All controls associated with the tuner have +// a scripted sister componenet available for composers to modify +// using radtuner. +// +class daSoundTuner : public IDaSoundTuner, + public radRefCount +{ +public: + IMPLEMENT_REFCOUNTED( "daSoundTuner" ); + + // + // Constructor and destructor + // + daSoundTuner( ); + virtual ~daSoundTuner( ); + + // + // IDaSoundTuner + // + void Initialize( void ); + void PostScriptLoadInitialize(); + void ServiceOncePerFrame( unsigned int elapsedTime ); + + void SetSoundOutputMode + ( + SoundOutputMode outputMode + ); + SoundOutputMode GetSoundOutputMode( void ); + + void ActivateDuck + ( + IDaSoundFadeState* pObject, + void* pUserData, + bool fadeIn + ); + + void ActivateSituationalDuck( IDaSoundFadeState* pObject, + DuckSituations situation, + void* pUserData, + bool fadeIn ); + + void ResetDuck(); + + void SetMasterVolume( daTrimValue volume ); + daTrimValue GetMasterVolume( void ); + + void SetDialogueVolume( daTrimValue volume ); + daTrimValue GetDialogueVolume( void ); + + void SetMusicVolume( daTrimValue volume ); + daTrimValue GetMusicVolume( void ); + + void SetAmbienceVolume( daTrimValue volume ); + daTrimValue GetAmbienceVolume( void ); + + void SetSfxVolume( daTrimValue volume ); + daTrimValue GetSfxVolume( void ); + + void SetCarVolume( daTrimValue volume ); + daTrimValue GetCarVolume( void ); + + daTrimValue GetGroupTrim( daSoundGroup group ); + daTrimValue GetFaderGroupTrim( daSoundGroup group ); + + void MuteNIS(); + void UnmuteNIS(); + + void SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim ); + + void FadeSounds( IDaSoundFadeState* pObject, + void* pUserData, + Fader* pFader, + bool fadeIn, + DuckVolumeSet* initialVolumes = NULL ); + + // + // IDaSoundWiring + // + void WirePath + ( + daSoundGroup soundGroup, + const char* path + ); + + void WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup ); + + // + // Sound group info + // + bool IsSlaveGroup( daSoundGroup slave, daSoundGroup master ); + +protected: + // + // Helper function for wiring knob paths + // + struct WirePathInfo + { + const char* m_Path; + size_t m_PathLen; + daSoundGroup m_SoundGroup; + }; + static void WireKnobToPathHelper + ( + IDaSoundResource* pRes, + void* pUserData + ); + +private: + + void activateDuckInternal( IDaSoundFadeState* pObject, + void* pUserData, + bool fadeOut, + Fader* faderObj ); + void calculateDuckedVolumes( DuckVolumeSet& volumes ); + + void refreshFaderSettings(); + + void serviceDebugInfo(); + +#ifdef SOUND_DEBUG_INFO_ENABLED + TunerDebugPage m_debugPage; +#endif + + // + // How many ducks to we have in progress? + // + DuckVolumeSet m_duckLevels[NUM_DUCK_SITUATIONS]; + + DuckVolumeSet m_finalDuckLevels; + + // + // Store our duck faders + // + Fader* m_pDuckFade; + + Fader* m_situationFaders[NUM_DUCK_SITUATIONS]; + + // + // Group volume settings + // + float m_MasterVolume; + + DuckVolumeSet m_userVolumes; + + // + // Our static sound group wirings + // + static short s_groupWirings[NUM_SOUND_GROUPS]; + + daSoundTuner_ActiveFadeInfo* m_activeFadeInfo; + + // + // NIS hack + // + float m_NISTrim; +}; + +} // Sound Namespace +#endif //_SOUNDTUNER_HPP + diff --git a/game/code/sound/soundrenderer/tunerdebugpage.cpp b/game/code/sound/soundrenderer/tunerdebugpage.cpp new file mode 100644 index 0000000..a792952 --- /dev/null +++ b/game/code/sound/soundrenderer/tunerdebugpage.cpp @@ -0,0 +1,231 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: tunerdebugpage.cpp +// +// Description: Displays debug info for the sound tuner +// +// History: 6/11/2003 + Created -- NAME +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundrenderer/tunerdebugpage.h> + +const char* s_duckNames[Sound::NUM_DUCK_SITUATIONS] = +{ + "Full fade", + "Pause", + "Mission", + "Letterbox", + "Dialogue", + "Store", + "On foot", + "Minigame", + "Just Music", + "Credits" +}; + +//***************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//***************************************************************************** + +//***************************************************************************** +// +// Public Member Functions +// +//***************************************************************************** + +//============================================================================= +// TunerDebugPage::TunerDebugPage +//============================================================================= +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +TunerDebugPage::TunerDebugPage() +{ + unsigned int i, j; + + // + // Zero out the tuner values + // + for( i = 0; i < Sound::NUM_DUCK_SITUATIONS; i++ ) + { + for( j = 0; j < Sound::NUM_DUCK_VOLUMES; j++ ) + { + m_duckLevels[i].duckVolume[j] = 0.0f; + } + } + + for( i = 0; i < Sound::NUM_DUCK_VOLUMES; i++ ) + { + m_userVolumes.duckVolume[i] = 0.0f; + } +} + +//============================================================================= +// TunerDebugPage::~TunerDebugPage +//============================================================================= +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================= +TunerDebugPage::~TunerDebugPage() +{ +} + +//============================================================================= +// TunerDebugPage::SetDuckLevel +//============================================================================= +// Description: Comment +// +// Parameters: ( DuckSituations situation, DuckVolumes volumeType, float volume ) +// +// Return: void +// +//============================================================================= +void TunerDebugPage::SetDuckLevel( Sound::DuckSituations situation, + Sound::DuckVolumes volumeType, + float volume ) +{ + m_duckLevels[situation].duckVolume[volumeType] = volume; +} + +//============================================================================= +// TunerDebugPage::SetFinalDuckLevel +//============================================================================= +// Description: Comment +// +// Parameters: ( DuckVolumes volumeType, float volume ) +// +// Return: void +// +//============================================================================= +void TunerDebugPage::SetFinalDuckLevel( Sound::DuckVolumes volumeType, float volume ) +{ + m_finalDuckLevel.duckVolume[volumeType] = volume; +} + +//============================================================================= +// TunerDebugPage::SetUserVolume +//============================================================================= +// Description: Comment +// +// Parameters: ( DuckVolumes volumeType, float volume ) +// +// Return: void +// +//============================================================================= +void TunerDebugPage::SetUserVolume( Sound::DuckVolumes volumeType, float volume ) +{ + m_userVolumes.duckVolume[volumeType] = volume; +} + +//***************************************************************************** +// +// Protected Member Functions +// +//***************************************************************************** + +//============================================================================= +// TunerDebugPage::fillLineBuffer +//============================================================================= +// Description: Fill the given buffer with text to display on the screen +// at the given line +// +// Parameters: lineNum - line number on screen where buffer will be displayed +// buffer - to be filled in with text to display +// +// Return: void +// +//============================================================================= +void TunerDebugPage::fillLineBuffer( int lineNum, char* buffer ) +{ + switch( lineNum ) + { + case 0: + strcpy( buffer, "Ducking volumes:" ); + break; + + case 1: + strcpy( buffer, " SFX Car Music Dialog Ambience" ); + break; + + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + sprintf( buffer, "%s: %0.2f %0.2f %0.2f %0.2f %0.2f", + s_duckNames[lineNum-2], + m_duckLevels[lineNum-2].duckVolume[0], + m_duckLevels[lineNum-2].duckVolume[1], + m_duckLevels[lineNum-2].duckVolume[2], + m_duckLevels[lineNum-2].duckVolume[3], + m_duckLevels[lineNum-2].duckVolume[4] ); + break; + + case 13: + sprintf( buffer, "Final duck volumes: %0.2f %0.2f %0.2f %0.2f %0.2f", + m_finalDuckLevel.duckVolume[0], + m_finalDuckLevel.duckVolume[1], + m_finalDuckLevel.duckVolume[2], + m_finalDuckLevel.duckVolume[3], + m_finalDuckLevel.duckVolume[4] ); + break; + + case 15: + sprintf( buffer, "User volumes: %0.2f %0.2f %0.2f %0.2f %0.2f", + m_userVolumes.duckVolume[0], + m_userVolumes.duckVolume[1], + m_userVolumes.duckVolume[2], + m_userVolumes.duckVolume[3], + m_userVolumes.duckVolume[4] ); + break; + + default: + buffer[0] = '\0'; + break; + } +} + +//============================================================================= +// DialogSoundDebugPage::getNumLines +//============================================================================= +// Description: Returns number of lines that we'll display on screen +// +// Parameters: None +// +// Return: Line count +// +//============================================================================= +int TunerDebugPage::getNumLines() +{ + return( 16 ); +} + +//***************************************************************************** +// +// Private Member Functions +// +//***************************************************************************** diff --git a/game/code/sound/soundrenderer/tunerdebugpage.h b/game/code/sound/soundrenderer/tunerdebugpage.h new file mode 100644 index 0000000..b48797c --- /dev/null +++ b/game/code/sound/soundrenderer/tunerdebugpage.h @@ -0,0 +1,68 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: tunerdebugpage.h +// +// Description: Displays debug info for the sound tuner +// +// History: 6/11/2003 + Created -- Esan +// +//============================================================================= + +#ifndef TUNERDEBUGPAGE_H +#define TUNERDEBUGPAGE_H + +//======================================== +// Nested Includes +//======================================== +#include <sound/sounddebug/sounddebugpage.h> + +#include <sound/soundrenderer/dasoundgroup.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: TunerDebugPage +// +//============================================================================= + +class TunerDebugPage : public SoundDebugPage +{ + public: + TunerDebugPage(); + virtual ~TunerDebugPage(); + + void SetDuckLevel( Sound::DuckSituations situation, Sound::DuckVolumes volumeType, float volume ); + void SetFinalDuckLevel( Sound::DuckVolumes volumeType, float volume ); + void SetUserVolume( Sound::DuckVolumes volumeType, float volume ); + + protected: + // + // Pure virtual functions from SoundDebugPage + // + void fillLineBuffer( int lineNum, char* buffer ); + int getNumLines(); + + private: + //Prevent wasteful constructor creation. + TunerDebugPage( const TunerDebugPage& tunerdebugpage ); + TunerDebugPage& operator=( const TunerDebugPage& tunerdebugpage ); + + // + // Ducking info + // + Sound::DuckVolumeSet m_duckLevels[Sound::NUM_DUCK_SITUATIONS]; + Sound::DuckVolumeSet m_finalDuckLevel; + Sound::DuckVolumeSet m_userVolumes; +}; + +//***************************************************************************** +// +//Inline Public Member Functions +// +//***************************************************************************** + +#endif //TUNERDEBUGPAGE_H diff --git a/game/code/sound/soundrenderer/wireplayers.cpp b/game/code/sound/soundrenderer/wireplayers.cpp new file mode 100644 index 0000000..c63b138 --- /dev/null +++ b/game/code/sound/soundrenderer/wireplayers.cpp @@ -0,0 +1 @@ +// This page made intentially empty.
\ No newline at end of file diff --git a/game/code/sound/soundrenderer/wiresystem.cpp b/game/code/sound/soundrenderer/wiresystem.cpp new file mode 100644 index 0000000..6582a16 --- /dev/null +++ b/game/code/sound/soundrenderer/wiresystem.cpp @@ -0,0 +1,104 @@ +//============================================================================= +// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved. +// +// File: wiresystem.cpp +// +// Subsystem: Dark Angel - Sound Tuner System +// +// Description: Wire the tuner +// +// Revisions: +// + Created October 24, 2001 -- breimer +// +//============================================================================= + +//============================================================================= +// Included Files +//============================================================================= + +#include <radobject.hpp> +#include <raddebug.hpp> + +#include <radsound.hpp> +#include <radsound_hal.hpp> + +#include <sound/soundrenderer/soundsystem.h> +#include <sound/soundrenderer/idasoundtuner.h> +#include <sound/soundrenderer/soundtuner.h> + +//============================================================================= +// Define Owning Namespace +//============================================================================= + +namespace Sound { + +//============================================================================= +// Public functions +//============================================================================= + +//============================================================================= +// Function: ::daSoundTunerWireSystem +//============================================================================= +// Description: Wire the sound system +// +// NOTE: Music and ambience are ignored here, because they're controlled by +// Radmusic and not us. +// +//----------------------------------------------------------------------------- + +void daSoundTunerWireSystem +( + IDaSoundWiring* pWiring +) +{ + // PATHS ////////////////////////////////////////////////////////////////// + + // + // Start by wiring up everything as dialogue by default. This is because + // we strip a little path info out of the dialogue files, so that we can + // easily switch between languages. + // + pWiring->WirePath( DIALOGUE, "" ); + + // Character + pWiring->WirePath + ( + CARSOUND, + "sound\\carsound" + ); + + // Collision + pWiring->WirePath + ( + NIS, + "sound\\nis" + ); + + pWiring->WirePath + ( + SOUND_EFFECTS, + "sound\\soundfx" + ); + + pWiring->WirePath + ( + OPTIONS_MENU_STINGERS, + "sound\\soundfx\\optionsmenu" + ); + + // SPECIAL GROUPS ///////////////////////////////////////////////////////// + + pWiring->WireGroup( NIS, DIALOGUE ); + + pWiring->WireGroup( DIALOGUE, DUCKABLE ); + pWiring->WireGroup( SOUND_EFFECTS, DUCKABLE ); + pWiring->WireGroup( CARSOUND, DUCKABLE ); + pWiring->WireGroup( OPTIONS_MENU_STINGERS, DUCKABLE ); + + pWiring->WireGroup( DIALOGUE, DIALOGUE_TUNE ); + pWiring->WireGroup( SOUND_EFFECTS, SOUND_EFFECTS_TUNE ); + pWiring->WireGroup( MASTER, MASTER_TUNE ); +} + + +} // Sound Namespace diff --git a/game/code/sound/soundrenderercallback.cpp b/game/code/sound/soundrenderercallback.cpp new file mode 100644 index 0000000..9f2cfb3 --- /dev/null +++ b/game/code/sound/soundrenderercallback.cpp @@ -0,0 +1,181 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundrenderercallback.cpp +// +// Description: Implement SoundRenderingPlayerCallback +// +// History: 06/07/2002 + Created -- NAME +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/soundrenderercallback.h> + +#include <sound/simpsonssoundplayer.h> + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Initialially the list is empty +// +SoundRenderingPlayerCallback* radLinkedClass< SoundRenderingPlayerCallback >::s_pLinkedClassHead = NULL; +SoundRenderingPlayerCallback* radLinkedClass< SoundRenderingPlayerCallback >::s_pLinkedClassTail = NULL; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// SoundRenderingPlayerCallback::SoundRenderingPlayerCallback +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundRenderingPlayerCallback::SoundRenderingPlayerCallback( SimpsonsSoundPlayer& playerObj, + SimpsonsSoundPlayerCallback* callbackObj ) : + m_callbackObj( callbackObj ), + m_playerObj( &playerObj ) +{ +} + +//============================================================================== +// SoundRenderingPlayerCallback::~SoundRenderingPlayerCallback +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +SoundRenderingPlayerCallback::~SoundRenderingPlayerCallback() +{ +} + +//============================================================================= +// SoundRenderingPlayerCallback::CancelGameCallbackAndRelease +//============================================================================= +// Description: Called from the model layer when we're no longer interested +// in callbacks, probably because the sound player is stopping. +// Empty out the callback member and release. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundRenderingPlayerCallback::CancelGameCallbackAndRelease() +{ + m_callbackObj = NULL; + m_playerObj = NULL; + Release(); +} + +//============================================================================= +// SoundRenderingPlayerCallback::OnSoundReady +//============================================================================= +// Description: Implements function for IDaSoundSoundState interface. Called +// when the sound renderer player has a sound cued and ready to +// play. +// +// Parameters: pData - user data +// +// Return: void +// +// Return: void +// +//============================================================================= +void SoundRenderingPlayerCallback::OnSoundReady( void* pData ) +{ + // + // Let the client know that the sound is ready for immediate playback + // + if( m_callbackObj != NULL ) + { + m_callbackObj->OnSoundReady(); + } +} + +//============================================================================= +// SoundRenderingPlayerCallback::OnSoundDone +//============================================================================= +// Description: Implements function for IDaSoundSoundState interface. Called +// when the sound renderer player is finished playing something. +// +// Parameters: pData - user data +// +// Return: void +// +//============================================================================= +void SoundRenderingPlayerCallback::OnSoundDone( void* pData ) +{ + SimpsonsSoundPlayerCallback* callbackObj = m_callbackObj; + + // + // AddRef so that none of the callback stuff below can wipe us out until + // we're done + // + AddRef(); + + // + // Trigger the player callback first so that it can clean itself up and + // make itself available for the client receiving the callback. Save + // a copy of the client callback, since the player might ask us to + // wipe it out + // + if( m_playerObj != NULL ) + { + m_playerObj->OnPlaybackComplete(); + } + + // + // Now the client, who can reuse the same player now + // + if( callbackObj != NULL ) + { + callbackObj->OnPlaybackComplete(); + } + + Release(); +} + +//============================================================================= +// SoundRenderingPlayerCallback::CompletionCheck +//============================================================================= +// Description: A sanity check, usable by anyone who wants to ensure that all +// sound players are closed and nobody is waiting around for +// callbacks anymore. If any SoundRenderingPlayerCallback objects +// are still kicking around, we fail. +// +// Parameters: None +// +// Return: void +// +//============================================================================= +void SoundRenderingPlayerCallback::CompletionCheck() +{ + rAssertMsg( GetLinkedClassHead() == NULL, "GAAAAAK!! Sound renderer players are still running when they're not supposed to be!\n" ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** diff --git a/game/code/sound/soundrenderercallback.h b/game/code/sound/soundrenderercallback.h new file mode 100644 index 0000000..e0a4744 --- /dev/null +++ b/game/code/sound/soundrenderercallback.h @@ -0,0 +1,70 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: soundrenderercallback.h +// +// Description: Declaration of SoundRenderingPlayerCallback class. Used from +// sound renderer for player-related callbacks. +// +// History: 06/07/2002 + Created -- Darren +// +//============================================================================= + +#ifndef SOUNDRENDERERCALLBACK_H +#define SOUNDRENDERERCALLBACK_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> +#include <radlinkedclass.hpp> + +#include <sound/soundrenderer/soundsystem.h> + +//======================================== +// Forward References +//======================================== + +struct SimpsonsSoundPlayerCallback; +class SimpsonsSoundPlayer; + +//============================================================================= +// +// Synopsis: SoundRenderingPlayerCallback +// +//============================================================================= + +class SoundRenderingPlayerCallback : public Sound::IDaSoundPlayerState, + public radLinkedClass< SoundRenderingPlayerCallback >, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "SoundRenderingPlayerCallback" ); + + SoundRenderingPlayerCallback( SimpsonsSoundPlayer& playerObj, + SimpsonsSoundPlayerCallback* callbackObj ); + virtual ~SoundRenderingPlayerCallback(); + + void CancelGameCallbackAndRelease(); + + static void CompletionCheck(); + + // Currently unused + void OnSoundReady( void* pData ); + + // Called when sound renderer player has completed playback + void OnSoundDone( void* pData ); + + private: + //Prevent wasteful constructor creation. + SoundRenderingPlayerCallback(); + SoundRenderingPlayerCallback( const SoundRenderingPlayerCallback& original ); + SoundRenderingPlayerCallback& operator=( const SoundRenderingPlayerCallback& rhs ); + + SimpsonsSoundPlayerCallback* m_callbackObj; + SimpsonsSoundPlayer* m_playerObj; +}; + + +#endif // SOUNDRENDERERCALLBACK_H + diff --git a/game/code/sound/tuning/allsoundtuning.cpp b/game/code/sound/tuning/allsoundtuning.cpp new file mode 100644 index 0000000..325a1ca --- /dev/null +++ b/game/code/sound/tuning/allsoundtuning.cpp @@ -0,0 +1 @@ +#include <sound/tuning/globalsettings.cpp> diff --git a/game/code/sound/tuning/globalsettings.cpp b/game/code/sound/tuning/globalsettings.cpp new file mode 100644 index 0000000..f6193fc --- /dev/null +++ b/game/code/sound/tuning/globalsettings.cpp @@ -0,0 +1,467 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: globalsettings.cpp +// +// Description: Implementation of globalSettings, which sets global sound values +// in the game (e.g. master volume, sound defaults). Created +// using RadScript, hence the lower-case g. +// +// History: 07/08/2002 + Created -- Darren +// +//============================================================================= + +//======================================== +// System Includes +//======================================== + +//======================================== +// Project Includes +//======================================== +#include <sound/tuning/globalsettings.h> + +#include <sound/soundmanager.h> +#include <memory/srrmemory.h> + +using namespace Sound; + +//****************************************************************************** +// +// Global Data, Local Data, Local Classes +// +//****************************************************************************** + +// +// Initialially the list is empty +// +globalSettings* radLinkedClass< globalSettings >::s_pLinkedClassHead = NULL; +globalSettings* radLinkedClass< globalSettings >::s_pLinkedClassTail = NULL; + +//****************************************************************************** +// +// Public Member Functions +// +//****************************************************************************** + +//============================================================================== +// globalSettings::globalSettings +//============================================================================== +// Description: Constructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +globalSettings::globalSettings() : + radRefCount( 0 ), + m_peeloutMin( 0.0f ), + m_peeloutMax( 1.0f ), + m_peeloutMaxTrim( 1.0f ), + m_roadSkidClip( NULL ), + m_dirtSkidClip( NULL ), + m_roadFootstepClip( NULL ), + m_metalFootstepClip( NULL ), + m_woodFootstepClip( NULL ), + m_coinPitchCount( 0 ), + m_ambienceVolume( 0.0f ), + m_musicVolume( 0.0f ), + m_sfxVolume( 0.0f ), + m_dialogueVolume( 0.0f ), + m_carVolume( 0.0f ) +{ + unsigned int i, j; + + for( i = 0; i < NUM_DUCK_SITUATIONS; i++ ) + { + for( j = 0; j < NUM_DUCK_VOLUMES; j++ ) + { + m_duckVolumes[i].duckVolume[j] = 0.0f; + } + } + + for( i = 0; i < s_maxCoinPitches; i++ ) + { + m_coinPitches[i] = 1.0f; + } +} + +//============================================================================== +// globalSettings::~globalSettings +//============================================================================== +// Description: Destructor. +// +// Parameters: None. +// +// Return: N/A. +// +//============================================================================== +globalSettings::~globalSettings() +{ + if( m_roadSkidClip != NULL ) + { + delete( GMA_AUDIO_PERSISTENT, m_roadSkidClip ); + } + if( m_dirtSkidClip != NULL ) + { + delete( GMA_AUDIO_PERSISTENT, m_dirtSkidClip ); + } + + if( m_roadFootstepClip != NULL ) + { + delete( GMA_AUDIO_PERSISTENT, m_roadFootstepClip ); + } + if( m_metalFootstepClip != NULL ) + { + delete( GMA_AUDIO_PERSISTENT, m_metalFootstepClip ); + } + if( m_woodFootstepClip != NULL ) + { + delete( GMA_AUDIO_PERSISTENT, m_woodFootstepClip ); + } +} + +//============================================================================= +// globalSettings::SetMasterVolume +//============================================================================= +// Description: Sets master volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetMasterVolume( float volume ) +{ + GetSoundManager()->SetMasterVolume( volume ); +} + +//============================================================================= +// globalSettings::SetSfxVolume +//============================================================================= +// Description: Sets sfx volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetSfxVolume( float volume ) +{ + m_sfxVolume = volume; +} + +//============================================================================= +// globalSettings::SetCarVolume +//============================================================================= +// Description: Sets car volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetCarVolume( float volume ) +{ + m_carVolume = volume; +} + +//============================================================================= +// globalSettings::SetMusicVolume +//============================================================================= +// Description: Sets music volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetMusicVolume( float volume ) +{ + m_musicVolume = volume; +} + +//============================================================================= +// globalSettings::SetDialogueVolume +//============================================================================= +// Description: Sets dialogue volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetDialogueVolume( float volume ) +{ + m_dialogueVolume = volume; +} + +//============================================================================= +// globalSettings::SetAmbienceVolume +//============================================================================= +// Description: Sets ambience volume, obviously +// +// Parameters: volume - new volume level +// +// Return: void +// +//============================================================================= +void globalSettings::SetAmbienceVolume( float volume ) +{ + m_ambienceVolume = volume; +} + +//============================================================================= +// globalSettings::SetPeeloutMin +//============================================================================= +// Description: Set minimum peelout value at which we play sound +// +// Parameters: min - peelout value, 0-1 +// +// Return: void +// +//============================================================================= +void globalSettings::SetPeeloutMin( float min ) +{ + rAssert( min >= 0.0f ); + rAssert( min <= 1.0f ); + + m_peeloutMin = min; +} + +//============================================================================= +// globalSettings::SetPeeloutMax +//============================================================================= +// Description: Set peelout value at which sound is at maximum volume +// +// Parameters: max - peelout value, 0-1 +// +// Return: void +// +//============================================================================= +void globalSettings::SetPeeloutMax( float max ) +{ + rAssert( max >= 0.0f ); + rAssert( max <= 1.0f ); + + m_peeloutMax = max; +} + +//============================================================================= +// globalSettings::SetPeeloutMaxTrim +//============================================================================= +// Description: Set maximum trim applied to peelout sound +// +// Parameters: trim - max trim value +// +// Return: void +// +//============================================================================= +void globalSettings::SetPeeloutMaxTrim( float trim ) +{ + rAssert( trim >= 0.0f ); + + m_peeloutMaxTrim = trim; +} + +//============================================================================= +// globalSettings::SetSkidRoadClipName +//============================================================================= +// Description: Set name of sound resource for road skids +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void globalSettings::SetSkidRoadClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_roadSkidClip = new char[strlen(clipName)+1]; + strcpy( m_roadSkidClip, clipName ); + + HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// globalSettings::SetSkidDirtClipName +//============================================================================= +// Description: Set name of sound resource for dirt skids +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void globalSettings::SetSkidDirtClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_dirtSkidClip = new char[strlen(clipName)+1]; + strcpy( m_dirtSkidClip, clipName ); + + HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// globalSettings::SetFootstepRoadClipName +//============================================================================= +// Description: Set name of sound resource for footsteps on road surfaces +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void globalSettings::SetFootstepRoadClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_roadFootstepClip = new char[strlen(clipName)+1]; + strcpy( m_roadFootstepClip, clipName ); + + HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// globalSettings::SetFootstepMetalClipName +//============================================================================= +// Description: Set name of sound resource for footsteps on metal surfaces +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void globalSettings::SetFootstepMetalClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_metalFootstepClip = new char[strlen(clipName)+1]; + strcpy( m_metalFootstepClip, clipName ); + + HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// globalSettings::SetFootstepWoodClipName +//============================================================================= +// Description: Set name of sound resource for footsteps on wood surfaces +// +// Parameters: clipName - name of sound resource +// +// Return: void +// +//============================================================================= +void globalSettings::SetFootstepWoodClipName( const char* clipName ) +{ + rAssert( clipName != NULL ); + + HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT ); + + m_woodFootstepClip = new char[strlen(clipName)+1]; + strcpy( m_woodFootstepClip, clipName ); + + HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT); +} + +//============================================================================= +// globalSettings::SetCoinPitch +//============================================================================= +// Description: Set pitch for coin pickup sound in sequence (so we can make +// a tune out of the coin sounds or something) +// +// Parameters: pitch - pitch for next coin playback in sequence +// +// Return: void +// +//============================================================================= +void globalSettings::SetCoinPitch( float pitch ) +{ + if( m_coinPitchCount < s_maxCoinPitches ) + { + m_coinPitches[m_coinPitchCount++] = pitch; + } + else + { + rDebugString( "Too many coin pitches specified in script\n" ); + } +} + +//============================================================================= +// globalSettings::GetCoinPitch +//============================================================================= +// Description: Get a particular pitch within coin sequence +// +// Parameters: index - index into coin pitch sequence +// +// Return: the pitch +// +//============================================================================= +float globalSettings::GetCoinPitch( unsigned int index ) +{ + rAssert( index < s_maxCoinPitches ); + + return( m_coinPitches[index] ); +} + +//****************************************************************************** +// +// Private Member Functions +// +//****************************************************************************** + +//============================================================================= +// globalSettings::setDuckVolume +//============================================================================= +// Description: Store the ducking volume for a particular volume setting and +// ducking situation +// +// Parameters: situation - ducking situation +// volumeToSet - which volume to set within that situation +// volume - volume value to set +// +// Return: void +// +//============================================================================= +void globalSettings::setDuckVolume( DuckSituations situation, DuckVolumes volumeToSet, float volume ) +{ + m_duckVolumes[situation].duckVolume[volumeToSet] = volume; +} + +//****************************************************************************** +// Factory functions +//****************************************************************************** + +//============================================================================== +// GlobalSettingsObjCreate +//============================================================================== +// Description: Factory function for creating globalSettings objects. +// Called by RadScript. +// +// Parameters: ppParametersObj - Address of ptr to new object +// allocator - FTT pool to allocate object within +// +// Return: N/A. +// +//============================================================================== +void GlobalSettingsObjCreate +( + IGlobalSettings** ppParametersObj, + radMemoryAllocator allocator +) +{ + rAssert( ppParametersObj != NULL ); + (*ppParametersObj) = new ( allocator ) globalSettings( ); + (*ppParametersObj)->AddRef( ); +} + diff --git a/game/code/sound/tuning/globalsettings.h b/game/code/sound/tuning/globalsettings.h new file mode 100644 index 0000000..13b8417 --- /dev/null +++ b/game/code/sound/tuning/globalsettings.h @@ -0,0 +1,224 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: globalsettings.h +// +// Description: Declaration of globalSettings, which sets global sound values +// in the game (e.g. master volume, sound defaults). Created +// using RadScript, hence the lower-case g. +// +// History: 07/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef GLOBALSETTINGS_H +#define GLOBALSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radlinkedclass.hpp> + +#include <sound/tuning/iglobalsettings.h> +#include <sound/soundrenderer/dasoundgroup.h> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: globalSettings +// +//============================================================================= + +class globalSettings : public IGlobalSettings, + public radLinkedClass< globalSettings >, + public radRefCount +{ + public: + IMPLEMENT_REFCOUNTED( "globalSettings" ); + + globalSettings(); + virtual ~globalSettings(); + + // + // Volume controls + // + void SetMasterVolume( float volume ); + + void SetSfxVolume( float volume ); + float GetSfxVolume() { return( m_sfxVolume ); } + + void SetCarVolume( float volume ); + float GetCarVolume() { return( m_carVolume ); } + + void SetMusicVolume( float volume ); + float GetMusicVolume() { return( m_musicVolume ); } + + void SetDialogueVolume( float volume ); + float GetDialogueVolume() { return( m_dialogueVolume ); } + + void SetAmbienceVolume( float volume ); + float GetAmbienceVolume() { return( m_ambienceVolume ); } + + // + // Ducking controls + // + float GetDuckVolume( Sound::DuckSituations situation, Sound::DuckVolumes volume ) { return( m_duckVolumes[situation].duckVolume[volume] ); } + + void SetPauseSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_SFX, volume ); } + void SetPauseCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_CAR, volume ); } + void SetPauseMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_MUSIC, volume ); } + void SetPauseDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_DIALOG, volume ); } + void SetPauseAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_AMBIENCE, volume ); } + + void SetMissionScreenSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_SFX, volume ); } + void SetMissionScreenCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_CAR, volume ); } + void SetMissionScreenMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_MUSIC, volume ); } + void SetMissionScreenDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_DIALOG, volume ); } + void SetMissionScreenAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_AMBIENCE, volume ); } + + void SetLetterboxSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_SFX, volume ); } + void SetLetterboxCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_CAR, volume ); } + void SetLetterboxMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_MUSIC, volume ); } + void SetLetterboxDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_DIALOG, volume ); } + void SetLetterboxAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_AMBIENCE, volume ); } + + void SetDialogueSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_SFX, volume ); } + void SetDialogueCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_CAR, volume ); } + void SetDialogueMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_MUSIC, volume ); } + void SetDialogueDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_DIALOG, volume ); } + void SetDialogueAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_AMBIENCE, volume ); } + + void SetStoreSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_SFX, volume ); } + void SetStoreCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_CAR, volume ); } + void SetStoreMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_MUSIC, volume ); } + void SetStoreDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_DIALOG, volume ); } + void SetStoreAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_AMBIENCE, volume ); } + + void SetOnFootSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_SFX, volume ); } + void SetOnFootCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_CAR, volume ); } + void SetOnFootMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_MUSIC, volume ); } + void SetOnFootDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_DIALOG, volume ); } + void SetOnFootAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_AMBIENCE, volume ); } + + void SetMinigameSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_SFX, volume ); } + void SetMinigameCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_CAR, volume ); } + void SetMinigameMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_MUSIC, volume ); } + void SetMinigameDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_DIALOG, volume ); } + void SetMinigameAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_AMBIENCE, volume ); } + + void SetJustMusicSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_SFX, volume ); } + void SetJustMusicCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_CAR, volume ); } + void SetJustMusicMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_MUSIC, volume ); } + void SetJustMusicDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_DIALOG, volume ); } + void SetJustMusicAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_AMBIENCE, volume ); } + + void SetCreditsSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_SFX, volume ); } + void SetCreditsCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_CAR, volume ); } + void SetCreditsMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_MUSIC, volume ); } + void SetCreditsDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_DIALOG, volume ); } + void SetCreditsAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_AMBIENCE, volume ); } + + // + // Car controls + // + void SetPeeloutMin( float min ); + float GetPeeloutMin() { return( m_peeloutMin ); } + + void SetPeeloutMax( float max ); + float GetPeeloutMax() { return( m_peeloutMax ); } + + void SetPeeloutMaxTrim( float trim ); + float GetPeeloutMaxTrim() { return( m_peeloutMaxTrim ); } + + void SetSkidRoadClipName( const char* clipName ); + const char* GetSkidRoadClipName() { return( m_roadSkidClip ); } + + void SetSkidDirtClipName( const char* clipName ); + const char* GetSkidDirtClipName() { return( m_dirtSkidClip ); } + + // + // Footstep sounds + // + void SetFootstepRoadClipName( const char* clipName ); + const char* GetFootstepRoadClipName() { return( m_roadFootstepClip ); } + + void SetFootstepMetalClipName( const char* clipName ); + const char* GetFootstepMetalClipName() { return( m_metalFootstepClip ); } + + void SetFootstepWoodClipName( const char* clipName ); + const char* GetFootstepWoodClipName() { return( m_woodFootstepClip ); } + + // + // Coin pitches + // + void SetCoinPitch( float pitch ); + float GetCoinPitch( unsigned int index ); + unsigned int GetNumCoinPitches() { return( m_coinPitchCount ); } + + private: + + //Prevent wasteful constructor creation. + globalSettings( const globalSettings& original ); + globalSettings& operator=( const globalSettings& rhs ); + + void setDuckVolume( Sound::DuckSituations situation, Sound::DuckVolumes volumeToSet, float volume ); + + // + // Ducking settings + // + Sound::DuckVolumeSet m_duckVolumes[Sound::NUM_DUCK_SITUATIONS]; + + // + // Car settings + // + float m_peeloutMin; + float m_peeloutMax; + float m_peeloutMaxTrim; + + char* m_roadSkidClip; + char* m_dirtSkidClip; + + // + // Footsteps + // + char* m_roadFootstepClip; + char* m_metalFootstepClip; + char* m_woodFootstepClip; + + // + // Coin pitches + // + static const unsigned int s_maxCoinPitches = 10; + float m_coinPitches[s_maxCoinPitches]; + unsigned int m_coinPitchCount; + + // + // Hack!! + // + float m_ambienceVolume; + float m_musicVolume; + float m_sfxVolume; + float m_dialogueVolume; + float m_carVolume; +}; + +//============================================================================= +// Factory Functions +//============================================================================= + +// +// Create a CarSoundParameters object +// +void GlobalSettingsObjCreate +( + IGlobalSettings** ppSoundResource, + radMemoryAllocator allocator +); + + + +#endif // GLOBALSETTINGS_H + diff --git a/game/code/sound/tuning/iglobalsettings.h b/game/code/sound/tuning/iglobalsettings.h new file mode 100644 index 0000000..3fac89c --- /dev/null +++ b/game/code/sound/tuning/iglobalsettings.h @@ -0,0 +1,128 @@ +//============================================================================= +// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. +// +// File: iglobalsettings.h +// +// Description: Declaration of interface class IGlobalSettings, which sets +// global sound values in the game (e.g. master volume, +// sound defaults). Created using RadScript. +// +// History: 07/08/2002 + Created -- Darren +// +//============================================================================= + +#ifndef IGLOBALSETTINGS_H +#define IGLOBALSETTINGS_H + +//======================================== +// Nested Includes +//======================================== +#include <radobject.hpp> + +//======================================== +// Forward References +//======================================== + +//============================================================================= +// +// Synopsis: IGlobalSettings +// +//============================================================================= + +class IGlobalSettings : public IRefCount +{ + public: + // + // Volume controls + // + virtual void SetMasterVolume( float volume ) = 0; + + virtual void SetSfxVolume( float volume ) = 0; + virtual void SetCarVolume( float volume ) = 0; + virtual void SetMusicVolume( float volume ) = 0; + virtual void SetDialogueVolume( float volume ) = 0; + virtual void SetAmbienceVolume( float volume ) = 0; + + // + // Ducking controls + // + virtual void SetPauseSfxVolume( float volume ) = 0; + virtual void SetPauseCarVolume( float volume ) = 0; + virtual void SetPauseMusicVolume( float volume ) = 0; + virtual void SetPauseDialogueVolume( float volume ) = 0; + virtual void SetPauseAmbienceVolume( float volume ) = 0; + + virtual void SetMissionScreenSfxVolume( float volume ) = 0; + virtual void SetMissionScreenCarVolume( float volume ) = 0; + virtual void SetMissionScreenMusicVolume( float volume ) = 0; + virtual void SetMissionScreenDialogueVolume( float volume ) = 0; + virtual void SetMissionScreenAmbienceVolume( float volume ) = 0; + + virtual void SetLetterboxSfxVolume( float volume ) = 0; + virtual void SetLetterboxCarVolume( float volume ) = 0; + virtual void SetLetterboxMusicVolume( float volume ) = 0; + virtual void SetLetterboxDialogueVolume( float volume ) = 0; + virtual void SetLetterboxAmbienceVolume( float volume ) = 0; + + virtual void SetDialogueSfxVolume( float volume ) = 0; + virtual void SetDialogueCarVolume( float volume ) = 0; + virtual void SetDialogueMusicVolume( float volume ) = 0; + virtual void SetDialogueDialogueVolume( float volume ) = 0; + virtual void SetDialogueAmbienceVolume( float volume ) = 0; + + virtual void SetStoreSfxVolume( float volume ) = 0; + virtual void SetStoreCarVolume( float volume ) = 0; + virtual void SetStoreMusicVolume( float volume ) = 0; + virtual void SetStoreDialogueVolume( float volume ) = 0; + virtual void SetStoreAmbienceVolume( float volume ) = 0; + + virtual void SetOnFootSfxVolume( float volume ) = 0; + virtual void SetOnFootCarVolume( float volume ) = 0; + virtual void SetOnFootMusicVolume( float volume ) = 0; + virtual void SetOnFootDialogueVolume( float volume ) = 0; + virtual void SetOnFootAmbienceVolume( float volume ) = 0; + + virtual void SetMinigameSfxVolume( float volume ) = 0; + virtual void SetMinigameCarVolume( float volume ) = 0; + virtual void SetMinigameMusicVolume( float volume ) = 0; + virtual void SetMinigameDialogueVolume( float volume ) = 0; + virtual void SetMinigameAmbienceVolume( float volume ) = 0; + + virtual void SetJustMusicSfxVolume( float volume ) = 0; + virtual void SetJustMusicCarVolume( float volume ) = 0; + virtual void SetJustMusicMusicVolume( float volume ) = 0; + virtual void SetJustMusicDialogueVolume( float volume ) = 0; + virtual void SetJustMusicAmbienceVolume( float volume ) = 0; + + virtual void SetCreditsSfxVolume( float volume ) = 0; + virtual void SetCreditsCarVolume( float volume ) = 0; + virtual void SetCreditsMusicVolume( float volume ) = 0; + virtual void SetCreditsDialogueVolume( float volume ) = 0; + virtual void SetCreditsAmbienceVolume( float volume ) = 0; + + // + // Car controls + // + virtual void SetPeeloutMin( float min ) = 0; + virtual void SetPeeloutMax( float max ) = 0; + virtual void SetPeeloutMaxTrim( float trim ) = 0; + + virtual void SetSkidRoadClipName( const char* clipName ) = 0; + virtual void SetSkidDirtClipName( const char* clipName ) = 0; + + // + // Footstep sounds + // + virtual void SetFootstepRoadClipName( const char* clipName ) = 0; + virtual void SetFootstepMetalClipName( const char* clipName ) = 0; + virtual void SetFootstepWoodClipName( const char* clipName ) = 0; + + // + // Coin pitches + // + virtual void SetCoinPitch( float pitch ) = 0; +}; + + +#endif // IGLOBALSETTINGS_H + |