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.

560 lines
16 KiB

  1. //========= Copyright 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.f, 0.9f);
  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.f, 1.f);
  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( engine->PEntityOfEntIndex(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::SetSpeakerName( string_t iszSpeakerName )
  316. {
  317. m_iszSpeakerName = iszSpeakerName;
  318. // Set the speaker to null. This will force it to find the speaker next time a sound is routed.
  319. m_hSpeaker = NULL;
  320. ActivateSpeaker();
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Listens for sounds and updates the value of the SoundLevel output.
  324. //-----------------------------------------------------------------------------
  325. void CEnvMicrophone::Think(void)
  326. {
  327. int nSound = CSoundEnt::ActiveList();
  328. bool fHearSound = false;
  329. float flMaxVolume = 0;
  330. //
  331. // Find the loudest sound that this microphone cares about.
  332. //
  333. while (nSound != SOUNDLIST_EMPTY)
  334. {
  335. CSound *pCurrentSound = CSoundEnt::SoundPointerForIndex(nSound);
  336. if (pCurrentSound)
  337. {
  338. if (m_nSoundMask & pCurrentSound->SoundType())
  339. {
  340. float flVolume = 0;
  341. if (CanHearSound(pCurrentSound, flVolume) && (flVolume > flMaxVolume))
  342. {
  343. flMaxVolume = flVolume;
  344. fHearSound = true;
  345. }
  346. }
  347. }
  348. nSound = pCurrentSound->NextSound();
  349. }
  350. if( fHearSound )
  351. {
  352. m_OnHeardSound.FireOutput( this, this );
  353. }
  354. if (flMaxVolume != m_SoundLevel.Get())
  355. {
  356. //
  357. // Don't smooth if we are within an epsilon. This allows the output to stop firing
  358. // much more quickly.
  359. //
  360. if (fabs(flMaxVolume - m_SoundLevel.Get()) < MICROPHONE_SETTLE_EPSILON)
  361. {
  362. m_SoundLevel.Set(flMaxVolume, this, this);
  363. }
  364. else
  365. {
  366. m_SoundLevel.Set(flMaxVolume * (1 - m_flSmoothFactor) + m_SoundLevel.Get() * m_flSmoothFactor, this, this);
  367. }
  368. }
  369. SetNextThink( gpGlobals->curtime + 0.1f );
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Hook for the sound system to tell us when a sound's been played
  373. //-----------------------------------------------------------------------------
  374. 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 )
  375. {
  376. if ( m_bAvoidFeedback )
  377. return MicrophoneResult_Ok;
  378. // Don't hear sounds that have already been heard by a microphone to avoid feedback!
  379. if ( iFlags & SND_SPEAKER )
  380. return MicrophoneResult_Ok;
  381. #ifdef DEBUG_MICROPHONE
  382. Msg("%s heard %s: ", STRING(GetEntityName()), soundname );
  383. #endif
  384. if ( !CanHearSound( entindex, soundlevel, flVolume, pOrigin ) )
  385. return MicrophoneResult_Ok;
  386. // We've heard it. Play it out our speaker. If our speaker's gone away, we're done.
  387. if ( !m_hSpeaker )
  388. {
  389. // First time, find our speaker. Done here, because finding it in Activate() wouldn't
  390. // find players, and we need to be able to specify !player for a speaker.
  391. if ( m_iszSpeakerName != NULL_STRING )
  392. {
  393. m_hSpeaker = gEntList.FindEntityByName(NULL, STRING(m_iszSpeakerName) );
  394. if ( !m_hSpeaker )
  395. {
  396. Warning( "EnvMicrophone %s specifies a non-existent speaker name: %s\n", STRING(GetEntityName()), STRING(m_iszSpeakerName) );
  397. m_iszSpeakerName = NULL_STRING;
  398. }
  399. }
  400. if ( !m_hSpeaker )
  401. {
  402. return MicrophoneResult_Remove;
  403. }
  404. }
  405. m_bAvoidFeedback = true;
  406. // Add the speaker flag. Detected at playback and applies the speaker filter.
  407. iFlags |= SND_SPEAKER;
  408. CPASAttenuationFilter filter( m_hSpeaker );
  409. EmitSound_t ep;
  410. ep.m_nChannel = CHAN_STATIC;
  411. ep.m_pSoundName = soundname;
  412. ep.m_flVolume = flVolume;
  413. ep.m_SoundLevel = soundlevel;
  414. ep.m_nFlags = iFlags;
  415. ep.m_nPitch = iPitch;
  416. ep.m_pOrigin = &m_hSpeaker->GetAbsOrigin();
  417. ep.m_flSoundTime = soundtime;
  418. ep.m_nSpeakerEntity = entindex;
  419. CBaseEntity::EmitSound( filter, m_hSpeaker->entindex(), ep );
  420. Q_strncpy( m_szLastSound, soundname, sizeof(m_szLastSound) );
  421. m_OnRoutedSound.FireOutput( this, this, 0 );
  422. m_bAvoidFeedback = false;
  423. // Copy emitted origin to soundorigins array
  424. for ( int i = 0; i < ep.m_UtlVecSoundOrigin.Count(); ++i )
  425. {
  426. soundorigins.AddToTail( ep.m_UtlVecSoundOrigin[ i ] );
  427. }
  428. // Do we want to allow the original sound to play?
  429. if ( m_spawnflags & SF_MICROPHONE_SWALLOW_ROUTED_SOUNDS )
  430. {
  431. return MicrophoneResult_Swallow;
  432. }
  433. return MicrophoneResult_Ok;
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose: Called by the sound system whenever a sound is played so that
  437. // active microphones can have a chance to pick up the sound.
  438. // Output : Returns whether or not the sound was swallowed by the microphone.
  439. // Swallowed sounds should not be played by the sound system.
  440. //-----------------------------------------------------------------------------
  441. 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 )
  442. {
  443. bool bSwallowed = false;
  444. // Loop through all registered microphones and tell them the sound was just played
  445. int iCount = s_Microphones.Count();
  446. if ( iCount > 0 )
  447. {
  448. // Iterate backwards because we might be deleting microphones.
  449. for ( int i = iCount - 1; i >= 0; i-- )
  450. {
  451. if ( s_Microphones[i] )
  452. {
  453. MicrophoneResult_t eResult = s_Microphones[i]->SoundPlayed(
  454. entindex,
  455. soundname,
  456. soundlevel,
  457. flVolume,
  458. iFlags,
  459. iPitch,
  460. pOrigin,
  461. soundtime,
  462. soundorigins );
  463. if ( eResult == MicrophoneResult_Swallow )
  464. {
  465. // Microphone told us to swallow it
  466. bSwallowed = true;
  467. }
  468. else if ( eResult == MicrophoneResult_Remove )
  469. {
  470. s_Microphones.FastRemove( i );
  471. }
  472. }
  473. }
  474. }
  475. return bSwallowed;
  476. }