source-engine/engine/EngineSoundServer.cpp

456 lines
15 KiB
C++
Raw Permalink Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "engine/IEngineSound.h"
#include "tier0/dbg.h"
#include "quakedef.h"
#include "vox.h"
#include "server.h"
#include "sv_main.h"
#include "edict.h"
#include "sound.h"
#include "host.h"
#include "vengineserver_impl.h"
#include "enginesingleuserfilter.h"
#include "snd_audio_source.h"
#include "soundchars.h"
#include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//
// Server-side implementation of the engine sound interface
//
//-----------------------------------------------------------------------------
class CEngineSoundServer : public IEngineSound
{
public:
// constructor, destructor
CEngineSoundServer();
virtual ~CEngineSoundServer();
virtual bool PrecacheSound( const char *pSample, bool bPreload, bool bIsUISound );
virtual bool IsSoundPrecached( const char *pSample );
virtual void PrefetchSound( const char *pSample );
virtual float GetSoundDuration( const char *pSample );
virtual void EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, float flAttenuation, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
virtual void EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
virtual void EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel, int iSentenceIndex,
float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
virtual void StopSound( int iEntIndex, int iChannel, const char *pSample );
virtual void StopAllSounds( bool bClearBuffers );
// Set the room type for a player
virtual void SetRoomType( IRecipientFilter& filter, int roomType );
virtual void SetPlayerDSP( IRecipientFilter& filter, int dspType, bool fastReset );
// emit an "ambient" sound that isn't spatialized - specify left/right volume
// only available on the client, assert on server
virtual void EmitAmbientSound( const char *pSample, float flVolume, int iPitch, int flags, float soundtime = 0.0f );
virtual float GetDistGainFromSoundLevel( soundlevel_t soundlevel, float dist );
// Client .dll only functions
virtual int GetGuidForLastSoundEmitted()
{
Warning( "Can't call GetGuidForLastSoundEmitted from server\n" );
return 0;
}
virtual bool IsSoundStillPlaying( int guid )
{
Warning( "Can't call IsSoundStillPlaying from server\n" );
return false;
}
virtual void StopSoundByGuid( int guid )
{
Warning( "Can't call StopSoundByGuid from server\n" );
return;
}
// Retrieves list of all active sounds
virtual void GetActiveSounds( CUtlVector< SndInfo_t >& sndlist )
{
Warning( "Can't call GetActiveSounds from server\n" );
return;
}
// Set's master volume (0.0->1.0)
virtual void SetVolumeByGuid( int guid, float fvol )
{
Warning( "Can't call SetVolumeByGuid from server\n" );
return;
}
virtual void PrecacheSentenceGroup( const char *pGroupName )
{
VOX_PrecacheSentenceGroup( this, pGroupName );
}
virtual void NotifyBeginMoviePlayback()
{
AssertMsg( 0, "Not supported" );
}
virtual void NotifyEndMoviePlayback()
{
AssertMsg( 0, "Not supported" );
}
private:
void EmitSoundInternal( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime = 0.0f, int speakerentity = -1 );
};
//-----------------------------------------------------------------------------
// Client-server neutral sound interface accessor
//-----------------------------------------------------------------------------
static CEngineSoundServer s_EngineSoundServer;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineSoundServer, IEngineSound,
IENGINESOUND_SERVER_INTERFACE_VERSION, s_EngineSoundServer );
IEngineSound *EngineSoundServer()
{
return &s_EngineSoundServer;
}
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CEngineSoundServer::CEngineSoundServer()
{
}
CEngineSoundServer::~CEngineSoundServer()
{
}
//-----------------------------------------------------------------------------
// Precache a particular sample
//-----------------------------------------------------------------------------
bool CEngineSoundServer::PrecacheSound( const char *pSample, bool bPreload, bool bIsUISound )
{
int i;
if ( pSample && TestSoundChar( pSample, CHAR_SENTENCE ) )
{
return true;
}
if ( pSample[0] <= ' ' )
{
Host_Error( "CEngineSoundServer::PrecacheSound: Bad string: %s", pSample );
}
// add the sound to the precache list
// Start at 1, since 0 is used to indicate an error in the sound precache
i = SV_FindOrAddSound( pSample, bPreload );
if ( i >= 0 )
return true;
Host_Error( "CEngineSoundServer::PrecacheSound: '%s' overflow", pSample );
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSample -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CEngineSoundServer::IsSoundPrecached( const char *pSample )
{
if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
{
return true;
}
int idx = SV_SoundIndex( pSample );
if ( idx == -1 )
{
return false;
}
return true;
}
void CEngineSoundServer::PrefetchSound( const char *pSample )
{
if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
{
return;
}
int idx = SV_SoundIndex( pSample );
if ( idx == -1 )
{
return;
}
// Tell clients to prefetch the sound
SVC_Prefetch msg;
msg.m_fType = SVC_Prefetch::SOUND;
msg.m_nSoundIndex = idx;
sv.BroadcastMessage( msg, true, false );
}
//-----------------------------------------------------------------------------
// Stops a sound
//-----------------------------------------------------------------------------
void CEngineSoundServer::EmitSoundInternal( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*=-1*/ )
{
AssertMsg( pDirection == NULL, "Direction specification not currently supported on server sounds" );
AssertMsg( bUpdatePositions, "Non-updated positions not currently supported on server sounds" );
if (flVolume < 0 || flVolume > 1)
{
Warning ("EmitSound: volume out of bounds = %f\n", flVolume);
return;
}
if ( ( iSoundLevel < soundlevel_t(MIN_SNDLVL_VALUE) ) || ( iSoundLevel > soundlevel_t(MAX_SNDLVL_VALUE) ) )
{
Warning ("EmitSound: soundlevel out of bounds = %d\n", iSoundLevel);
return;
}
if (iPitch < 0 || iPitch > 255)
{
Warning ("EmitSound: pitch out of bounds = %i\n", iPitch);
return;
}
edict_t *pEdict = (iEntIndex >= 0) ? &sv.edicts[iEntIndex] : NULL;
SV_StartSound( filter, pEdict, iChannel, pSample, flVolume, iSoundLevel,
iFlags, iPitch, iSpecialDSP, pOrigin, soundtime, speakerentity, pUtlVecOrigins );
}
//-----------------------------------------------------------------------------
// Plays a sentence
//-----------------------------------------------------------------------------
void CEngineSoundServer::EmitSentenceByIndex( IRecipientFilter& filter, int iEntIndex, int iChannel,
int iSentenceIndex, float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
{
if ( iSentenceIndex >= 0 )
{
char pName[8];
Q_snprintf( pName, sizeof(pName), "!%d", iSentenceIndex );
EmitSoundInternal( filter, iEntIndex, iChannel, pName, flVolume, iSoundLevel,
iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
}
}
//-----------------------------------------------------------------------------
// Emits a sound
//-----------------------------------------------------------------------------
void CEngineSoundServer::EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, float flAttenuation, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
{
VPROF( "CEngineSoundServer::EmitSound" );
EmitSound( filter, iEntIndex, iChannel, pSample, flVolume, ATTN_TO_SNDLVL( flAttenuation ), iFlags,
iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
}
void CEngineSoundServer::EmitSound( IRecipientFilter& filter, int iEntIndex, int iChannel, const char *pSample,
float flVolume, soundlevel_t iSoundLevel, int iFlags, int iPitch, int iSpecialDSP,
const Vector *pOrigin, const Vector *pDirection, CUtlVector< Vector >* pUtlVecOrigins, bool bUpdatePositions, float soundtime /*= 0.0f*/, int speakerentity /*= -1*/ )
{
VPROF( "CEngineSoundServer::EmitSound" );
if ( pSample && TestSoundChar(pSample, CHAR_SENTENCE) )
{
int iSentenceIndex = -1;
VOX_LookupString( PSkipSoundChars(pSample), &iSentenceIndex );
if (iSentenceIndex >= 0)
{
EmitSentenceByIndex( filter, iEntIndex, iChannel, iSentenceIndex, flVolume,
iSoundLevel, iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
}
else
{
DevWarning( 2, "Unable to find %s in sentences.txt\n", PSkipSoundChars(pSample) );
}
}
else
{
EmitSoundInternal( filter, iEntIndex, iChannel, pSample, flVolume, iSoundLevel,
iFlags, iPitch, iSpecialDSP, pOrigin, pDirection, pUtlVecOrigins, bUpdatePositions, soundtime, speakerentity );
}
}
void BuildRecipientList( CUtlVector< edict_t * >& list, const IRecipientFilter& filter )
{
int c = filter.GetRecipientCount();
for ( int i = 0; i < c; i++ )
{
int playerindex = filter.GetRecipientIndex( i );
if ( playerindex < 1 || playerindex > sv.GetClientCount() )
continue;
CGameClient *cl = sv.Client( playerindex - 1 );
// Never output to bots
if ( cl->IsFakeClient() )
continue;
if ( !cl->IsSpawned() )
continue;
list.AddToTail( cl->edict );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : filter -
// roomType -
//-----------------------------------------------------------------------------
void CEngineSoundServer::SetRoomType( IRecipientFilter& filter, int roomType )
{
CUtlVector< edict_t * > players;
BuildRecipientList( players, filter );
for ( int i = 0 ; i < players.Count(); i++ )
{
g_pVEngineServer->ClientCommand( players[ i ], "room_type %i\n", roomType );
}
}
// Set the dsp preset for a player (client only)
//-----------------------------------------------------------------------------
// Purpose:
// Input : filter -
// dspType -
//-----------------------------------------------------------------------------
void CEngineSoundServer::SetPlayerDSP( IRecipientFilter& filter, int dspType, bool fastReset )
{
Assert( !fastReset );
if ( fastReset )
{
Warning( "SetPlayerDSP: fastReset only valid from client\n" );
}
CUtlVector< edict_t * > players;
BuildRecipientList( players, filter );
for ( int i = 0 ; i < players.Count(); i++ )
{
g_pVEngineServer->ClientCommand( players[ i ], "dsp_player %i\n", dspType );
}
}
void CEngineSoundServer::StopAllSounds(bool bClearBuffers)
{
AssertMsg( 0, "Not supported" );
}
void CEngineSoundServer::EmitAmbientSound( const char *pSample, float flVolume, int iPitch, int flags, float soundtime /*= 0.0f*/ )
{
AssertMsg( 0, "Not supported" );
}
//-----------------------------------------------------------------------------
// Stops a sound
//-----------------------------------------------------------------------------
void CEngineSoundServer::StopSound( int iEntIndex, int iChannel, const char *pSample )
{
CEngineRecipientFilter filter;
filter.AddAllPlayers();
filter.MakeReliable();
EmitSound( filter, iEntIndex, iChannel, pSample, 0, SNDLVL_NONE, SND_STOP, PITCH_NORM, 0,
NULL, NULL, NULL, true );
}
float SV_GetSoundDuration( const char *pSample )
{
#ifdef SWDS
return 0; // TODO: make this return a real value (i.e implement an OS independent version of the sound code)
#else
return AudioSource_GetSoundDuration( PSkipSoundChars( pSample ) );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSample -
// Output : float
//-----------------------------------------------------------------------------
float CEngineSoundServer::GetSoundDuration( const char *pSample )
{
return Host_GetSoundDuration( pSample );
}
float CEngineSoundServer::GetDistGainFromSoundLevel( soundlevel_t soundlevel, float dist )
{
return S_GetGainFromSoundLevel( soundlevel, dist );
}
/*
//-----------------------------------------------------------------------------
// FIXME: Move into the CEngineSoundServer class?
//-----------------------------------------------------------------------------
void Host_RestartAmbientSounds()
{
if (!sv.active)
{
return;
}
#ifndef SWDS
const int NUM_INFOS = 64;
SoundInfo_t soundInfo[NUM_INFOS];
int nSounds = S_GetCurrentStaticSounds( soundInfo, NUM_INFOS, CHAN_STATIC );
for ( int i = 0; i < nSounds; i++)
{
if (soundInfo[i].looping &&
soundInfo[i].entity != -1 )
{
Msg("Restarting sound %s...\n", soundInfo[i].name);
S_StopSound(soundInfo[i].entity, soundInfo[i].channel);
CEngineRecipientFilter filter;
filter.AddAllPlayers();
SV_StartSound( filter, EDICT_NUM(soundInfo[i].entity),
CHAN_STATIC,
soundInfo[i].name,
soundInfo[i].volume,
soundInfo[i].soundlevel,
0, // @Q (toml 05-09-02): Is this correct, or will I need to squirrel away the original flags?
soundInfo[i].pitch );
}
}
#endif
} */