Skip to content

Commit 162d8cf

Browse files
committed
Allow multiple sounds per entity
1 parent 1b8efb9 commit 162d8cf

File tree

3 files changed

+127
-55
lines changed

3 files changed

+127
-55
lines changed

src/engine/audio/Audio.cpp

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,58 @@ namespace Audio {
6363
sfxHandle_t newSfx;
6464
sfxHandle_t oldSfx;
6565
};
66-
static entityLoop_t entityLoops[MAX_GENTITIES];
66+
67+
struct EntityMultiLoop {
68+
entityLoop_t loops[MAX_ENTITY_SOUNDS];
69+
70+
entityLoop_t& FindSlot( const sfxHandle_t sfx ) {
71+
uint32_t bestSlot = 0;
72+
float minGain = FLT_MAX;
73+
74+
for ( entityLoop_t& loop : loops ) {
75+
if ( sfx == loop.oldSfx ) {
76+
return loop;
77+
}
78+
79+
if ( !loop.sound ) {
80+
return loop;
81+
}
82+
83+
if ( loop.sound->currentGain < minGain ) {
84+
bestSlot = &loop - loops;
85+
minGain = loop.sound->currentGain;
86+
}
87+
}
88+
89+
return loops[bestSlot];
90+
}
91+
92+
void StopAll() {
93+
for ( entityLoop_t& loop : loops ) {
94+
if ( loop.sound ) {
95+
loop.sound->Stop();
96+
}
97+
98+
loop = { false, false, nullptr, -1, -1 };
99+
}
100+
}
101+
102+
void ResetAll() {
103+
for ( entityLoop_t& loop : loops ) {
104+
loop = { false, false, nullptr, -1, -1 };
105+
}
106+
}
107+
108+
void ClearLoopingSounds() {
109+
for ( entityLoop_t& loop : loops ) {
110+
if ( loop.sound ) {
111+
loop.addedThisFrame = false;
112+
}
113+
}
114+
}
115+
};
116+
117+
static EntityMultiLoop entityLoops[MAX_GENTITIES];
67118

68119
static std::shared_ptr<StreamingSound> streams[N_STREAMS];
69120

@@ -148,8 +199,8 @@ namespace Audio {
148199

149200
UpdateListenerGain();
150201

151-
for (auto &loop : entityLoops) {
152-
loop = {false, false, nullptr, -1, -1};
202+
for ( EntityMultiLoop& loop : entityLoops ) {
203+
loop.ResetAll();
153204
}
154205

155206
return true;
@@ -161,11 +212,8 @@ namespace Audio {
161212
}
162213

163214
// Shuts down the wrapper
164-
for (auto &loop : entityLoops) {
165-
if (loop.sound) {
166-
loop.sound->Stop();
167-
}
168-
loop = {false, false, nullptr, -1, -1};
215+
for ( EntityMultiLoop& loop : entityLoops ) {
216+
loop.StopAll();
169217
}
170218

171219
StopMusic();
@@ -194,26 +242,29 @@ namespace Audio {
194242
return;
195243
}
196244

197-
for (int i = 0; i < MAX_GENTITIES; i++) {
198-
entityLoop_t& loop = entityLoops[i];
199-
if (loop.sound and not loop.addedThisFrame) {
200-
if ( loop.persistent ) {
201-
loop.sound->soundGain = 0;
202-
} else {
203-
// The loop wasn't added this frame, that means it has to be removed.
245+
for ( uint32_t i = 0; i < MAX_GENTITIES; i++ ) {
246+
EntityMultiLoop& multiLoop = entityLoops[i];
247+
248+
for ( entityLoop_t& loop : multiLoop.loops ) {
249+
if ( loop.sound and not loop.addedThisFrame ) {
250+
if ( loop.persistent ) {
251+
loop.sound->soundGain = 0;
252+
} else {
253+
// The loop wasn't added this frame, that means it has to be removed.
254+
loop.sound->FadeOutAndDie();
255+
loop = { false, false, nullptr, -1, -1 };
256+
}
257+
} else if ( loop.oldSfx != loop.newSfx ) {
258+
// The last sfx added in the frame is not the current one being played
259+
// To mimic the previous sound system's behavior we sart playing the new one.
204260
loop.sound->FadeOutAndDie();
205-
loop = { false, false, nullptr, -1, -1 };
206-
}
207-
} else if (loop.oldSfx != loop.newSfx) {
208-
// The last sfx added in the frame is not the current one being played
209-
// To mimic the previous sound system's behavior we sart playing the new one.
210-
loop.sound->FadeOutAndDie();
211261

212-
int newSfx = loop.newSfx;
213-
bool persistent = loop.persistent;
214-
loop = {false, false, nullptr, -1, -1};
262+
int newSfx = loop.newSfx;
263+
bool persistent = loop.persistent;
264+
loop = { false, false, nullptr, -1, -1 };
215265

216-
AddEntityLoopingSound(i, newSfx, persistent);
266+
AddEntityLoopingSound( i, newSfx, persistent );
267+
}
217268
}
218269
}
219270

@@ -224,15 +275,17 @@ namespace Audio {
224275
UpdateEmitters();
225276
UpdateSounds();
226277

227-
for (auto &loop : entityLoops) {
228-
loop.addedThisFrame = false;
229-
// if we are the unique owner of a loop pointer, then it means it was stopped, free it.
230-
if (loop.sound.use_count() == 1) {
231-
loop = {false, false, nullptr, -1, -1};
278+
for ( EntityMultiLoop& multiLoop : entityLoops ) {
279+
for ( entityLoop_t& loop : multiLoop.loops ) {
280+
loop.addedThisFrame = false;
281+
// if we are the unique owner of a loop pointer, then it means it was stopped, free it.
282+
if ( loop.sound.use_count() == 1 ) {
283+
loop = { false, false, nullptr, -1, -1 };
284+
}
232285
}
233286
}
234287

235-
for (auto &stream : streams) {
288+
for ( std::shared_ptr<StreamingSound> &stream : streams ) {
236289
if (stream and stream.use_count() == 1) {
237290
stream = nullptr;
238291
}
@@ -298,14 +351,15 @@ namespace Audio {
298351
return;
299352
}
300353

301-
entityLoop_t& loop = entityLoops[entityNum];
354+
entityLoop_t& loop = entityLoops[entityNum].FindSlot( sfx );
302355

303356
// If we have no sound we can play the loop directly
304357
if (not loop.sound) {
305358
loop.sound = std::make_shared<LoopingSound>(Sample::FromHandle(sfx));
306359
loop.oldSfx = sfx;
307360
AddSound(GetEmitterForEntity(entityNum), loop.sound, 1);
308361
}
362+
309363
loop.addedThisFrame = true;
310364
loop.persistent = persistent;
311365

@@ -328,9 +382,7 @@ namespace Audio {
328382
return;
329383
}
330384

331-
if (entityLoops[entityNum].sound) {
332-
entityLoops[entityNum].addedThisFrame = false;
333-
}
385+
entityLoops[entityNum].ClearLoopingSounds();
334386
}
335387

336388
void StartMusic(Str::StringRef leadingSound, Str::StringRef loopSound) {

src/engine/audio/AudioPrivate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ namespace Audio {
6363
// There is only a small number of reverb slots because by default we can create only 4 AuxEffects
6464
CONSTEXPR int N_REVERB_SLOTS = 3;
6565

66+
constexpr uint32_t MAX_ENTITY_SOUNDS = 4;
67+
6668
// Tweaks the value given by the audio slider
6769
float SliderToAmplitude(float slider);
6870

src/engine/audio/Emitter.cpp

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,30 @@ namespace Audio {
4141
static entityData_t entities[MAX_GENTITIES];
4242
static int listenerEntity = -1;
4343

44-
// Keep entity Emitters in an array because there is at most one per entity.
45-
static std::shared_ptr<Emitter> entityEmitters[MAX_GENTITIES];
44+
struct EntityMultiEmitter {
45+
std::shared_ptr<Emitter> emitters[MAX_ENTITY_SOUNDS];
46+
47+
std::shared_ptr<Emitter>& FindSlot( const int entityNum ) {
48+
for ( std::shared_ptr<Emitter>& emitter : emitters ) {
49+
if ( !emitter ) {
50+
emitter = std::make_shared<EntityEmitter>( entityNum );
51+
return emitter;
52+
}
53+
}
54+
55+
return emitters[0];
56+
}
57+
58+
void Shutdown() {
59+
for ( std::shared_ptr<Emitter>& emitter : emitters ) {
60+
if ( emitter ) {
61+
emitter = nullptr;
62+
}
63+
}
64+
}
65+
};
66+
67+
static EntityMultiEmitter entityEmitters[MAX_GENTITIES];
4668

4769
// Position Emitters can be reused so we keep the list of all of them
4870
// this is not very efficient but we cannot have more position emitters
@@ -100,15 +122,13 @@ namespace Audio {
100122

101123
localEmitter = nullptr;
102124

103-
for (auto &slot : reverbSlots) {
125+
for ( ReverbSlot& slot : reverbSlots ) {
104126
delete slot.effect;
105127
slot.effect = nullptr;
106128
}
107129

108-
for (auto &emitter : entityEmitters) {
109-
if ( emitter ) {
110-
emitter = nullptr;
111-
}
130+
for ( EntityMultiEmitter& emitter : entityEmitters ) {
131+
emitter.Shutdown();
112132
}
113133

114134
posEmitters.clear();
@@ -124,16 +144,18 @@ namespace Audio {
124144
// Both PositionEmitters and EntityEmitters are ref-counted.
125145
// If we hold the only reference to them then no sound is still using
126146
// the Emitter that can be destroyed.
127-
for (auto &emitter : entityEmitters) {
128-
if (not emitter) {
129-
continue;
130-
}
147+
for ( EntityMultiEmitter& multiEmitter : entityEmitters ) {
148+
for ( std::shared_ptr<Emitter>& emitter : multiEmitter.emitters ) {
149+
if ( not emitter ) {
150+
continue;
151+
}
131152

132-
emitter->Update();
153+
emitter->Update();
133154

134-
// No sound is using this emitter, destroy it
135-
if (emitter.use_count() == 1) {
136-
emitter = nullptr;
155+
// No sound is using this emitter, destroy it
156+
if ( emitter.use_count() == 1 ) {
157+
emitter = nullptr;
158+
}
137159
}
138160
}
139161

@@ -165,11 +187,7 @@ namespace Audio {
165187
}
166188

167189
std::shared_ptr<Emitter> GetEmitterForEntity(int entityNum) {
168-
if (not entityEmitters[entityNum]) {
169-
entityEmitters[entityNum] = std::make_shared<EntityEmitter>(entityNum);
170-
}
171-
172-
return entityEmitters[entityNum];
190+
return entityEmitters[entityNum].FindSlot( entityNum );
173191
}
174192

175193
std::shared_ptr<Emitter> GetEmitterForPosition(Vec3 position) {

0 commit comments

Comments
 (0)