diff --git a/src/engine/audio/Audio.cpp b/src/engine/audio/Audio.cpp index c4ae0e2bf1..b206150e29 100644 --- a/src/engine/audio/Audio.cpp +++ b/src/engine/audio/Audio.cpp @@ -58,6 +58,7 @@ namespace Audio { // in a frame, it means it sould be destroyed. struct entityLoop_t { bool addedThisFrame; + bool persistent; std::shared_ptr sound; sfxHandle_t newSfx; sfxHandle_t oldSfx; @@ -148,7 +149,7 @@ namespace Audio { UpdateListenerGain(); for (auto &loop : entityLoops) { - loop = {false, nullptr, -1, -1}; + loop = {false, false, nullptr, -1, -1}; } return true; @@ -164,7 +165,7 @@ namespace Audio { if (loop.sound) { loop.sound->Stop(); } - loop = {false, nullptr, -1, -1}; + loop = {false, false, nullptr, -1, -1}; } StopMusic(); @@ -194,21 +195,25 @@ namespace Audio { } for (int i = 0; i < MAX_GENTITIES; i++) { - auto& loop = entityLoops[i]; + entityLoop_t& loop = entityLoops[i]; if (loop.sound and not loop.addedThisFrame) { - // The loop wasn't added this frame, that means it has to be removed. - loop.sound->FadeOutAndDie(); - loop = {false, nullptr, -1, -1}; - + if ( loop.persistent ) { + loop.sound->soundGain = 0; + } else { + // The loop wasn't added this frame, that means it has to be removed. + loop.sound->FadeOutAndDie(); + loop = { false, false, nullptr, -1, -1 }; + } } else if (loop.oldSfx != loop.newSfx) { // The last sfx added in the frame is not the current one being played // To mimic the previous sound system's behavior we sart playing the new one. loop.sound->FadeOutAndDie(); int newSfx = loop.newSfx; - loop = {false, nullptr, -1, -1}; + bool persistent = loop.persistent; + loop = {false, false, nullptr, -1, -1}; - AddEntityLoopingSound(i, newSfx); + AddEntityLoopingSound(i, newSfx, persistent); } } @@ -223,7 +228,7 @@ namespace Audio { loop.addedThisFrame = false; // if we are the unique owner of a loop pointer, then it means it was stopped, free it. if (loop.sound.use_count() == 1) { - loop = {false, nullptr, -1, -1}; + loop = {false, false, nullptr, -1, -1}; } } @@ -288,7 +293,7 @@ namespace Audio { AddSound(GetLocalEmitter(), std::make_shared(Sample::FromHandle(sfx)), 1); } - void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx) { + void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx, bool persistent) { if (not initialized or not Sample::IsValidHandle(sfx) or not IsValidEntity(entityNum)) { return; } @@ -302,6 +307,7 @@ namespace Audio { AddSound(GetEmitterForEntity(entityNum), loop.sound, 1); } loop.addedThisFrame = true; + loop.persistent = persistent; // We remember what is the last sfx asked because cgame expects the sfx added last in the frame to be played loop.newSfx = sfx; @@ -343,7 +349,7 @@ namespace Audio { StopMusic(); music = std::make_shared(loopingSample, leadingSample); - music->SetVolumeModifier(musicVolume); + music->volumeModifier = &musicVolume; AddSound(GetLocalEmitter(), music, 1); } @@ -381,7 +387,7 @@ namespace Audio { } } - streams[streamNum]->SetGain(volume); + streams[streamNum]->soundGain = volume; AudioData audioData(rate, width, channels, (width * numSamples * channels), reinterpret_cast(data)); diff --git a/src/engine/audio/Audio.h b/src/engine/audio/Audio.h index cbd30c989a..220c2422a0 100644 --- a/src/engine/audio/Audio.h +++ b/src/engine/audio/Audio.h @@ -50,7 +50,7 @@ namespace Audio { void StartSound(int entityNum, Vec3 origin, sfxHandle_t sfx); void StartLocalSound(int entityNum); - void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx); + void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx, bool persistent); void ClearAllLoopingSounds(); void ClearLoopingSoundsForEntity(int entityNum); diff --git a/src/engine/audio/Emitter.cpp b/src/engine/audio/Emitter.cpp index e388545f87..d4b09a54e1 100644 --- a/src/engine/audio/Emitter.cpp +++ b/src/engine/audio/Emitter.cpp @@ -261,7 +261,7 @@ namespace Audio { Emitter::~Emitter() = default; void Emitter::SetupSound(Sound& sound) { - sound.GetSource().SetReferenceDistance(120.0f); + sound.source->SetReferenceDistance(120.0f); InternalSetupSound(sound); UpdateSound(sound); } @@ -278,7 +278,7 @@ namespace Audio { } void EntityEmitter::UpdateSound(Sound& sound) { - AL::Source& source = sound.GetSource(); + AL::Source& source = *sound.source; if (entityNum == listenerEntity) { MakeLocal(source); @@ -288,7 +288,7 @@ namespace Audio { } void EntityEmitter::InternalSetupSound(Sound& sound) { - AL::Source& source = sound.GetSource(); + AL::Source& source = *sound.source; Make3D(source, entities[entityNum].position, entities[entityNum].velocity); } @@ -306,13 +306,13 @@ namespace Audio { } void PositionEmitter::UpdateSound(Sound& sound) { - AL::Source& source = sound.GetSource(); + AL::Source& source = *sound.source; Make3D(source, position, origin); } void PositionEmitter::InternalSetupSound(Sound& sound) { - AL::Source& source = sound.GetSource(); + AL::Source& source = *sound.source; Make3D(source, position, origin); } @@ -334,7 +334,7 @@ namespace Audio { } void LocalEmitter::InternalSetupSound(Sound& sound) { - AL::Source& source = sound.GetSource(); + AL::Source& source = *sound.source; MakeLocal(source); } diff --git a/src/engine/audio/Sound.cpp b/src/engine/audio/Sound.cpp index a474d7479f..3cb392299d 100644 --- a/src/engine/audio/Sound.cpp +++ b/src/engine/audio/Sound.cpp @@ -85,18 +85,18 @@ namespace Audio { for (int i = 0; i < nSources; i++) { if (sources[i].active) { - auto sound = sources[i].usingSound; + std::shared_ptr sound = sources[i].usingSound; // Update and Emitter::UpdateSound can call Sound::Stop - if (not sound->IsStopped()) { + if ( sound->playing ) { sound->Update(); } - if (not sound->IsStopped()) { - sound->GetEmitter()->UpdateSound(*sound); + if ( sound->playing ) { + sound->emitter->UpdateSound(*sound); } - if (sound->IsStopped()) { + if ( !sound->playing ) { sources[i].active = false; sources[i].usingSound = nullptr; } @@ -126,7 +126,7 @@ namespace Audio { if (source) { // Make the source forget if it was a "static" or a "streaming" source. source->source.ResetBuffer(); - sound->SetEmitter(emitter); + sound->emitter = emitter; sound->AcquireSource(source->source); source->usingSound = sound; source->priority = priority; @@ -188,40 +188,6 @@ namespace Audio { playing = false; } - bool Sound::IsStopped() { - return not playing; - } - - void Sound::SetPositionalGain(float gain) { - positionalGain = gain; - } - - void Sound::SetSoundGain(float gain) { - soundGain = gain; - } - - float Sound::GetCurrentGain() { - return currentGain; - } - - void Sound::SetVolumeModifier(const Cvar::Range>& volumeMod) - { - this->volumeModifier = &volumeMod; - } - - float Sound::GetVolumeModifier() const - { - return volumeModifier->Get(); - } - - void Sound::SetEmitter(std::shared_ptr emitter) { - this->emitter = emitter; - } - - std::shared_ptr Sound::GetEmitter() { - return emitter; - } - void Sound::AcquireSource(AL::Source& source) { this->source = &source; @@ -231,19 +197,15 @@ namespace Audio { emitter->SetupSound(*this); } - AL::Source& Sound::GetSource() { - return *source; - } - // Set the gain before the source is started to avoid having a few milliseconds of very loud sound void Sound::FinishSetup() { - currentGain = positionalGain * soundGain * SliderToAmplitude(GetVolumeModifier()); + currentGain = positionalGain * soundGain * SliderToAmplitude(volumeModifier->Get()); source->SetGain(currentGain); } void Sound::Update() { // Fade the Gain update to avoid "ticking" sounds when there is a gain discontinuity - float targetGain = positionalGain * soundGain * SliderToAmplitude(GetVolumeModifier()); + float targetGain = positionalGain * soundGain * SliderToAmplitude(volumeModifier->Get()); //TODO make it framerate independent and fade out in about 1/8 seconds ? if (currentGain > targetGain) { @@ -267,15 +229,15 @@ namespace Audio { void OneShotSound::SetupSource(AL::Source& source) { source.SetBuffer(sample->GetBuffer()); - SetSoundGain(GetVolumeModifier()); + soundGain = volumeModifier->Get(); } void OneShotSound::InternalUpdate() { - if (GetSource().IsStopped()) { + if ( source->IsStopped() ) { Stop(); return; } - SetSoundGain(GetVolumeModifier()); + soundGain = volumeModifier->Get(); } // Implementation of LoopingSound @@ -289,7 +251,7 @@ namespace Audio { void LoopingSound::FadeOutAndDie() { fadingOut = true; - SetSoundGain(0.0f); + soundGain = 0.0f; } void LoopingSound::SetupSource(AL::Source& source) { @@ -298,23 +260,23 @@ namespace Audio { } else { SetupLoopingSound(source); } - SetSoundGain(GetVolumeModifier()); + soundGain = volumeModifier->Get(); } void LoopingSound::InternalUpdate() { - if (fadingOut and GetCurrentGain() == 0.0f) { + if (fadingOut and currentGain == 0.0f) { Stop(); } if (not fadingOut) { if (leadingSample) { - if (GetSource().IsStopped()) { - SetupLoopingSound(GetSource()); - GetSource().Play(); + if ( source->IsStopped() ) { + SetupLoopingSound( *source ); + source->Play(); leadingSample = nullptr; } } - SetSoundGain(GetVolumeModifier()); + soundGain = volumeModifier->Get(); } } @@ -335,32 +297,25 @@ namespace Audio { } void StreamingSound::InternalUpdate() { - AL::Source& source = GetSource(); - - while (source.GetNumProcessedBuffers() > 0) { - source.PopBuffer(); + while ( source->GetNumProcessedBuffers() > 0 ) { + source->PopBuffer(); } - if (source.GetNumQueuedBuffers() == 0) { + if ( source->GetNumQueuedBuffers() == 0 ) { Stop(); } } //TODO somehow try to catch back when data is coming faster than we consume (e.g. capture data) void StreamingSound::AppendBuffer(AL::Buffer buffer) { - if (IsStopped()) { + if ( !playing ) { return; } - AL::Source& source = GetSource(); - source.QueueBuffer(std::move(buffer)); + source->QueueBuffer(std::move(buffer)); - if (source.IsStopped()) { - source.Play(); + if ( source->IsStopped() ) { + source->Play(); } } - - void StreamingSound::SetGain(float gain) { - SetSoundGain(gain); - } } diff --git a/src/engine/audio/Sound.h b/src/engine/audio/Sound.h index fa2f3a1415..1bcc28ffef 100644 --- a/src/engine/audio/Sound.h +++ b/src/engine/audio/Sound.h @@ -54,29 +54,24 @@ namespace Audio { //TODO sound.mute class Sound { public: + float positionalGain; + float soundGain; + float currentGain; + + bool playing; + const Cvar::Range>* volumeModifier; + + AL::Source* source; + std::shared_ptr emitter; + Sound(); virtual ~Sound(); void Play(); // Stop the source and marks the sound for deletion. void Stop(); - bool IsStopped(); - - // The is attenuated because of its inherent porperties and because of its position. - // Each attenuation can be set separately. - void SetPositionalGain(float gain); - void SetSoundGain(float gain); - float GetCurrentGain(); - - // sfx vs. music - void SetVolumeModifier(const Cvar::Range>& volumeMod); - float GetVolumeModifier() const; - - void SetEmitter(std::shared_ptr emitter); - std::shared_ptr GetEmitter(); void AcquireSource(AL::Source& source); - AL::Source& GetSource(); // Used to setup a source for a specific kind of sound and to start the sound. virtual void SetupSource(AL::Source& source) = 0; @@ -85,16 +80,6 @@ namespace Audio { void Update(); // Called each frame, after emitters have been updated. virtual void InternalUpdate() = 0; - - private: - float positionalGain; - float soundGain; - float currentGain; - - bool playing; - std::shared_ptr emitter; - const Cvar::Range>* volumeModifier; - AL::Source* source; }; // A sound that is played once. @@ -139,7 +124,6 @@ namespace Audio { virtual void InternalUpdate() override; void AppendBuffer(AL::Buffer buffer); - void SetGain(float gain); }; } diff --git a/src/engine/client/cg_msgdef.h b/src/engine/client/cg_msgdef.h index 9918f5b3d3..acf1248865 100644 --- a/src/engine/client/cg_msgdef.h +++ b/src/engine/client/cg_msgdef.h @@ -282,7 +282,7 @@ namespace Audio { using StartSoundMsg = IPC::Message, bool, Vec3, int, int>; using StartLocalSoundMsg = IPC::Message, int>; using ClearLoopingSoundsMsg = IPC::Message>; - using AddLoopingSoundMsg = IPC::Message, int, int>; + using AddLoopingSoundMsg = IPC::Message, int, int, bool>; using StopLoopingSoundMsg = IPC::Message, int>; using UpdateEntityPositionMsg = IPC::Message, int, Vec3>; using RespatializeMsg = IPC::Message, int, std::array>; diff --git a/src/engine/client/cl_cgame.cpp b/src/engine/client/cl_cgame.cpp index b1e4791324..60a7564d8c 100644 --- a/src/engine/client/cl_cgame.cpp +++ b/src/engine/client/cl_cgame.cpp @@ -1506,8 +1506,8 @@ void CGameVM::CmdBuffer::HandleCommandBufferSyscall(int major, int minor, Util:: break; case CG_S_ADDLOOPINGSOUND: - HandleMsg(std::move(reader), [this] (int entityNum, int sfx) { - Audio::AddEntityLoopingSound(entityNum, sfx); + HandleMsg(std::move(reader), [this] (int entityNum, int sfx, bool persistent) { + Audio::AddEntityLoopingSound(entityNum, sfx, persistent); }); break; diff --git a/src/engine/null/NullAudio.cpp b/src/engine/null/NullAudio.cpp index 3b96d95e52..cc213e8363 100644 --- a/src/engine/null/NullAudio.cpp +++ b/src/engine/null/NullAudio.cpp @@ -65,7 +65,7 @@ namespace Audio { } - void AddEntityLoopingSound(int, sfxHandle_t) { + void AddEntityLoopingSound(int, sfxHandle_t, bool) { } void ClearAllLoopingSounds() { diff --git a/src/engine/server/sg_api.h b/src/engine/server/sg_api.h index 7ba16db755..db1d211ee8 100644 --- a/src/engine/server/sg_api.h +++ b/src/engine/server/sg_api.h @@ -44,6 +44,7 @@ along with this program. If not, see . #define SVF_SELF_PORTAL_EXCLUSIVE 0x00010000 #define SVF_RIGID_BODY 0x00020000 // ignored by the engine #define SVF_CLIENTS_IN_RANGE 0x00040000 // clients within range +#define SVF_BROADCAST_ONCE 0x00040000 // broadcasted to newly connecting clients, and once to connected clients when spawned #define MAX_ENT_CLUSTERS 16 diff --git a/src/engine/server/sv_snapshot.cpp b/src/engine/server/sv_snapshot.cpp index 4bb29b85d5..f9a29fccf8 100644 --- a/src/engine/server/sv_snapshot.cpp +++ b/src/engine/server/sv_snapshot.cpp @@ -341,7 +341,7 @@ static void SV_AddEntToSnapshot( svEntity_t *svEnt, sharedEntity_t *gEnt, SV_AddEntitiesVisibleFromPoint =============== */ -static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, +static void SV_AddEntitiesVisibleFromPoint( client_t* client, vec3_t origin, clientSnapshot_t *frame, // snapshotEntityNumbers_t *eNums, bool portal, clientSnapshot_t *oldframe, bool localClient ) { // snapshotEntityNumbers_t *eNums, bool portal ) { snapshotEntityNumbers_t *eNums /*, bool portal, bool localClient */ ) @@ -379,7 +379,7 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra if ( playerEnt->r.svFlags & SVF_SELF_PORTAL ) { - SV_AddEntitiesVisibleFromPoint( playerEnt->s.origin2, frame, eNums ); + SV_AddEntitiesVisibleFromPoint( client, playerEnt->s.origin2, frame, eNums ); } for ( e = 0; e < sv.num_entities; e++ ) @@ -462,6 +462,11 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra continue; } + if ( ( ent->r.svFlags & SVF_BROADCAST_ONCE ) && !client->reliableAcknowledge ) { + SV_AddEntToSnapshot( svEnt, ent, eNums ); + continue; + } + // send entity if the client is in range if ( (ent->r.svFlags & SVF_CLIENTS_IN_RANGE) && Distance( ent->s.origin, playerEnt->s.origin ) <= ent->r.clientRadius ) @@ -637,7 +642,7 @@ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *fra } // SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, true, oldframe, localClient ); - SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums /*, true, localClient */ ); + SV_AddEntitiesVisibleFromPoint( client, ent->s.origin2, frame, eNums /*, true, localClient */ ); } continue; @@ -720,7 +725,7 @@ static void SV_BuildClientSnapshot( client_t *client ) // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints - SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers /*, false, client->netchan.remoteAddress.type == NA_LOOPBACK */ ); + SV_AddEntitiesVisibleFromPoint( client, org, frame, &entityNumbers /*, false, client->netchan.remoteAddress.type == NA_LOOPBACK */ ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression diff --git a/src/shared/client/cg_api.cpp b/src/shared/client/cg_api.cpp index 9eea1f66be..995928b160 100644 --- a/src/shared/client/cg_api.cpp +++ b/src/shared/client/cg_api.cpp @@ -153,7 +153,7 @@ void trap_S_ClearLoopingSounds( bool ) cmdBuffer.SendMsg(); } -void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) +void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, bool persistent ) { if (origin) { trap_S_UpdateEntityPosition(entityNum, origin); @@ -161,12 +161,7 @@ void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t ve if (velocity) { trap_S_UpdateEntityVelocity(entityNum, velocity); } - cmdBuffer.SendMsg(entityNum, sfx); -} - -void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) -{ - trap_S_AddLoopingSound(entityNum, origin, velocity, sfx); + cmdBuffer.SendMsg( entityNum, sfx, persistent ); } void trap_S_StopLoopingSound( int entityNum ) diff --git a/src/shared/client/cg_api.h b/src/shared/client/cg_api.h index 4e2781c3d7..a8467c05d2 100644 --- a/src/shared/client/cg_api.h +++ b/src/shared/client/cg_api.h @@ -53,8 +53,8 @@ void trap_CM_BatchMarkFragments( void trap_S_StartSound( vec3_t origin, int entityNum, soundChannel_t entchannel, sfxHandle_t sfx ); void trap_S_StartLocalSound( sfxHandle_t sfx, soundChannel_t channelNum ); void trap_S_ClearLoopingSounds( bool killall ); -void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); -void trap_S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ); +void trap_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, + bool persistent = false ); void trap_S_StopLoopingSound( int entityNum ); void trap_S_UpdateEntityPosition( int entityNum, const vec3_t origin ); void trap_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[ 3 ], int inwater );