diff options
Diffstat (limited to 'engine/engsoundservice.cpp')
| -rw-r--r-- | engine/engsoundservice.cpp | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/engine/engsoundservice.cpp b/engine/engsoundservice.cpp new file mode 100644 index 0000000..079910f --- /dev/null +++ b/engine/engsoundservice.cpp @@ -0,0 +1,539 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Engine implementation of services required by the audio subsystem +// +// $NoKeywords: $ +//=============================================================================// + +#include "quakedef.h" +#include "soundservice.h" +#include "zone.h" +#include "cdll_engine_int.h" +#include "gl_model_private.h" +#include "icliententity.h" +#include "icliententitylist.h" +#include "mouthinfo.h" +#include "host.h" +#include "vstdlib/random.h" +#include "tier0/icommandline.h" +#include "igame.h" +#include "client.h" +#include "server.h" +#include "filesystem.h" +#include "filesystem_engine.h" +#include "sound.h" +#include "vgui_controls/Controls.h" +#include "vgui/ILocalize.h" +#include "vgui_baseui_interface.h" +#include "datacache/idatacache.h" +#include "sys_dll.h" +#include "toolframework/itoolframework.h" +#include "tier0/vprof.h" +#include "cl_steamauth.h" +#include "tier1/fmtstr.h" +#include "MapReslistGenerator.h" +#include "cl_main.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void Snd_Restart_f(); + +#define MAPLIST_FILE "maplist.txt" + +class CEngineSoundServices : public ISoundServices +{ +public: + CEngineSoundServices() { m_frameTime = 0; } + + virtual void *LevelAlloc( int nBytes, const char *pszTag ) + { + return Hunk_AllocName(nBytes, pszTag); + } + + virtual void OnExtraUpdate() + { + if ( IsPC() && g_ClientDLL && game && game->IsActiveApp() ) + { + g_ClientDLL->IN_Accumulate(); + } + } + + virtual bool GetSoundSpatialization( int entIndex, SpatializationInfo_t& info ) + { + if ( !entitylist ) + { + return false; + } + + // Entity has been deleted + IClientEntity *pClientEntity = entitylist->GetClientEntity( entIndex ); + if ( !pClientEntity ) + { + // FIXME: Should this assert? + return false; + } + + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + bool bResult = pClientEntity->GetSoundSpatialization( info ); + + return bResult; + } + + virtual bool GetToolSpatialization( int iUserData, int guid, SpatializationInfo_t& info ) + { + if ( IsX360() ) + { + return false; + } + + return toolframework->GetSoundSpatialization( iUserData, guid, info ); + } + + virtual float GetClientTime() + { + return cl.GetTime(); + } + + // Filtered local time + virtual float GetHostTime() + { + return host_time; + } + + virtual int GetViewEntity() + { + return cl.m_nViewEntity; + } + + virtual void SetSoundFrametime( float realDt, float hostDt ) + { + extern bool IsReplayRendering(); + if ( cl_movieinfo.IsRecording() || IsReplayRendering() ) + { + m_frameTime = hostDt; + } + else + { + m_frameTime = realDt; + } + } + + virtual float GetHostFrametime() + { + return m_frameTime; + } + + virtual int GetServerCount() + { + return cl.m_nServerCount; + } + + virtual bool IsPlayer( SoundSource source ) + { + return ( source == cl.m_nPlayerSlot + 1 ); + } + + virtual void OnChangeVoiceStatus( int entity, bool status) + { + ClientDLL_VoiceStatus(entity, status); + } + + virtual bool IsConnected() + { + return cl.IsConnected(); + } + + // Calls into client .dll with list of close caption tokens to construct a caption out of + virtual void EmitSentenceCloseCaption( char const *tokenstream ) + { + if ( g_ClientDLL ) + { + g_ClientDLL->EmitSentenceCloseCaption( tokenstream ); + } + } + + virtual void EmitCloseCaption( char const *captionname, float duration ) + { + if ( g_ClientDLL ) + { + g_ClientDLL->EmitCloseCaption( captionname, duration ); + } + } + + virtual char const *GetGameDir() + { + return com_gamedir; + } + + // If the game is paused, certain audio will pause, too (anything with phoneme/sentence data for now) + virtual bool IsGamePaused() + { + extern IVEngineClient *engineClient; + if ( !engineClient ) + { + Assert( !"No engineClient, bug???" ); + return false; + } + + return engineClient->IsPaused(); + } + + virtual bool IsGameActive() + { + extern IVEngineClient *engineClient; + if ( !engineClient ) + { + Assert( !"No engineClient, bug???" ); + return true; + } + + return engineClient->IsActiveApp(); + } + + virtual void RestartSoundSystem() + { + Snd_Restart_f(); + } + + virtual void GetAllManifestFiles( CUtlRBTree< FileNameHandle_t, int >& list ) + { + list.RemoveAll(); + + // Load them in + FileHandle_t resfilehandle = g_pFileSystem->Open( MAPLIST_FILE, "rb", "MOD" ); + if ( FILESYSTEM_INVALID_HANDLE != resfilehandle ) + { + // Read in and parse mapcycle.txt + int length = g_pFileSystem->Size(resfilehandle); + if ( length > 0 ) + { + char *pStart = (char *)new char[ length + 1 ]; + if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) ) + ) + { + pStart[ length ] = 0; + const char *pFileList = pStart; + + while ( 1 ) + { + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) <= 0 ) + break; + + char manifest_file[ 512 ]; + Q_snprintf( manifest_file, sizeof( manifest_file ), "%s/%s.manifest", AUDIOSOURCE_CACHE_ROOTDIR, com_token ); + + if ( g_pFileSystem->FileExists( manifest_file, "MOD" ) ) + { + FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( manifest_file ); + if ( list.Find( handle ) == list.InvalidIndex() ) + { + list.Insert( handle ); + } + } + + // Any more tokens on this line? + while ( COM_TokenWaiting( pFileList ) ) + { + pFileList = COM_Parse( pFileList ); + } + } + } + delete[] pStart; + } + + g_pFileSystem->Close(resfilehandle); + } + else + { + Warning( "GetAllManifestFiles: Unable to load %s\n", MAPLIST_FILE ); + } + } + + virtual void GetAllSoundFilesInManifest( CUtlRBTree< FileNameHandle_t, int >& list, char const *manifestfile ) + { + list.RemoveAll(); + CacheSoundsFromResFile( true, list, manifestfile, false ); + } + + virtual void GetAllSoundFilesReferencedInReslists( CUtlRBTree< FileNameHandle_t, int >& list ) + { + char reslistdir[ MAX_PATH ]; + Q_strncpy( reslistdir, MapReslistGenerator().GetResListDirectory(), sizeof( reslistdir ) ); + list.RemoveAll(); + + // Load them in + FileHandle_t resfilehandle = g_pFileSystem->Open( MAPLIST_FILE, "rb", "MOD" ); + if ( FILESYSTEM_INVALID_HANDLE != resfilehandle ) + { + // Read in and parse mapcycle.txt + int length = g_pFileSystem->Size(resfilehandle); + if ( length > 0 ) + { + char *pStart = (char *)new char[ length + 1 ]; + if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) ) + ) + { + pStart[ length ] = 0; + const char *pFileList = pStart; + + while ( 1 ) + { + char resfile[ 512 ]; + + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) <= 0 ) + break; + + Q_snprintf( resfile, sizeof( resfile ), "%s\\%s.lst", reslistdir, com_token ); + + CacheSoundsFromResFile( false, list, resfile ); + + // Any more tokens on this line? + while ( COM_TokenWaiting( pFileList ) ) + { + pFileList = COM_Parse( pFileList ); + } + } + } + delete[] pStart; + } + + g_pFileSystem->Close(resfilehandle); + + CacheSoundsFromResFile( false, list, CFmtStr( "%s\\engine.lst", reslistdir ) ); + CacheSoundsFromResFile( false, list, CFmtStr( "%s\\all.lst", reslistdir ) ); + } + else + { + Warning( "GetAllSoundFilesReferencedInReslists: Unable to load file %s\n", MAPLIST_FILE ); + } + } + + virtual void CacheBuildingStart() + { + if ( IsX360() ) + { + return; + } + + EngineVGui()->ActivateGameUI(); + EngineVGui()->StartCustomProgress(); + const wchar_t *str = g_pVGuiLocalize->Find( "#Valve_CreatingCache" ); + if ( str ) + { + EngineVGui()->UpdateCustomProgressBar( 0.0f, str ); + } + } + + virtual void CacheBuildingUpdateProgress( float percent, char const *cachefile ) + { + if ( IsX360() ) + { + return; + } + + const wchar_t *format = g_pVGuiLocalize->Find( "Valve_CreatingSpecificSoundCache" ); + if ( format ) + { + wchar_t constructed[ 1024 ]; + wchar_t file[ 256 ]; + g_pVGuiLocalize->ConvertANSIToUnicode( cachefile, file, sizeof( file ) ); + + g_pVGuiLocalize->ConstructString_safe( + constructed, + ( wchar_t * )format, + 1, + file ); + + EngineVGui()->UpdateCustomProgressBar( percent, constructed ); + } + } + + virtual void CacheBuildingFinish() + { + if ( IsX360() ) + { + return; + } + + EngineVGui()->FinishCustomProgress(); + EngineVGui()->HideGameUI(); + } + + virtual int GetPrecachedSoundCount() + { + if ( !sv.IsActive() ) + return 0; + + INetworkStringTable *table = sv.GetSoundPrecacheTable(); + if ( !table ) + return 0; + + return table->GetNumStrings(); + } + + virtual char const *GetPrecachedSound( int index ) + { + Assert( sv.IsActive() ); + + INetworkStringTable *table = sv.GetSoundPrecacheTable(); + if ( !table ) + return ""; + + return table->GetString( index ); + } + + virtual bool ShouldSuppressNonUISounds() + { + return EngineVGui()->IsGameUIVisible() || IsGamePaused(); + } + + virtual char const *GetUILanguage() + { + extern ConVar cl_language; + return cl_language.GetString(); + } + +private: + + float m_frameTime; + + void CacheSoundsFromResFile( bool quiet, CUtlRBTree< FileNameHandle_t, int >& list, char const *resfile, bool checkandcleanname = true ) + { + if ( !g_pFileSystem->FileExists( resfile, "MOD" ) ) + { + Warning( "CacheSoundsFromResFile: Unable to find '%s'\n", resfile ); + return; + } + + int oldCount = list.Count(); + + FileHandle_t resfilehandle = g_pFileSystem->Open( resfile, "rb", "MOD" ); + if ( FILESYSTEM_INVALID_HANDLE != resfilehandle ) + { + // Read in and parse mapcycle.txt + int length = g_pFileSystem->Size(resfilehandle); + if ( length > 0 ) + { + char *pStart = (char *)new char[ length + 1 ]; + if ( pStart && ( length == g_pFileSystem->Read(pStart, length, resfilehandle) ) + ) + { + pStart[ length ] = 0; + const char *pFileList = pStart; + + while ( 1 ) + { + pFileList = COM_Parse( pFileList ); + if ( strlen( com_token ) <= 0 ) + break; + + if ( checkandcleanname ) + { + if ( Q_stristr( com_token, ".wav" ) || + Q_stristr( com_token, ".mp3" ) ) + { + // skip past the game/mod directory "hl2/sound/player/footstep.wav" + Q_FixSlashes(com_token); // "hl2\sound\player\footstep.wav" + const char *pName = com_token; + while (pName[0] && pName[0] != CORRECT_PATH_SEPARATOR) + { + pName++; + } // "\sound\player\footstep.wav" + FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( pName+1 ); // "sound\player\footstep.wav" + if ( list.Find( handle ) == list.InvalidIndex() ) + { + list.Insert( handle ); + } + } + } + else + { + FileNameHandle_t handle = g_pFileSystem->FindOrAddFileName( com_token ); + if ( list.Find( handle ) == list.InvalidIndex() ) + { + list.Insert( handle ); + } + } + } + } + delete[] pStart; + } + + g_pFileSystem->Close(resfilehandle); + } + + int newCount = list.Count(); + + if ( !quiet ) + { + Msg( "Processing (%i new) from %s\n", newCount - oldCount, resfile ); + } + } + + virtual void OnSoundStarted( int guid, StartSoundParams_t& params, char const *soundname ) + { + VPROF("OnSoundStarted"); + + if ( IsX360() || !toolframework->IsToolRecording() || params.suppressrecording ) + return; + + KeyValues *msg = new KeyValues( "StartSound" ); + msg->SetInt( "guid", guid ); + msg->SetFloat( "time", cl.GetTime() ); + msg->SetInt( "staticsound", params.staticsound ? 1 : 0 ); + msg->SetInt( "soundsource", params.soundsource ); + msg->SetInt( "entchannel", params.entchannel ); + msg->SetString( "soundname", soundname ); + msg->SetFloat( "originx", params.origin.x ); + msg->SetFloat( "originy", params.origin.y ); + msg->SetFloat( "originz", params.origin.z ); + msg->SetFloat( "directionx", params.direction.x ); + msg->SetFloat( "directiony", params.direction.y ); + msg->SetFloat( "directionz", params.direction.z ); + msg->SetInt( "updatepositions", params.bUpdatePositions ); + msg->SetFloat( "fvol", params.fvol ); + msg->SetInt( "soundlevel", (int)params.soundlevel ); + msg->SetInt( "flags", params.flags ); + msg->SetInt( "pitch", params.pitch ); + msg->SetInt( "specialdsp", params.specialdsp ); + msg->SetInt( "fromserver", params.fromserver ? 1 : 0 ); + msg->SetFloat( "delay", params.delay ); + msg->SetInt( "speakerentity", params.speakerentity ); + + toolframework->PostMessage( msg ); + + msg->deleteThis(); + } + + virtual void OnSoundStopped( int guid, int soundsource, int channel, char const *soundname ) + { + // NOTE: At the moment, if we don't receive a StartSound message but we do + // receive a StopSound message, the StopSound message is ignored. In a perfect + // world, if the StartSound message was not sent, a StopSound message should not + // be sent for that guid either. This requires more plumbing, though, and + // for the moment, it's not necessary to do that plumbing. + + VPROF("OnSoundStopped"); + + if ( IsX360() || !toolframework->IsToolRecording() ) + return; + + KeyValues *msg = new KeyValues( "StopSound" ); + msg->SetInt( "guid", guid ); + msg->SetFloat( "time", cl.GetTime() ); + msg->SetInt( "soundsource", soundsource ); + msg->SetInt( "entchannel", channel ); + msg->SetString( "soundname", soundname ); + + toolframework->PostMessage( msg ); + + msg->deleteThis(); + } +}; + + +static CEngineSoundServices g_EngineSoundServices; +ISoundServices *g_pSoundServices = &g_EngineSoundServices; |