456 lines
15 KiB
C++
456 lines
15 KiB
C++
|
//========= 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
|
||
|
} */
|
||
|
|
||
|
|
||
|
|