Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1456 lines
43 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "sharedInterface.h"
  11. #include "soundenvelope.h"
  12. #include "engine/IEngineSound.h"
  13. #include "IEffects.h"
  14. #include "isaverestore.h"
  15. #include "saverestore_utlvector.h"
  16. #include "gamestringpool.h"
  17. #include "igamesystem.h"
  18. #include "utlpriorityqueue.h"
  19. #include "mempool.h"
  20. #include "SoundEmitterSystem/isoundemittersystembase.h"
  21. #include "tier0/vprof.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. static ConVar soundpatch_captionlength( "soundpatch_captionlength", "2.0", FCVAR_REPLICATED, "How long looping soundpatch captions should display for." );
  25. // Envelope
  26. // This is a class that controls a ramp for a sound (pitch / volume / etc)
  27. class CSoundEnvelope
  28. {
  29. public:
  30. DECLARE_SIMPLE_DATADESC();
  31. CSoundEnvelope()
  32. {
  33. m_current = 0.0f;
  34. m_target = 0.0f;
  35. m_rate = 0.0f;
  36. m_forceupdate = false;
  37. }
  38. void SetTarget( float target, float deltaTime );
  39. void SetValue( float value );
  40. bool ShouldUpdate( void );
  41. void Update( float time );
  42. inline float Value( void ) { return m_current; }
  43. private:
  44. float m_current;
  45. float m_target;
  46. float m_rate;
  47. bool m_forceupdate;
  48. };
  49. BEGIN_SIMPLE_DATADESC( CSoundEnvelope )
  50. DEFINE_FIELD( m_current, FIELD_FLOAT ),
  51. DEFINE_FIELD( m_target, FIELD_FLOAT ),
  52. DEFINE_FIELD( m_rate, FIELD_FLOAT ),
  53. DEFINE_FIELD( m_forceupdate, FIELD_BOOLEAN ),
  54. END_DATADESC()
  55. //-----------------------------------------------------------------------------
  56. // Purpose: Set the new target value for this ramp. Reach this target in deltaTime
  57. // seconds from now
  58. // Input : target - new target value
  59. // deltaTime - time to reach target
  60. //-----------------------------------------------------------------------------
  61. void CSoundEnvelope::SetTarget( float target, float deltaTime )
  62. {
  63. float deltaValue = target - m_current;
  64. if ( deltaValue && deltaTime > 0 )
  65. {
  66. m_target = target;
  67. m_rate = MAX( 0.1, fabs(deltaValue / deltaTime) );
  68. }
  69. else
  70. {
  71. if ( target != m_current )
  72. {
  73. m_forceupdate = true;
  74. }
  75. SetValue( target );
  76. }
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose: Instantaneously set the value of this ramp
  80. // Input : value - new value
  81. //-----------------------------------------------------------------------------
  82. void CSoundEnvelope::SetValue( float value )
  83. {
  84. if ( m_target != value )
  85. {
  86. m_forceupdate = true;
  87. }
  88. m_current = m_target = value;
  89. m_rate = 0;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: Check to see if I need to update this envelope
  93. // Output : Returns true if this envelope is changing
  94. //-----------------------------------------------------------------------------
  95. bool CSoundEnvelope::ShouldUpdate( void )
  96. {
  97. if ( m_forceupdate )
  98. {
  99. m_forceupdate = false;
  100. return true;
  101. }
  102. if ( m_current != m_target )
  103. {
  104. return true;
  105. }
  106. return false;
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose: Update the envelope for the current frame time
  110. // Input : time - amount of time that has passed
  111. //-----------------------------------------------------------------------------
  112. void CSoundEnvelope::Update( float deltaTime )
  113. {
  114. m_current = Approach( m_target, m_current, m_rate * deltaTime );
  115. }
  116. class CCopyRecipientFilter : public IRecipientFilter
  117. {
  118. public:
  119. DECLARE_SIMPLE_DATADESC();
  120. CCopyRecipientFilter() : m_Flags(0) {}
  121. void Init( IRecipientFilter *pSrc )
  122. {
  123. m_Flags = FLAG_ACTIVE;
  124. if ( pSrc->IsReliable() )
  125. {
  126. m_Flags |= FLAG_RELIABLE;
  127. }
  128. if ( pSrc->IsInitMessage() )
  129. {
  130. m_Flags |= FLAG_INIT_MESSAGE;
  131. }
  132. for ( int i = 0; i < pSrc->GetRecipientCount(); i++ )
  133. {
  134. int index = pSrc->GetRecipientIndex( i );
  135. if ( index >= 0 )
  136. m_Recipients.AddToTail( index );
  137. }
  138. }
  139. bool IsActive() const
  140. {
  141. return (m_Flags & FLAG_ACTIVE) != 0;
  142. }
  143. virtual bool IsReliable( void ) const
  144. {
  145. return (m_Flags & FLAG_RELIABLE) != 0;
  146. }
  147. virtual int GetRecipientCount( void ) const
  148. {
  149. return m_Recipients.Count();
  150. }
  151. virtual int GetRecipientIndex( int slot ) const
  152. {
  153. return m_Recipients[ slot ];
  154. }
  155. virtual bool IsInitMessage( void ) const
  156. {
  157. return (m_Flags & FLAG_INIT_MESSAGE) != 0;
  158. }
  159. virtual bool AddRecipient( CBasePlayer *player )
  160. {
  161. Assert( player );
  162. int index = player->entindex();
  163. if ( index < 0 )
  164. return false;
  165. // Already in list
  166. if ( m_Recipients.Find( index ) != m_Recipients.InvalidIndex() )
  167. return false;
  168. m_Recipients.AddToTail( index );
  169. return true;
  170. }
  171. private:
  172. enum
  173. {
  174. FLAG_ACTIVE = 0x1,
  175. FLAG_RELIABLE = 0x2,
  176. FLAG_INIT_MESSAGE = 0x4,
  177. };
  178. int m_Flags;
  179. CUtlVector< int > m_Recipients;
  180. };
  181. BEGIN_SIMPLE_DATADESC( CCopyRecipientFilter )
  182. DEFINE_FIELD( m_Flags, FIELD_INTEGER ),
  183. DEFINE_UTLVECTOR( m_Recipients, FIELD_INTEGER ),
  184. END_DATADESC()
  185. #include "tier0/memdbgoff.h"
  186. // This is the a basic sound controller, a "patch"
  187. // It has envelopes for pitch and volume and can manage state changes to those
  188. class CSoundPatch
  189. {
  190. public:
  191. DECLARE_SIMPLE_DATADESC();
  192. static int g_SoundPatchCount;
  193. CSoundPatch()
  194. {
  195. g_SoundPatchCount++;
  196. m_iszSoundName = NULL_STRING;
  197. m_iszSoundScriptName = NULL_STRING;
  198. m_flCloseCaptionDuration = soundpatch_captionlength.GetFloat();
  199. m_soundOrigin.Init();
  200. m_soundEntityIndex = -1;
  201. m_guid = -1;
  202. m_hSoundScriptHash = SOUNDEMITTER_INVALID_HASH;
  203. m_nSoundEntryVersion = 1;
  204. }
  205. ~CSoundPatch()
  206. {
  207. g_SoundPatchCount--;
  208. }
  209. void Init( IRecipientFilter *pFilter, CBaseEntity *pEnt, int channel, const char *pSoundName,
  210. soundlevel_t iSoundLevel, const Vector *pSoundOrigin, float scriptVolume = 1.0f );
  211. void ChangePitch( float pitchTarget, float deltaTime );
  212. void ChangeVolume( float volumeTarget, float deltaTime );
  213. void FadeOut( float deltaTime, bool destroyOnFadeout );
  214. float GetPitch( void );
  215. float GetVolume( void );
  216. string_t GetName() { return m_iszSoundName; };
  217. #ifdef CLIENT_DLL
  218. int GetGuid() { return m_guid; };
  219. float GetElapsedTime( void );
  220. bool IsStillPlaying( void );
  221. #endif
  222. string_t GetScriptName() { return m_iszSoundScriptName; }
  223. // UNDONE: Don't call this, use the controller to shut down
  224. void Shutdown( void );
  225. bool Update( float time, float deltaTime );
  226. void Reset( void );
  227. void StartSound( float flStartTime = 0 );
  228. void ResumeSound( void );
  229. int IsPlaying( void ) { return m_isPlaying; }
  230. float GetShutdownTime( void ) const { return m_shutdownTime; } // TERROR: debugging
  231. void AddPlayerPost( CBasePlayer *pPlayer );
  232. void SetCloseCaptionDuration( float flDuration ) { m_flCloseCaptionDuration = flDuration; }
  233. void SetBaseFlags( int iFlags ) { m_baseFlags = iFlags; }
  234. // Returns the ent index
  235. int EntIndex() const;
  236. private:
  237. // SoundPatches take volumes between 0 & 1, and use that to multiply the sounds.txt specified volume.
  238. // This function is an internal method of accessing the real volume passed into the engine (i.e. post multiply)
  239. float GetVolumeForEngine( void );
  240. private:
  241. CSoundEnvelope m_pitch;
  242. CSoundEnvelope m_volume;
  243. int m_guid;
  244. soundlevel_t m_soundlevel;
  245. float m_shutdownTime;
  246. string_t m_iszSoundName;
  247. string_t m_iszSoundScriptName;
  248. HSOUNDSCRIPTHASH m_hSoundScriptHash;
  249. int m_nSoundEntryVersion;
  250. EHANDLE m_hEnt;
  251. int m_entityChannel;
  252. int m_soundEntityIndex;
  253. Vector m_soundOrigin;
  254. int m_flags;
  255. int m_baseFlags;
  256. int m_isPlaying;
  257. float m_flScriptVolume; // Volume for this sound in sounds.txt
  258. CCopyRecipientFilter m_Filter;
  259. float m_flCloseCaptionDuration;
  260. #ifdef _DEBUG
  261. // Used to get the classname of the entity associated with the sound
  262. string_t m_iszClassName;
  263. #endif
  264. DECLARE_FIXEDSIZE_ALLOCATOR(CSoundPatch);
  265. };
  266. #include "tier0/memdbgon.h"
  267. int CSoundPatch::g_SoundPatchCount = 0;
  268. #ifdef CLIENT_DLL
  269. CON_COMMAND( cl_report_soundpatch, "reports client-side sound patch count" )
  270. #else
  271. CON_COMMAND( report_soundpatch, "reports sound patch count" )
  272. #endif
  273. {
  274. #ifndef CLIENT_DLL
  275. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  276. return;
  277. #endif
  278. Msg("Current sound patches: %d\n", CSoundPatch::g_SoundPatchCount );
  279. }
  280. DEFINE_FIXEDSIZE_ALLOCATOR( CSoundPatch, 64, CUtlMemoryPool::GROW_FAST );
  281. BEGIN_SIMPLE_DATADESC( CSoundPatch )
  282. DEFINE_EMBEDDED( m_pitch ),
  283. DEFINE_EMBEDDED( m_volume ),
  284. DEFINE_FIELD( m_soundlevel, FIELD_INTEGER ),
  285. DEFINE_FIELD( m_shutdownTime, FIELD_TIME ),
  286. DEFINE_FIELD( m_iszSoundName, FIELD_STRING ),
  287. DEFINE_FIELD( m_iszSoundScriptName, FIELD_STRING ),
  288. DEFINE_FIELD( m_hSoundScriptHash, FIELD_INTEGER ),
  289. DEFINE_FIELD( m_nSoundEntryVersion, FIELD_INTEGER ),
  290. DEFINE_FIELD( m_hEnt, FIELD_EHANDLE ),
  291. DEFINE_FIELD( m_entityChannel, FIELD_INTEGER ),
  292. DEFINE_FIELD( m_flags, FIELD_INTEGER ),
  293. DEFINE_FIELD( m_baseFlags, FIELD_INTEGER ),
  294. DEFINE_FIELD( m_isPlaying, FIELD_INTEGER ),
  295. DEFINE_FIELD( m_flScriptVolume, FIELD_FLOAT ),
  296. DEFINE_EMBEDDED( m_Filter ),
  297. DEFINE_FIELD( m_flCloseCaptionDuration, FIELD_FLOAT ),
  298. // Not saved, it's debug only
  299. // DEFINE_FIELD( m_iszClassName, FIELD_STRING ),
  300. END_DATADESC()
  301. //-----------------------------------------------------------------------------
  302. // Purpose: Setup the patch
  303. // Input : nEntIndex - index of the edict that owns the sound channel
  304. // channel - This is a sound channel (CHAN_ITEM, CHAN_STATIC)
  305. // *pSoundName - sound script string name
  306. // attenuation - attenuation of this sound (not animated)
  307. //-----------------------------------------------------------------------------
  308. void CSoundPatch::Init( IRecipientFilter *pFilter, CBaseEntity *pEnt, int channel, const char *pSoundName,
  309. soundlevel_t soundlevel, const Vector *pSoundOrigin , float scriptVolume)
  310. {
  311. m_hEnt = pEnt;
  312. if ( pEnt )
  313. {
  314. m_soundEntityIndex = pEnt->entindex();
  315. }
  316. m_entityChannel = channel;
  317. // if not a direct wave reference, crack the script
  318. CSoundParameters params;
  319. if ( !Q_stristr( pSoundName, ".wav" ) && !Q_stristr( pSoundName, ".mp3" ) &&
  320. CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) )
  321. {
  322. m_flScriptVolume = params.volume;
  323. // This has to be the actual .wav because rndwave would cause a bunch of new .wavs to play... bad...
  324. // e.g., when you pitch shift it would start a different wav instead.
  325. m_iszSoundScriptName = AllocPooledString( pSoundName );
  326. m_hSoundScriptHash = params.m_hSoundScriptHash;
  327. m_nSoundEntryVersion = params.m_nSoundEntryVersion;
  328. pSoundName = params.soundname;
  329. m_soundlevel = params.soundlevel;
  330. // TERROR: if we say we want CHAN_USER_BASE + N, we mean it!
  331. if ( m_entityChannel < CHAN_USER_BASE )
  332. {
  333. m_entityChannel = params.channel;
  334. }
  335. }
  336. else
  337. {
  338. m_iszSoundScriptName = AllocPooledString( pSoundName );
  339. m_flScriptVolume = scriptVolume;
  340. m_soundlevel = soundlevel;
  341. }
  342. m_iszSoundName = AllocPooledString( pSoundName );
  343. m_volume.SetValue( 0 );
  344. m_pitch.SetValue( 0 );
  345. m_isPlaying = false;
  346. m_shutdownTime = 0;
  347. m_Filter.Init( pFilter );
  348. m_baseFlags = 0;
  349. if( pSoundOrigin )
  350. {
  351. m_soundOrigin.x = pSoundOrigin->x;
  352. m_soundOrigin.y = pSoundOrigin->y;
  353. m_soundOrigin.z = pSoundOrigin->z;
  354. }
  355. #ifdef _DEBUG
  356. if ( pEnt )
  357. {
  358. m_iszClassName = AllocPooledString( pEnt->GetClassname() );
  359. }
  360. #endif
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: Ramps the pitch to a new value
  364. // Input : pitchTarget - new value
  365. // deltaTime - seconds to reach the value
  366. //-----------------------------------------------------------------------------
  367. void CSoundPatch::ChangePitch( float pitchTarget, float deltaTime )
  368. {
  369. m_flags |= SND_CHANGE_PITCH;
  370. m_pitch.SetTarget( pitchTarget, deltaTime );
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: Ramps the volume to a new value
  374. // Input : volumeTarget - new volume
  375. // deltaTime - seconds to reach the new volume
  376. //-----------------------------------------------------------------------------
  377. void CSoundPatch::ChangeVolume( float volumeTarget, float deltaTime )
  378. {
  379. m_flags |= SND_CHANGE_VOL;
  380. if ( volumeTarget > 1.0 )
  381. volumeTarget = 1.0;
  382. m_volume.SetTarget( volumeTarget, deltaTime );
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Fade volume to zero AND SHUT DOWN THIS SOUND
  386. // Input : deltaTime - seconds before done/shutdown
  387. //-----------------------------------------------------------------------------
  388. void CSoundPatch::FadeOut( float deltaTime, bool destroyOnFadeout )
  389. {
  390. ChangeVolume( 0, deltaTime );
  391. if ( !destroyOnFadeout )
  392. {
  393. m_shutdownTime = g_pEffects->Time() + deltaTime;
  394. }
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Purpose: Get the sound's current pitch
  398. //-----------------------------------------------------------------------------
  399. float CSoundPatch::GetPitch( void )
  400. {
  401. return m_pitch.Value();
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: Get the sound's current volume
  405. //-----------------------------------------------------------------------------
  406. float CSoundPatch::GetVolume( void )
  407. {
  408. return m_volume.Value();
  409. }
  410. #ifdef CLIENT_DLL
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Get the playing status of the sound
  413. // Returns: Sounds playing status from the engine
  414. //-----------------------------------------------------------------------------
  415. bool CSoundPatch::IsStillPlaying( void )
  416. {
  417. return enginesound->IsSoundStillPlaying(m_guid);
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: Get the sound's current elapsed time
  421. // Returns: Time in seconds
  422. //-----------------------------------------------------------------------------
  423. float CSoundPatch::GetElapsedTime( void )
  424. {
  425. // convert to seconds
  426. return enginesound->GetElapsedTimeByGuid(m_guid) * 0.01;
  427. }
  428. #endif
  429. //-----------------------------------------------------------------------------
  430. // Returns the ent index
  431. //-----------------------------------------------------------------------------
  432. inline int CSoundPatch::EntIndex() const
  433. {
  434. Assert( !m_hEnt.IsValid() || m_hEnt.Get() );
  435. return m_hEnt.Get() ? m_hEnt->entindex() : -1;
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: SoundPatches take volumes between 0 & 1, and use that to multiply the sounds.txt specified volume.
  439. // This function is an internal method of accessing the real volume passed into the engine (i.e. post multiply)
  440. // Output : float
  441. //-----------------------------------------------------------------------------
  442. float CSoundPatch::GetVolumeForEngine( void )
  443. {
  444. return ( m_flScriptVolume * m_volume.Value() );
  445. }
  446. //-----------------------------------------------------------------------------
  447. // Purpose: Stop the sound
  448. //-----------------------------------------------------------------------------
  449. void CSoundPatch::Shutdown( void )
  450. {
  451. // Msg( "Removing sound %s\n", m_pszSoundName );
  452. if ( m_isPlaying )
  453. {
  454. int entIndex = -1;
  455. if ( m_hEnt.Get() )
  456. {
  457. entIndex = EntIndex();
  458. }
  459. else
  460. {
  461. // may have deleted the entity after starting the sound, but before stopping the sound, try the saved index
  462. // this will handle that case so a sound patch doesn't get stuck on
  463. entIndex = m_soundEntityIndex;
  464. }
  465. Assert( entIndex >= 0 );
  466. // BUGBUG: Don't crash in release mode
  467. if ( entIndex >= 0 )
  468. {
  469. if( m_hSoundScriptHash != SOUNDEMITTER_INVALID_HASH )
  470. {
  471. CBaseEntity::StopSound( entIndex, STRING( m_iszSoundScriptName ) );
  472. }
  473. else
  474. {
  475. CBaseEntity::StopSound( entIndex, m_entityChannel, STRING( m_iszSoundName ) );
  476. }
  477. }
  478. m_isPlaying = false;
  479. }
  480. }
  481. //-----------------------------------------------------------------------------
  482. // Purpose: Update all envelopes and send appropriate data to the client
  483. // Input : time - new global clock
  484. // deltaTime - amount of time that has passed
  485. // Output : Returns true on success, false on failure.
  486. //-----------------------------------------------------------------------------
  487. bool CSoundPatch::Update( float time, float deltaTime )
  488. {
  489. VPROF( "CSoundPatch::Update" );
  490. if ( m_shutdownTime && time > m_shutdownTime )
  491. {
  492. Shutdown();
  493. return false;
  494. }
  495. if ( EntIndex() < 0 )
  496. {
  497. // FIXME: The pointer to this soundpatch is probably leaked since no entity is around to clean it up (ywb)
  498. DevWarning( "CSoundPatch::Update: Removing CSoundPatch (%s) with NULL EHandle\n", STRING(m_iszSoundName) );
  499. return false;
  500. }
  501. if ( m_pitch.ShouldUpdate() )
  502. {
  503. m_pitch.Update( deltaTime );
  504. m_flags |= SND_CHANGE_PITCH;
  505. }
  506. else
  507. {
  508. m_flags &= ~SND_CHANGE_PITCH;
  509. }
  510. if ( m_volume.ShouldUpdate() )
  511. {
  512. m_volume.Update( deltaTime );
  513. m_flags |= SND_CHANGE_VOL;
  514. }
  515. else
  516. {
  517. m_flags &= ~SND_CHANGE_VOL;
  518. }
  519. // if ( m_flags && m_Filter.IsActive() )
  520. if ( m_flags )
  521. {
  522. // SoundPatches take volumes between 0 & 1, and use that to multiply the sounds.txt specified volume.
  523. // Because of this, we need to always set the SND_CHANGE_VOL flag when we emit sound, or it'll use the scriptfile's instead.
  524. m_flags |= SND_CHANGE_VOL;
  525. EmitSound_t ep;
  526. ep.m_nChannel = m_entityChannel;
  527. ep.m_pSoundName = STRING(m_iszSoundName);
  528. ep.m_flVolume = GetVolumeForEngine();
  529. ep.m_SoundLevel = m_soundlevel;
  530. ep.m_nFlags = m_flags;
  531. ep.m_nPitch = (int)m_pitch.Value();
  532. ep.m_hSoundScriptHash = m_hSoundScriptHash;
  533. ep.m_nSoundEntryVersion = m_nSoundEntryVersion;
  534. // only pass the position if it's coming from the world
  535. if( EntIndex() == 0 )
  536. ep.m_pOrigin = &m_soundOrigin;
  537. CBaseEntity::EmitSound( m_Filter, EntIndex(), ep );
  538. m_flags = 0;
  539. }
  540. return true;
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Purpose: Sound is going to start playing again, clear any shutdown time
  544. //-----------------------------------------------------------------------------
  545. void CSoundPatch::Reset( void )
  546. {
  547. m_shutdownTime = 0;
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose: Start playing the sound - send updates to the client
  551. //-----------------------------------------------------------------------------
  552. void CSoundPatch::StartSound( float flStartTime )
  553. {
  554. // Msg( "Start sound %s\n", m_pszSoundName );
  555. m_flags = 0;
  556. if ( m_Filter.IsActive() )
  557. {
  558. EmitSound_t ep;
  559. ep.m_nChannel = m_entityChannel;
  560. ep.m_pSoundName = STRING(m_iszSoundName);
  561. ep.m_flVolume = GetVolumeForEngine();
  562. ep.m_SoundLevel = m_soundlevel;
  563. ep.m_hSoundScriptHash = m_hSoundScriptHash;
  564. ep.m_nSoundEntryVersion = m_nSoundEntryVersion;
  565. // only pass the position if it's coming from the world
  566. if( EntIndex() == 0 )
  567. ep.m_pOrigin = &m_soundOrigin;
  568. if ( V_stristr( STRING(m_iszSoundName), "music" ) )
  569. {
  570. ep.m_nFlags = m_baseFlags;
  571. }
  572. else
  573. {
  574. ep.m_nFlags = (SND_CHANGE_VOL | m_baseFlags);
  575. }
  576. ep.m_nFlags |= SND_GENERATE_GUID; // We need GUID in all cases, even for threaded sounds
  577. ep.m_nPitch = (int)m_pitch.Value();
  578. ep.m_bEmitCloseCaption = false;
  579. if ( flStartTime )
  580. {
  581. ep.m_flSoundTime = flStartTime;
  582. }
  583. //#ifdef CLIENT_DLL
  584. #ifdef ___NOT
  585. if ( V_stristr( STRING(m_iszSoundName), "music" ) )
  586. {
  587. // Don't play synchronously - we'll get it with the volume adjustments
  588. //engine->ClientCmd( VarArgs("play %s\n", ep.m_pSoundName) );
  589. }
  590. else
  591. #endif
  592. {
  593. #ifdef CLIENT_DLL
  594. m_guid = CBaseEntity::EmitSound( m_Filter, EntIndex(), ep );
  595. #else
  596. CBaseEntity::EmitSound( m_Filter, EntIndex(), ep );
  597. #endif
  598. }
  599. CBaseEntity::EmitCloseCaption( m_Filter, EntIndex(), STRING( m_iszSoundScriptName ), ep.m_UtlVecSoundOrigin, m_flCloseCaptionDuration, true );
  600. }
  601. m_isPlaying = true;
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose: resumes playing the sound on restore
  605. //-----------------------------------------------------------------------------
  606. void CSoundPatch::ResumeSound( void )
  607. {
  608. if ( IsPlaying() && m_Filter.IsActive() )
  609. {
  610. if ( EntIndex() >= 0 )
  611. {
  612. EmitSound_t ep;
  613. ep.m_nChannel = m_entityChannel;
  614. ep.m_pSoundName = STRING(m_iszSoundName);
  615. ep.m_flVolume = GetVolumeForEngine();
  616. ep.m_SoundLevel = m_soundlevel;
  617. ep.m_nFlags = (SND_CHANGE_VOL | SND_CHANGE_PITCH | m_baseFlags);
  618. ep.m_nPitch = (int)m_pitch.Value();
  619. ep.m_hSoundScriptHash = m_hSoundScriptHash;
  620. ep.m_nSoundEntryVersion = m_nSoundEntryVersion;
  621. // only pass the position if it's coming from the world
  622. if( EntIndex() == 0 )
  623. ep.m_pOrigin = &m_soundOrigin;
  624. CBaseEntity::EmitSound( m_Filter, EntIndex(), ep );
  625. }
  626. else
  627. {
  628. // FIXME: Lost the entity on restore. It might have been suppressed by the save/restore system.
  629. // This will probably leak the sound patch since there's no one to delete it, but the next
  630. // call to CSoundPatch::Update should at least remove it from the list of sound patches.
  631. DevWarning( "CSoundPatch::ResumeSound: Lost EHAndle on restore - destroy the sound patch in your entity's StopLoopingSounds! (%s)\n", STRING( m_iszSoundName ) );
  632. }
  633. }
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose: A new player's entered the game. See if we need to restart our sound.
  637. //-----------------------------------------------------------------------------
  638. void CSoundPatch::AddPlayerPost( CBasePlayer *pPlayer )
  639. {
  640. if ( m_Filter.IsActive() && m_Filter.AddRecipient(pPlayer) )
  641. {
  642. // Alrighty, he's new. We need to restart our sound just to him.
  643. // Create a new filter just to him.
  644. CSingleUserRecipientFilter filter( pPlayer );
  645. EmitSound_t ep;
  646. ep.m_nChannel = m_entityChannel;
  647. ep.m_pSoundName = STRING(m_iszSoundName);
  648. ep.m_flVolume = GetVolumeForEngine();
  649. ep.m_SoundLevel = m_soundlevel;
  650. ep.m_nFlags = (SND_CHANGE_VOL | m_baseFlags);
  651. ep.m_nPitch = (int)m_pitch.Value();
  652. ep.m_hSoundScriptHash = m_hSoundScriptHash;
  653. ep.m_nSoundEntryVersion = m_nSoundEntryVersion;
  654. // only pass the position if it's coming from the world
  655. if( EntIndex() == 0 )
  656. ep.m_pOrigin = &m_soundOrigin;
  657. CBaseEntity::EmitSound( filter, EntIndex(), ep );
  658. }
  659. }
  660. // This is an entry in the command queue. It's used to queue up various pitch and volume changes
  661. // so you can define an envelope without writing timing code in an entity. Existing queued commands
  662. // can be deleted later if the envelope changes dynamically.
  663. #include "tier0/memdbgoff.h"
  664. struct SoundCommand_t
  665. {
  666. SoundCommand_t( void ) { memset( this, 0, sizeof(*this) ); }
  667. SoundCommand_t( CSoundPatch *pSound, float executeTime, soundcommands_t command, float deltaTime, float value ) : m_pPatch(pSound), m_time(executeTime), m_deltaTime(deltaTime), m_command(command), m_value(value) {}
  668. CSoundPatch *m_pPatch;
  669. float m_time;
  670. float m_deltaTime;
  671. soundcommands_t m_command;
  672. float m_value;
  673. SoundCommand_t *m_pNext;
  674. DECLARE_SIMPLE_DATADESC();
  675. DECLARE_FIXEDSIZE_ALLOCATOR(SoundCommand_t);
  676. };
  677. #include "tier0/memdbgon.h"
  678. DEFINE_FIXEDSIZE_ALLOCATOR( SoundCommand_t, 32, CUtlMemoryPool::GROW_FAST );
  679. BEGIN_SIMPLE_DATADESC( SoundCommand_t )
  680. // NOTE: This doesn't need to be saved, sound commands are saved right after the patch
  681. // they are associated with
  682. // DEFINE_FIELD( m_pPatch, FIELD_????? )
  683. DEFINE_FIELD( m_time, FIELD_TIME ),
  684. DEFINE_FIELD( m_deltaTime, FIELD_FLOAT ),
  685. DEFINE_FIELD( m_command, FIELD_INTEGER ),
  686. DEFINE_FIELD( m_value, FIELD_FLOAT ),
  687. // DEFINE_FIELD( m_pNext, FIELD_????? )
  688. END_DATADESC()
  689. typedef SoundCommand_t *SOUNDCOMMANDPTR;
  690. bool SoundCommandLessFunc( const SOUNDCOMMANDPTR &lhs, const SOUNDCOMMANDPTR &rhs )
  691. {
  692. // NOTE: A greater time means "less" priority
  693. return ( lhs->m_time > rhs->m_time );
  694. }
  695. // This implements the sound controller
  696. class CSoundControllerImp : public CSoundEnvelopeController, public CAutoGameSystemPerFrame
  697. {
  698. //-----------------------------------------------------------------------------
  699. // internal functions, private to this file
  700. //-----------------------------------------------------------------------------
  701. public:
  702. CSoundControllerImp( void ) : CAutoGameSystemPerFrame( "CSoundControllerImp" )
  703. {
  704. m_commandList.SetLessFunc( SoundCommandLessFunc );
  705. }
  706. void ProcessCommand( SoundCommand_t *pCmd );
  707. void RemoveFromList( CSoundPatch *pSound );
  708. void SaveSoundPatch( CSoundPatch *pSound, ISave *pSave );
  709. void RestoreSoundPatch( CSoundPatch **ppSound, IRestore *pRestore );
  710. virtual void OnRestore();
  711. //-----------------------------------------------------------------------------
  712. // external interface functions (from CSoundEnvelopeController)
  713. //-----------------------------------------------------------------------------
  714. public:
  715. // Start this sound playing, or reset if already playing with new volume/pitch
  716. void Play( CSoundPatch *pSound, float volume, float pitch, float flStartTime = 0 );
  717. void CommandAdd( CSoundPatch *pSound, float executeDeltaTime, soundcommands_t command, float commandTime, float commandValue );
  718. void SystemReset( void );
  719. void SystemUpdate( void );
  720. void CommandClear( CSoundPatch *pSound );
  721. void Shutdown( CSoundPatch *pSound );
  722. CSoundPatch *SoundCreate( IRecipientFilter& filter, int nEntIndex, const char *pSoundName );
  723. CSoundPatch *SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel, const char *pSoundName,
  724. float attenuation, float scriptVolume = 1.0f );
  725. CSoundPatch *SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel, const char *pSoundName,
  726. float attenuation, const Vector *pSoundOrigin, float scriptVolume = 1.0f );
  727. CSoundPatch *SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel, const char *pSoundName,
  728. soundlevel_t soundlevel );
  729. CSoundPatch *SoundCreate( IRecipientFilter& filter, int nEntIndex, const EmitSound_t &es );
  730. void SoundDestroy( CSoundPatch *pSound );
  731. void SoundChangePitch( CSoundPatch *pSound, float pitchTarget, float deltaTime );
  732. void SoundChangeVolume( CSoundPatch *pSound, float volumeTarget, float deltaTime );
  733. void SoundFadeOut( CSoundPatch *pSound, float deltaTime, bool destroyOnFadeout );
  734. float SoundGetPitch( CSoundPatch *pSound );
  735. float SoundGetVolume( CSoundPatch *pSound );
  736. #ifdef CLIENT_DLL
  737. float SoundGetElapsedTime( CSoundPatch *pSound );
  738. bool SoundIsStillPlaying( CSoundPatch *pSound );
  739. int SoundGetGuid( CSoundPatch *pSound );
  740. #endif
  741. string_t SoundGetName( CSoundPatch *pSound ) { return pSound->GetName(); }
  742. string_t SoundGetScriptName( CSoundPatch *pSound ) { return pSound->GetScriptName(); }
  743. void SoundSetCloseCaptionDuration( CSoundPatch *pSound, float flDuration ) { pSound->SetCloseCaptionDuration(flDuration); }
  744. float SoundPlayEnvelope( CSoundPatch *pSound, soundcommands_t soundCommand, envelopePoint_t *points, int numPoints );
  745. float SoundPlayEnvelope( CSoundPatch *pSound, soundcommands_t soundCommand, envelopeDescription_t *envelope );
  746. void CheckLoopingSoundsForPlayer( CBasePlayer *pPlayer );
  747. // Inserts the command into the list, sorted by time
  748. void CommandInsert( SoundCommand_t *pCommand );
  749. #ifdef CLIENT_DLL
  750. // CAutoClientSystem
  751. virtual void Update( float frametime )
  752. {
  753. SystemUpdate();
  754. }
  755. #else
  756. virtual void PreClientUpdate()
  757. {
  758. SystemUpdate();
  759. }
  760. #endif
  761. virtual void LevelShutdownPreEntity()
  762. {
  763. SystemReset();
  764. }
  765. private:
  766. CUtlVector<CSoundPatch *> m_soundList;
  767. CUtlPriorityQueue<SoundCommand_t *> m_commandList;
  768. float m_flLastTime;
  769. };
  770. // Execute a command from the list
  771. // currently only 3 commands
  772. // UNDONE: Add start command?
  773. void CSoundControllerImp::ProcessCommand( SoundCommand_t *pCmd )
  774. {
  775. switch( pCmd->m_command )
  776. {
  777. case SOUNDCTRL_CHANGE_VOLUME:
  778. pCmd->m_pPatch->ChangeVolume( pCmd->m_value, pCmd->m_deltaTime );
  779. break;
  780. case SOUNDCTRL_CHANGE_PITCH:
  781. pCmd->m_pPatch->ChangePitch( pCmd->m_value, pCmd->m_deltaTime );
  782. break;
  783. case SOUNDCTRL_STOP:
  784. pCmd->m_pPatch->Shutdown();
  785. break;
  786. case SOUNDCTRL_DESTROY:
  787. RemoveFromList( pCmd->m_pPatch );
  788. delete pCmd->m_pPatch;
  789. pCmd->m_pPatch = NULL;
  790. break;
  791. }
  792. }
  793. //-----------------------------------------------------------------------------
  794. // Purpose: Remove this sound from the sound list & shutdown (not in external interface)
  795. // Input : *pSound - patch to remove
  796. //-----------------------------------------------------------------------------
  797. void CSoundControllerImp::RemoveFromList( CSoundPatch *pSound )
  798. {
  799. m_soundList.FindAndRemove( pSound );
  800. pSound->Shutdown();
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Start this sound playing, or reset if already playing with new volume/pitch
  804. //-----------------------------------------------------------------------------
  805. void CSoundControllerImp::Play( CSoundPatch *pSound, float volume, float pitch, float flStartTime )
  806. {
  807. // reset the vars
  808. pSound->Reset();
  809. pSound->ChangeVolume( volume, 0 );
  810. pSound->ChangePitch( pitch, 0 );
  811. if ( pSound->IsPlaying() )
  812. {
  813. // remove any previous commands in the queue
  814. CommandClear( pSound );
  815. }
  816. else
  817. {
  818. m_soundList.AddToTail( pSound );
  819. pSound->StartSound( flStartTime );
  820. }
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Inserts the command into the list, sorted by time
  824. //-----------------------------------------------------------------------------
  825. void CSoundControllerImp::CommandInsert( SoundCommand_t *pCommand )
  826. {
  827. m_commandList.Insert( pCommand );
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose: puts a command into the queue
  831. // Input : *pSound - patch this command affects
  832. // executeDeltaTime - relative time to execute this command
  833. // command - command to execute (SOUNDCTRL_*)
  834. // commandTime - commands have 2 parameters, a time and a value
  835. // value -
  836. // Output : void
  837. //-----------------------------------------------------------------------------
  838. void CSoundControllerImp::CommandAdd( CSoundPatch *pSound, float executeDeltaTime, soundcommands_t command, float commandTime, float commandValue )
  839. {
  840. SoundCommand_t *pCommand = new SoundCommand_t( pSound, g_pEffects->Time() + executeDeltaTime, command, commandTime, commandValue );
  841. CommandInsert( pCommand );
  842. }
  843. // Reset the whole system (level change, etc.)
  844. void CSoundControllerImp::SystemReset( void )
  845. {
  846. for ( int i = m_soundList.Count()-1; i >=0; i-- )
  847. {
  848. CSoundPatch *pNode = m_soundList[i];
  849. // shutdown all active sounds
  850. pNode->Shutdown();
  851. }
  852. // clear the list
  853. m_soundList.Purge();
  854. // clear the command queue
  855. m_commandList.RemoveAll();
  856. }
  857. //-----------------------------------------------------------------------------
  858. // Purpose: Update the active sounds, dequeue any events and move the ramps
  859. //-----------------------------------------------------------------------------
  860. void CSoundControllerImp::SystemUpdate( void )
  861. {
  862. VPROF( "CSoundControllerImp::SystemUpdate" );
  863. float time = g_pEffects->Time();
  864. float deltaTime = time - m_flLastTime;
  865. // handle clock resets
  866. if ( deltaTime < 0 )
  867. deltaTime = 0;
  868. m_flLastTime = time;
  869. {
  870. VPROF( "CSoundControllerImp::SystemUpdate:processcommandlist" );
  871. while ( m_commandList.Count() )
  872. {
  873. SoundCommand_t *pCmd = m_commandList.ElementAtHead();
  874. // Commands are sorted by time.
  875. // process any that should occur by the current time
  876. if ( time >= pCmd->m_time )
  877. {
  878. m_commandList.RemoveAtHead();
  879. ProcessCommand( pCmd );
  880. delete pCmd;
  881. }
  882. else
  883. {
  884. break;
  885. }
  886. }
  887. }
  888. // NOTE: Because this loop goes from the end to the beginning
  889. // we can fast remove inside it without breaking the indexing
  890. {
  891. VPROF( "CSoundControllerImp::SystemUpdate:removesounds" );
  892. for ( int i = m_soundList.Count()-1; i >=0; i-- )
  893. {
  894. CSoundPatch *pNode = m_soundList[i];
  895. if ( !pNode->Update( time, deltaTime ) )
  896. {
  897. pNode->Reset();
  898. m_soundList.FastRemove( i );
  899. }
  900. }
  901. }
  902. }
  903. // Remove any envelope commands from the list (dynamically changing envelope)
  904. void CSoundControllerImp::CommandClear( CSoundPatch *pSound )
  905. {
  906. for ( int i = m_commandList.Count()-1; i >= 0; i-- )
  907. {
  908. SoundCommand_t *pCmd = m_commandList.Element( i );
  909. if ( pCmd->m_pPatch == pSound )
  910. {
  911. m_commandList.RemoveAt(i);
  912. delete pCmd;
  913. }
  914. }
  915. }
  916. //-----------------------------------------------------------------------------
  917. // Saves the sound patch + associated commands
  918. //-----------------------------------------------------------------------------
  919. void CSoundControllerImp::SaveSoundPatch( CSoundPatch *pSoundPatch, ISave *pSave )
  920. {
  921. int i;
  922. // Write out the sound patch
  923. pSave->StartBlock();
  924. pSave->WriteAll( pSoundPatch );
  925. pSave->EndBlock();
  926. // Count the number of commands that refer to the sound patch
  927. int nCount = 0;
  928. for ( i = m_commandList.Count()-1; i >= 0; i-- )
  929. {
  930. SoundCommand_t *pCmd = m_commandList.Element( i );
  931. if ( pCmd->m_pPatch == pSoundPatch )
  932. {
  933. nCount++;
  934. }
  935. }
  936. // Write out the number of commands, followed by each command itself
  937. pSave->StartBlock();
  938. pSave->WriteInt( &nCount );
  939. for ( i = m_commandList.Count()-1; i >= 0; i-- )
  940. {
  941. SoundCommand_t *pCmd = m_commandList.Element( i );
  942. if ( pCmd->m_pPatch == pSoundPatch )
  943. {
  944. pSave->StartBlock();
  945. pSave->WriteAll( pCmd );
  946. pSave->EndBlock();
  947. }
  948. }
  949. pSave->EndBlock();
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Restores the sound patch + associated commands
  953. //-----------------------------------------------------------------------------
  954. void CSoundControllerImp::RestoreSoundPatch( CSoundPatch **ppSoundPatch, IRestore *pRestore )
  955. {
  956. CSoundPatch *pPatch = new CSoundPatch;
  957. // read the sound patch data from the memory block
  958. pRestore->StartBlock();
  959. bool bOk = ( pRestore->ReadAll( pPatch ) != 0 );
  960. pRestore->EndBlock();
  961. bOk = (bOk && pPatch->IsPlaying()) ? true : false;
  962. if (bOk)
  963. {
  964. m_soundList.AddToTail( pPatch );
  965. }
  966. // Count the number of commands that refer to the sound patch
  967. pRestore->StartBlock();
  968. if ( bOk )
  969. {
  970. int nCount;
  971. pRestore->ReadInt( &nCount );
  972. while ( --nCount >= 0 )
  973. {
  974. SoundCommand_t *pCommand = new SoundCommand_t;
  975. pRestore->StartBlock();
  976. if ( pRestore->ReadAll( pCommand ) )
  977. {
  978. pCommand->m_pPatch = pPatch;
  979. CommandInsert( pCommand );
  980. }
  981. pRestore->EndBlock();
  982. }
  983. }
  984. pRestore->EndBlock();
  985. *ppSoundPatch = pPatch;
  986. }
  987. //-----------------------------------------------------------------------------
  988. // Purpose: immediately stop playing this sound
  989. // Input : *pSound - Patch to shut down
  990. //-----------------------------------------------------------------------------
  991. void CSoundControllerImp::Shutdown( CSoundPatch *pSound )
  992. {
  993. if ( !pSound )
  994. return;
  995. pSound->Shutdown();
  996. CommandClear( pSound );
  997. RemoveFromList( pSound );
  998. }
  999. CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, const char *pSoundName )
  1000. {
  1001. CSoundPatch *pSound = new CSoundPatch;
  1002. // FIXME: This is done so we don't have to futz with the public interface
  1003. IHandleEntity* pEnt = ( nEntIndex != -1 ) ? g_pEntityList->LookupEntityByNetworkIndex( nEntIndex ) : nullptr;
  1004. pSound->Init( &filter, static_cast<CBaseEntity*>(pEnt), CHAN_AUTO, pSoundName, SNDLVL_NORM, NULL );
  1005. return pSound;
  1006. }
  1007. CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel,
  1008. const char *pSoundName, float attenuation, float scriptVolume )
  1009. {
  1010. CSoundPatch *pSound = new CSoundPatch;
  1011. IHandleEntity* pEnt = ( nEntIndex != -1 ) ? g_pEntityList->LookupEntityByNetworkIndex( nEntIndex ) : nullptr;
  1012. pSound->Init( &filter, static_cast<CBaseEntity*>(pEnt), channel, pSoundName, ATTN_TO_SNDLVL( attenuation ), NULL, scriptVolume );
  1013. return pSound;
  1014. }
  1015. CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel,
  1016. const char *pSoundName, float attenuation, const Vector *pSoundOrigin, float scriptVolume )
  1017. {
  1018. CSoundPatch *pSound = new CSoundPatch;
  1019. IHandleEntity* pEnt = ( nEntIndex != -1 ) ? g_pEntityList->LookupEntityByNetworkIndex( nEntIndex ) : nullptr;
  1020. pSound->Init( &filter, static_cast<CBaseEntity*>(pEnt), channel, pSoundName, ATTN_TO_SNDLVL( attenuation ), pSoundOrigin, scriptVolume );
  1021. return pSound;
  1022. }
  1023. CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, int channel,
  1024. const char *pSoundName, soundlevel_t soundlevel )
  1025. {
  1026. CSoundPatch *pSound = new CSoundPatch;
  1027. IHandleEntity* pEnt = ( nEntIndex != -1 ) ? g_pEntityList->LookupEntityByNetworkIndex( nEntIndex ) : nullptr;
  1028. pSound->Init( &filter, static_cast<CBaseEntity*>(pEnt), channel, pSoundName, soundlevel, NULL );
  1029. return pSound;
  1030. }
  1031. CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, const EmitSound_t &es )
  1032. {
  1033. CSoundPatch *pSound = new CSoundPatch;
  1034. // FIXME: This is done so we don't have to futz with the public interface
  1035. IHandleEntity* pEnt = ( nEntIndex != -1 ) ? g_pEntityList->LookupEntityByNetworkIndex( nEntIndex ) : nullptr;
  1036. pSound->Init( &filter, static_cast<CBaseEntity*>(pEnt), es.m_nChannel, es.m_pSoundName, es.m_SoundLevel, es.m_pOrigin );
  1037. pSound->ChangeVolume( es.m_flVolume, 0 );
  1038. pSound->ChangePitch( es.m_nPitch, 0 );
  1039. if ( es.m_nFlags & SND_SHOULDPAUSE )
  1040. {
  1041. pSound->SetBaseFlags( SND_SHOULDPAUSE );
  1042. }
  1043. return pSound;
  1044. }
  1045. void CSoundControllerImp::SoundDestroy( CSoundPatch *pSound )
  1046. {
  1047. if ( !pSound )
  1048. return;
  1049. Shutdown( pSound );
  1050. delete pSound;
  1051. }
  1052. void CSoundControllerImp::SoundChangePitch( CSoundPatch *pSound, float pitchTarget, float deltaTime )
  1053. {
  1054. pSound->ChangePitch( pitchTarget, deltaTime );
  1055. }
  1056. void CSoundControllerImp::SoundChangeVolume( CSoundPatch *pSound, float volumeTarget, float deltaTime )
  1057. {
  1058. pSound->ChangeVolume( volumeTarget, deltaTime );
  1059. }
  1060. #ifdef CLIENT_DLL
  1061. int CSoundControllerImp::SoundGetGuid( CSoundPatch *pSound )
  1062. {
  1063. return pSound->GetGuid();
  1064. }
  1065. float CSoundControllerImp::SoundGetElapsedTime( CSoundPatch *pSound )
  1066. {
  1067. return pSound->GetElapsedTime();
  1068. }
  1069. bool CSoundControllerImp::SoundIsStillPlaying( CSoundPatch *pSound )
  1070. {
  1071. return pSound->IsStillPlaying();
  1072. }
  1073. #endif
  1074. float CSoundControllerImp::SoundGetPitch( CSoundPatch *pSound )
  1075. {
  1076. return pSound->GetPitch();
  1077. }
  1078. float CSoundControllerImp::SoundGetVolume( CSoundPatch *pSound )
  1079. {
  1080. return pSound->GetVolume();
  1081. }
  1082. void CSoundControllerImp::SoundFadeOut( CSoundPatch *pSound, float deltaTime, bool destroyOnFadeout )
  1083. {
  1084. if ( destroyOnFadeout && (deltaTime == 0.0f) )
  1085. {
  1086. SoundDestroy( pSound );
  1087. return;
  1088. }
  1089. pSound->FadeOut( deltaTime, destroyOnFadeout );
  1090. if ( destroyOnFadeout )
  1091. {
  1092. CommandAdd( pSound, deltaTime, SOUNDCTRL_DESTROY, 0.0f, 0.0f );
  1093. }
  1094. }
  1095. //-----------------------------------------------------------------------------
  1096. // Purpose: Queue a list of envelope points into a sound patch's event list
  1097. // Input : *pSound - The sound patch to be operated on
  1098. // soundCommand - Type of operation the envelope describes
  1099. // *points - List of enevelope points
  1100. // numPoints - Number of points provided
  1101. // Output : float - Returns the total duration of the envelope
  1102. //-----------------------------------------------------------------------------
  1103. float CSoundControllerImp::SoundPlayEnvelope( CSoundPatch *pSound, soundcommands_t soundCommand, envelopePoint_t *points, int numPoints )
  1104. {
  1105. float amplitude = 0.0f;
  1106. float duration = 0.0f;
  1107. float totalDuration = 0.0f;
  1108. Assert( points );
  1109. // Clear out all previously acting commands
  1110. CommandClear( pSound );
  1111. // Evaluate and queue all points
  1112. for ( int i = 0; i < numPoints; i++ )
  1113. {
  1114. // See if we're keeping our last amplitude for this new point
  1115. if ( ( points[i].amplitudeMin != -1.0f ) || ( points[i].amplitudeMax != -1.0f ) )
  1116. {
  1117. amplitude = random->RandomFloat( points[i].amplitudeMin, points[i].amplitudeMax );
  1118. }
  1119. else if ( i == 0 )
  1120. {
  1121. // Can't do this on the first entry
  1122. Msg( "Invalid starting amplitude value in envelope! (Cannot be -1)\n" );
  1123. }
  1124. // See if we're keeping our last duration for this new point
  1125. if ( ( points[i].durationMin != -1.0f ) || ( points[i].durationMax != -1.0f ) )
  1126. {
  1127. duration = random->RandomFloat( points[i].durationMin, points[i].durationMax );
  1128. //duration = points[i].durationMin;
  1129. }
  1130. else if ( i == 0 )
  1131. {
  1132. // Can't do this on the first entry
  1133. Msg( "Invalid starting duration value in envelope! (Cannot be -1)\n" );
  1134. }
  1135. // Queue the command
  1136. CommandAdd( pSound, totalDuration, soundCommand, duration, amplitude );
  1137. // Tack this command's duration onto the running duration
  1138. totalDuration += duration;
  1139. }
  1140. return totalDuration;
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. // Purpose: Queue a list of envelope points into a sound patch's event list
  1144. // Input : *pSound - The sound patch to be operated on
  1145. // soundCommand - Type of operation the envelope describes
  1146. // *envelope - The envelope description to be queued
  1147. // Output : float - Returns the total duration of the envelope
  1148. //-----------------------------------------------------------------------------
  1149. float CSoundControllerImp::SoundPlayEnvelope( CSoundPatch *pSound, soundcommands_t soundCommand, envelopeDescription_t *envelope )
  1150. {
  1151. return SoundPlayEnvelope( pSound, soundCommand, envelope->pPoints, envelope->nNumPoints );
  1152. }
  1153. //-----------------------------------------------------------------------------
  1154. // Purpose: Looping sounds are often started in entity spawn/activate functions.
  1155. // In singleplayer, the player's not ready to receive sounds then, so restart
  1156. // and SoundPatches that are active and have no receivers.
  1157. //-----------------------------------------------------------------------------
  1158. void CSoundControllerImp::CheckLoopingSoundsForPlayer( CBasePlayer *pPlayer )
  1159. {
  1160. for ( int i = m_soundList.Count()-1; i >=0; i-- )
  1161. {
  1162. CSoundPatch *pNode = m_soundList[i];
  1163. pNode->AddPlayerPost( pPlayer );
  1164. }
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. // Purpose: Resumes saved soundpatches
  1168. //-----------------------------------------------------------------------------
  1169. void CSoundControllerImp::OnRestore()
  1170. {
  1171. for ( int i = m_soundList.Count()-1; i >=0; i-- )
  1172. {
  1173. CSoundPatch *pNode = m_soundList[i];
  1174. if ( pNode && pNode->IsPlaying() )
  1175. {
  1176. pNode->ResumeSound();
  1177. }
  1178. }
  1179. }
  1180. //-----------------------------------------------------------------------------
  1181. // Singleton accessors
  1182. //-----------------------------------------------------------------------------
  1183. static CSoundControllerImp g_Controller;
  1184. CSoundEnvelopeController &CSoundEnvelopeController::GetController( void )
  1185. {
  1186. return g_Controller;
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Queues up sound patches to save/load
  1190. //-----------------------------------------------------------------------------
  1191. class CSoundPatchSaveRestoreOps : public CClassPtrSaveRestoreOps
  1192. {
  1193. public:
  1194. virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
  1195. {
  1196. pSave->StartBlock();
  1197. int nSoundPatchCount = fieldInfo.pTypeDesc->fieldSize;
  1198. CSoundPatch **ppSoundPatch = (CSoundPatch**)fieldInfo.pField;
  1199. while ( --nSoundPatchCount >= 0 )
  1200. {
  1201. // Write out commands associated with this sound patch
  1202. g_Controller.SaveSoundPatch( *ppSoundPatch, pSave );
  1203. ++ppSoundPatch;
  1204. }
  1205. pSave->EndBlock();
  1206. }
  1207. virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
  1208. {
  1209. pRestore->StartBlock();
  1210. int nSoundPatchCount = fieldInfo.pTypeDesc->fieldSize;
  1211. CSoundPatch **ppSoundPatch = (CSoundPatch**)fieldInfo.pField;
  1212. while ( --nSoundPatchCount >= 0 )
  1213. {
  1214. // Write out commands associated with this sound patch
  1215. g_Controller.RestoreSoundPatch( ppSoundPatch, pRestore );
  1216. ++ppSoundPatch;
  1217. }
  1218. pRestore->EndBlock();
  1219. }
  1220. };
  1221. static CSoundPatchSaveRestoreOps s_SoundPatchSaveRestoreOps;
  1222. ISaveRestoreOps *GetSoundSaveRestoreOps( )
  1223. {
  1224. return &s_SoundPatchSaveRestoreOps;
  1225. }