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
455 lines
15 KiB
//========= 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 |
|
} */ |
|
|
|
|
|
|
|
|