Team Fortress 2 Source Code as on 22/4/2020
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.

1324 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Soundscapes.txt resource file processor
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include <KeyValues.h>
  9. #include "engine/IEngineSound.h"
  10. #include "filesystem.h"
  11. #include "SoundEmitterSystem/isoundemittersystembase.h"
  12. #include "soundchars.h"
  13. #include "view.h"
  14. #include "engine/ivdebugoverlay.h"
  15. #include "tier0/icommandline.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. // Only allow recursive references to be 8 levels deep.
  19. // This test will flag any circular references and bail.
  20. #define MAX_SOUNDSCAPE_RECURSION 8
  21. const float DEFAULT_SOUND_RADIUS = 36.0f;
  22. // Keep an array of all looping sounds so they can be faded in/out
  23. // OPTIMIZE: Get a handle/pointer to the engine's sound channel instead
  24. // of searching each frame!
  25. struct loopingsound_t
  26. {
  27. Vector position; // position (if !isAmbient)
  28. const char *pWaveName; // name of the wave file
  29. float volumeTarget; // target volume level (fading towards this)
  30. float volumeCurrent; // current volume level
  31. soundlevel_t soundlevel; // sound level (if !isAmbient)
  32. int pitch; // pitch shift
  33. int id; // Used to fade out sounds that don't belong to the most current setting
  34. bool isAmbient; // Ambient sounds have no spatialization - they play from everywhere
  35. };
  36. ConVar soundscape_fadetime( "soundscape_fadetime", "3.0", FCVAR_CHEAT, "Time to crossfade sound effects between soundscapes" );
  37. #include "interval.h"
  38. struct randomsound_t
  39. {
  40. Vector position;
  41. float nextPlayTime; // time to play a sound from the set
  42. interval_t time;
  43. interval_t volume;
  44. interval_t pitch;
  45. interval_t soundlevel;
  46. float masterVolume;
  47. int waveCount;
  48. bool isAmbient;
  49. bool isRandom;
  50. KeyValues *pWaves;
  51. void Init()
  52. {
  53. memset( this, 0, sizeof(*this) );
  54. }
  55. };
  56. struct subsoundscapeparams_t
  57. {
  58. int recurseLevel; // test for infinite loops in the script / circular refs
  59. float masterVolume;
  60. int startingPosition;
  61. int positionOverride; // forces all sounds to this position
  62. int ambientPositionOverride; // forces all ambient sounds to this position
  63. bool allowDSP;
  64. bool wroteSoundMixer;
  65. bool wroteDSPVolume;
  66. };
  67. class C_SoundscapeSystem : public CBaseGameSystemPerFrame
  68. {
  69. public:
  70. virtual char const *Name() { return "C_SoundScapeSystem"; }
  71. C_SoundscapeSystem()
  72. {
  73. m_nRestoreFrame = -1;
  74. }
  75. ~C_SoundscapeSystem() {}
  76. void OnStopAllSounds()
  77. {
  78. m_params.ent.Set( NULL );
  79. m_params.soundscapeIndex = -1;
  80. m_loopingSounds.Purge();
  81. m_randomSounds.Purge();
  82. }
  83. // IClientSystem hooks, not needed
  84. virtual void LevelInitPreEntity()
  85. {
  86. Shutdown();
  87. Init();
  88. TouchSoundFiles();
  89. }
  90. virtual void LevelInitPostEntity()
  91. {
  92. if ( !m_pSoundMixerVar )
  93. {
  94. m_pSoundMixerVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
  95. }
  96. if ( !m_pDSPVolumeVar )
  97. {
  98. m_pDSPVolumeVar = (ConVar *)cvar->FindVar( "dsp_volume" );
  99. }
  100. }
  101. // The level is shutdown in two parts
  102. virtual void LevelShutdownPreEntity() {}
  103. // Entities are deleted / released here...
  104. virtual void LevelShutdownPostEntity()
  105. {
  106. OnStopAllSounds();
  107. }
  108. virtual void OnSave() {}
  109. virtual void OnRestore()
  110. {
  111. m_nRestoreFrame = gpGlobals->framecount;
  112. }
  113. virtual void SafeRemoveIfDesired() {}
  114. // Called before rendering
  115. virtual void PreRender() { }
  116. // Called after rendering
  117. virtual void PostRender() { }
  118. // IClientSystem hooks used
  119. virtual bool Init();
  120. virtual void Shutdown();
  121. // Gets called each frame
  122. virtual void Update( float frametime );
  123. void PrintDebugInfo()
  124. {
  125. Msg( "\n------- CLIENT SOUNDSCAPES -------\n" );
  126. for ( int i=0; i < m_soundscapes.Count(); i++ )
  127. {
  128. Msg( "- %d: %s\n", i, m_soundscapes[i]->GetName() );
  129. }
  130. if ( m_forcedSoundscapeIndex >= 0 )
  131. {
  132. Msg( "- PLAYING DEBUG SOUNDSCAPE: %d [%s]\n", m_forcedSoundscapeIndex, SoundscapeNameByIndex(m_forcedSoundscapeIndex) );
  133. }
  134. Msg( "- CURRENT SOUNDSCAPE: %d [%s]\n", m_params.soundscapeIndex.Get(), SoundscapeNameByIndex(m_params.soundscapeIndex) );
  135. Msg( "----------------------------------\n\n" );
  136. }
  137. // local functions
  138. void UpdateAudioParams( audioparams_t &audio );
  139. void GetAudioParams( audioparams_t &out ) const { out = m_params; }
  140. int GetCurrentSoundscape()
  141. {
  142. if ( m_forcedSoundscapeIndex >= 0 )
  143. return m_forcedSoundscapeIndex;
  144. return m_params.soundscapeIndex;
  145. }
  146. void DevReportSoundscapeName( int index );
  147. void UpdateLoopingSounds( float frametime );
  148. int AddLoopingAmbient( const char *pSoundName, float volume, int pitch );
  149. void UpdateLoopingSound( loopingsound_t &loopSound );
  150. void StopLoopingSound( loopingsound_t &loopSound );
  151. int AddLoopingSound( const char *pSoundName, bool isAmbient, float volume,
  152. soundlevel_t soundLevel, int pitch, const Vector &position );
  153. int AddRandomSound( const randomsound_t &sound );
  154. void PlayRandomSound( randomsound_t &sound );
  155. void UpdateRandomSounds( float gameClock );
  156. Vector GenerateRandomSoundPosition();
  157. void ForceSoundscape( const char *pSoundscapeName, float radius );
  158. int FindSoundscapeByName( const char *pSoundscapeName );
  159. const char *SoundscapeNameByIndex( int index );
  160. KeyValues *SoundscapeByIndex( int index );
  161. // main-level soundscape processing, called on new soundscape
  162. void StartNewSoundscape( KeyValues *pSoundscape );
  163. void StartSubSoundscape( KeyValues *pSoundscape, subsoundscapeparams_t &params );
  164. // root level soundscape keys
  165. // add a process for each new command here
  166. // "dsp"
  167. void ProcessDSP( KeyValues *pDSP );
  168. // "dsp_player"
  169. void ProcessDSPPlayer( KeyValues *pDSPPlayer );
  170. // "playlooping"
  171. void ProcessPlayLooping( KeyValues *pPlayLooping, const subsoundscapeparams_t &params );
  172. // "playrandom"
  173. void ProcessPlayRandom( KeyValues *pPlayRandom, const subsoundscapeparams_t &params );
  174. // "playsoundscape"
  175. void ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t &params );
  176. // "soundmixer"
  177. void ProcessSoundMixer( KeyValues *pSoundMixer, subsoundscapeparams_t &params );
  178. // "dsp_volume"
  179. void ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t &params );
  180. private:
  181. bool IsBeingRestored() const
  182. {
  183. return gpGlobals->framecount == m_nRestoreFrame ? true : false;
  184. }
  185. void AddSoundScapeFile( const char *filename );
  186. void TouchPlayLooping( KeyValues *pAmbient );
  187. void TouchPlayRandom( KeyValues *pPlayRandom );
  188. void TouchWaveFiles( KeyValues *pSoundScape );
  189. void TouchSoundFile( char const *wavefile );
  190. void TouchSoundFiles();
  191. int m_nRestoreFrame;
  192. CUtlVector< KeyValues * > m_SoundscapeScripts; // The whole script file in memory
  193. CUtlVector<KeyValues *> m_soundscapes; // Lookup by index of each root section
  194. audioparams_t m_params; // current player audio params
  195. CUtlVector<loopingsound_t> m_loopingSounds; // list of currently playing sounds
  196. CUtlVector<randomsound_t> m_randomSounds; // list of random sound commands
  197. float m_nextRandomTime; // next time to play a random sound
  198. int m_loopingSoundId; // marks when the sound was issued
  199. int m_forcedSoundscapeIndex;// >= 0 if this a "forced" soundscape? i.e. debug mode?
  200. float m_forcedSoundscapeRadius;// distance to spatialized sounds
  201. static ConVar *m_pDSPVolumeVar;
  202. static ConVar *m_pSoundMixerVar;
  203. };
  204. // singleton system
  205. C_SoundscapeSystem g_SoundscapeSystem;
  206. ConVar *C_SoundscapeSystem::m_pDSPVolumeVar = NULL;
  207. ConVar *C_SoundscapeSystem::m_pSoundMixerVar = NULL;
  208. IGameSystem *ClientSoundscapeSystem()
  209. {
  210. return &g_SoundscapeSystem;
  211. }
  212. void Soundscape_OnStopAllSounds()
  213. {
  214. g_SoundscapeSystem.OnStopAllSounds();
  215. }
  216. // player got a network update
  217. void Soundscape_Update( audioparams_t &audio )
  218. {
  219. g_SoundscapeSystem.UpdateAudioParams( audio );
  220. }
  221. #define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt"
  222. void C_SoundscapeSystem::AddSoundScapeFile( const char *filename )
  223. {
  224. KeyValues *script = new KeyValues( filename );
  225. #ifndef _XBOX
  226. if ( script->LoadFromFile( filesystem, filename ) )
  227. #else
  228. if ( filesystem->LoadKeyValues( *script, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) )
  229. #endif
  230. {
  231. // parse out all of the top level sections and save their names
  232. KeyValues *pKeys = script;
  233. while ( pKeys )
  234. {
  235. // save pointers to all sections in the root
  236. // each one is a soundscape
  237. if ( pKeys->GetFirstSubKey() )
  238. {
  239. m_soundscapes.AddToTail( pKeys );
  240. }
  241. pKeys = pKeys->GetNextKey();
  242. }
  243. // Keep pointer around so we can delete it at exit
  244. m_SoundscapeScripts.AddToTail( script );
  245. }
  246. else
  247. {
  248. script->deleteThis();
  249. }
  250. }
  251. // parse the script file, setup index table
  252. bool C_SoundscapeSystem::Init()
  253. {
  254. m_loopingSoundId = 0;
  255. const char *mapname = MapName();
  256. const char *mapSoundscapeFilename = NULL;
  257. if ( mapname && *mapname )
  258. {
  259. mapSoundscapeFilename = VarArgs( "scripts/soundscapes_%s.txt", mapname );
  260. }
  261. KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE );
  262. if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) )
  263. {
  264. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  265. {
  266. if ( !Q_stricmp( sub->GetName(), "file" ) )
  267. {
  268. // Add
  269. AddSoundScapeFile( sub->GetString() );
  270. if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) )
  271. {
  272. mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape
  273. }
  274. continue;
  275. }
  276. Warning( "C_SoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
  277. SOUNDSCAPE_MANIFEST_FILE, sub->GetName() );
  278. }
  279. if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) )
  280. {
  281. AddSoundScapeFile( mapSoundscapeFilename );
  282. }
  283. }
  284. else
  285. {
  286. Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE );
  287. }
  288. manifest->deleteThis();
  289. return true;
  290. }
  291. int C_SoundscapeSystem::FindSoundscapeByName( const char *pSoundscapeName )
  292. {
  293. // UNDONE: Bad perf, linear search!
  294. for ( int i = m_soundscapes.Count()-1; i >= 0; --i )
  295. {
  296. if ( !Q_stricmp( m_soundscapes[i]->GetName(), pSoundscapeName ) )
  297. return i;
  298. }
  299. return -1;
  300. }
  301. KeyValues *C_SoundscapeSystem::SoundscapeByIndex( int index )
  302. {
  303. if ( m_soundscapes.IsValidIndex(index) )
  304. return m_soundscapes[index];
  305. return NULL;
  306. }
  307. const char *C_SoundscapeSystem::SoundscapeNameByIndex( int index )
  308. {
  309. if ( index < m_soundscapes.Count() )
  310. {
  311. return m_soundscapes[index]->GetName();
  312. }
  313. return NULL;
  314. }
  315. void C_SoundscapeSystem::Shutdown()
  316. {
  317. for ( int i = m_loopingSounds.Count() - 1; i >= 0; --i )
  318. {
  319. loopingsound_t &sound = m_loopingSounds[i];
  320. // sound is done, remove from list.
  321. StopLoopingSound( sound );
  322. }
  323. // These are only necessary so we can use shutdown/init calls
  324. // to flush soundscape data
  325. m_loopingSounds.RemoveAll();
  326. m_randomSounds.RemoveAll();
  327. m_soundscapes.RemoveAll();
  328. m_params.ent.Set( NULL );
  329. m_params.soundscapeIndex = -1;
  330. while ( m_SoundscapeScripts.Count() > 0 )
  331. {
  332. KeyValues *kv = m_SoundscapeScripts[ 0 ];
  333. m_SoundscapeScripts.Remove( 0 );
  334. kv->deleteThis();
  335. }
  336. }
  337. // NOTE: This will not flush the server side so you cannot add or remove
  338. // soundscapes from the list, only change their parameters!!!!
  339. CON_COMMAND_F(cl_soundscape_flush, "Flushes the client side soundscapes", FCVAR_SERVER_CAN_EXECUTE|FCVAR_CHEAT)
  340. {
  341. // save the current soundscape
  342. audioparams_t tmp;
  343. g_SoundscapeSystem.GetAudioParams( tmp );
  344. // kill the system
  345. g_SoundscapeSystem.Shutdown();
  346. // restart the system
  347. g_SoundscapeSystem.Init();
  348. // reload the soundscape params from the temp copy
  349. Soundscape_Update( tmp );
  350. }
  351. static int SoundscapeCompletion( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  352. {
  353. int current = 0;
  354. const char *cmdname = "playsoundscape";
  355. char *substring = NULL;
  356. int substringLen = 0;
  357. if ( Q_strstr( partial, cmdname ) && strlen(partial) > strlen(cmdname) + 1 )
  358. {
  359. substring = (char *)partial + strlen( cmdname ) + 1;
  360. substringLen = strlen(substring);
  361. }
  362. int i = 0;
  363. const char *pSoundscapeName = g_SoundscapeSystem.SoundscapeNameByIndex( i );
  364. while ( pSoundscapeName && current < COMMAND_COMPLETION_MAXITEMS )
  365. {
  366. if ( !substring || !Q_strncasecmp( pSoundscapeName, substring, substringLen ) )
  367. {
  368. Q_snprintf( commands[ current ], sizeof( commands[ current ] ), "%s %s", cmdname, pSoundscapeName );
  369. current++;
  370. }
  371. i++;
  372. pSoundscapeName = g_SoundscapeSystem.SoundscapeNameByIndex( i );
  373. }
  374. return current;
  375. }
  376. CON_COMMAND_F_COMPLETION( playsoundscape, "Forces a soundscape to play", FCVAR_CHEAT, SoundscapeCompletion )
  377. {
  378. if ( args.ArgC() < 2 )
  379. {
  380. g_SoundscapeSystem.DevReportSoundscapeName( g_SoundscapeSystem.GetCurrentSoundscape() );
  381. return;
  382. }
  383. const char *pSoundscapeName = args[1];
  384. float radius = args.ArgC() > 2 ? atof( args[2] ) : DEFAULT_SOUND_RADIUS;
  385. g_SoundscapeSystem.ForceSoundscape( pSoundscapeName, radius );
  386. }
  387. CON_COMMAND_F( stopsoundscape, "Stops all soundscape processing and fades current looping sounds", FCVAR_CHEAT )
  388. {
  389. g_SoundscapeSystem.StartNewSoundscape( NULL );
  390. }
  391. void C_SoundscapeSystem::ForceSoundscape( const char *pSoundscapeName, float radius )
  392. {
  393. int index = g_SoundscapeSystem.FindSoundscapeByName( pSoundscapeName );
  394. if ( index >= 0 )
  395. {
  396. m_forcedSoundscapeIndex = index;
  397. m_forcedSoundscapeRadius = radius;
  398. g_SoundscapeSystem.StartNewSoundscape( SoundscapeByIndex(index) );
  399. }
  400. else
  401. {
  402. DevWarning("Can't find soundscape %s\n", pSoundscapeName );
  403. }
  404. }
  405. void C_SoundscapeSystem::DevReportSoundscapeName( int index )
  406. {
  407. const char *pName = "none";
  408. if ( index >= 0 && index < m_soundscapes.Count() )
  409. {
  410. pName = m_soundscapes[index]->GetName();
  411. }
  412. DevMsg( 1, "Soundscape: %s\n", pName );
  413. }
  414. // This makes all currently playing loops fade toward their target volume
  415. void C_SoundscapeSystem::UpdateLoopingSounds( float frametime )
  416. {
  417. float period = soundscape_fadetime.GetFloat();
  418. float amount = frametime;
  419. if ( period > 0 )
  420. {
  421. amount *= 1.0 / period;
  422. }
  423. int fadeCount = m_loopingSounds.Count();
  424. while ( fadeCount > 0 )
  425. {
  426. fadeCount--;
  427. loopingsound_t &sound = m_loopingSounds[fadeCount];
  428. if ( sound.volumeCurrent != sound.volumeTarget )
  429. {
  430. sound.volumeCurrent = Approach( sound.volumeTarget, sound.volumeCurrent, amount );
  431. if ( sound.volumeTarget == 0 && sound.volumeCurrent == 0 )
  432. {
  433. // sound is done, remove from list.
  434. StopLoopingSound( sound );
  435. m_loopingSounds.FastRemove( fadeCount );
  436. }
  437. else
  438. {
  439. // tell the engine about the new volume
  440. UpdateLoopingSound( sound );
  441. }
  442. }
  443. }
  444. }
  445. void C_SoundscapeSystem::Update( float frametime )
  446. {
  447. if ( m_forcedSoundscapeIndex >= 0 )
  448. {
  449. // generate fake positional sources
  450. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  451. if ( pPlayer )
  452. {
  453. Vector origin, forward, right;
  454. pPlayer->EyePositionAndVectors( &origin, &forward, &right, NULL );
  455. // put the sound origins at the corners of a box around the player
  456. m_params.localSound.Set( 0, origin + m_forcedSoundscapeRadius * (forward-right) );
  457. m_params.localSound.Set( 1, origin + m_forcedSoundscapeRadius * (forward+right) );
  458. m_params.localSound.Set( 2, origin + m_forcedSoundscapeRadius * (-forward-right) );
  459. m_params.localSound.Set( 3, origin + m_forcedSoundscapeRadius * (-forward+right) );
  460. m_params.localBits = 0x0007;
  461. }
  462. }
  463. // fade out the old sounds over soundscape_fadetime seconds
  464. UpdateLoopingSounds( frametime );
  465. UpdateRandomSounds( gpGlobals->curtime );
  466. }
  467. void C_SoundscapeSystem::UpdateAudioParams( audioparams_t &audio )
  468. {
  469. if ( m_params.soundscapeIndex == audio.soundscapeIndex && m_params.ent.Get() == audio.ent.Get() )
  470. return;
  471. m_params = audio;
  472. m_forcedSoundscapeIndex = -1;
  473. if ( audio.ent.Get() && audio.soundscapeIndex >= 0 && audio.soundscapeIndex < m_soundscapes.Count() )
  474. {
  475. DevReportSoundscapeName( audio.soundscapeIndex );
  476. StartNewSoundscape( m_soundscapes[audio.soundscapeIndex] );
  477. }
  478. else
  479. {
  480. // bad index (and the soundscape file actually existed...)
  481. if ( audio.ent.Get() != 0 &&
  482. audio.soundscapeIndex != -1 )
  483. {
  484. DevMsg(1, "Error: Bad soundscape!\n");
  485. }
  486. }
  487. }
  488. // Called when a soundscape is activated (leading edge of becoming the active soundscape)
  489. void C_SoundscapeSystem::StartNewSoundscape( KeyValues *pSoundscape )
  490. {
  491. int i;
  492. // Reset the system
  493. // fade out the current loops
  494. for ( i = m_loopingSounds.Count()-1; i >= 0; --i )
  495. {
  496. m_loopingSounds[i].volumeTarget = 0;
  497. if ( !pSoundscape )
  498. {
  499. // if we're cancelling the soundscape, stop the sound immediately
  500. m_loopingSounds[i].volumeCurrent = 0;
  501. }
  502. }
  503. // update ID
  504. m_loopingSoundId++;
  505. // clear all random sounds
  506. m_randomSounds.RemoveAll();
  507. m_nextRandomTime = gpGlobals->curtime;
  508. if ( pSoundscape )
  509. {
  510. subsoundscapeparams_t params;
  511. params.allowDSP = true;
  512. params.wroteSoundMixer = false;
  513. params.wroteDSPVolume = false;
  514. params.masterVolume = 1.0;
  515. params.startingPosition = 0;
  516. params.recurseLevel = 0;
  517. params.positionOverride = -1;
  518. params.ambientPositionOverride = -1;
  519. StartSubSoundscape( pSoundscape, params );
  520. if ( !params.wroteDSPVolume )
  521. {
  522. m_pDSPVolumeVar->Revert();
  523. }
  524. if ( !params.wroteSoundMixer )
  525. {
  526. m_pSoundMixerVar->Revert();
  527. }
  528. }
  529. }
  530. void C_SoundscapeSystem::StartSubSoundscape( KeyValues *pSoundscape, subsoundscapeparams_t &params )
  531. {
  532. // Parse/process all of the commands
  533. KeyValues *pKey = pSoundscape->GetFirstSubKey();
  534. while ( pKey )
  535. {
  536. if ( !Q_strcasecmp( pKey->GetName(), "dsp" ) )
  537. {
  538. if ( params.allowDSP )
  539. {
  540. ProcessDSP( pKey );
  541. }
  542. }
  543. else if ( !Q_strcasecmp( pKey->GetName(), "dsp_player" ) )
  544. {
  545. if ( params.allowDSP )
  546. {
  547. ProcessDSPPlayer( pKey );
  548. }
  549. }
  550. else if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  551. {
  552. ProcessPlayLooping( pKey, params );
  553. }
  554. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  555. {
  556. ProcessPlayRandom( pKey, params );
  557. }
  558. else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
  559. {
  560. ProcessPlaySoundscape( pKey, params );
  561. }
  562. else if ( !Q_strcasecmp( pKey->GetName(), "Soundmixer" ) )
  563. {
  564. if ( params.allowDSP )
  565. {
  566. ProcessSoundMixer( pKey, params );
  567. }
  568. }
  569. else if ( !Q_strcasecmp( pKey->GetName(), "dsp_volume" ) )
  570. {
  571. if ( params.allowDSP )
  572. {
  573. ProcessDSPVolume( pKey, params );
  574. }
  575. }
  576. // add new commands here
  577. else
  578. {
  579. DevMsg( 1, "Soundscape %s:Unknown command %s\n", pSoundscape->GetName(), pKey->GetName() );
  580. }
  581. pKey = pKey->GetNextKey();
  582. }
  583. }
  584. // add a process for each new command here
  585. // change DSP effect
  586. void C_SoundscapeSystem::ProcessDSP( KeyValues *pDSP )
  587. {
  588. int roomType = pDSP->GetInt();
  589. CLocalPlayerFilter filter;
  590. enginesound->SetRoomType( filter, roomType );
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose:
  594. // Input : *pDSPPlayer -
  595. //-----------------------------------------------------------------------------
  596. void C_SoundscapeSystem::ProcessDSPPlayer( KeyValues *pDSPPlayer )
  597. {
  598. int dspType = pDSPPlayer->GetInt();
  599. CLocalPlayerFilter filter;
  600. enginesound->SetPlayerDSP( filter, dspType, false );
  601. }
  602. void C_SoundscapeSystem::ProcessSoundMixer( KeyValues *pSoundMixer, subsoundscapeparams_t &params )
  603. {
  604. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  605. if ( !pPlayer || pPlayer->CanSetSoundMixer() )
  606. {
  607. m_pSoundMixerVar->SetValue( pSoundMixer->GetString() );
  608. params.wroteSoundMixer = true;
  609. }
  610. }
  611. void C_SoundscapeSystem::ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t &params )
  612. {
  613. m_pDSPVolumeVar->SetValue( pKey->GetFloat() );
  614. params.wroteDSPVolume = true;
  615. }
  616. // start a new looping sound
  617. void C_SoundscapeSystem::ProcessPlayLooping( KeyValues *pAmbient, const subsoundscapeparams_t &params )
  618. {
  619. float volume = 0;
  620. soundlevel_t soundlevel = ATTN_TO_SNDLVL(ATTN_NORM);
  621. const char *pSoundName = NULL;
  622. int pitch = PITCH_NORM;
  623. int positionIndex = -1;
  624. bool suppress = false;
  625. KeyValues *pKey = pAmbient->GetFirstSubKey();
  626. while ( pKey )
  627. {
  628. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  629. {
  630. volume = params.masterVolume * RandomInterval( ReadInterval( pKey->GetString() ) );
  631. }
  632. else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
  633. {
  634. pitch = RandomInterval( ReadInterval( pKey->GetString() ) );
  635. }
  636. else if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
  637. {
  638. pSoundName = pKey->GetString();
  639. }
  640. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  641. {
  642. positionIndex = params.startingPosition + pKey->GetInt();
  643. }
  644. else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) )
  645. {
  646. soundlevel = ATTN_TO_SNDLVL( RandomInterval( ReadInterval( pKey->GetString() ) ) );
  647. }
  648. else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
  649. {
  650. if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
  651. {
  652. soundlevel = TextToSoundLevel( pKey->GetString() );
  653. }
  654. else
  655. {
  656. soundlevel = (soundlevel_t)((int)RandomInterval( ReadInterval( pKey->GetString() ) ));
  657. }
  658. }
  659. else if ( !Q_strcasecmp( pKey->GetName(), "suppress_on_restore" ) )
  660. {
  661. suppress = Q_atoi( pKey->GetString() ) != 0 ? true : false;
  662. }
  663. else
  664. {
  665. DevMsg( 1, "Ambient %s:Unknown command %s\n", pAmbient->GetName(), pKey->GetName() );
  666. }
  667. pKey = pKey->GetNextKey();
  668. }
  669. if ( positionIndex < 0 )
  670. {
  671. positionIndex = params.ambientPositionOverride;
  672. }
  673. else if ( params.positionOverride >= 0 )
  674. {
  675. positionIndex = params.positionOverride;
  676. }
  677. // Sound is mared as "suppress_on_restore" so don't restart it
  678. if ( IsBeingRestored() && suppress )
  679. {
  680. return;
  681. }
  682. if ( volume != 0 && pSoundName != NULL )
  683. {
  684. if ( positionIndex < 0 )
  685. {
  686. AddLoopingAmbient( pSoundName, volume, pitch );
  687. }
  688. else
  689. {
  690. if ( positionIndex > 31 || !(m_params.localBits & (1<<positionIndex) ) )
  691. {
  692. // suppress sounds if the position isn't available
  693. //DevMsg( 1, "Bad position %d\n", positionIndex );
  694. return;
  695. }
  696. AddLoopingSound( pSoundName, false, volume, soundlevel, pitch, m_params.localSound[positionIndex] );
  697. }
  698. }
  699. }
  700. void C_SoundscapeSystem::TouchSoundFile( char const *wavefile )
  701. {
  702. filesystem->GetFileTime( VarArgs( "sound/%s", PSkipSoundChars( wavefile ) ), "GAME" );
  703. }
  704. // start a new looping sound
  705. void C_SoundscapeSystem::TouchPlayLooping( KeyValues *pAmbient )
  706. {
  707. KeyValues *pKey = pAmbient->GetFirstSubKey();
  708. while ( pKey )
  709. {
  710. if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
  711. {
  712. char const *pSoundName = pKey->GetString();
  713. // Touch the file
  714. TouchSoundFile( pSoundName );
  715. }
  716. pKey = pKey->GetNextKey();
  717. }
  718. }
  719. Vector C_SoundscapeSystem::GenerateRandomSoundPosition()
  720. {
  721. float angle = random->RandomFloat( -180, 180 );
  722. float sinAngle, cosAngle;
  723. SinCos( angle, &sinAngle, &cosAngle );
  724. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  725. if ( pPlayer )
  726. {
  727. Vector origin, forward, right;
  728. pPlayer->EyePositionAndVectors( &origin, &forward, &right, NULL );
  729. return origin + DEFAULT_SOUND_RADIUS * (cosAngle * right + sinAngle * forward);
  730. }
  731. else
  732. {
  733. return CurrentViewOrigin() + DEFAULT_SOUND_RADIUS * (cosAngle * CurrentViewRight() + sinAngle * CurrentViewForward());
  734. }
  735. }
  736. void C_SoundscapeSystem::TouchSoundFiles()
  737. {
  738. if ( !CommandLine()->FindParm( "-makereslists" ) )
  739. return;
  740. int c = m_soundscapes.Count();
  741. for ( int i = 0; i < c ; ++i )
  742. {
  743. TouchWaveFiles( m_soundscapes[ i ] );
  744. }
  745. }
  746. void C_SoundscapeSystem::TouchWaveFiles( KeyValues *pSoundScape )
  747. {
  748. KeyValues *pKey = pSoundScape->GetFirstSubKey();
  749. while ( pKey )
  750. {
  751. if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  752. {
  753. TouchPlayLooping( pKey );
  754. }
  755. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  756. {
  757. TouchPlayRandom( pKey );
  758. }
  759. pKey = pKey->GetNextKey();
  760. }
  761. }
  762. // puts a recurring random sound event into the queue
  763. void C_SoundscapeSystem::TouchPlayRandom( KeyValues *pPlayRandom )
  764. {
  765. KeyValues *pKey = pPlayRandom->GetFirstSubKey();
  766. while ( pKey )
  767. {
  768. if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
  769. {
  770. KeyValues *pWaves = pKey->GetFirstSubKey();
  771. while ( pWaves )
  772. {
  773. TouchSoundFile( pWaves->GetString() );
  774. pWaves = pWaves->GetNextKey();
  775. }
  776. }
  777. pKey = pKey->GetNextKey();
  778. }
  779. }
  780. // puts a recurring random sound event into the queue
  781. void C_SoundscapeSystem::ProcessPlayRandom( KeyValues *pPlayRandom, const subsoundscapeparams_t &params )
  782. {
  783. randomsound_t sound;
  784. sound.Init();
  785. sound.masterVolume = params.masterVolume;
  786. int positionIndex = -1;
  787. bool suppress = false;
  788. bool randomPosition = false;
  789. KeyValues *pKey = pPlayRandom->GetFirstSubKey();
  790. while ( pKey )
  791. {
  792. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  793. {
  794. sound.volume = ReadInterval( pKey->GetString() );
  795. }
  796. else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
  797. {
  798. sound.pitch = ReadInterval( pKey->GetString() );
  799. }
  800. else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) )
  801. {
  802. interval_t atten = ReadInterval( pKey->GetString() );
  803. sound.soundlevel.start = ATTN_TO_SNDLVL( atten.start );
  804. sound.soundlevel.range = ATTN_TO_SNDLVL( atten.start + atten.range ) - sound.soundlevel.start;
  805. }
  806. else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
  807. {
  808. if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
  809. {
  810. sound.soundlevel.start = TextToSoundLevel( pKey->GetString() );
  811. sound.soundlevel.range = 0;
  812. }
  813. else
  814. {
  815. sound.soundlevel = ReadInterval( pKey->GetString() );
  816. }
  817. }
  818. else if ( !Q_strcasecmp( pKey->GetName(), "time" ) )
  819. {
  820. sound.time = ReadInterval( pKey->GetString() );
  821. }
  822. else if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
  823. {
  824. KeyValues *pWaves = pKey->GetFirstSubKey();
  825. sound.pWaves = pWaves;
  826. sound.waveCount = 0;
  827. while ( pWaves )
  828. {
  829. sound.waveCount++;
  830. pWaves = pWaves->GetNextKey();
  831. }
  832. }
  833. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  834. {
  835. if ( !Q_strcasecmp( pKey->GetString(), "random" ) )
  836. {
  837. randomPosition = true;
  838. }
  839. else
  840. {
  841. positionIndex = params.startingPosition + pKey->GetInt();
  842. }
  843. }
  844. else if ( !Q_strcasecmp( pKey->GetName(), "suppress_on_restore" ) )
  845. {
  846. suppress = Q_atoi( pKey->GetString() ) != 0 ? true : false;
  847. }
  848. else
  849. {
  850. DevMsg( 1, "Random Sound %s:Unknown command %s\n", pPlayRandom->GetName(), pKey->GetName() );
  851. }
  852. pKey = pKey->GetNextKey();
  853. }
  854. if ( positionIndex < 0 )
  855. {
  856. positionIndex = params.ambientPositionOverride;
  857. }
  858. else if ( params.positionOverride >= 0 )
  859. {
  860. positionIndex = params.positionOverride;
  861. randomPosition = false; // override trumps random position
  862. }
  863. // Sound is mared as "suppress_on_restore" so don't restart it
  864. if ( IsBeingRestored() && suppress )
  865. {
  866. return;
  867. }
  868. if ( sound.waveCount != 0 )
  869. {
  870. if ( positionIndex < 0 && !randomPosition )
  871. {
  872. sound.isAmbient = true;
  873. AddRandomSound( sound );
  874. }
  875. else
  876. {
  877. sound.isAmbient = false;
  878. if ( randomPosition )
  879. {
  880. sound.isRandom = true;
  881. }
  882. else
  883. {
  884. if ( positionIndex > 31 || !(m_params.localBits & (1<<positionIndex) ) )
  885. {
  886. // suppress sounds if the position isn't available
  887. //DevMsg( 1, "Bad position %d\n", positionIndex );
  888. return;
  889. }
  890. sound.position = m_params.localSound[positionIndex];
  891. }
  892. AddRandomSound( sound );
  893. }
  894. }
  895. }
  896. void C_SoundscapeSystem::ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t &paramsIn )
  897. {
  898. subsoundscapeparams_t subParams = paramsIn;
  899. // sub-soundscapes NEVER set the DSP effects
  900. subParams.allowDSP = false;
  901. subParams.recurseLevel++;
  902. if ( subParams.recurseLevel > MAX_SOUNDSCAPE_RECURSION )
  903. {
  904. DevMsg( "Error! Soundscape recursion overrun!\n" );
  905. return;
  906. }
  907. KeyValues *pKey = pPlaySoundscape->GetFirstSubKey();
  908. const char *pSoundscapeName = NULL;
  909. while ( pKey )
  910. {
  911. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  912. {
  913. subParams.masterVolume = paramsIn.masterVolume * RandomInterval( ReadInterval( pKey->GetString() ) );
  914. }
  915. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  916. {
  917. subParams.startingPosition = paramsIn.startingPosition + pKey->GetInt();
  918. }
  919. else if ( !Q_strcasecmp( pKey->GetName(), "positionoverride" ) )
  920. {
  921. if ( paramsIn.positionOverride < 0 )
  922. {
  923. subParams.positionOverride = paramsIn.startingPosition + pKey->GetInt();
  924. // positionoverride is only ever used to make a whole soundscape come from a point in space
  925. // So go ahead and default ambients there too.
  926. subParams.ambientPositionOverride = paramsIn.startingPosition + pKey->GetInt();
  927. }
  928. }
  929. else if ( !Q_strcasecmp( pKey->GetName(), "ambientpositionoverride" ) )
  930. {
  931. if ( paramsIn.ambientPositionOverride < 0 )
  932. {
  933. subParams.ambientPositionOverride = paramsIn.startingPosition + pKey->GetInt();
  934. }
  935. }
  936. else if ( !Q_strcasecmp( pKey->GetName(), "name" ) )
  937. {
  938. pSoundscapeName = pKey->GetString();
  939. }
  940. else if ( !Q_strcasecmp(pKey->GetName(), "soundlevel") )
  941. {
  942. DevMsg(1,"soundlevel not supported on sub-soundscapes\n");
  943. }
  944. else
  945. {
  946. DevMsg( 1, "Playsoundscape %s:Unknown command %s\n", pSoundscapeName ? pSoundscapeName : pPlaySoundscape->GetName(), pKey->GetName() );
  947. }
  948. pKey = pKey->GetNextKey();
  949. }
  950. if ( pSoundscapeName )
  951. {
  952. KeyValues *pSoundscapeKeys = SoundscapeByIndex( FindSoundscapeByName( pSoundscapeName ) );
  953. if ( pSoundscapeKeys )
  954. {
  955. StartSubSoundscape( pSoundscapeKeys, subParams );
  956. }
  957. else
  958. {
  959. DevMsg( 1, "Trying to play unknown soundscape %s\n", pSoundscapeName );
  960. }
  961. }
  962. }
  963. // special kind of looping sound with no spatialization
  964. int C_SoundscapeSystem::AddLoopingAmbient( const char *pSoundName, float volume, int pitch )
  965. {
  966. return AddLoopingSound( pSoundName, true, volume, SNDLVL_NORM, pitch, vec3_origin );
  967. }
  968. // add a looping sound to the list
  969. // NOTE: will reuse existing entry (fade from current volume) if possible
  970. // this prevents pops
  971. int C_SoundscapeSystem::AddLoopingSound( const char *pSoundName, bool isAmbient, float volume, soundlevel_t soundlevel, int pitch, const Vector &position )
  972. {
  973. loopingsound_t *pSoundSlot = NULL;
  974. int soundSlot = m_loopingSounds.Count() - 1;
  975. bool bForceSoundUpdate = false;
  976. while ( soundSlot >= 0 )
  977. {
  978. loopingsound_t &sound = m_loopingSounds[soundSlot];
  979. // NOTE: Will always restart/crossfade positional sounds
  980. if ( sound.id != m_loopingSoundId &&
  981. sound.pitch == pitch &&
  982. !Q_strcasecmp( pSoundName, sound.pWaveName ) )
  983. {
  984. // Ambient sounds can reuse the slots.
  985. if ( isAmbient == true &&
  986. sound.isAmbient == true )
  987. {
  988. // reuse this sound
  989. pSoundSlot = &sound;
  990. break;
  991. }
  992. // Positional sounds can reuse the slots if the positions are the same.
  993. else if ( isAmbient == sound.isAmbient )
  994. {
  995. if ( VectorsAreEqual( position, sound.position, 0.1f ) )
  996. {
  997. // reuse this sound
  998. pSoundSlot = &sound;
  999. break;
  1000. }
  1001. else
  1002. {
  1003. // If it's trying to fade out one positional sound and fade in another, then it gets screwy
  1004. // because it'll be sending alternating commands to the sound engine, referencing the same sound
  1005. // (SOUND_FROM_WORLD, CHAN_STATIC, pSoundName). One of the alternating commands will be as
  1006. // it fades the sound out, and one will be fading the sound in.
  1007. // Because this will occasionally cause the sound to vanish entirely, we stop the old sound immediately.
  1008. StopLoopingSound(sound);
  1009. pSoundSlot = &sound;
  1010. // make a note to update the sound immediately. Otherwise, if its volume happens to be
  1011. // the same as the old sound's volume, it will never update at all.
  1012. bForceSoundUpdate = true;
  1013. break;
  1014. }
  1015. }
  1016. }
  1017. soundSlot--;
  1018. }
  1019. if ( soundSlot < 0 )
  1020. {
  1021. // can't find the sound in the list, make a new one
  1022. soundSlot = m_loopingSounds.AddToTail();
  1023. if ( isAmbient )
  1024. {
  1025. // start at 0 and fade in
  1026. enginesound->EmitAmbientSound( pSoundName, 0, pitch );
  1027. m_loopingSounds[soundSlot].volumeCurrent = 0.0;
  1028. }
  1029. else
  1030. {
  1031. // non-ambients at 0 volume are culled, so start at 0.05
  1032. CLocalPlayerFilter filter;
  1033. EmitSound_t ep;
  1034. ep.m_nChannel = CHAN_STATIC;
  1035. ep.m_pSoundName = pSoundName;
  1036. ep.m_flVolume = 0.05;
  1037. ep.m_SoundLevel = soundlevel;
  1038. ep.m_nPitch = pitch;
  1039. ep.m_pOrigin = &position;
  1040. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1041. m_loopingSounds[soundSlot].volumeCurrent = 0.05;
  1042. }
  1043. }
  1044. loopingsound_t &sound = m_loopingSounds[soundSlot];
  1045. // fill out the slot
  1046. sound.pWaveName = pSoundName;
  1047. sound.volumeTarget = volume;
  1048. sound.pitch = pitch;
  1049. sound.id = m_loopingSoundId;
  1050. sound.isAmbient = isAmbient;
  1051. sound.position = position;
  1052. sound.soundlevel = soundlevel;
  1053. if (bForceSoundUpdate)
  1054. {
  1055. UpdateLoopingSound(sound);
  1056. }
  1057. return soundSlot;
  1058. }
  1059. // stop this loop forever
  1060. void C_SoundscapeSystem::StopLoopingSound( loopingsound_t &loopSound )
  1061. {
  1062. if ( loopSound.isAmbient )
  1063. {
  1064. enginesound->EmitAmbientSound( loopSound.pWaveName, 0, 0, SND_STOP );
  1065. }
  1066. else
  1067. {
  1068. C_BaseEntity::StopSound( SOUND_FROM_WORLD, CHAN_STATIC, loopSound.pWaveName );
  1069. }
  1070. }
  1071. // update with new volume
  1072. void C_SoundscapeSystem::UpdateLoopingSound( loopingsound_t &loopSound )
  1073. {
  1074. if ( loopSound.isAmbient )
  1075. {
  1076. enginesound->EmitAmbientSound( loopSound.pWaveName, loopSound.volumeCurrent, loopSound.pitch, SND_CHANGE_VOL );
  1077. }
  1078. else
  1079. {
  1080. CLocalPlayerFilter filter;
  1081. EmitSound_t ep;
  1082. ep.m_nChannel = CHAN_STATIC;
  1083. ep.m_pSoundName = loopSound.pWaveName;
  1084. ep.m_flVolume = loopSound.volumeCurrent;
  1085. ep.m_SoundLevel = loopSound.soundlevel;
  1086. ep.m_nFlags = SND_CHANGE_VOL;
  1087. ep.m_nPitch = loopSound.pitch;
  1088. ep.m_pOrigin = &loopSound.position;
  1089. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1090. }
  1091. }
  1092. // add a recurring random sound event
  1093. int C_SoundscapeSystem::AddRandomSound( const randomsound_t &sound )
  1094. {
  1095. int index = m_randomSounds.AddToTail( sound );
  1096. m_randomSounds[index].nextPlayTime = gpGlobals->curtime + 0.5 * RandomInterval( sound.time );
  1097. return index;
  1098. }
  1099. // play a random sound randomly from this parameterization table
  1100. void C_SoundscapeSystem::PlayRandomSound( randomsound_t &sound )
  1101. {
  1102. Assert( sound.waveCount > 0 );
  1103. int waveId = random->RandomInt( 0, sound.waveCount-1 );
  1104. KeyValues *pWaves = sound.pWaves;
  1105. while ( waveId > 0 && pWaves )
  1106. {
  1107. pWaves = pWaves->GetNextKey();
  1108. waveId--;
  1109. }
  1110. if ( !pWaves )
  1111. return;
  1112. const char *pWaveName = pWaves->GetString();
  1113. if ( !pWaveName )
  1114. return;
  1115. if ( sound.isAmbient )
  1116. {
  1117. enginesound->EmitAmbientSound( pWaveName, sound.masterVolume * RandomInterval( sound.volume ), (int)RandomInterval( sound.pitch ) );
  1118. }
  1119. else
  1120. {
  1121. CLocalPlayerFilter filter;
  1122. EmitSound_t ep;
  1123. ep.m_nChannel = CHAN_STATIC;
  1124. ep.m_pSoundName = pWaveName;
  1125. ep.m_flVolume = sound.masterVolume * RandomInterval( sound.volume );
  1126. ep.m_SoundLevel = (soundlevel_t)(int)RandomInterval( sound.soundlevel );
  1127. ep.m_nPitch = (int)RandomInterval( sound.pitch );
  1128. if ( sound.isRandom )
  1129. {
  1130. sound.position = GenerateRandomSoundPosition();
  1131. }
  1132. ep.m_pOrigin = &sound.position;
  1133. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1134. }
  1135. }
  1136. // walk the list of random sound commands and update
  1137. void C_SoundscapeSystem::UpdateRandomSounds( float gameTime )
  1138. {
  1139. if ( gameTime < m_nextRandomTime )
  1140. return;
  1141. m_nextRandomTime = gameTime + 3600; // add some big time to check again (an hour)
  1142. for ( int i = m_randomSounds.Count()-1; i >= 0; i-- )
  1143. {
  1144. // time to play?
  1145. if ( gameTime >= m_randomSounds[i].nextPlayTime )
  1146. {
  1147. // UNDONE: add this in to fix range?
  1148. // float dt = m_randomSounds[i].nextPlayTime - gameTime;
  1149. PlayRandomSound( m_randomSounds[i] );
  1150. // now schedule the next occurrance
  1151. // UNDONE: add support for "play once" sounds? FastRemove() here.
  1152. m_randomSounds[i].nextPlayTime = gameTime + RandomInterval( m_randomSounds[i].time );
  1153. }
  1154. // update next time to check the queue
  1155. if ( m_randomSounds[i].nextPlayTime < m_nextRandomTime )
  1156. {
  1157. m_nextRandomTime = m_randomSounds[i].nextPlayTime;
  1158. }
  1159. }
  1160. }
  1161. CON_COMMAND(cl_soundscape_printdebuginfo, "print soundscapes")
  1162. {
  1163. g_SoundscapeSystem.PrintDebugInfo();
  1164. }