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.

572 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Engine implementation of services required by the audio subsystem
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "quakedef.h"
  8. #include "cdll_int.h"
  9. #include "soundservice.h"
  10. #include "zone.h"
  11. #include "cdll_engine_int.h"
  12. #include "gl_model_private.h"
  13. #include "icliententity.h"
  14. #include "icliententitylist.h"
  15. #include "mouthinfo.h"
  16. #include "host.h"
  17. #include "vstdlib/random.h"
  18. #include "tier0/icommandline.h"
  19. #include "igame.h"
  20. #include "client.h"
  21. #include "server.h"
  22. #include "filesystem.h"
  23. #include "filesystem_engine.h"
  24. #include "sound.h"
  25. #include "vgui_controls/Controls.h"
  26. #include "vgui/ILocalize.h"
  27. #include "vgui_baseui_interface.h"
  28. #include "datacache/idatacache.h"
  29. #include "sys_dll.h"
  30. #include "toolframework/itoolframework.h"
  31. #include "tier0/vprof.h"
  32. #include "cl_steamauth.h"
  33. #include "tier1/fmtstr.h"
  34. #include "MapReslistGenerator.h"
  35. #include "cl_main.h"
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. void Snd_Restart_f();
  39. #define MAPLIST_FILE "maplist.txt"
  40. class CEngineSoundServices : public ISoundServices
  41. {
  42. public:
  43. CEngineSoundServices() { m_frameTime = 0; }
  44. virtual void *LevelAlloc( int nBytes, const char *pszTag )
  45. {
  46. return Hunk_AllocName(nBytes, pszTag);
  47. }
  48. virtual void OnExtraUpdate()
  49. {
  50. if ( IsPC() && g_ClientDLL && game && game->IsActiveApp() )
  51. {
  52. g_ClientDLL->IN_Accumulate();
  53. }
  54. }
  55. virtual bool GetSoundSpatialization( int entIndex, SpatializationInfo_t& info )
  56. {
  57. if ( !entitylist )
  58. {
  59. return false;
  60. }
  61. // Entity has been deleted
  62. IClientEntity *pClientEntity = entitylist->GetClientEntity( entIndex );
  63. if ( !pClientEntity )
  64. {
  65. // FIXME: Should this assert?
  66. return false;
  67. }
  68. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  69. bool bResult = pClientEntity->GetSoundSpatialization( info );
  70. return bResult;
  71. }
  72. virtual bool GetToolSpatialization( int iUserData, int guid, SpatializationInfo_t& info )
  73. {
  74. if ( IsX360() )
  75. {
  76. return false;
  77. }
  78. return toolframework->GetSoundSpatialization( iUserData, guid, info );
  79. }
  80. virtual float GetClientTime()
  81. {
  82. return GetBaseLocalClient().GetTime();
  83. }
  84. // Filtered local time
  85. virtual float GetHostTime()
  86. {
  87. return host_time;
  88. }
  89. virtual int GetViewEntity( int nSlot )
  90. {
  91. if ( g_ClientDLL != nullptr )
  92. {
  93. const int nInEyeEntity = g_ClientDLL->GetInEyeEntity();
  94. if (nInEyeEntity >= 0)
  95. return nInEyeEntity;
  96. }
  97. return GetLocalClient( nSlot ).GetViewEntity();
  98. }
  99. virtual void SetSoundFrametime( float realDt, float hostDt )
  100. {
  101. if ( cl_movieinfo.IsRecording() )
  102. {
  103. m_frameTime = hostDt;
  104. }
  105. else
  106. {
  107. m_frameTime = realDt;
  108. }
  109. }
  110. virtual float GetHostFrametime()
  111. {
  112. return m_frameTime;
  113. }
  114. virtual int GetServerCount()
  115. {
  116. return GetBaseLocalClient().m_nServerCount;
  117. }
  118. virtual bool IsPlayer( SoundSource source )
  119. {
  120. if ( source == GetSpectatorTarget( NULL ) )
  121. {
  122. return true;
  123. }
  124. if ( splitscreen->IsLocalPlayerResolvable() )
  125. {
  126. return ( source == GetLocalClient().m_nPlayerSlot + 1 );
  127. }
  128. FOR_EACH_VALID_SPLITSCREEN_PLAYER( i )
  129. {
  130. if ( GetLocalClient( i ).m_nPlayerSlot + 1 == source )
  131. return true;
  132. }
  133. return false;
  134. }
  135. virtual int GetSpectatorTarget( ClientDLLObserverMode_t *pObserverMode )
  136. {
  137. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  138. return ClientDLL_GetSpectatorTarget( pObserverMode );
  139. }
  140. virtual void OnChangeVoiceStatus( int entity, int iSsSlot, bool status )
  141. {
  142. // Local player changing state
  143. if ( iSsSlot >= 0 )
  144. {
  145. if ( Steam3Client().SteamFriends() && Steam3Client().SteamUser() )
  146. {
  147. // Tell Friends' Voice chat that the local user is speaking!!!
  148. Steam3Client().SteamFriends()->SetInGameVoiceSpeaking( Steam3Client().SteamUser()->GetSteamID(), status );
  149. }
  150. }
  151. ClientDLL_VoiceStatus( entity, iSsSlot, status );
  152. }
  153. virtual bool GetPlayerAudible( int iPlayerIndex )
  154. {
  155. return ClientDLL_IsPlayerAudible( iPlayerIndex );
  156. }
  157. virtual bool IsConnected()
  158. {
  159. return GetBaseLocalClient().IsConnected();
  160. }
  161. // Calls into client .dll with list of close caption tokens to construct a caption out of
  162. virtual void EmitSentenceCloseCaption( char const *tokenstream )
  163. {
  164. if ( g_ClientDLL )
  165. {
  166. g_ClientDLL->EmitSentenceCloseCaption( tokenstream );
  167. }
  168. }
  169. virtual void EmitCloseCaption( char const *captionname, float duration )
  170. {
  171. if ( g_ClientDLL )
  172. {
  173. g_ClientDLL->EmitCloseCaption( captionname, duration );
  174. }
  175. }
  176. virtual char const *GetGameDir()
  177. {
  178. return com_gamedir;
  179. }
  180. // If the game is paused, certain audio will pause, too (anything with phoneme/sentence data for now)
  181. virtual bool IsGamePaused()
  182. {
  183. extern IVEngineClient *engineClient;
  184. if ( !engineClient )
  185. {
  186. Assert( !"No engineClient, bug???" );
  187. return false;
  188. }
  189. return engineClient->IsPaused();
  190. }
  191. virtual void RestartSoundSystem()
  192. {
  193. Snd_Restart_f();
  194. }
  195. virtual void GetAllManifestFiles( CUtlRBTree< FileNameHandle_t, int >& list )
  196. {
  197. list.RemoveAll();
  198. // Load them in
  199. FileHandle_t resfilehandle = g_pFileSystem->Open( MAPLIST_FILE, "rb", "MOD" );
  200. if ( FILESYSTEM_INVALID_HANDLE != resfilehandle )
  201. {
  202. // Read in and parse mapcycle.txt
  203. int length = g_pFileSystem->Size(resfilehandle);
  204. if ( length > 0 )
  205. {
  206. char *pStart = (char *)new char[ length + 1 ];
  207. if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) )
  208. )
  209. {
  210. pStart[ length ] = 0;
  211. const char *pFileList = pStart;
  212. while ( 1 )
  213. {
  214. pFileList = COM_Parse( pFileList );
  215. if ( strlen( com_token ) <= 0 )
  216. break;
  217. char manifest_file[ 512 ];
  218. Q_snprintf( manifest_file, sizeof( manifest_file ), "%s/%s.manifest", AUDIOSOURCE_CACHE_ROOTDIR, com_token );
  219. if ( g_pFileSystem->FileExists( manifest_file, "MOD" ) )
  220. {
  221. FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( manifest_file );
  222. if ( list.Find( handle ) == list.InvalidIndex() )
  223. {
  224. list.Insert( handle );
  225. }
  226. }
  227. // Any more tokens on this line?
  228. while ( COM_TokenWaiting( pFileList ) )
  229. {
  230. pFileList = COM_Parse( pFileList );
  231. }
  232. }
  233. }
  234. delete[] pStart;
  235. }
  236. g_pFileSystem->Close(resfilehandle);
  237. }
  238. else
  239. {
  240. Warning( "GetAllManifestFiles: Unable to load %s\n", MAPLIST_FILE );
  241. }
  242. }
  243. virtual void GetAllSoundFilesInManifest( CUtlRBTree< FileNameHandle_t, int >& list, char const *manifestfile )
  244. {
  245. list.RemoveAll();
  246. CacheSoundsFromResFile( true, list, manifestfile, false );
  247. }
  248. virtual void GetAllSoundFilesReferencedInReslists( CUtlRBTree< FileNameHandle_t, int >& list )
  249. {
  250. char reslistdir[ MAX_PATH ];
  251. Q_strncpy( reslistdir, MapReslistGenerator().GetResListDirectory(), sizeof( reslistdir ) );
  252. list.RemoveAll();
  253. // Load them in
  254. FileHandle_t resfilehandle = g_pFileSystem->Open( MAPLIST_FILE, "rb", "MOD" );
  255. if ( FILESYSTEM_INVALID_HANDLE != resfilehandle )
  256. {
  257. // Read in and parse mapcycle.txt
  258. int length = g_pFileSystem->Size(resfilehandle);
  259. if ( length > 0 )
  260. {
  261. char *pStart = (char *)new char[ length + 1 ];
  262. if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) )
  263. )
  264. {
  265. pStart[ length ] = 0;
  266. const char *pFileList = pStart;
  267. while ( 1 )
  268. {
  269. char resfile[ 512 ];
  270. pFileList = COM_Parse( pFileList );
  271. if ( strlen( com_token ) <= 0 )
  272. break;
  273. Q_snprintf( resfile, sizeof( resfile ), "%s\\%s.lst", reslistdir, com_token );
  274. CacheSoundsFromResFile( false, list, resfile );
  275. // Any more tokens on this line?
  276. while ( COM_TokenWaiting( pFileList ) )
  277. {
  278. pFileList = COM_Parse( pFileList );
  279. }
  280. }
  281. }
  282. delete[] pStart;
  283. }
  284. g_pFileSystem->Close(resfilehandle);
  285. CacheSoundsFromResFile( false, list, CFmtStr( "%s\\engine.lst", reslistdir ) );
  286. CacheSoundsFromResFile( false, list, CFmtStr( "%s\\all.lst", reslistdir ) );
  287. }
  288. else
  289. {
  290. Warning( "GetAllSoundFilesReferencedInReslists: Unable to load file %s\n", MAPLIST_FILE );
  291. }
  292. }
  293. virtual void CacheBuildingStart()
  294. {
  295. if ( IsX360() )
  296. {
  297. return;
  298. }
  299. EngineVGui()->ActivateGameUI();
  300. EngineVGui()->StartCustomProgress();
  301. const wchar_t *str = g_pVGuiLocalize->Find( "#Valve_CreatingCache" );
  302. if ( str )
  303. {
  304. EngineVGui()->UpdateCustomProgressBar( 0.0f, str );
  305. }
  306. }
  307. virtual void CacheBuildingUpdateProgress( float percent, char const *cachefile )
  308. {
  309. if ( IsX360() )
  310. {
  311. return;
  312. }
  313. const wchar_t *format = g_pVGuiLocalize->Find( "Valve_CreatingSpecificSoundCache" );
  314. if ( format )
  315. {
  316. wchar_t constructed[ 1024 ];
  317. wchar_t file[ 256 ];
  318. g_pVGuiLocalize->ConvertANSIToUnicode( cachefile, file, sizeof( file ) );
  319. g_pVGuiLocalize->ConstructString(
  320. constructed,
  321. sizeof( constructed ),
  322. ( wchar_t * )format,
  323. 1,
  324. file );
  325. EngineVGui()->UpdateCustomProgressBar( percent, constructed );
  326. }
  327. }
  328. virtual void CacheBuildingFinish()
  329. {
  330. if ( IsX360() )
  331. {
  332. return;
  333. }
  334. EngineVGui()->FinishCustomProgress();
  335. EngineVGui()->HideGameUI();
  336. }
  337. virtual int GetPrecachedSoundCount()
  338. {
  339. if ( !sv.IsActive() )
  340. return 0;
  341. INetworkStringTable *table = sv.GetSoundPrecacheTable();
  342. if ( !table )
  343. return 0;
  344. return table->GetNumStrings();
  345. }
  346. virtual char const *GetPrecachedSound( int index )
  347. {
  348. Assert( sv.IsActive() );
  349. INetworkStringTable *table = sv.GetSoundPrecacheTable();
  350. if ( !table )
  351. return "";
  352. return table->GetString( index );
  353. }
  354. virtual bool ShouldSuppressNonUISounds()
  355. {
  356. return EngineVGui()->IsGameUIVisible() || IsGamePaused();
  357. }
  358. virtual char const *GetUILanguage()
  359. {
  360. extern ConVar cl_language;
  361. return cl_language.GetString();
  362. }
  363. private:
  364. float m_frameTime;
  365. void CacheSoundsFromResFile( bool quiet, CUtlRBTree< FileNameHandle_t, int >& list, char const *resfile, bool checkandcleanname = true )
  366. {
  367. if ( !g_pFileSystem->FileExists( resfile, "MOD" ) )
  368. {
  369. Warning( "CacheSoundsFromResFile: Unable to find '%s'\n", resfile );
  370. return;
  371. }
  372. int oldCount = list.Count();
  373. FileHandle_t resfilehandle = g_pFileSystem->Open( resfile, "rb", "MOD" );
  374. if ( FILESYSTEM_INVALID_HANDLE != resfilehandle )
  375. {
  376. // Read in and parse mapcycle.txt
  377. int length = g_pFileSystem->Size(resfilehandle);
  378. if ( length > 0 )
  379. {
  380. char *pStart = (char *)new char[ length + 1 ];
  381. if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) )
  382. )
  383. {
  384. pStart[ length ] = 0;
  385. const char *pFileList = pStart;
  386. while ( 1 )
  387. {
  388. pFileList = COM_Parse( pFileList );
  389. if ( strlen( com_token ) <= 0 )
  390. break;
  391. if ( checkandcleanname )
  392. {
  393. if ( Q_stristr( com_token, ".wav" ) ||
  394. Q_stristr( com_token, ".mp3" ) )
  395. {
  396. // skip past the game/mod directory "hl2/sound/player/footstep.wav"
  397. Q_FixSlashes(com_token); // "hl2\sound\player\footstep.wav"
  398. const char *pName = com_token;
  399. while (pName[0] && pName[0] != CORRECT_PATH_SEPARATOR)
  400. {
  401. pName++;
  402. } // "\sound\player\footstep.wav"
  403. FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( pName+1 ); // "sound\player\footstep.wav"
  404. if ( list.Find( handle ) == list.InvalidIndex() )
  405. {
  406. list.Insert( handle );
  407. }
  408. }
  409. }
  410. else
  411. {
  412. FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( com_token );
  413. if ( list.Find( handle ) == list.InvalidIndex() )
  414. {
  415. list.Insert( handle );
  416. }
  417. }
  418. }
  419. }
  420. delete[] pStart;
  421. }
  422. g_pFileSystem->Close(resfilehandle);
  423. }
  424. int newCount = list.Count();
  425. if ( !quiet )
  426. {
  427. Msg( "Processing (%i new) from %s\n", newCount - oldCount, resfile );
  428. }
  429. }
  430. virtual void OnSoundStarted( int guid, StartSoundParams_t& params, char const *soundname )
  431. {
  432. VPROF("OnSoundStarted");
  433. // Don't send the sound message to the tool framework if active tool
  434. // is not recording or the sound originated from the tool.
  435. if ( IsX360() || !toolframework->IsToolRecording() || params.bToolSound )
  436. return;
  437. KeyValues *msg = new KeyValues( "StartSound" );
  438. msg->SetInt( "guid", guid );
  439. msg->SetFloat( "time", GetBaseLocalClient().GetTime() );
  440. msg->SetBool( "staticsound", params.staticsound );
  441. msg->SetInt( "soundsource", params.soundsource );
  442. msg->SetInt( "entchannel", params.entchannel );
  443. msg->SetString( "soundname", soundname );
  444. msg->SetFloat( "originx", params.origin.x );
  445. msg->SetFloat( "originy", params.origin.y );
  446. msg->SetFloat( "originz", params.origin.z );
  447. msg->SetFloat( "directionx", params.direction.x );
  448. msg->SetFloat( "directiony", params.direction.y );
  449. msg->SetFloat( "directionz", params.direction.z );
  450. msg->SetInt( "updatepositions", params.bUpdatePositions );
  451. msg->SetFloat( "fvol", params.fvol );
  452. msg->SetInt( "soundlevel", (int)params.soundlevel );
  453. msg->SetInt( "flags", params.flags );
  454. msg->SetInt( "pitch", params.pitch );
  455. msg->SetBool( "fromserver", params.fromserver );
  456. msg->SetFloat( "delay", params.delay );
  457. msg->SetInt( "speakerentity", params.speakerentity );
  458. toolframework->PostMessage( msg );
  459. msg->deleteThis();
  460. }
  461. virtual void OnSoundStopped( int guid, int soundsource, int channel, char const *soundname )
  462. {
  463. // NOTE: At the moment, if we don't receive a StartSound message but we do
  464. // receive a StopSound message, the StopSound message is ignored. In a perfect
  465. // world, if the StartSound message was not sent, a StopSound message should not
  466. // be sent for that guid either. This requires more plumbing, though, and
  467. // for the moment, it's not necessary to do that plumbing.
  468. VPROF("OnSoundStopped");
  469. if ( IsX360() || !toolframework->IsToolRecording() )
  470. return;
  471. KeyValues *msg = new KeyValues( "StopSound" );
  472. msg->SetInt( "guid", guid );
  473. msg->SetFloat( "time", GetBaseLocalClient().GetTime() );
  474. msg->SetInt( "soundsource", soundsource );
  475. msg->SetInt( "entchannel", channel );
  476. msg->SetString( "soundname", soundname );
  477. toolframework->PostMessage( msg );
  478. msg->deleteThis();
  479. }
  480. };
  481. static CEngineSoundServices g_EngineSoundServices;
  482. ISoundServices *g_pSoundServices = &g_EngineSoundServices;