Skip to content

Commit 74e26bb

Browse files
committed
Implement some sound priorities
Use the sound's volume and distance to determine which sounds to replace. Player/bot sounds get a higher priority within `a_clientSoundPriorityMaxDistance` distance, with the multiplier `a_clientSoundPriorityMultiplier`.
1 parent 162d8cf commit 74e26bb

File tree

11 files changed

+111
-48
lines changed

11 files changed

+111
-48
lines changed

src/engine/audio/Audio.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ namespace Audio {
120120

121121
static bool initialized = false;
122122

123+
int playerClientNum;
124+
123125
static AL::Device* device;
124126
static AL::Context* context;
125127

@@ -292,11 +294,13 @@ namespace Audio {
292294
}
293295
}
294296

295-
void BeginRegistration() {
297+
void BeginRegistration( const int playerNum ) {
296298
if (not initialized) {
297299
return;
298300
}
299301

302+
playerClientNum = playerNum;
303+
300304
BeginSampleRegistration();
301305
}
302306

@@ -317,6 +321,10 @@ namespace Audio {
317321
EndSampleRegistration();
318322
}
319323

324+
static int GetSoundPriorityForEntity( const int entityNum ) {
325+
return entityNum < MAX_CLIENTS ? CLIENT : ANY;
326+
}
327+
320328
void StartSound(int entityNum, Vec3 origin, sfxHandle_t sfx) {
321329
if (not initialized or not Sample::IsValidHandle(sfx)) {
322330
return;
@@ -335,15 +343,15 @@ namespace Audio {
335343
return;
336344
}
337345

338-
AddSound(emitter, std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
346+
AddSound( emitter, std::make_shared<OneShotSound>( Sample::FromHandle( sfx ) ), GetSoundPriorityForEntity( entityNum ) );
339347
}
340348

341349
void StartLocalSound(sfxHandle_t sfx) {
342350
if (not initialized or not Sample::IsValidHandle(sfx)) {
343351
return;
344352
}
345353

346-
AddSound(GetLocalEmitter(), std::make_shared<OneShotSound>(Sample::FromHandle(sfx)), 1);
354+
AddSound( GetLocalEmitter(), std::make_shared<OneShotSound>( Sample::FromHandle( sfx ) ), ANY );
347355
}
348356

349357
void AddEntityLoopingSound(int entityNum, sfxHandle_t sfx, bool persistent) {
@@ -357,7 +365,7 @@ namespace Audio {
357365
if (not loop.sound) {
358366
loop.sound = std::make_shared<LoopingSound>(Sample::FromHandle(sfx));
359367
loop.oldSfx = sfx;
360-
AddSound(GetEmitterForEntity(entityNum), loop.sound, 1);
368+
AddSound( GetEmitterForEntity( entityNum ), loop.sound, GetSoundPriorityForEntity( entityNum ) );
361369
}
362370

363371
loop.addedThisFrame = true;
@@ -402,7 +410,7 @@ namespace Audio {
402410
StopMusic();
403411
music = std::make_shared<LoopingSound>(loopingSample, leadingSample);
404412
music->volumeModifier = &musicVolume;
405-
AddSound(GetLocalEmitter(), music, 1);
413+
AddSound( GetLocalEmitter(), music, ANY );
406414
}
407415

408416
void StopMusic() {
@@ -433,9 +441,9 @@ namespace Audio {
433441
if (not streams[streamNum]) {
434442
streams[streamNum] = std::make_shared<StreamingSound>();
435443
if (IsValidEntity(entityNum)) {
436-
AddSound(GetEmitterForEntity(entityNum), streams[streamNum], 1);
444+
AddSound( GetEmitterForEntity( entityNum ), streams[streamNum], GetSoundPriorityForEntity( entityNum ) );
437445
} else {
438-
AddSound(GetLocalEmitter(), streams[streamNum], 1);
446+
AddSound( GetLocalEmitter(), streams[streamNum], ANY );
439447
}
440448
}
441449

src/engine/audio/Audio.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace Audio {
4343
void Shutdown();
4444
void Update();
4545

46-
void BeginRegistration();
46+
void BeginRegistration( const int playerNum );
4747
sfxHandle_t RegisterSFX(Str::StringRef filename);
4848
void EndRegistration();
4949

src/engine/audio/AudioPrivate.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ namespace Audio {
6565

6666
constexpr uint32_t MAX_ENTITY_SOUNDS = 4;
6767

68+
extern int playerClientNum;
69+
70+
struct entityData_t {
71+
Vec3 position;
72+
Vec3 velocity;
73+
float occlusion;
74+
};
75+
76+
extern entityData_t entities[MAX_GENTITIES];
77+
78+
enum EmitterPriority {
79+
ANY,
80+
CLIENT
81+
};
82+
6883
// Tweaks the value given by the audio slider
6984
float SliderToAmplitude(float slider);
7085

src/engine/audio/Emitter.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131
#include "AudioPrivate.h"
3232

3333
namespace Audio {
34-
35-
// Structures to keep the state of entities we were given
36-
struct entityData_t {
37-
Vec3 position;
38-
Vec3 velocity;
39-
float occlusion;
40-
};
41-
static entityData_t entities[MAX_GENTITIES];
34+
entityData_t entities[MAX_GENTITIES];
4235
static int listenerEntity = -1;
4336

4437
struct EntityMultiEmitter {
@@ -311,6 +304,10 @@ namespace Audio {
311304
Make3D(source, entities[entityNum].position, entities[entityNum].velocity);
312305
}
313306

307+
Vec3 EntityEmitter::GetPosition() const {
308+
return entities[entityNum].position;
309+
}
310+
314311
// Implementation of PositionEmitter
315312

316313
PositionEmitter::PositionEmitter(Vec3 position){
@@ -357,6 +354,10 @@ namespace Audio {
357354
MakeLocal(source);
358355
}
359356

357+
Vec3 LocalEmitter::GetPosition() const {
358+
return Vec3 {};
359+
}
360+
360361
class TestReverbCmd : public Cmd::StaticCmd {
361362
public:
362363
TestReverbCmd(): StaticCmd("testReverb", Cmd::AUDIO, "Tests a reverb preset.") {

src/engine/audio/Emitter.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ namespace Audio {
6464
void SetupSound(Sound& sound);
6565

6666
// Called each frame before any UpdateSound is called, used to factor computations
67-
void virtual Update() = 0;
67+
virtual void Update() = 0;
6868
// Update the Sound's source's spatialization
6969
virtual void UpdateSound(Sound& sound) = 0;
7070
// Setup a source for the spatialization of this Emitter
7171
virtual void InternalSetupSound(Sound& sound) = 0;
72+
73+
virtual Vec3 GetPosition() const = 0;
7274
};
7375

7476
// An Emitter that will follow an entity
@@ -81,6 +83,8 @@ namespace Audio {
8183
virtual void UpdateSound(Sound& sound) override;
8284
virtual void InternalSetupSound(Sound& sound) override;
8385

86+
Vec3 GetPosition() const override;
87+
8488
private:
8589
int entityNum;
8690
};
@@ -95,7 +99,7 @@ namespace Audio {
9599
virtual void UpdateSound(Sound& sound) override;
96100
virtual void InternalSetupSound(Sound& sound) override;
97101

98-
Vec3 GetPosition() const;
102+
Vec3 GetPosition() const override;
99103

100104
private:
101105
Vec3 position;
@@ -110,6 +114,8 @@ namespace Audio {
110114
void virtual Update() override;
111115
virtual void UpdateSound(Sound& sound) override;
112116
virtual void InternalSetupSound(Sound& sound) override;
117+
118+
Vec3 GetPosition() const override;
113119
};
114120

115121
}

src/engine/audio/Sound.cpp

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ namespace Audio {
4949
static sourceRecord_t* sources = nullptr;
5050
static CONSTEXPR int nSources = 128; //TODO see what's the limit for OpenAL soft
5151

52-
sourceRecord_t* GetSource(int priority);
53-
5452
static bool initialized = false;
5553

5654
void InitSounds() {
@@ -116,44 +114,55 @@ namespace Audio {
116114
}
117115
}
118116

119-
void AddSound(std::shared_ptr<Emitter> emitter, std::shared_ptr<Sound> sound, int priority) {
120-
if (not initialized) {
121-
return;
122-
}
117+
static Cvar::Range<Cvar::Cvar<int>> a_clientSoundPriorityMaxDistance( "a_clientSoundPriorityMaxDistance",
118+
"Sounds emitted by players/bots within this distance (in qu) will have higher priority than other sounds"
119+
" (multiplier: a_clientSoundPriorityMultiplier)", Cvar::NONE, 32 * 32, 0, BIT( 16 ) );
123120

124-
sourceRecord_t* source = GetSource(priority);
121+
static Cvar::Range<Cvar::Cvar<float>> a_clientSoundPriorityMultiplier( "a_clientSoundPriorityMultiplier",
122+
"Sounds emitted by players/bots within a_clientSoundPriorityMaxDistance"
123+
" will use this value as their priority multiplier",
124+
Cvar::NONE, 2.0f, 0.0f, 1024.0f );
125125

126-
if (source) {
127-
// Make the source forget if it was a "static" or a "streaming" source.
128-
source->source.ResetBuffer();
129-
sound->emitter = emitter;
130-
sound->AcquireSource(source->source);
131-
source->usingSound = sound;
132-
source->priority = priority;
133-
source->active = true;
126+
static float GetAdjustedVolumeForPosition( const Vec3& origin, const Vec3& src, const bool isClient ) {
127+
vec3_t v0 { origin.Data()[0], origin.Data()[1], origin.Data()[2] };
128+
vec3_t v1 { src.Data()[0], src.Data()[1], src.Data()[2] };
134129

135-
sound->FinishSetup();
136-
sound->Play();
130+
float totalPriority = VectorDistanceSquared( v0, v1 );
131+
132+
const float distanceThreshold = a_clientSoundPriorityMaxDistance.Get();
133+
if ( isClient && totalPriority < distanceThreshold * distanceThreshold ) {
134+
totalPriority *= 1.0f / a_clientSoundPriorityMultiplier.Get();
137135
}
136+
137+
return 1.0f / totalPriority;
138138
}
139139

140140
// Finds a inactive or low-priority source to play a new sound.
141-
sourceRecord_t* GetSource(int priority) {
142-
//TODO make a better heuristic? (take into account the distance / the volume /... ?)
141+
static sourceRecord_t* GetSource( const Vec3& position, int priority, const float currentGain ) {
143142
int best = -1;
144-
int bestPriority = priority;
145143

146-
// Gets the minimum sound by comparing activity first then priority
144+
const Vec3& playerPos = entities[playerClientNum].position;
145+
146+
// Sound volume is inversely proportional to distance to the source
147+
const float adjustedVolume = Q_rsqrt_fast( currentGain )
148+
* GetAdjustedVolumeForPosition( playerPos, position, priority == CLIENT );
149+
150+
// Gets the minimum sound by comparing activity first, then the adjusted volume
147151
for (int i = 0; i < nSources; i++) {
148152
sourceRecord_t& source = sources[i];
149153

150154
if (not source.active) {
151155
return &source;
152156
}
153157

154-
if (source.priority < bestPriority || (best < 0 && source.priority <= priority)) {
158+
const Vec3& sourcePos = source.usingSound->emitter->GetPosition();
159+
160+
// Sound volume is inversely proportional to distance to the source
161+
const float adjustedSourceVolume = Q_rsqrt_fast( source.usingSound->currentGain )
162+
* GetAdjustedVolumeForPosition( playerPos, sourcePos, source.priority == CLIENT );
163+
164+
if ( adjustedVolume > adjustedSourceVolume ) {
155165
best = i;
156-
bestPriority = source.priority;
157166
continue;
158167
}
159168
}
@@ -171,6 +180,30 @@ namespace Audio {
171180
}
172181
}
173182

183+
void AddSound( std::shared_ptr<Emitter> emitter, std::shared_ptr<Sound> sound, int priority ) {
184+
if ( not initialized ) {
185+
return;
186+
}
187+
188+
const Vec3& position = emitter->GetPosition();
189+
const float currentGain = sound->positionalGain * sound->soundGain
190+
* SliderToAmplitude( sound->volumeModifier->Get() );
191+
sourceRecord_t* source = GetSource( position, priority, currentGain );
192+
193+
if ( source ) {
194+
// Make the source forget if it was a "static" or a "streaming" source.
195+
source->source.ResetBuffer();
196+
sound->emitter = emitter;
197+
sound->AcquireSource( source->source );
198+
source->usingSound = sound;
199+
source->priority = priority;
200+
source->active = true;
201+
202+
sound->FinishSetup();
203+
sound->Play();
204+
}
205+
}
206+
174207
// Implementation of Sound
175208

176209
Sound::Sound() : positionalGain(1.0f), soundGain(1.0f), currentGain(1.0f),

src/engine/client/cg_msgdef.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ namespace Audio {
291291
using UpdateEntityVelocityMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_UPDATEENTITYVELOCITY>, int, Vec3>;
292292
using UpdateEntityPositionVelocityMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_UPDATEENTITYPOSITIONVELOCITY>, int, Vec3, Vec3>;
293293
using SetReverbMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_SETREVERB>, int, std::string, float>;
294-
using BeginRegistrationMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_BEGINREGISTRATION>>;
294+
using BeginRegistrationMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_BEGINREGISTRATION>, int>;
295295
using EndRegistrationMsg = IPC::Message<IPC::Id<VM::QVM, CG_S_ENDREGISTRATION>>;
296296
}
297297

src/engine/client/cl_cgame.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,8 +1561,8 @@ void CGameVM::CmdBuffer::HandleCommandBufferSyscall(int major, int minor, Util::
15611561
break;
15621562

15631563
case CG_S_BEGINREGISTRATION:
1564-
HandleMsg<Audio::BeginRegistrationMsg>(std::move(reader), [this] {
1565-
Audio::BeginRegistration();
1564+
HandleMsg<Audio::BeginRegistrationMsg>(std::move(reader), [this] ( const int playerNum ) {
1565+
Audio::BeginRegistration( playerNum );
15661566
});
15671567
break;
15681568

src/engine/null/NullAudio.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace Audio {
4747
}
4848

4949

50-
void BeginRegistration() {
50+
void BeginRegistration( const int ) {
5151
}
5252

5353
sfxHandle_t RegisterSFX(Str::StringRef) {

src/shared/client/cg_api.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,9 @@ void trap_S_SetReverb( int slotNum, const char* name, float ratio )
218218
cmdBuffer.SendMsg<Audio::SetReverbMsg>(slotNum, name, ratio);
219219
}
220220

221-
void trap_S_BeginRegistration()
221+
void trap_S_BeginRegistration( const int playerNum )
222222
{
223-
cmdBuffer.SendMsg<Audio::BeginRegistrationMsg>();
223+
cmdBuffer.SendMsg<Audio::BeginRegistrationMsg>( playerNum );
224224
}
225225

226226
void trap_S_EndRegistration()

0 commit comments

Comments
 (0)