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.

599 lines
17 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ====
  2. //
  3. // Purpose: Implements an entity that measures sound volume at a point in a map.
  4. //
  5. // This entity listens as though it is an NPC, meaning it will only
  6. // hear sounds that were emitted using the CSound::InsertSound function.
  7. //
  8. // It does not hear danger sounds since they are not technically sounds.
  9. //
  10. //=============================================================================
  11. #include "cbase.h"
  12. #include "entityinput.h"
  13. #include "entityoutput.h"
  14. #include "eventqueue.h"
  15. #include "mathlib/mathlib.h"
  16. #include "soundent.h"
  17. #include "envmicrophone.h"
  18. #include "soundflags.h"
  19. #include "engine/IEngineSound.h"
  20. #include "filters.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. //#define DEBUG_MICROPHONE
  24. const float MICROPHONE_SETTLE_EPSILON = 0.005;
  25. // List of env_microphones who want to be told whenever a sound is started
  26. static CUtlVector< CHandle<CEnvMicrophone> > s_Microphones;
  27. LINK_ENTITY_TO_CLASS(env_microphone, CEnvMicrophone);
  28. BEGIN_DATADESC( CEnvMicrophone )
  29. DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"),
  30. DEFINE_FIELD(m_hMeasureTarget, FIELD_EHANDLE),
  31. DEFINE_KEYFIELD(m_nSoundMask, FIELD_INTEGER, "SoundMask"),
  32. DEFINE_KEYFIELD(m_flSensitivity, FIELD_FLOAT, "Sensitivity"),
  33. DEFINE_KEYFIELD(m_flSmoothFactor, FIELD_FLOAT, "SmoothFactor"),
  34. DEFINE_KEYFIELD(m_iszSpeakerName, FIELD_STRING, "SpeakerName"),
  35. DEFINE_KEYFIELD(m_iszListenFilter, FIELD_STRING, "ListenFilter"),
  36. DEFINE_FIELD(m_hListenFilter, FIELD_EHANDLE),
  37. DEFINE_FIELD(m_hSpeaker, FIELD_EHANDLE),
  38. // DEFINE_FIELD(m_bAvoidFeedback, FIELD_BOOLEAN), // DONT SAVE
  39. DEFINE_KEYFIELD(m_iSpeakerDSPPreset, FIELD_INTEGER, "speaker_dsp_preset" ),
  40. DEFINE_KEYFIELD(m_flMaxRange, FIELD_FLOAT, "MaxRange"),
  41. DEFINE_AUTO_ARRAY(m_szLastSound, FIELD_CHARACTER),
  42. DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable),
  43. DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable),
  44. DEFINE_INPUTFUNC(FIELD_STRING, "SetSpeakerName", InputSetSpeakerName),
  45. DEFINE_OUTPUT(m_SoundLevel, "SoundLevel"),
  46. DEFINE_OUTPUT(m_OnRoutedSound, "OnRoutedSound" ),
  47. DEFINE_OUTPUT(m_OnHeardSound, "OnHeardSound" ),
  48. END_DATADESC()
  49. //-----------------------------------------------------------------------------
  50. // Purpose:
  51. //-----------------------------------------------------------------------------
  52. CEnvMicrophone::~CEnvMicrophone( void )
  53. {
  54. s_Microphones.FindAndRemove( this );
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose: Called before spawning, after keyvalues have been handled.
  58. //-----------------------------------------------------------------------------
  59. void CEnvMicrophone::Spawn(void)
  60. {
  61. //
  62. // Build our sound type mask from our spawnflags.
  63. //
  64. static int nFlags[][2] =
  65. {
  66. { SF_MICROPHONE_SOUND_COMBAT, SOUND_COMBAT },
  67. { SF_MICROPHONE_SOUND_WORLD, SOUND_WORLD },
  68. { SF_MICROPHONE_SOUND_PLAYER, SOUND_PLAYER },
  69. { SF_MICROPHONE_SOUND_BULLET_IMPACT, SOUND_BULLET_IMPACT },
  70. { SF_MICROPHONE_SOUND_EXPLOSION, SOUND_CONTEXT_EXPLOSION },
  71. };
  72. for (int i = 0; i < sizeof(nFlags) / sizeof(nFlags[0]); i++)
  73. {
  74. if (m_spawnflags & nFlags[i][0])
  75. {
  76. m_nSoundMask |= nFlags[i][1];
  77. }
  78. }
  79. if (m_flSensitivity == 0)
  80. {
  81. //
  82. // Avoid a divide by zero in CanHearSound.
  83. //
  84. m_flSensitivity = 1;
  85. }
  86. else if (m_flSensitivity > 10)
  87. {
  88. m_flSensitivity = 10;
  89. }
  90. m_flSmoothFactor = clamp(m_flSmoothFactor, 0, 0.9);
  91. if (!m_bDisabled)
  92. {
  93. SetNextThink( gpGlobals->curtime + 0.1f );
  94. }
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Called after all entities have spawned and after a load game.
  98. // Finds the reference point at which to measure sound level.
  99. //-----------------------------------------------------------------------------
  100. void CEnvMicrophone::Activate(void)
  101. {
  102. BaseClass::Activate();
  103. // Get a handle to my filter entity if there is one
  104. if (m_iszListenFilter != NULL_STRING)
  105. {
  106. m_hListenFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszListenFilter ));
  107. }
  108. if (m_target != NULL_STRING)
  109. {
  110. m_hMeasureTarget = gEntList.FindEntityByName(NULL, STRING(m_target) );
  111. //
  112. // If we were given a bad measure target, just measure sound where we are.
  113. //
  114. if ((m_hMeasureTarget == NULL) || (m_hMeasureTarget->edict() == NULL))
  115. {
  116. // We've decided to disable this warning since this seems to be the 90% case.
  117. //Warning( "EnvMicrophone - Measure target not found or measure target with no origin. Using Self.!\n");
  118. m_hMeasureTarget = this;
  119. }
  120. }
  121. else
  122. {
  123. m_hMeasureTarget = this;
  124. }
  125. ActivateSpeaker();
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose:
  129. //-----------------------------------------------------------------------------
  130. void CEnvMicrophone::OnRestore( void )
  131. {
  132. BaseClass::OnRestore();
  133. ActivateSpeaker();
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose: If we've got a speaker, add ourselves to the list of microphones that want to listen
  137. //-----------------------------------------------------------------------------
  138. void CEnvMicrophone::ActivateSpeaker( void )
  139. {
  140. // If we're enabled, set the dsp_speaker preset to my specified one
  141. if ( !m_bDisabled )
  142. {
  143. ConVarRef dsp_speaker( "dsp_speaker" );
  144. if ( dsp_speaker.IsValid() )
  145. {
  146. int iDSPPreset = m_iSpeakerDSPPreset;
  147. if ( !iDSPPreset )
  148. {
  149. // Reset it to the default
  150. iDSPPreset = atoi( dsp_speaker.GetDefault() );
  151. }
  152. DevMsg( 2, "Microphone %s set dsp_speaker to %d.\n", STRING(GetEntityName()), iDSPPreset);
  153. dsp_speaker.SetValue( m_iSpeakerDSPPreset );
  154. }
  155. }
  156. if ( m_iszSpeakerName != NULL_STRING )
  157. {
  158. // We've got a speaker to play heard sounds through. To do this, we need to add ourselves
  159. // to the list of microphones who want to be told whenever a sound is played.
  160. if ( s_Microphones.Find( this ) == -1 )
  161. {
  162. s_Microphones.AddToTail( this );
  163. }
  164. }
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Stops the microphone from sampling the sound level and firing the
  168. // SoundLevel output.
  169. //-----------------------------------------------------------------------------
  170. void CEnvMicrophone::InputEnable( inputdata_t &inputdata )
  171. {
  172. if (m_bDisabled)
  173. {
  174. m_bDisabled = false;
  175. SetNextThink( gpGlobals->curtime + 0.1f );
  176. ActivateSpeaker();
  177. }
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose: Resumes sampling the sound level and firing the SoundLevel output.
  181. //-----------------------------------------------------------------------------
  182. void CEnvMicrophone::InputDisable( inputdata_t &inputdata )
  183. {
  184. m_bDisabled = true;
  185. if ( m_hSpeaker )
  186. {
  187. CBaseEntity::StopSound( m_hSpeaker->entindex(), CHAN_STATIC, m_szLastSound );
  188. m_szLastSound[0] = 0;
  189. // Remove ourselves from the list of active mics
  190. s_Microphones.FindAndRemove( this );
  191. }
  192. SetNextThink( TICK_NEVER_THINK );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. // Input : &inputdata -
  197. //-----------------------------------------------------------------------------
  198. void CEnvMicrophone::InputSetSpeakerName( inputdata_t &inputdata )
  199. {
  200. SetSpeakerName( inputdata.value.StringID() );
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose: Checks whether this microphone can hear a given sound, and at what
  204. // relative volume level.
  205. // Input : pSound - Sound to test.
  206. // flVolume - Returns with the relative sound volume from 0 - 1.
  207. // Output : Returns true if the sound could be heard at the sample point, false if not.
  208. //-----------------------------------------------------------------------------
  209. bool CEnvMicrophone::CanHearSound(CSound *pSound, float &flVolume)
  210. {
  211. flVolume = 0;
  212. if ( m_bDisabled )
  213. {
  214. return false;
  215. }
  216. // Cull out sounds except from specific entities
  217. CBaseFilter *pFilter = m_hListenFilter.Get();
  218. if ( pFilter )
  219. {
  220. CBaseEntity *pSoundOwner = pSound->m_hOwner.Get();
  221. if ( !pSoundOwner || !pFilter->PassesFilter( this, pSoundOwner ) )
  222. {
  223. return false;
  224. }
  225. }
  226. float flDistance = (pSound->GetSoundOrigin() - m_hMeasureTarget->GetAbsOrigin()).Length();
  227. if (flDistance == 0)
  228. {
  229. flVolume = 1.0;
  230. return true;
  231. }
  232. // Over our max range?
  233. if ( m_flMaxRange && flDistance > m_flMaxRange )
  234. {
  235. return false;
  236. }
  237. if (flDistance <= pSound->Volume() * m_flSensitivity)
  238. {
  239. flVolume = 1 - (flDistance / (pSound->Volume() * m_flSensitivity));
  240. flVolume = clamp(flVolume, 0, 1);
  241. return true;
  242. }
  243. return false;
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Return true if the microphone can hear the specified sound
  247. //-----------------------------------------------------------------------------
  248. bool CEnvMicrophone::CanHearSound( int entindex, soundlevel_t soundlevel, float &flVolume, const Vector *pOrigin )
  249. {
  250. if ( m_bDisabled )
  251. {
  252. flVolume = 0;
  253. return false;
  254. }
  255. if ( ( m_spawnflags & SF_MICROPHONE_IGNORE_NONATTENUATED ) && soundlevel == SNDLVL_NONE )
  256. {
  257. return false;
  258. }
  259. // Sound might be coming from an origin or from an entity.
  260. CBaseEntity *pEntity = NULL;
  261. if ( entindex )
  262. {
  263. pEntity = CBaseEntity::Instance( INDEXENT(entindex) );
  264. }
  265. // Cull out sounds except from specific entities
  266. CBaseFilter *pFilter = m_hListenFilter.Get();
  267. if ( pFilter )
  268. {
  269. if ( !pEntity || !pFilter->PassesFilter( this, pEntity ) )
  270. {
  271. flVolume = 0;
  272. return false;
  273. }
  274. }
  275. float flDistance = 0;
  276. if ( pOrigin )
  277. {
  278. flDistance = pOrigin->DistTo( m_hMeasureTarget->GetAbsOrigin() );
  279. }
  280. else if ( pEntity )
  281. {
  282. flDistance = pEntity->WorldSpaceCenter().DistTo( m_hMeasureTarget->GetAbsOrigin() );
  283. }
  284. // Over our max range?
  285. if ( m_flMaxRange && flDistance > m_flMaxRange )
  286. {
  287. #ifdef DEBUG_MICROPHONE
  288. Msg("OUT OF RANGE.\n" );
  289. #endif
  290. return false;
  291. }
  292. #ifdef DEBUG_MICROPHONE
  293. Msg(" flVolume %f ", flVolume );
  294. #endif
  295. // Reduce the volume by the amount it fell to get to the microphone
  296. float gain = enginesound->GetDistGainFromSoundLevel( soundlevel, flDistance );
  297. flVolume *= gain;
  298. #ifdef DEBUG_MICROPHONE
  299. Msg("dist %2f, soundlevel %d: gain %f", flDistance, (int)soundlevel, gain );
  300. if ( !flVolume )
  301. {
  302. Msg(" : REJECTED\n" );
  303. }
  304. else
  305. {
  306. Msg(" : SENT\n" );
  307. }
  308. #endif
  309. return ( flVolume > 0 );
  310. }
  311. void CEnvMicrophone::SetSensitivity( float flSensitivity )
  312. {
  313. m_flSensitivity = flSensitivity;
  314. }
  315. void CEnvMicrophone::SetMaxRange( float flMaxRange )
  316. {
  317. m_flMaxRange = flMaxRange;
  318. }
  319. void CEnvMicrophone::SetSpeakerName( string_t iszSpeakerName )
  320. {
  321. m_iszSpeakerName = iszSpeakerName;
  322. // Set the speaker to null. This will force it to find the speaker next time a sound is routed.
  323. m_hSpeaker = NULL;
  324. ActivateSpeaker();
  325. }
  326. //--------------------------------------------------------------------------------------------------
  327. // Same as above, but skips the speaker name lookup
  328. //--------------------------------------------------------------------------------------------------
  329. void CEnvMicrophone::SetSpeaker( string_t iszSpeakerName, EHANDLE hSpeaker )
  330. {
  331. m_iszSpeakerName = iszSpeakerName;
  332. m_hSpeaker = hSpeaker;
  333. ActivateSpeaker();
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Listens for sounds and updates the value of the SoundLevel output.
  337. //-----------------------------------------------------------------------------
  338. void CEnvMicrophone::Think(void)
  339. {
  340. int nSound = CSoundEnt::ActiveList();
  341. bool fHearSound = false;
  342. float flMaxVolume = 0;
  343. //
  344. // Find the loudest sound that this microphone cares about.
  345. //
  346. while (nSound != SOUNDLIST_EMPTY)
  347. {
  348. CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex(nSound);
  349. if (pCurrentSound)
  350. {
  351. if (m_nSoundMask & pCurrentSound->SoundType())
  352. {
  353. float flVolume = 0;
  354. if (CanHearSound(pCurrentSound, flVolume) && (flVolume > flMaxVolume))
  355. {
  356. flMaxVolume = flVolume;
  357. fHearSound = true;
  358. }
  359. }
  360. }
  361. nSound = pCurrentSound->NextSound();
  362. }
  363. if( fHearSound )
  364. {
  365. m_OnHeardSound.FireOutput( this, this );
  366. }
  367. if (flMaxVolume != m_SoundLevel.Get())
  368. {
  369. //
  370. // Don't smooth if we are within an epsilon. This allows the output to stop firing
  371. // much more quickly.
  372. //
  373. if (fabs(flMaxVolume - m_SoundLevel.Get()) < MICROPHONE_SETTLE_EPSILON)
  374. {
  375. m_SoundLevel.Set(flMaxVolume, this, this);
  376. }
  377. else
  378. {
  379. m_SoundLevel.Set(flMaxVolume * (1 - m_flSmoothFactor) + m_SoundLevel.Get() * m_flSmoothFactor, this, this);
  380. }
  381. }
  382. SetNextThink( gpGlobals->curtime + 0.1f );
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Hook for the sound system to tell us when a sound's been played
  386. //-----------------------------------------------------------------------------
  387. MicrophoneResult_t CEnvMicrophone::SoundPlayed( int entindex, const char *soundname, soundlevel_t soundlevel, float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins )
  388. {
  389. if ( m_bAvoidFeedback )
  390. return MicrophoneResult_Ok;
  391. // Don't hear sounds that have already been heard by a microphone to avoid feedback!
  392. if ( iFlags & SND_SPEAKER )
  393. return MicrophoneResult_Ok;
  394. #ifdef DEBUG_MICROPHONE
  395. Msg("%s heard %s: ", STRING(GetEntityName()), soundname );
  396. #endif
  397. if ( !CanHearSound( entindex, soundlevel, flVolume, pOrigin ) )
  398. return MicrophoneResult_Ok;
  399. // We've heard it. Play it out our speaker. If our speaker's gone away, we're done.
  400. if ( !m_hSpeaker )
  401. {
  402. // First time, find our speaker. Done here, because finding it in Activate() wouldn't
  403. // find players, and we need to be able to specify !player for a speaker.
  404. if ( m_iszSpeakerName != NULL_STRING )
  405. {
  406. m_hSpeaker = gEntList.FindEntityByName(NULL, STRING(m_iszSpeakerName) );
  407. if ( !m_hSpeaker )
  408. {
  409. Warning( "EnvMicrophone %s specifies a non-existent speaker name: %s\n", STRING(GetEntityName()), STRING(m_iszSpeakerName) );
  410. m_iszSpeakerName = NULL_STRING;
  411. }
  412. }
  413. if ( !m_hSpeaker )
  414. {
  415. return MicrophoneResult_Remove;
  416. }
  417. }
  418. m_bAvoidFeedback = true;
  419. // Add the speaker flag. Detected at playback and applies the speaker filter.
  420. iFlags |= SND_SPEAKER;
  421. CPASAttenuationFilter filter( m_hSpeaker );
  422. EmitSound_t ep;
  423. ep.m_nChannel = CHAN_STATIC;
  424. ep.m_pSoundName = soundname;
  425. ep.m_flVolume = flVolume;
  426. ep.m_SoundLevel = soundlevel;
  427. ep.m_nFlags = iFlags;
  428. ep.m_nPitch = iPitch;
  429. ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin();
  430. ep.m_flSoundTime = soundtime;
  431. ep.m_nSpeakerEntity = entindex;
  432. CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep );
  433. Q_strncpy( m_szLastSound, soundname, sizeof(m_szLastSound) );
  434. m_OnRoutedSound.FireOutput( this, this, 0 );
  435. m_bAvoidFeedback = false;
  436. // Copy emitted origin to soundorigins array
  437. for ( int i = 0; i < ep.m_UtlVecSoundOrigin.Count(); ++i )
  438. {
  439. soundorigins.AddToTail( ep.m_UtlVecSoundOrigin[ i ] );
  440. }
  441. // Do we want to allow the original sound to play?
  442. if ( m_spawnflags & SF_MICROPHONE_SWALLOW_ROUTED_SOUNDS )
  443. {
  444. return MicrophoneResult_Swallow;
  445. }
  446. return MicrophoneResult_Ok;
  447. }
  448. void CEnvMicrophone::SoundStopped( const char *soundname )
  449. {
  450. if ( m_hSpeaker )
  451. {
  452. CBaseEntity::StopSound( m_hSpeaker->entindex(), CHAN_STATIC, soundname, true );
  453. }
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Called by the sound system whenever a sound is played so that
  457. // active microphones can have a chance to pick up the sound.
  458. // Output : Returns whether or not the sound was swallowed by the microphone.
  459. // Swallowed sounds should not be played by the sound system.
  460. //-----------------------------------------------------------------------------
  461. bool CEnvMicrophone::OnSoundPlayed( int entindex, const char *soundname, soundlevel_t soundlevel, float flVolume, int iFlags, int iPitch, const Vector *pOrigin, float soundtime, CUtlVector< Vector >& soundorigins )
  462. {
  463. bool bSwallowed = false;
  464. // Loop through all registered microphones and tell them the sound was just played
  465. int iCount = s_Microphones.Count();
  466. if ( iCount > 0 )
  467. {
  468. // Iterate backwards because we might be deleting microphones.
  469. for ( int i = iCount - 1; i >= 0; i-- )
  470. {
  471. if ( s_Microphones[i] )
  472. {
  473. MicrophoneResult_t eResult = s_Microphones[i]->SoundPlayed(
  474. entindex,
  475. soundname,
  476. soundlevel,
  477. flVolume,
  478. iFlags,
  479. iPitch,
  480. pOrigin,
  481. soundtime,
  482. soundorigins );
  483. if ( eResult == MicrophoneResult_Swallow )
  484. {
  485. // Microphone told us to swallow it
  486. bSwallowed = true;
  487. }
  488. else if ( eResult == MicrophoneResult_Remove )
  489. {
  490. s_Microphones.FastRemove( i );
  491. }
  492. }
  493. }
  494. }
  495. return bSwallowed;
  496. }
  497. void CEnvMicrophone::OnSoundStopped( const char *soundname )
  498. {
  499. // Loop through all registered microphones and tell them which sound to stop
  500. int iCount = s_Microphones.Count();
  501. if ( iCount > 0 )
  502. {
  503. for ( int i = iCount - 1; i >= 0; i-- )
  504. {
  505. if ( s_Microphones[i] )
  506. {
  507. s_Microphones[i]->SoundStopped( soundname );
  508. }
  509. }
  510. }
  511. }