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.

455 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "engine/IEngineSound.h"
  8. #include "tier0/dbg.h"
  9. #include "quakedef.h"
  10. #include "vox.h"
  11. #include "server.h"
  12. #include "sv_main.h"
  13. #include "edict.h"
  14. #include "sound.h"
  15. #include "host.h"
  16. #include "vengineserver_impl.h"
  17. #include "enginesingleuserfilter.h"
  18. #include "snd_audio_source.h"
  19. #include "soundchars.h"
  20. #include "tier0/vprof.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. //-----------------------------------------------------------------------------
  24. //
  25. // Server-side implementation of the engine sound interface
  26. //
  27. //-----------------------------------------------------------------------------
  28. class CEngineSoundServer : public IEngineSound
  29. {
  30. public:
  31. // constructor, destructor
  32. CEngineSoundServer();
  33. virtual ~CEngineSoundServer();
  34. virtual bool PrecacheSound( const char *pSample, bool bPreload, bool bIsUISound );
  35. virtual bool IsSoundPrecached( const char *pSample );
  36. virtual void PrefetchSound( const char *pSample );
  37. virtual float GetSoundDuration( const char *pSample );
  38. virtual void EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  39. float flVolume, float flAttenuation, int iFlags, int iPitch, int iSpecialDSP,
  40. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
  41. virtual void EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  42. float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  43. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
  44. virtual void EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex,
  45. float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  46. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
  47. virtual void StopSound( int iEntIndex, int iChannel, const char *pSample );
  48. virtual void StopAllSounds( bool bClearBuffers );
  49. // Set the room type for a player
  50. virtual void SetRoomType( IRecipientFilter& filter, int roomType );
  51. virtual void SetPlayerDSP( IRecipientFilter& filter, int dspType, bool fastReset );
  52. // emit an "ambient" sound that isn't spatialized - specify left/right volume
  53. // only available on the client, assert on server
  54. virtual void EmitAmbientSound( const char *pSample, float flVolume, int iPitch, int flags, float soundtime = 0.0f );
  55. virtual float GetDistGainFromSoundLevel( soundlevel_t soundlevel, float dist );
  56. // Client .dll only functions
  57. virtual int GetGuidForLastSoundEmitted()
  58. {
  59. Warning( "Can't call GetGuidForLastSoundEmitted from server\n" );
  60. return 0;
  61. }
  62. virtual bool IsSoundStillPlaying( int guid )
  63. {
  64. Warning( "Can't call IsSoundStillPlaying from server\n" );
  65. return false;
  66. }
  67. virtual void StopSoundByGuid( int guid )
  68. {
  69. Warning( "Can't call StopSoundByGuid from server\n" );
  70. return;
  71. }
  72. // Retrieves list of all active sounds
  73. virtual void GetActiveSounds( CUtlVector< SndInfo_t >& sndlist )
  74. {
  75. Warning( "Can't call GetActiveSounds from server\n" );
  76. return;
  77. }
  78. // Set's master volume (0.0->1.0)
  79. virtual void SetVolumeByGuid( int guid, float fvol )
  80. {
  81. Warning( "Can't call SetVolumeByGuid from server\n" );
  82. return;
  83. }
  84. virtual void PrecacheSentenceGroup( const char *pGroupName )
  85. {
  86. VOX_PrecacheSentenceGroup( this, pGroupName );
  87. }
  88. virtual void NotifyBeginMoviePlayback()
  89. {
  90. AssertMsg( 0, "Not supported" );
  91. }
  92. virtual void NotifyEndMoviePlayback()
  93. {
  94. AssertMsg( 0, "Not supported" );
  95. }
  96. private:
  97. void EmitSoundInternal( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  98. float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  99. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
  100. };
  101. //-----------------------------------------------------------------------------
  102. // Client-server neutral sound interface accessor
  103. //-----------------------------------------------------------------------------
  104. static CEngineSoundServer s_EngineSoundServer;
  105. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineSoundServer, IEngineSound,
  106. IENGINESOUND_SERVER_INTERFACE_VERSION, s_EngineSoundServer );
  107. IEngineSound *EngineSoundServer()
  108. {
  109. return &s_EngineSoundServer;
  110. }
  111. //-----------------------------------------------------------------------------
  112. // constructor, destructor
  113. //-----------------------------------------------------------------------------
  114. CEngineSoundServer::CEngineSoundServer()
  115. {
  116. }
  117. CEngineSoundServer::~CEngineSoundServer()
  118. {
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Precache a particular sample
  122. //-----------------------------------------------------------------------------
  123. bool CEngineSoundServer::PrecacheSound( const char *pSample, bool bPreload, bool bIsUISound )
  124. {
  125. int i;
  126. if ( pSample && TestSoundChar( pSample, CHAR_SENTENCE ) )
  127. {
  128. return true;
  129. }
  130. if ( pSample[0] <= ' ' )
  131. {
  132. Host_Error( "CEngineSoundServer::PrecacheSound: Bad string: %s", pSample );
  133. }
  134. // add the sound to the precache list
  135. // Start at 1, since 0 is used to indicate an error in the sound precache
  136. i = SV_FindOrAddSound( pSample, bPreload );
  137. if ( i >= 0 )
  138. return true;
  139. Host_Error( "CEngineSoundServer::PrecacheSound: '%s' overflow", pSample );
  140. return false;
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose:
  144. // Input : *pSample -
  145. // Output : Returns true on success, false on failure.
  146. //-----------------------------------------------------------------------------
  147. bool CEngineSoundServer::IsSoundPrecached( const char *pSample )
  148. {
  149. if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
  150. {
  151. return true;
  152. }
  153. int idx = SV_SoundIndex( pSample );
  154. if ( idx == -1 )
  155. {
  156. return false;
  157. }
  158. return true;
  159. }
  160. void CEngineSoundServer::PrefetchSound( const char *pSample )
  161. {
  162. if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
  163. {
  164. return;
  165. }
  166. int idx = SV_SoundIndex( pSample );
  167. if ( idx == -1 )
  168. {
  169. return;
  170. }
  171. // Tell clients to prefetch the sound
  172. SVC_Prefetch msg;
  173. msg.m_fType = SVC_Prefetch::SOUND;
  174. msg.m_nSoundIndex = idx;
  175. sv.BroadcastMessage( msg, true, false );
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Stops a sound
  179. //-----------------------------------------------------------------------------
  180. void CEngineSoundServer::EmitSoundInternal( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  181. float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  182. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*=-1*/ )
  183. {
  184. AssertMsg( pDirection == NULL, "Direction specification not currently supported on server sounds" );
  185. AssertMsg( bUpdatePositions, "Non-updated positions not currently supported on server sounds" );
  186. if (flVolume < 0 || flVolume > 1)
  187. {
  188. Warning ("EmitSound: volume out of bounds = %f\n", flVolume);
  189. return;
  190. }
  191. if ( ( iSoundLevel < soundlevel_t(MIN_SNDLVL_VALUE) ) || ( iSoundLevel > soundlevel_t(MAX_SNDLVL_VALUE) ) )
  192. {
  193. Warning ("EmitSound: soundlevel out of bounds = %d\n", iSoundLevel);
  194. return;
  195. }
  196. if (iPitch < 0 || iPitch > 255)
  197. {
  198. Warning ("EmitSound: pitch out of bounds = %i\n", iPitch);
  199. return;
  200. }
  201. edict_t *pEdict = (iEntIndex >= 0) ? &sv.edicts[iEntIndex] : NULL;
  202. SV_StartSound( filter, pEdict, iChannel, pSample, flVolume, iSoundLevel,
  203. iFlags, iPitch, iSpecialDSP, pOrigin, soundtime, speakerentity, pUtlVecOrigins );
  204. }
  205. //-----------------------------------------------------------------------------
  206. // Plays a sentence
  207. //-----------------------------------------------------------------------------
  208. void CEngineSoundServer::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel,
  209. int iSentenceIndex, float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  210. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
  211. {
  212. if ( iSentenceIndex >= 0 )
  213. {
  214. char pName[8];
  215. Q_snprintf( pName, sizeof(pName), "!%d", iSentenceIndex );
  216. EmitSoundInternal( filter, iEntIndex, iChannel, pName, flVolume, iSoundLevel,
  217. iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Emits a sound
  222. //-----------------------------------------------------------------------------
  223. void CEngineSoundServer::EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  224. float flVolume, float flAttenuation, int iFlags, int iPitch, int iSpecialDSP,
  225. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
  226. {
  227. VPROF( "CEngineSoundServer::EmitSound" );
  228. EmitSound( filter, iEntIndex, iChannel, pSample, flVolume, ATTN_TO_SNDLVL( flAttenuation ), iFlags,
  229. iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
  230. }
  231. void CEngineSoundServer::EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
  232. float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
  233. const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
  234. {
  235. VPROF( "CEngineSoundServer::EmitSound" );
  236. if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
  237. {
  238. int iSentenceIndex = -1;
  239. VOX_LookupString( PSkipSoundChars(pSample), &iSentenceIndex );
  240. if (iSentenceIndex >= 0)
  241. {
  242. EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, flVolume,
  243. iSoundLevel, iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
  244. }
  245. else
  246. {
  247. DevWarning( 2, "Unable to find %s in sentences.txt\n", PSkipSoundChars(pSample) );
  248. }
  249. }
  250. else
  251. {
  252. EmitSoundInternal( filter, iEntIndex, iChannel, pSample, flVolume, iSoundLevel,
  253. iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
  254. }
  255. }
  256. void BuildRecipientList( CUtlVector< edict_t * >& list, const IRecipientFilter& filter )
  257. {
  258. int c = filter.GetRecipientCount();
  259. for ( int i = 0; i < c; i++ )
  260. {
  261. int playerindex = filter.GetRecipientIndex( i );
  262. if ( playerindex < 1 || playerindex > sv.GetClientCount() )
  263. continue;
  264. CGameClient *cl = sv.Client( playerindex - 1 );
  265. // Never output to bots
  266. if ( cl->IsFakeClient() )
  267. continue;
  268. if ( !cl->IsSpawned() )
  269. continue;
  270. list.AddToTail( cl->edict );
  271. }
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose:
  275. // Input : filter -
  276. // roomType -
  277. //-----------------------------------------------------------------------------
  278. void CEngineSoundServer::SetRoomType( IRecipientFilter& filter, int roomType )
  279. {
  280. CUtlVector< edict_t * > players;
  281. BuildRecipientList( players, filter );
  282. for ( int i = 0 ; i < players.Count(); i++ )
  283. {
  284. g_pVEngineServer->ClientCommand( players[ i ], "room_type %i\n", roomType );
  285. }
  286. }
  287. // Set the dsp preset for a player (client only)
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. // Input : filter -
  291. // dspType -
  292. //-----------------------------------------------------------------------------
  293. void CEngineSoundServer::SetPlayerDSP( IRecipientFilter& filter, int dspType, bool fastReset )
  294. {
  295. Assert( !fastReset );
  296. if ( fastReset )
  297. {
  298. Warning( "SetPlayerDSP: fastReset only valid from client\n" );
  299. }
  300. CUtlVector< edict_t * > players;
  301. BuildRecipientList( players, filter );
  302. for ( int i = 0 ; i < players.Count(); i++ )
  303. {
  304. g_pVEngineServer->ClientCommand( players[ i ], "dsp_player %i\n", dspType );
  305. }
  306. }
  307. void CEngineSoundServer::StopAllSounds(bool bClearBuffers)
  308. {
  309. AssertMsg( 0, "Not supported" );
  310. }
  311. void CEngineSoundServer::EmitAmbientSound( const char *pSample, float flVolume, int iPitch, int flags, float soundtime /*= 0.0f*/ )
  312. {
  313. AssertMsg( 0, "Not supported" );
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Stops a sound
  317. //-----------------------------------------------------------------------------
  318. void CEngineSoundServer::StopSound( int iEntIndex, int iChannel, const char *pSample )
  319. {
  320. CEngineRecipientFilter filter;
  321. filter.AddAllPlayers();
  322. filter.MakeReliable();
  323. EmitSound( filter, iEntIndex, iChannel, pSample, 0, SNDLVL_NONE, SND_STOP, PITCH_NORM, 0,
  324. NULL, NULL, NULL, true );
  325. }
  326. float SV_GetSoundDuration( const char *pSample )
  327. {
  328. #ifdef SWDS
  329. return 0; // TODO: make this return a real value (i.e implement an OS independent version of the sound code)
  330. #else
  331. return AudioSource_GetSoundDuration( PSkipSoundChars( pSample ) );
  332. #endif
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose:
  336. // Input : *pSample -
  337. // Output : float
  338. //-----------------------------------------------------------------------------
  339. float CEngineSoundServer::GetSoundDuration( const char *pSample )
  340. {
  341. return Host_GetSoundDuration( pSample );
  342. }
  343. float CEngineSoundServer::GetDistGainFromSoundLevel( soundlevel_t soundlevel, float dist )
  344. {
  345. return S_GetGainFromSoundLevel( soundlevel, dist );
  346. }
  347. /*
  348. //-----------------------------------------------------------------------------
  349. // FIXME: Move into the CEngineSoundServer class?
  350. //-----------------------------------------------------------------------------
  351. void Host_RestartAmbientSounds()
  352. {
  353. if (!sv.active)
  354. {
  355. return;
  356. }
  357. #ifndef SWDS
  358. const int NUM_INFOS = 64;
  359. SoundInfo_t soundInfo[NUM_INFOS];
  360. int nSounds = S_GetCurrentStaticSounds( soundInfo, NUM_INFOS, CHAN_STATIC );
  361. for ( int i = 0; i < nSounds; i++)
  362. {
  363. if (soundInfo[i].looping &&
  364. soundInfo[i].entity != -1 )
  365. {
  366. Msg("Restarting sound %s...\n", soundInfo[i].name);
  367. S_StopSound(soundInfo[i].entity, soundInfo[i].channel);
  368. CEngineRecipientFilter filter;
  369. filter.AddAllPlayers();
  370. SV_StartSound( filter, EDICT_NUM(soundInfo[i].entity),
  371. CHAN_STATIC,
  372. soundInfo[i].name,
  373. soundInfo[i].volume,
  374. soundInfo[i].soundlevel,
  375. 0, // @Q (toml 05-09-02): Is this correct, or will I need to squirrel away the original flags?
  376. soundInfo[i].pitch );
  377. }
  378. }
  379. #endif
  380. } */