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.

1625 lines
44 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Soundscapes.txt resource file processor
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <keyvalues.h>
  8. #include "engine/IEngineSound.h"
  9. #include "filesystem.h"
  10. #include "SoundEmitterSystem/isoundemittersystembase.h"
  11. #include "soundchars.h"
  12. #include "view.h"
  13. #include "engine/ivdebugoverlay.h"
  14. #include "tier0/icommandline.h"
  15. #include "strtools.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. enum soundfadestyle_t
  26. {
  27. FADE_VOLUME_LINEAR = 0,
  28. FADE_VOLUME_SINE = 1,
  29. };
  30. // contains a set of data to implement a simple envelope to fade in/out sounds
  31. struct soundfader_t
  32. {
  33. float m_flCurrent;
  34. float m_flTarget;
  35. float m_flRate;
  36. float m_flStart;
  37. float m_flFadeT;
  38. int m_nType;
  39. bool IsFading()
  40. {
  41. return ( m_flCurrent != m_flTarget ) ? true : false;
  42. }
  43. void FadeToValue( float flTarget, float flRate, soundfadestyle_t fadeType )
  44. {
  45. m_flStart = m_flCurrent;
  46. m_flFadeT = 0;
  47. m_flTarget = flTarget;
  48. m_flRate = flRate;
  49. m_nType = fadeType;
  50. }
  51. void ForceToTargetValue( float flTarget )
  52. {
  53. m_flFadeT = 1.0f;
  54. m_flCurrent = m_flTarget = flTarget;
  55. m_flRate = 0;
  56. }
  57. void UpdateFade( float flDt )
  58. {
  59. m_flFadeT += flDt * m_flRate;
  60. if ( m_flFadeT >= 1.0f )
  61. {
  62. ForceToTargetValue( m_flTarget );
  63. return;
  64. }
  65. float flFactor = m_flFadeT;
  66. float flDelta = m_flTarget - m_flStart;
  67. switch ( m_nType )
  68. {
  69. case FADE_VOLUME_LINEAR:
  70. break;
  71. case FADE_VOLUME_SINE:
  72. if ( flDelta >= 0 )
  73. {
  74. flFactor = sin( m_flFadeT * M_PI * 0.5f );
  75. }
  76. else
  77. {
  78. flFactor = 1.0f - cos( m_flFadeT * M_PI * 0.5f );
  79. }
  80. break;
  81. }
  82. m_flCurrent = m_flStart + flDelta * flFactor;
  83. }
  84. };
  85. struct loopingsound_t
  86. {
  87. Vector position; // position (if !isAmbient)
  88. const char *pWaveName; // name of the wave file
  89. soundfader_t m_volume;
  90. soundlevel_t soundlevel; // sound level (if !isAmbient)
  91. int pitch; // pitch shift
  92. int id; // Used to fade out sounds that don't belong to the most current setting
  93. int engineGuid;
  94. float radius; // if set, sound plays at full volume inside the radius and fallsoff as you move out of the radius. Sound will lose directionality as you move inside the radius
  95. bool isAmbient; // Ambient sounds have no spatialization - they play from everywhere
  96. };
  97. ConVar soundscape_fadetime( "soundscape_fadetime", "3.0", FCVAR_CHEAT, "Time to crossfade sound effects between soundscapes" );
  98. ConVar soundscape_message("soundscape_message","0");
  99. ConVar soundscape_radius_debug( "soundscape_radius_debug", "0", FCVAR_CHEAT, "Prints current volume of radius sounds" );
  100. float GetSoundscapeFadeRate()
  101. {
  102. float flFadeTime = soundscape_fadetime.GetFloat();
  103. float flFadeRate = 1.0f / (flFadeTime > 0 ? flFadeTime : 3.0f);
  104. return flFadeRate;
  105. }
  106. #include "tier2/interval.h"
  107. struct randomsound_t
  108. {
  109. Vector position;
  110. float nextPlayTime; // time to play a sound from the set
  111. interval_t time;
  112. interval_t volume;
  113. interval_t pitch;
  114. interval_t soundlevel;
  115. float masterVolume;
  116. int waveCount;
  117. bool isAmbient;
  118. bool isRandom;
  119. KeyValues *pWaves;
  120. void Init()
  121. {
  122. memset( this, 0, sizeof(*this) );
  123. }
  124. };
  125. struct subsoundscapeparams_t
  126. {
  127. Vector vForcedTextOriginAmbient;
  128. int recurseLevel; // test for infinite loops in the script / circular refs
  129. float masterVolume;
  130. float flFadeRate;
  131. int startingPosition;
  132. int positionOverride; // forces all sounds to this position
  133. int ambientPositionOverride; // forces all ambient sounds to this position
  134. bool allowDSP;
  135. bool wroteSoundMixer;
  136. bool wroteDSPVolume;
  137. bool bForceTextOriginAmbient;
  138. };
  139. Vector getVectorFromString(const char *pString)
  140. {
  141. char tempString[128];
  142. Q_strncpy( tempString, pString, sizeof(tempString) );
  143. Vector result;
  144. int i = 0;
  145. char *token = strtok( tempString, "," );
  146. while( token )
  147. {
  148. result[i] = atof( token );
  149. token = strtok( NULL, "," );
  150. i++;
  151. }
  152. return result;
  153. }
  154. class C_SoundscapeSystem : public CBaseGameSystemPerFrame
  155. {
  156. public:
  157. virtual char const *Name() { return "C_SoundScapeSystem"; }
  158. C_SoundscapeSystem()
  159. {
  160. m_nRestoreFrame = -1;
  161. }
  162. ~C_SoundscapeSystem() {}
  163. void OnStopAllSounds()
  164. {
  165. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  166. {
  167. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  168. GetPerUser(hh).m_params.entIndex = 0;
  169. GetPerUser(hh).m_params.soundscapeIndex = -1;
  170. GetPerUser(hh).m_loopingSounds.Purge();
  171. GetPerUser(hh).m_randomSounds.Purge();
  172. }
  173. }
  174. // IClientSystem hooks, not needed
  175. virtual void LevelInitPreEntity()
  176. {
  177. Shutdown();
  178. Init();
  179. TouchSoundFiles();
  180. }
  181. virtual void LevelInitPostEntity()
  182. {
  183. if ( !m_pSoundMixerVar )
  184. {
  185. m_pSoundMixerVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
  186. }
  187. if ( !m_pDSPVolumeVar )
  188. {
  189. m_pDSPVolumeVar = (ConVar *)cvar->FindVar( "dsp_volume" );
  190. }
  191. }
  192. // The level is shutdown in two parts
  193. virtual void LevelShutdownPreEntity() {}
  194. // Entities are deleted / released here...
  195. virtual void LevelShutdownPostEntity()
  196. {
  197. OnStopAllSounds();
  198. }
  199. virtual void OnSave() {}
  200. virtual void OnRestore()
  201. {
  202. m_nRestoreFrame = gpGlobals->framecount;
  203. }
  204. virtual void SafeRemoveIfDesired() {}
  205. // Called before rendering
  206. virtual void PreRender() { }
  207. // Called after rendering
  208. virtual void PostRender() { }
  209. // IClientSystem hooks used
  210. virtual bool Init();
  211. virtual void Shutdown();
  212. // Gets called each frame
  213. virtual void Update( float frametime );
  214. void PrintDebugInfo()
  215. {
  216. Msg( "\n------- CLIENT SOUNDSCAPES -------\n" );
  217. for ( int i=0; i < m_soundscapes.Count(); i++ )
  218. {
  219. Msg( "- %d: %s\n", i, m_soundscapes[i]->GetName() );
  220. }
  221. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  222. if ( slot.m_forcedSoundscapeIndex )
  223. {
  224. Msg( "- PLAYING DEBUG SOUNDSCAPE: %d [%s]\n", slot.m_forcedSoundscapeIndex, SoundscapeNameByIndex(slot.m_forcedSoundscapeIndex) );
  225. }
  226. Msg( "- CURRENT SOUNDSCAPE: %d [%s]\n", slot.m_params.soundscapeIndex.Get(), SoundscapeNameByIndex(slot.m_params.soundscapeIndex) );
  227. Msg( "----------------------------------\n\n" );
  228. }
  229. // local functions
  230. void UpdateAudioParams( audioparams_t &audio );
  231. void GetAudioParams( audioparams_t &out ) const { out = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT()).m_params; }
  232. int GetCurrentSoundscape()
  233. {
  234. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  235. if ( slot.m_forcedSoundscapeIndex >= 0 )
  236. return slot.m_forcedSoundscapeIndex;
  237. return slot.m_params.soundscapeIndex;
  238. }
  239. void DevReportSoundscapeName( int index );
  240. void UpdateLoopingSounds( float frametime );
  241. int AddLoopingAmbient( const char *pSoundName, float volume, int pitch, float radius, float flFadeRate );
  242. void UpdateLoopingSound( loopingsound_t &loopSound );
  243. void StopLoopingSound( loopingsound_t &loopSound );
  244. int AddLoopingSound( const char *pSoundName, bool isAmbient, float volume,
  245. soundlevel_t soundLevel, int pitch, const Vector &position, float radius, float flFadeRate );
  246. int AddRandomSound( const randomsound_t &sound );
  247. void PlayRandomSound( randomsound_t &sound );
  248. void UpdateRandomSounds( float gameClock );
  249. Vector GenerateRandomSoundPosition();
  250. void ForceSoundscape( const char *pSoundscapeName, float radius );
  251. int FindSoundscapeByName( const char *pSoundscapeName );
  252. const char *SoundscapeNameByIndex( int index );
  253. KeyValues *SoundscapeByIndex( int index );
  254. // main-level soundscape processing, called on new soundscape
  255. void StartNewSoundscape( KeyValues *pSoundscape );
  256. void StartSubSoundscape( KeyValues *pSoundscape, subsoundscapeparams_t &params );
  257. // root level soundscape keys
  258. // add a process for each new command here
  259. // "dsp"
  260. void ProcessDSP( KeyValues *pDSP );
  261. // "dsp_player"
  262. void ProcessDSPPlayer( KeyValues *pDSPPlayer );
  263. // "fadetime"
  264. void ProcessSoundscapeFadetime( KeyValues *pKey, subsoundscapeparams_t &params );
  265. // "playlooping"
  266. void ProcessPlayLooping( KeyValues *pPlayLooping, const subsoundscapeparams_t &params );
  267. // "playrandom"
  268. void ProcessPlayRandom( KeyValues *pPlayRandom, const subsoundscapeparams_t &params );
  269. // "playsoundscape"
  270. void ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t &params );
  271. // "soundmixer"
  272. void ProcessSoundMixer( KeyValues *pSoundMixer, subsoundscapeparams_t &params );
  273. // "dsp_volume"
  274. void ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t &params );
  275. private:
  276. bool IsBeingRestored() const
  277. {
  278. return gpGlobals->framecount == m_nRestoreFrame ? true : false;
  279. }
  280. void AddSoundScapeFile( const char *filename );
  281. void TouchPlayLooping( KeyValues *pAmbient );
  282. void TouchPlayRandom( KeyValues *pPlayRandom );
  283. void TouchWaveFiles( KeyValues *pSoundScape );
  284. void TouchSoundFile( char const *wavefile );
  285. void TouchSoundFiles();
  286. int m_nRestoreFrame;
  287. CUtlVector< KeyValues * > m_SoundscapeScripts; // The whole script file in memory
  288. CUtlVector<KeyValues *> m_soundscapes; // Lookup by index of each root section
  289. struct Split_t
  290. {
  291. audioparams_t m_params; // current player audio params
  292. CUtlVector<loopingsound_t> m_loopingSounds; // list of currently playing sounds
  293. CUtlVector<randomsound_t> m_randomSounds; // list of random sound commands
  294. float m_nextRandomTime; // next time to play a random sound
  295. int m_loopingSoundId; // marks when the sound was issued
  296. int m_forcedSoundscapeIndex;// >= 0 if this a "forced" soundscape? i.e. debug mode?
  297. float m_forcedSoundscapeRadius;// distance to spatialized sounds
  298. };
  299. Split_t m_PerUser[ MAX_SPLITSCREEN_PLAYERS ];
  300. Split_t &GetPerUser( int nSlot )
  301. {
  302. return m_PerUser[ nSlot ];
  303. }
  304. const Split_t &GetPerUser( int nSlot ) const
  305. {
  306. return m_PerUser[ nSlot ];
  307. }
  308. static ConVar *m_pDSPVolumeVar;
  309. static ConVar *m_pSoundMixerVar;
  310. };
  311. // singleton system
  312. C_SoundscapeSystem g_SoundscapeSystem;
  313. ConVar *C_SoundscapeSystem::m_pDSPVolumeVar = NULL;
  314. ConVar *C_SoundscapeSystem::m_pSoundMixerVar = NULL;
  315. IGameSystem *ClientSoundscapeSystem()
  316. {
  317. return &g_SoundscapeSystem;
  318. }
  319. C_SoundscapeSystem *GetClientSoundscapeSystem()
  320. {
  321. return &g_SoundscapeSystem;
  322. }
  323. void Soundscape_OnStopAllSounds()
  324. {
  325. GetClientSoundscapeSystem()->OnStopAllSounds();
  326. }
  327. // player got a network update
  328. void Soundscape_Update( audioparams_t &audio )
  329. {
  330. GetClientSoundscapeSystem()->UpdateAudioParams( audio );
  331. }
  332. #define SOUNDSCAPE_MANIFEST_FILE "scripts/soundscapes_manifest.txt"
  333. void C_SoundscapeSystem::AddSoundScapeFile( const char *filename )
  334. {
  335. KeyValues *script = new KeyValues( filename );
  336. if ( filesystem->LoadKeyValues( *script, IFileSystem::TYPE_SOUNDSCAPE, filename, "GAME" ) )
  337. {
  338. // parse out all of the top level sections and save their names
  339. KeyValues *pKeys = script;
  340. while ( pKeys )
  341. {
  342. // save pointers to all sections in the root
  343. // each one is a soundscape
  344. if ( pKeys->GetFirstSubKey() )
  345. {
  346. m_soundscapes.AddToTail( pKeys );
  347. }
  348. pKeys = pKeys->GetNextKey();
  349. }
  350. // Keep pointer around so we can delete it at exit
  351. m_SoundscapeScripts.AddToTail( script );
  352. }
  353. else
  354. {
  355. script->deleteThis();
  356. }
  357. }
  358. // parse the script file, setup index table
  359. bool C_SoundscapeSystem::Init()
  360. {
  361. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  362. {
  363. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  364. GetPerUser(hh).m_loopingSoundId = 0;
  365. }
  366. const char *mapname = MapName();
  367. const char *mapSoundscapeFilename = NULL;
  368. if ( mapname && *mapname )
  369. {
  370. mapSoundscapeFilename = VarArgs( "scripts/soundscapes_%s.txt", mapname );
  371. }
  372. KeyValues *manifest = new KeyValues( SOUNDSCAPE_MANIFEST_FILE );
  373. if ( filesystem->LoadKeyValues( *manifest, IFileSystem::TYPE_SOUNDSCAPE, SOUNDSCAPE_MANIFEST_FILE, "GAME" ) )
  374. {
  375. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  376. {
  377. if ( !Q_stricmp( sub->GetName(), "file" ) )
  378. {
  379. // Add
  380. AddSoundScapeFile( sub->GetString() );
  381. if ( mapSoundscapeFilename && FStrEq( sub->GetString(), mapSoundscapeFilename ) )
  382. {
  383. mapSoundscapeFilename = NULL; // we've already loaded the map's soundscape
  384. }
  385. continue;
  386. }
  387. Warning( "C_SoundscapeSystem::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n",
  388. SOUNDSCAPE_MANIFEST_FILE, sub->GetName() );
  389. }
  390. if ( mapSoundscapeFilename && filesystem->FileExists( mapSoundscapeFilename ) )
  391. {
  392. AddSoundScapeFile( mapSoundscapeFilename );
  393. }
  394. }
  395. else
  396. {
  397. Error( "Unable to load manifest file '%s'\n", SOUNDSCAPE_MANIFEST_FILE );
  398. }
  399. manifest->deleteThis();
  400. return true;
  401. }
  402. int C_SoundscapeSystem::FindSoundscapeByName( const char *pSoundscapeName )
  403. {
  404. // UNDONE: Bad perf, linear search!
  405. for ( int i = m_soundscapes.Count()-1; i >= 0; --i )
  406. {
  407. if ( !Q_stricmp( m_soundscapes[i]->GetName(), pSoundscapeName ) )
  408. return i;
  409. }
  410. return -1;
  411. }
  412. KeyValues *C_SoundscapeSystem::SoundscapeByIndex( int index )
  413. {
  414. if ( m_soundscapes.IsValidIndex(index) )
  415. return m_soundscapes[index];
  416. return NULL;
  417. }
  418. const char *C_SoundscapeSystem::SoundscapeNameByIndex( int index )
  419. {
  420. if ( index < m_soundscapes.Count() )
  421. {
  422. return m_soundscapes[index]->GetName();
  423. }
  424. return NULL;
  425. }
  426. void C_SoundscapeSystem::Shutdown()
  427. {
  428. for ( int hh = 0; hh < MAX_SPLITSCREEN_PLAYERS; ++hh )
  429. {
  430. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  431. for ( int i = GetPerUser(hh).m_loopingSounds.Count() - 1; i >= 0; --i )
  432. {
  433. loopingsound_t &sound = GetPerUser(hh).m_loopingSounds[i];
  434. // sound is done, remove from list.
  435. StopLoopingSound( sound );
  436. }
  437. // These are only necessary so we can use shutdown/init calls
  438. // to flush soundscape data
  439. GetPerUser(hh).m_loopingSounds.RemoveAll();
  440. GetPerUser(hh).m_randomSounds.RemoveAll();
  441. GetPerUser(hh).m_params.entIndex = 0;
  442. GetPerUser(hh).m_params.soundscapeIndex = -1;
  443. }
  444. m_soundscapes.RemoveAll();
  445. while ( m_SoundscapeScripts.Count() > 0 )
  446. {
  447. KeyValues *kv = m_SoundscapeScripts[ 0 ];
  448. m_SoundscapeScripts.Remove( 0 );
  449. kv->deleteThis();
  450. }
  451. }
  452. // NOTE: This will not flush the server side so you cannot add or remove
  453. // soundscapes from the list, only change their parameters!!!!
  454. CON_COMMAND_F(cl_soundscape_flush, "Flushes the client side soundscapes", FCVAR_SERVER_CAN_EXECUTE|FCVAR_CHEAT)
  455. {
  456. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  457. {
  458. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  459. // save the current soundscape
  460. audioparams_t tmp;
  461. GetClientSoundscapeSystem()->GetAudioParams( tmp );
  462. // kill the system
  463. GetClientSoundscapeSystem()->Shutdown();
  464. // restart the system
  465. GetClientSoundscapeSystem()->Init();
  466. // reload the soundscape params from the temp copy
  467. Soundscape_Update( tmp );
  468. }
  469. }
  470. static int SoundscapeCompletion( const char *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  471. {
  472. // Autocomplete can just look at the base system
  473. ACTIVE_SPLITSCREEN_PLAYER_GUARD( 0 );
  474. int current = 0;
  475. const char *cmdname = "playsoundscape";
  476. char *substring = NULL;
  477. int substringLen = 0;
  478. if ( Q_strstr( partial, cmdname ) && strlen(partial) > strlen(cmdname) + 1 )
  479. {
  480. substring = (char *)partial + strlen( cmdname ) + 1;
  481. substringLen = strlen(substring);
  482. }
  483. int i = 0;
  484. const char *pSoundscapeName = GetClientSoundscapeSystem()->SoundscapeNameByIndex( i );
  485. while ( pSoundscapeName && current < COMMAND_COMPLETION_MAXITEMS )
  486. {
  487. if ( !substring || !Q_strncasecmp( pSoundscapeName, substring, substringLen ) )
  488. {
  489. Q_snprintf( commands[ current ], sizeof( commands[ current ] ), "%s %s", cmdname, pSoundscapeName );
  490. current++;
  491. }
  492. i++;
  493. pSoundscapeName = GetClientSoundscapeSystem()->SoundscapeNameByIndex( i );
  494. }
  495. return current;
  496. }
  497. CON_COMMAND_F_COMPLETION( playsoundscape, "Forces a soundscape to play", FCVAR_CHEAT, SoundscapeCompletion )
  498. {
  499. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  500. {
  501. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  502. if ( args.ArgC() < 2 )
  503. {
  504. GetClientSoundscapeSystem()->DevReportSoundscapeName( GetClientSoundscapeSystem()->GetCurrentSoundscape() );
  505. continue;
  506. }
  507. const char *pSoundscapeName = args[1];
  508. float radius = args.ArgC() > 2 ? atof( args[2] ) : DEFAULT_SOUND_RADIUS;
  509. GetClientSoundscapeSystem()->ForceSoundscape( pSoundscapeName, radius );
  510. }
  511. }
  512. CON_COMMAND_F( stopsoundscape, "Stops all soundscape processing and fades current looping sounds", FCVAR_CHEAT )
  513. {
  514. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  515. {
  516. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  517. GetClientSoundscapeSystem()->StartNewSoundscape( NULL );
  518. }
  519. }
  520. void C_SoundscapeSystem::ForceSoundscape( const char *pSoundscapeName, float radius )
  521. {
  522. int index = FindSoundscapeByName( pSoundscapeName );
  523. if ( index >= 0 )
  524. {
  525. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  526. GetPerUser(nSlot).m_forcedSoundscapeIndex = index;
  527. GetPerUser(nSlot).m_forcedSoundscapeRadius = radius;
  528. StartNewSoundscape( SoundscapeByIndex(index) );
  529. }
  530. else
  531. {
  532. DevWarning("Can't find soundscape %s\n", pSoundscapeName );
  533. }
  534. }
  535. void C_SoundscapeSystem::DevReportSoundscapeName( int index )
  536. {
  537. const char *pName = "none";
  538. if ( index >= 0 && index < m_soundscapes.Count() )
  539. {
  540. pName = m_soundscapes[index]->GetName();
  541. }
  542. if ( soundscape_message.GetBool() )
  543. {
  544. Msg( "Soundscape[%d]: %s\n", GET_ACTIVE_SPLITSCREEN_SLOT(), pName );
  545. }
  546. }
  547. // This makes all currently playing loops fade toward their target volume
  548. void C_SoundscapeSystem::UpdateLoopingSounds( float frametime )
  549. {
  550. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  551. int fadeCount = slot.m_loopingSounds.Count();
  552. while ( fadeCount > 0 )
  553. {
  554. fadeCount--;
  555. loopingsound_t &sound = slot.m_loopingSounds[fadeCount];
  556. bool bUpdateSound = sound.m_volume.IsFading();
  557. // for radius looping sounds, volume is manually set based on listener's distance
  558. if ( sound.radius > 0 )
  559. {
  560. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  561. if ( pPlayer )
  562. {
  563. C_BaseEntity *pEnt = pPlayer->GetSoundscapeListener();
  564. if ( pEnt )
  565. {
  566. float distance = pEnt->GetAbsOrigin().DistTo( sound.position );
  567. if ( distance > sound.radius * 100.0f )
  568. {
  569. // long way away, let sound fade to silence
  570. sound.m_volume.FadeToValue( 0.01f, 1.0f, FADE_VOLUME_LINEAR ); // HACK: Don't set sound to zero volume else it'll be removed and never started again!
  571. }
  572. else
  573. {
  574. float flTarget = 1.0f;
  575. // inside the radius, full volume, outside fade out
  576. if ( distance >= sound.radius )
  577. {
  578. flTarget = 1.0f / ( 1 + 0.5f * ( distance - sound.radius ) / sound.radius );
  579. }
  580. sound.m_volume.ForceToTargetValue( flTarget );
  581. }
  582. if ( soundscape_radius_debug.GetBool() )
  583. {
  584. DevMsg( 1, "Updated looping radius sound %d to vol=%f\n", fadeCount, sound.m_volume.m_flTarget );
  585. }
  586. bUpdateSound = true;
  587. }
  588. }
  589. }
  590. if ( bUpdateSound )
  591. {
  592. sound.m_volume.UpdateFade( frametime );
  593. if ( sound.m_volume.m_flTarget == 0 && sound.m_volume.m_flCurrent == 0 )
  594. {
  595. // sound is done, remove from list.
  596. StopLoopingSound( sound );
  597. slot.m_loopingSounds.FastRemove( fadeCount );
  598. }
  599. else
  600. {
  601. // tell the engine about the new volume
  602. UpdateLoopingSound( sound );
  603. }
  604. }
  605. }
  606. }
  607. void C_SoundscapeSystem::Update( float frametime )
  608. {
  609. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  610. {
  611. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  612. if ( GetPerUser(hh).m_forcedSoundscapeIndex >= 0 )
  613. {
  614. // generate fake positional sources
  615. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  616. if ( pPlayer )
  617. {
  618. Vector origin, forward, right;
  619. pPlayer->EyePositionAndVectors( &origin, &forward, &right, NULL );
  620. // put the sound origins at the corners of a box around the player
  621. GetPerUser(hh).m_params.localSound.Set( 0, origin + GetPerUser(hh).m_forcedSoundscapeRadius * (forward-right) );
  622. GetPerUser(hh).m_params.localSound.Set( 1, origin + GetPerUser(hh).m_forcedSoundscapeRadius * (forward+right) );
  623. GetPerUser(hh).m_params.localSound.Set( 2, origin + GetPerUser(hh).m_forcedSoundscapeRadius * (-forward-right) );
  624. GetPerUser(hh).m_params.localSound.Set( 3, origin + GetPerUser(hh).m_forcedSoundscapeRadius * (-forward+right) );
  625. GetPerUser(hh).m_params.localBits = 0x0007;
  626. }
  627. }
  628. // fade out the old sounds over soundscape_fadetime seconds
  629. UpdateLoopingSounds( frametime );
  630. UpdateRandomSounds( gpGlobals->curtime );
  631. }
  632. }
  633. void C_SoundscapeSystem::UpdateAudioParams( audioparams_t &audio )
  634. {
  635. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  636. if ( slot.m_params.soundscapeIndex == audio.soundscapeIndex && slot.m_params.entIndex == audio.entIndex )
  637. return;
  638. slot.m_params = audio;
  639. slot.m_forcedSoundscapeIndex = -1;
  640. if ( audio.entIndex > 0 && audio.soundscapeIndex >= 0 && audio.soundscapeIndex < m_soundscapes.Count() )
  641. {
  642. DevReportSoundscapeName( audio.soundscapeIndex );
  643. StartNewSoundscape( m_soundscapes[audio.soundscapeIndex] );
  644. }
  645. else
  646. {
  647. // bad index (and the soundscape file actually existed...)
  648. if ( audio.entIndex > 0 &&
  649. audio.soundscapeIndex != -1 )
  650. {
  651. DevMsg(1, "Error: Bad soundscape!\n");
  652. }
  653. }
  654. }
  655. // Called when a soundscape is activated (leading edge of becoming the active soundscape)
  656. void C_SoundscapeSystem::StartNewSoundscape( KeyValues *pSoundscape )
  657. {
  658. int i;
  659. float flFadeRate = GetSoundscapeFadeRate();
  660. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  661. // Reset the system
  662. // fade out the current loops
  663. // save off the count of old looping sounds
  664. int nOldLoopingSoundMax = slot.m_loopingSounds.Count()-1;
  665. for ( i = slot.m_loopingSounds.Count()-1; i >= 0; --i )
  666. {
  667. slot.m_loopingSounds[i].m_volume.FadeToValue( 0, flFadeRate, FADE_VOLUME_SINE );
  668. if ( !pSoundscape )
  669. {
  670. // if we're cancelling the soundscape, stop the sound immediately
  671. slot.m_loopingSounds[i].m_volume.ForceToTargetValue( 0 );
  672. }
  673. }
  674. // update ID
  675. slot.m_loopingSoundId++;
  676. // clear all random sounds
  677. slot.m_randomSounds.RemoveAll();
  678. slot.m_nextRandomTime = gpGlobals->curtime;
  679. if ( pSoundscape )
  680. {
  681. subsoundscapeparams_t params;
  682. params.allowDSP = true;
  683. params.wroteSoundMixer = false;
  684. params.wroteDSPVolume = false;
  685. params.masterVolume = 1.0;
  686. params.startingPosition = 0;
  687. params.recurseLevel = 0;
  688. params.positionOverride = -1;
  689. params.ambientPositionOverride = -1;
  690. params.flFadeRate = flFadeRate;
  691. params.bForceTextOriginAmbient = false;
  692. params.vForcedTextOriginAmbient.Init();
  693. StartSubSoundscape( pSoundscape, params );
  694. if ( !params.wroteDSPVolume )
  695. {
  696. m_pDSPVolumeVar->Revert();
  697. }
  698. if ( !params.wroteSoundMixer )
  699. {
  700. m_pSoundMixerVar->Revert();
  701. }
  702. // if we processed a fade rate, update the fade
  703. // This is a little bit of a hack but since we don't pre-parse soundscapes
  704. // into structs we can't know if there is a rate change on this soundscape
  705. if ( params.flFadeRate != flFadeRate )
  706. {
  707. for ( i = nOldLoopingSoundMax; i >= 0; --i )
  708. {
  709. // if we're still fading out at the old rate, fade at the new rate
  710. if ( slot.m_loopingSounds[i].m_volume.m_flTarget == 0.0f && slot.m_loopingSounds[i].m_volume.m_flRate == flFadeRate )
  711. {
  712. slot.m_loopingSounds[i].m_volume.m_flRate = params.flFadeRate;
  713. }
  714. }
  715. }
  716. }
  717. }
  718. void C_SoundscapeSystem::StartSubSoundscape( KeyValues *pSoundscape, subsoundscapeparams_t &params )
  719. {
  720. // Parse/process all of the commands
  721. KeyValues *pKey = pSoundscape->GetFirstSubKey();
  722. while ( pKey )
  723. {
  724. if ( !Q_strcasecmp( pKey->GetName(), "dsp" ) )
  725. {
  726. if ( params.allowDSP )
  727. {
  728. ProcessDSP( pKey );
  729. }
  730. }
  731. else if ( !Q_strcasecmp( pKey->GetName(), "dsp_player" ) )
  732. {
  733. if ( params.allowDSP )
  734. {
  735. ProcessDSPPlayer( pKey );
  736. }
  737. }
  738. else if ( !Q_strcasecmp( pKey->GetName(), "fadetime" ) )
  739. {
  740. // don't allow setting these recursively since they are order dependent
  741. if ( params.recurseLevel < 1 )
  742. {
  743. ProcessSoundscapeFadetime( pKey, params );
  744. }
  745. }
  746. else if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  747. {
  748. ProcessPlayLooping( pKey, params );
  749. }
  750. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  751. {
  752. ProcessPlayRandom( pKey, params );
  753. }
  754. else if ( !Q_strcasecmp( pKey->GetName(), "playsoundscape" ) )
  755. {
  756. ProcessPlaySoundscape( pKey, params );
  757. }
  758. else if ( !Q_strcasecmp( pKey->GetName(), "Soundmixer" ) )
  759. {
  760. if ( params.allowDSP )
  761. {
  762. ProcessSoundMixer( pKey, params );
  763. }
  764. }
  765. else if ( !Q_strcasecmp( pKey->GetName(), "dsp_volume" ) )
  766. {
  767. if ( params.allowDSP )
  768. {
  769. ProcessDSPVolume( pKey, params );
  770. }
  771. }
  772. // add new commands here
  773. else
  774. {
  775. DevMsg( 1, "Soundscape %s:Unknown command %s\n", pSoundscape->GetName(), pKey->GetName() );
  776. }
  777. pKey = pKey->GetNextKey();
  778. }
  779. }
  780. // add a process for each new command here
  781. // change DSP effect
  782. void C_SoundscapeSystem::ProcessDSP( KeyValues *pDSP )
  783. {
  784. int roomType = pDSP->GetInt();
  785. CLocalPlayerFilter filter;
  786. enginesound->SetRoomType( filter, roomType );
  787. }
  788. //-----------------------------------------------------------------------------
  789. // Purpose:
  790. // Input : *pDSPPlayer -
  791. //-----------------------------------------------------------------------------
  792. void C_SoundscapeSystem::ProcessDSPPlayer( KeyValues *pDSPPlayer )
  793. {
  794. int dspType = pDSPPlayer->GetInt();
  795. CLocalPlayerFilter filter;
  796. enginesound->SetPlayerDSP( filter, dspType, false );
  797. }
  798. void C_SoundscapeSystem::ProcessSoundMixer( KeyValues *pSoundMixer, subsoundscapeparams_t &params )
  799. {
  800. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  801. if ( !pPlayer || pPlayer->CanSetSoundMixer() )
  802. {
  803. m_pSoundMixerVar->SetValue( pSoundMixer->GetString() );
  804. params.wroteSoundMixer = true;
  805. }
  806. }
  807. void C_SoundscapeSystem::ProcessDSPVolume( KeyValues *pKey, subsoundscapeparams_t &params )
  808. {
  809. m_pDSPVolumeVar->SetValue( pKey->GetFloat() );
  810. params.wroteDSPVolume = true;
  811. }
  812. void C_SoundscapeSystem::ProcessSoundscapeFadetime( KeyValues *pKey, subsoundscapeparams_t &params )
  813. {
  814. float flFadeTime = pKey->GetFloat();
  815. if ( flFadeTime > 0.0f )
  816. {
  817. params.flFadeRate = 1.0f / flFadeTime;
  818. }
  819. }
  820. // start a new looping sound
  821. void C_SoundscapeSystem::ProcessPlayLooping( KeyValues *pAmbient, const subsoundscapeparams_t &params )
  822. {
  823. float volume = 0;
  824. soundlevel_t soundlevel = ATTN_TO_SNDLVL(ATTN_NORM);
  825. const char *pSoundName = NULL;
  826. int pitch = PITCH_NORM;
  827. int positionIndex = -1;
  828. bool randomPosition = false;
  829. bool suppress = false;
  830. bool useTextOrigin = false;
  831. Vector textOrigin;
  832. float radius = 0;
  833. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  834. KeyValues *pKey = pAmbient->GetFirstSubKey();
  835. while ( pKey )
  836. {
  837. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  838. {
  839. volume = params.masterVolume * RandomInterval( ReadInterval( pKey->GetString() ) );
  840. }
  841. else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
  842. {
  843. pitch = RandomInterval( ReadInterval( pKey->GetString() ) );
  844. }
  845. else if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
  846. {
  847. pSoundName = pKey->GetString();
  848. }
  849. else if ( !Q_strcasecmp( pKey->GetName(), "origin" ) )
  850. {
  851. textOrigin = getVectorFromString(pKey->GetString());
  852. useTextOrigin = true;
  853. }
  854. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  855. {
  856. if ( !Q_strcasecmp( pKey->GetString(), "random" ) )
  857. {
  858. randomPosition = true;
  859. }
  860. else
  861. {
  862. positionIndex = params.startingPosition + pKey->GetInt();
  863. }
  864. // positionIndex = params.startingPosition + pKey->GetInt();
  865. }
  866. else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) )
  867. {
  868. soundlevel = ATTN_TO_SNDLVL( RandomInterval( ReadInterval( pKey->GetString() ) ) );
  869. }
  870. else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
  871. {
  872. if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
  873. {
  874. soundlevel = TextToSoundLevel( pKey->GetString() );
  875. }
  876. else
  877. {
  878. soundlevel = (soundlevel_t)((int)RandomInterval( ReadInterval( pKey->GetString() ) ));
  879. }
  880. }
  881. else if ( !Q_strcasecmp( pKey->GetName(), "suppress_on_restore" ) )
  882. {
  883. suppress = Q_atoi( pKey->GetString() ) != 0 ? true : false;
  884. }
  885. else if ( !Q_strcasecmp( pKey->GetName(), "radius" ) )
  886. {
  887. radius = (float) atof( pKey->GetString() );
  888. }
  889. else
  890. {
  891. DevMsg( 1, "Ambient %s:Unknown command %s\n", pAmbient->GetName(), pKey->GetName() );
  892. }
  893. pKey = pKey->GetNextKey();
  894. }
  895. if ( positionIndex < 0 )
  896. {
  897. positionIndex = params.ambientPositionOverride;
  898. }
  899. else if ( params.positionOverride >= 0 )
  900. {
  901. positionIndex = params.positionOverride;
  902. }
  903. if ( params.bForceTextOriginAmbient && positionIndex < 0 )
  904. {
  905. useTextOrigin = true;
  906. textOrigin = params.vForcedTextOriginAmbient;
  907. }
  908. // Sound is mared as "suppress_on_restore" so don't restart it
  909. if ( IsBeingRestored() && suppress )
  910. {
  911. return;
  912. }
  913. if ( volume != 0 && pSoundName != NULL )
  914. {
  915. if ( randomPosition )
  916. {
  917. AddLoopingSound( pSoundName, false, volume, soundlevel, pitch, GenerateRandomSoundPosition(), radius, params.flFadeRate );
  918. }
  919. else if ( useTextOrigin )
  920. {
  921. AddLoopingSound( pSoundName, false, volume, soundlevel, pitch, textOrigin, radius, params.flFadeRate );
  922. }
  923. else if ( positionIndex < 0 )
  924. {
  925. AddLoopingAmbient( pSoundName, volume, pitch, radius, params.flFadeRate );
  926. }
  927. else
  928. {
  929. if ( positionIndex > 31 || !(slot.m_params.localBits & (1<<positionIndex) ) )
  930. {
  931. // suppress sounds if the position isn't available
  932. //DevMsg( 1, "Bad position %d\n", positionIndex );
  933. return;
  934. }
  935. AddLoopingSound( pSoundName, false, volume, soundlevel, pitch, slot.m_params.localSound[positionIndex], radius, params.flFadeRate );
  936. }
  937. }
  938. }
  939. void C_SoundscapeSystem::TouchSoundFile( char const *wavefile )
  940. {
  941. filesystem->GetFileTime( VarArgs( "sound/%s", PSkipSoundChars( wavefile ) ), "GAME" );
  942. }
  943. // start a new looping sound
  944. void C_SoundscapeSystem::TouchPlayLooping( KeyValues *pAmbient )
  945. {
  946. KeyValues *pKey = pAmbient->GetFirstSubKey();
  947. while ( pKey )
  948. {
  949. if ( !Q_strcasecmp( pKey->GetName(), "wave" ) )
  950. {
  951. char const *pSoundName = pKey->GetString();
  952. // Touch the file
  953. TouchSoundFile( pSoundName );
  954. }
  955. pKey = pKey->GetNextKey();
  956. }
  957. }
  958. Vector C_SoundscapeSystem::GenerateRandomSoundPosition()
  959. {
  960. float angle = random->RandomFloat( -180, 180 );
  961. float sinAngle, cosAngle;
  962. SinCos( angle, &sinAngle, &cosAngle );
  963. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  964. if ( pPlayer )
  965. {
  966. Vector origin, forward, right;
  967. pPlayer->EyePositionAndVectors( &origin, &forward, &right, NULL );
  968. return origin + DEFAULT_SOUND_RADIUS * (cosAngle * right + sinAngle * forward);
  969. }
  970. else
  971. {
  972. return CurrentViewOrigin() + DEFAULT_SOUND_RADIUS * (cosAngle * CurrentViewRight() + sinAngle * CurrentViewForward());
  973. }
  974. }
  975. void C_SoundscapeSystem::TouchSoundFiles()
  976. {
  977. if ( !CommandLine()->FindParm( "-makereslists" ) )
  978. return;
  979. int c = m_soundscapes.Count();
  980. for ( int i = 0; i < c ; ++i )
  981. {
  982. TouchWaveFiles( m_soundscapes[ i ] );
  983. }
  984. }
  985. void C_SoundscapeSystem::TouchWaveFiles( KeyValues *pSoundScape )
  986. {
  987. KeyValues *pKey = pSoundScape->GetFirstSubKey();
  988. while ( pKey )
  989. {
  990. if ( !Q_strcasecmp( pKey->GetName(), "playlooping" ) )
  991. {
  992. TouchPlayLooping( pKey );
  993. }
  994. else if ( !Q_strcasecmp( pKey->GetName(), "playrandom" ) )
  995. {
  996. TouchPlayRandom( pKey );
  997. }
  998. pKey = pKey->GetNextKey();
  999. }
  1000. }
  1001. // puts a recurring random sound event into the queue
  1002. void C_SoundscapeSystem::TouchPlayRandom( KeyValues *pPlayRandom )
  1003. {
  1004. KeyValues *pKey = pPlayRandom->GetFirstSubKey();
  1005. while ( pKey )
  1006. {
  1007. if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
  1008. {
  1009. KeyValues *pWaves = pKey->GetFirstSubKey();
  1010. while ( pWaves )
  1011. {
  1012. TouchSoundFile( pWaves->GetString() );
  1013. pWaves = pWaves->GetNextKey();
  1014. }
  1015. }
  1016. pKey = pKey->GetNextKey();
  1017. }
  1018. }
  1019. // puts a recurring random sound event into the queue
  1020. void C_SoundscapeSystem::ProcessPlayRandom( KeyValues *pPlayRandom, const subsoundscapeparams_t &params )
  1021. {
  1022. randomsound_t sound;
  1023. sound.Init();
  1024. sound.masterVolume = params.masterVolume;
  1025. int positionIndex = -1;
  1026. bool suppress = false;
  1027. bool randomPosition = false;
  1028. bool useTextOrigin = false;
  1029. Vector textOrigin;
  1030. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  1031. KeyValues *pKey = pPlayRandom->GetFirstSubKey();
  1032. while ( pKey )
  1033. {
  1034. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  1035. {
  1036. sound.volume = ReadInterval( pKey->GetString() );
  1037. }
  1038. else if ( !Q_strcasecmp( pKey->GetName(), "pitch" ) )
  1039. {
  1040. sound.pitch = ReadInterval( pKey->GetString() );
  1041. }
  1042. else if ( !Q_strcasecmp( pKey->GetName(), "attenuation" ) )
  1043. {
  1044. interval_t atten = ReadInterval( pKey->GetString() );
  1045. sound.soundlevel.start = ATTN_TO_SNDLVL( atten.start );
  1046. sound.soundlevel.range = ATTN_TO_SNDLVL( atten.start + atten.range ) - sound.soundlevel.start;
  1047. }
  1048. else if ( !Q_strcasecmp( pKey->GetName(), "soundlevel" ) )
  1049. {
  1050. if ( !Q_strncasecmp( pKey->GetString(), "SNDLVL_", strlen( "SNDLVL_" ) ) )
  1051. {
  1052. sound.soundlevel.start = TextToSoundLevel( pKey->GetString() );
  1053. sound.soundlevel.range = 0;
  1054. }
  1055. else
  1056. {
  1057. sound.soundlevel = ReadInterval( pKey->GetString() );
  1058. }
  1059. }
  1060. else if ( !Q_strcasecmp( pKey->GetName(), "time" ) )
  1061. {
  1062. sound.time = ReadInterval( pKey->GetString() );
  1063. }
  1064. else if ( !Q_strcasecmp( pKey->GetName(), "rndwave" ) )
  1065. {
  1066. KeyValues *pWaves = pKey->GetFirstSubKey();
  1067. sound.pWaves = pWaves;
  1068. sound.waveCount = 0;
  1069. while ( pWaves )
  1070. {
  1071. sound.waveCount++;
  1072. pWaves = pWaves->GetNextKey();
  1073. }
  1074. }
  1075. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  1076. {
  1077. if ( !Q_strcasecmp( pKey->GetString(), "random" ) )
  1078. {
  1079. randomPosition = true;
  1080. }
  1081. else
  1082. {
  1083. positionIndex = params.startingPosition + pKey->GetInt();
  1084. }
  1085. }
  1086. else if ( !Q_strcasecmp( pKey->GetName(), "origin" ) )
  1087. {
  1088. const char *originString = pKey->GetString();
  1089. textOrigin = getVectorFromString(originString);
  1090. useTextOrigin = true;
  1091. }
  1092. else if ( !Q_strcasecmp( pKey->GetName(), "suppress_on_restore" ) )
  1093. {
  1094. suppress = Q_atoi( pKey->GetString() ) != 0 ? true : false;
  1095. }
  1096. else
  1097. {
  1098. DevMsg( 1, "Random Sound %s:Unknown command %s\n", pPlayRandom->GetName(), pKey->GetName() );
  1099. }
  1100. pKey = pKey->GetNextKey();
  1101. }
  1102. if ( positionIndex < 0 )
  1103. {
  1104. positionIndex = params.ambientPositionOverride;
  1105. }
  1106. else if ( params.positionOverride >= 0 )
  1107. {
  1108. positionIndex = params.positionOverride;
  1109. randomPosition = false; // override trumps random position
  1110. }
  1111. if ( params.bForceTextOriginAmbient && positionIndex < 0 )
  1112. {
  1113. useTextOrigin = true;
  1114. textOrigin = params.vForcedTextOriginAmbient;
  1115. randomPosition = false;
  1116. }
  1117. // Sound is mared as "suppress_on_restore" so don't restart it
  1118. if ( IsBeingRestored() && suppress )
  1119. {
  1120. return;
  1121. }
  1122. if ( sound.waveCount != 0 )
  1123. {
  1124. if ( positionIndex < 0 && !randomPosition && !useTextOrigin )
  1125. {
  1126. sound.isAmbient = true;
  1127. AddRandomSound( sound );
  1128. }
  1129. else
  1130. {
  1131. sound.isAmbient = false;
  1132. if ( randomPosition )
  1133. {
  1134. sound.isRandom = true;
  1135. }
  1136. else if ( useTextOrigin )
  1137. {
  1138. sound.position = textOrigin;
  1139. }
  1140. else
  1141. {
  1142. if ( positionIndex > 31 || !(slot.m_params.localBits & (1<<positionIndex) ) )
  1143. {
  1144. // suppress sounds if the position isn't available
  1145. //DevMsg( 1, "Bad position %d\n", positionIndex );
  1146. return;
  1147. }
  1148. sound.position = slot.m_params.localSound[positionIndex];
  1149. }
  1150. AddRandomSound( sound );
  1151. }
  1152. }
  1153. }
  1154. void C_SoundscapeSystem::ProcessPlaySoundscape( KeyValues *pPlaySoundscape, subsoundscapeparams_t &paramsIn )
  1155. {
  1156. subsoundscapeparams_t subParams = paramsIn;
  1157. // sub-soundscapes NEVER set the DSP effects
  1158. subParams.allowDSP = false;
  1159. subParams.recurseLevel++;
  1160. if ( subParams.recurseLevel > MAX_SOUNDSCAPE_RECURSION )
  1161. {
  1162. DevMsg( "Error! Soundscape recursion overrun!\n" );
  1163. return;
  1164. }
  1165. KeyValues *pKey = pPlaySoundscape->GetFirstSubKey();
  1166. const char *pSoundscapeName = NULL;
  1167. while ( pKey )
  1168. {
  1169. if ( !Q_strcasecmp( pKey->GetName(), "volume" ) )
  1170. {
  1171. subParams.masterVolume = paramsIn.masterVolume * RandomInterval( ReadInterval( pKey->GetString() ) );
  1172. }
  1173. else if ( !Q_strcasecmp( pKey->GetName(), "position" ) )
  1174. {
  1175. subParams.startingPosition = paramsIn.startingPosition + pKey->GetInt();
  1176. }
  1177. else if ( !Q_strcasecmp( pKey->GetName(), "positionoverride" ) )
  1178. {
  1179. if ( paramsIn.positionOverride < 0 )
  1180. {
  1181. subParams.positionOverride = paramsIn.startingPosition + pKey->GetInt();
  1182. // positionoverride is only ever used to make a whole soundscape come from a point in space
  1183. // So go ahead and default ambients there too.
  1184. subParams.ambientPositionOverride = paramsIn.startingPosition + pKey->GetInt();
  1185. }
  1186. }
  1187. else if ( !Q_strcasecmp( pKey->GetName(), "ambientpositionoverride" ) )
  1188. {
  1189. if ( paramsIn.ambientPositionOverride < 0 )
  1190. {
  1191. subParams.ambientPositionOverride = paramsIn.startingPosition + pKey->GetInt();
  1192. }
  1193. }
  1194. else if ( !Q_strcasecmp( pKey->GetName(), "ambientoriginoverride" ) )
  1195. {
  1196. subParams.vForcedTextOriginAmbient = getVectorFromString(pKey->GetString());
  1197. subParams.bForceTextOriginAmbient = true;
  1198. }
  1199. else if ( !Q_strcasecmp( pKey->GetName(), "name" ) )
  1200. {
  1201. pSoundscapeName = pKey->GetString();
  1202. }
  1203. else if ( !Q_strcasecmp(pKey->GetName(), "soundlevel") )
  1204. {
  1205. DevMsg(1,"soundlevel not supported on sub-soundscapes\n");
  1206. }
  1207. else
  1208. {
  1209. DevMsg( 1, "Playsoundscape %s:Unknown command %s\n", pSoundscapeName ? pSoundscapeName : pPlaySoundscape->GetName(), pKey->GetName() );
  1210. }
  1211. pKey = pKey->GetNextKey();
  1212. }
  1213. if ( pSoundscapeName )
  1214. {
  1215. KeyValues *pSoundscapeKeys = SoundscapeByIndex( FindSoundscapeByName( pSoundscapeName ) );
  1216. if ( pSoundscapeKeys )
  1217. {
  1218. StartSubSoundscape( pSoundscapeKeys, subParams );
  1219. }
  1220. else
  1221. {
  1222. DevMsg( 1, "Trying to play unknown soundscape %s\n", pSoundscapeName );
  1223. }
  1224. }
  1225. }
  1226. // special kind of looping sound with no spatialization
  1227. int C_SoundscapeSystem::AddLoopingAmbient( const char *pSoundName, float volume, int pitch, float radius, float flFadeRate )
  1228. {
  1229. return AddLoopingSound( pSoundName, true, volume, SNDLVL_NORM, pitch, vec3_origin, radius, flFadeRate );
  1230. }
  1231. // add a looping sound to the list
  1232. // NOTE: will reuse existing entry (fade from current volume) if possible
  1233. // this prevents pops
  1234. int C_SoundscapeSystem::AddLoopingSound( const char *pSoundName, bool isAmbient, float volume, soundlevel_t soundlevel, int pitch, const Vector &position, float radius, float flFadeRate )
  1235. {
  1236. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  1237. loopingsound_t *pSoundSlot = NULL;
  1238. int soundSlot = slot.m_loopingSounds.Count() - 1;
  1239. bool bForceSoundUpdate = false;
  1240. while ( soundSlot >= 0 )
  1241. {
  1242. loopingsound_t &sound = slot.m_loopingSounds[soundSlot];
  1243. // NOTE: Will always restart/crossfade positional sounds
  1244. if ( sound.id != slot.m_loopingSoundId &&
  1245. sound.pitch == pitch &&
  1246. !Q_strcasecmp( pSoundName, sound.pWaveName ) )
  1247. {
  1248. // Ambient sounds can reuse the slots.
  1249. if ( isAmbient == true &&
  1250. sound.isAmbient == true )
  1251. {
  1252. // reuse this sound
  1253. pSoundSlot = &sound;
  1254. break;
  1255. }
  1256. // Positional sounds can reuse the slots if the positions are the same.
  1257. else if ( isAmbient == sound.isAmbient )
  1258. {
  1259. if ( VectorsAreEqual( position, sound.position, 0.1f ) )
  1260. {
  1261. // reuse this sound
  1262. pSoundSlot = &sound;
  1263. break;
  1264. }
  1265. }
  1266. }
  1267. soundSlot--;
  1268. }
  1269. if ( soundSlot < 0 )
  1270. {
  1271. // can't find the sound in the list, make a new one
  1272. soundSlot = slot.m_loopingSounds.AddToTail();
  1273. if ( isAmbient )
  1274. {
  1275. // start at 0 and fade in
  1276. enginesound->EmitAmbientSound( pSoundName, 0, pitch );
  1277. slot.m_loopingSounds[soundSlot].m_volume.m_flCurrent = 0.0;
  1278. }
  1279. else
  1280. {
  1281. // non-ambients at 0 volume are culled, so start at 0.05
  1282. CLocalPlayerFilter filter;
  1283. EmitSound_t ep;
  1284. ep.m_nChannel = CHAN_STATIC;
  1285. ep.m_pSoundName = pSoundName;
  1286. ep.m_flVolume = 0.05;
  1287. ep.m_SoundLevel = soundlevel;
  1288. ep.m_nPitch = pitch;
  1289. ep.m_pOrigin = &position;
  1290. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1291. slot.m_loopingSounds[soundSlot].m_volume.m_flCurrent = 0.05;
  1292. }
  1293. slot.m_loopingSounds[soundSlot].engineGuid = enginesound->GetGuidForLastSoundEmitted();
  1294. }
  1295. loopingsound_t &sound = slot.m_loopingSounds[soundSlot];
  1296. // fill out the slot
  1297. sound.pWaveName = pSoundName;
  1298. sound.m_volume.FadeToValue( volume, flFadeRate, FADE_VOLUME_SINE );
  1299. sound.pitch = pitch;
  1300. sound.id = slot.m_loopingSoundId;
  1301. sound.isAmbient = isAmbient;
  1302. sound.position = position;
  1303. sound.radius = radius;
  1304. if ( radius > 0 )
  1305. {
  1306. sound.soundlevel = SNDLVL_NONE; // play without attenuation if sound has a radius (volume will be manually set based on distance of listener to the radius)
  1307. }
  1308. else
  1309. {
  1310. sound.soundlevel = soundlevel;
  1311. }
  1312. if (bForceSoundUpdate)
  1313. {
  1314. UpdateLoopingSound(sound);
  1315. }
  1316. return soundSlot;
  1317. }
  1318. // stop this loop forever
  1319. void C_SoundscapeSystem::StopLoopingSound( loopingsound_t &loopSound )
  1320. {
  1321. enginesound->StopSoundByGuid( loopSound.engineGuid );
  1322. }
  1323. // update with new volume
  1324. void C_SoundscapeSystem::UpdateLoopingSound( loopingsound_t &loopSound )
  1325. {
  1326. if ( enginesound->IsSoundStillPlaying(loopSound.engineGuid) )
  1327. {
  1328. enginesound->SetVolumeByGuid( loopSound.engineGuid, loopSound.m_volume.m_flCurrent );
  1329. return;
  1330. }
  1331. if ( loopSound.isAmbient )
  1332. {
  1333. enginesound->EmitAmbientSound( loopSound.pWaveName, loopSound.m_volume.m_flCurrent, loopSound.pitch, SND_CHANGE_VOL );
  1334. }
  1335. else
  1336. {
  1337. CLocalPlayerFilter filter;
  1338. EmitSound_t ep;
  1339. ep.m_nChannel = CHAN_STATIC;
  1340. ep.m_pSoundName = loopSound.pWaveName;
  1341. ep.m_flVolume = loopSound.m_volume.m_flCurrent;
  1342. ep.m_SoundLevel = loopSound.soundlevel;
  1343. ep.m_nFlags = SND_CHANGE_VOL;
  1344. ep.m_nPitch = loopSound.pitch;
  1345. ep.m_pOrigin = &loopSound.position;
  1346. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1347. }
  1348. loopSound.engineGuid = enginesound->GetGuidForLastSoundEmitted();
  1349. }
  1350. // add a recurring random sound event
  1351. int C_SoundscapeSystem::AddRandomSound( const randomsound_t &sound )
  1352. {
  1353. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  1354. int index = slot.m_randomSounds.AddToTail( sound );
  1355. slot.m_randomSounds[index].nextPlayTime = gpGlobals->curtime + 0.5 * RandomInterval( sound.time );
  1356. return index;
  1357. }
  1358. // play a random sound randomly from this parameterization table
  1359. void C_SoundscapeSystem::PlayRandomSound( randomsound_t &sound )
  1360. {
  1361. Assert( sound.waveCount > 0 );
  1362. int waveId = random->RandomInt( 0, sound.waveCount-1 );
  1363. KeyValues *pWaves = sound.pWaves;
  1364. while ( waveId > 0 && pWaves )
  1365. {
  1366. pWaves = pWaves->GetNextKey();
  1367. waveId--;
  1368. }
  1369. if ( !pWaves )
  1370. return;
  1371. const char *pWaveName = pWaves->GetString();
  1372. if ( !pWaveName )
  1373. return;
  1374. if ( sound.isAmbient )
  1375. {
  1376. enginesound->EmitAmbientSound( pWaveName, sound.masterVolume * RandomInterval( sound.volume ), (int)RandomInterval( sound.pitch ) );
  1377. }
  1378. else
  1379. {
  1380. CLocalPlayerFilter filter;
  1381. EmitSound_t ep;
  1382. ep.m_nChannel = CHAN_STATIC;
  1383. ep.m_pSoundName = pWaveName;
  1384. ep.m_flVolume = sound.masterVolume * RandomInterval( sound.volume );
  1385. ep.m_SoundLevel = (soundlevel_t)(int)RandomInterval( sound.soundlevel );
  1386. ep.m_nPitch = (int)RandomInterval( sound.pitch );
  1387. if ( sound.isRandom )
  1388. {
  1389. sound.position = GenerateRandomSoundPosition();
  1390. }
  1391. ep.m_pOrigin = &sound.position;
  1392. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  1393. }
  1394. }
  1395. // walk the list of random sound commands and update
  1396. void C_SoundscapeSystem::UpdateRandomSounds( float gameTime )
  1397. {
  1398. Split_t &slot = GetPerUser(GET_ACTIVE_SPLITSCREEN_SLOT());
  1399. if ( gameTime < slot.m_nextRandomTime )
  1400. return;
  1401. slot.m_nextRandomTime = gameTime + 3600; // add some big time to check again (an hour)
  1402. for ( int i = slot.m_randomSounds.Count()-1; i >= 0; i-- )
  1403. {
  1404. // time to play?
  1405. if ( gameTime >= slot.m_randomSounds[i].nextPlayTime )
  1406. {
  1407. // UNDONE: add this in to fix range?
  1408. // float dt = m_randomSounds[i].nextPlayTime - gameTime;
  1409. PlayRandomSound( slot.m_randomSounds[i] );
  1410. // now schedule the next occurrance
  1411. // UNDONE: add support for "play once" sounds? FastRemove() here.
  1412. slot.m_randomSounds[i].nextPlayTime = gameTime + RandomInterval( slot.m_randomSounds[i].time );
  1413. }
  1414. // update next time to check the queue
  1415. if ( slot.m_randomSounds[i].nextPlayTime < slot.m_nextRandomTime )
  1416. {
  1417. slot.m_nextRandomTime = slot.m_randomSounds[i].nextPlayTime;
  1418. }
  1419. }
  1420. }
  1421. CON_COMMAND(cl_soundscape_printdebuginfo, "print soundscapes")
  1422. {
  1423. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  1424. {
  1425. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  1426. GetClientSoundscapeSystem()->PrintDebugInfo();
  1427. }
  1428. }
  1429. CON_COMMAND(cl_ss_origin, "print origin in script format")
  1430. {
  1431. Vector org = MainViewOrigin( GET_ACTIVE_SPLITSCREEN_SLOT() );
  1432. Warning("\"origin\"\t\"%.1f, %.1f, %.1f\"\n", org.x, org.y, org.z );
  1433. }