Skip to content
This repository was archived by the owner on Jan 5, 2024. It is now read-only.

Commit ac728ea

Browse files
committed
Added SoundContainer SoundOverlapMode, to determine how it'll respond to being told to play while playing
Used this to add HDFirearm FireEchoSound, which plays alongside the regular FireSound, but is set to restart when overlapping. Also made ActiveSound no longer not affected by global pitch, since that stuff was cleaned up before Made gold dig guisound also restart when overlapping so it's not so out of control Reorganized and cleaned up SoundContainer header and cpp a bit Added lua bindings for SoundContainer SoundCycleMode, SoundOverlapMode and Restart. Changelog entries will come later to minimize merge conflicts.
1 parent 3f9b40a commit ac728ea

File tree

6 files changed

+143
-43
lines changed

6 files changed

+143
-43
lines changed

Entities/HDFirearm.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ void HDFirearm::Clear()
3636

3737
m_pFlash = 0;
3838
m_FireSound.Reset();
39+
m_FireEchoSound.Reset();
3940
m_ActiveSound.Reset();
4041
m_DeactivationSound.Reset();
4142
m_EmptySound.Reset();
@@ -112,6 +113,7 @@ int HDFirearm::Create(const HDFirearm &reference)
112113
m_pFlash->Attach(this, m_pFlash->GetParentOffset());
113114
}
114115
m_FireSound = reference.m_FireSound;
116+
m_FireEchoSound = reference.m_FireEchoSound;
115117
m_ActiveSound = reference.m_ActiveSound;
116118
m_DeactivationSound = reference.m_DeactivationSound;
117119
m_EmptySound = reference.m_EmptySound;
@@ -175,12 +177,14 @@ int HDFirearm::ReadProperty(std::string propName, Reader &reader)
175177
}
176178
else if (propName == "FireSound")
177179
reader >> m_FireSound;
178-
else if (propName == "ActiveSound") {
180+
else if (propName == "FireEchoSound") {
181+
reader >> m_FireEchoSound;
182+
m_FireEchoSound.SetSoundOverlapMode(SoundContainer::SoundOverlapMode::MODE_RESTART);
183+
} else if (propName == "ActiveSound") {
179184
reader >> m_ActiveSound;
180-
m_ActiveSound.SetAffectedByGlobalPitch(false); //Active sound (i.e. weapon spinup) modifies its pitch, so it has to account for global pitch on its own.
181-
} else if (propName == "DeactivationSound")
185+
} else if (propName == "DeactivationSound") {
182186
reader >> m_DeactivationSound;
183-
else if (propName == "EmptySound")
187+
} else if (propName == "EmptySound")
184188
reader >> m_EmptySound;
185189
else if (propName == "ReloadStartSound")
186190
reader >> m_ReloadStartSound;
@@ -256,6 +260,8 @@ int HDFirearm::Save(Writer &writer) const
256260
writer << m_pFlash;
257261
writer.NewProperty("FireSound");
258262
writer << m_FireSound;
263+
writer.NewProperty("FireEchoSound");
264+
writer << m_FireEchoSound;
259265
writer.NewProperty("ActiveSound");
260266
writer << m_ActiveSound;
261267
writer.NewProperty("DeactivationSound");
@@ -313,6 +319,7 @@ void HDFirearm::Destroy(bool notInherited)
313319
delete m_pMagazine;
314320
delete m_pFlash;
315321
m_FireSound.Stop();
322+
m_FireEchoSound.Stop();
316323
m_ActiveSound.Stop();
317324
m_DeactivationSound.Stop();
318325

@@ -987,8 +994,12 @@ void HDFirearm::Update()
987994

988995
// Play firing sound
989996
// Only start playing if it's not a looping fire sound that is already playing, and if there's a mag
990-
if (!(m_FireSound.GetLoopSetting() == -1 && m_FireSound.IsBeingPlayed()) && m_pMagazine)
991-
m_FireSound.Play(m_Pos);
997+
if (m_pMagazine) {
998+
if (!(m_FireSound.GetLoopSetting() == -1 && m_FireSound.IsBeingPlayed())) {
999+
m_FireSound.Play(m_Pos);
1000+
}
1001+
m_FireEchoSound.Play(m_Pos);
1002+
}
9921003
}
9931004
else {
9941005
m_Recoiled = false;

Entities/HDFirearm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ ClassInfoGetters
743743

744744
// The audio of this FireArm being fired.
745745
SoundContainer m_FireSound;
746+
SoundContainer m_FireEchoSound; //!< The audio that is played as the echo for the gun. Each shot will restart this sound, so it doesn't ever overlap.
746747
// The audio that is played immediately upon activation, but perhaps before actual first firing, if there's a pre-delay
747748
SoundContainer m_ActiveSound;
748749
// The audio that is played immediately upon cease of activation

Entities/SoundContainer.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ namespace RTE {
55

66
ConcreteClassInfo(SoundContainer, Entity, 50);
77

8-
const std::unordered_map<std::string, SoundContainer::SoundCycleMode> SoundContainer::c_CycleModeMap = {
8+
const std::unordered_map<std::string, SoundContainer::SoundCycleMode> SoundContainer::c_SoundCycleModeMap = {
99
{"Random", SoundContainer::SoundCycleMode::MODE_RANDOM},
1010
{"Forwards", SoundContainer::SoundCycleMode::MODE_FORWARDS}
1111
};
12+
const std::unordered_map<std::string, SoundContainer::SoundOverlapMode> SoundContainer::c_SoundOverlapModeMap = {
13+
{"Overlap", SoundContainer::SoundOverlapMode::MODE_OVERLAP},
14+
{"Restart", SoundContainer::SoundOverlapMode::MODE_RESTART},
15+
{"Ignore Play", SoundContainer::SoundOverlapMode::MODE_IGNORE_PLAY}
16+
};
1217

1318
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1419

@@ -18,6 +23,7 @@ namespace RTE {
1823
m_SoundSelectionCycleMode = MODE_RANDOM;
1924

2025
m_PlayingChannels.clear();
26+
m_SoundOverlapMode = MODE_OVERLAP;
2127

2228
m_Immobile = false;
2329
m_AttenuationStartDistance = c_DefaultAttenuationStartDistance;
@@ -48,6 +54,7 @@ namespace RTE {
4854
m_SoundSelectionCycleMode = reference.m_SoundSelectionCycleMode;
4955

5056
m_PlayingChannels.clear();
57+
m_SoundOverlapMode = reference.m_SoundOverlapMode;
5158

5259
m_Immobile = reference.m_Immobile;
5360
m_AttenuationStartDistance = reference.m_AttenuationStartDistance;
@@ -70,11 +77,18 @@ namespace RTE {
7077
return ReadSoundOrSoundSet(propName, reader);
7178
} else if (propName == "CycleMode") {
7279
std::string cycleModeString = reader.ReadPropValue();
73-
if (c_CycleModeMap.find(cycleModeString) != c_CycleModeMap.end()) {
74-
m_SoundSelectionCycleMode = c_CycleModeMap.find(cycleModeString)->second;
80+
if (c_SoundCycleModeMap.find(cycleModeString) != c_SoundCycleModeMap.end()) {
81+
m_SoundSelectionCycleMode = c_SoundCycleModeMap.find(cycleModeString)->second;
7582
} else {
7683
reader.ReportError("Cycle mode " + cycleModeString + " is invalid.");
7784
}
85+
} else if (propName == "OverlapMode") {
86+
std::string overlapModeString = reader.ReadPropValue();
87+
if (c_SoundOverlapModeMap.find(overlapModeString) != c_SoundOverlapModeMap.end()) {
88+
m_SoundOverlapMode = c_SoundOverlapModeMap.find(overlapModeString)->second;
89+
} else {
90+
reader.ReportError("Cycle mode " + overlapModeString + " is invalid.");
91+
}
7892
} else if (propName == "Immobile") {
7993
reader >> m_Immobile;
8094
} else if (propName == "AttenuationStartDistance") {
@@ -190,14 +204,21 @@ namespace RTE {
190204
}
191205

192206
writer.NewProperty("CycleMode");
193-
bool t = m_SoundSelectionCycleMode == SoundCycleMode::MODE_FORWARDS;
194-
std::list<std::pair<const std::string, SoundContainer::SoundCycleMode>>::const_iterator cycleModeMapEntry = std::find_if(c_CycleModeMap.begin(), c_CycleModeMap.end(), [&soundSelectionCycleMode = m_SoundSelectionCycleMode](auto element) { return element.second == soundSelectionCycleMode; });
195-
if (cycleModeMapEntry != c_CycleModeMap.end()) {
207+
std::list<std::pair<const std::string, SoundCycleMode>>::const_iterator cycleModeMapEntry = std::find_if(c_SoundCycleModeMap.begin(), c_SoundCycleModeMap.end(), [&soundSelectionCycleMode = m_SoundSelectionCycleMode](auto element) { return element.second == soundSelectionCycleMode; });
208+
if (cycleModeMapEntry != c_SoundCycleModeMap.end()) {
196209
writer << cycleModeMapEntry->first;
197210
} else {
198211
writer << m_SoundSelectionCycleMode;
199212
}
200213

214+
writer.NewProperty("OverlapMode");
215+
std::list<std::pair<const std::string, SoundOverlapMode>>::const_iterator overlapModeMapEntry = std::find_if(c_SoundOverlapModeMap.begin(), c_SoundOverlapModeMap.end(), [&soundOverlapMode = m_SoundOverlapMode](auto element) { return element.second == soundOverlapMode; });
216+
if (overlapModeMapEntry != c_SoundOverlapModeMap.end()) {
217+
writer << overlapModeMapEntry->first;
218+
} else {
219+
writer << m_SoundOverlapMode;
220+
}
221+
201222
writer.NewProperty("Immobile");
202223
writer << m_Immobile;
203224
writer.NewProperty("AttenuationStartDistance");
@@ -261,6 +282,23 @@ namespace RTE {
261282
return NULL;
262283
}
263284

285+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
286+
287+
bool SoundContainer::Play(int player) {
288+
if (HasAnySounds()) {
289+
if (IsBeingPlayed()) {
290+
if (m_SoundOverlapMode == MODE_RESTART) {
291+
Restart(player);
292+
} else if (m_SoundOverlapMode == MODE_IGNORE_PLAY) {
293+
return false;
294+
}
295+
}
296+
return g_AudioMan.PlaySoundContainer(this, player);
297+
298+
}
299+
return false;
300+
}
301+
264302
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
265303

266304
bool SoundContainer::SelectNextSoundSet() {

Entities/SoundContainer.h

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,24 @@ namespace RTE {
2020
ClassInfoGetters
2121

2222
/// <summary>
23-
/// How the SoundContainer should choose the next SoundSet to play when SelectNextSoundSet is called
23+
/// How the SoundContainer should choose the next SoundSet to play when SelectNextSoundSet is called.
2424
/// </summary>
2525
enum SoundCycleMode {
2626
MODE_RANDOM = 0,
2727
MODE_FORWARDS = 1
2828
};
2929

3030
/// <summary>
31-
/// Self-contained struct defining an individual sound in a SoundSet
31+
/// How the SoundContainer should behave when it tries to play again while already playing.
32+
/// </summary>
33+
enum SoundOverlapMode {
34+
MODE_OVERLAP = 0,
35+
MODE_RESTART = 1,
36+
MODE_IGNORE_PLAY = 2
37+
};
38+
39+
/// <summary>
40+
/// Self-contained struct defining an individual sound in a SoundSet.
3241
/// </summary>
3342
struct SoundData {
3443
ContentFile SoundFile;
@@ -155,30 +164,6 @@ namespace RTE {
155164
/// <returns>Whether this sound has any samples.</returns>
156165
bool HasAnySounds() const { return !m_SoundSets.empty(); }
157166

158-
/// <summary>
159-
/// Gets the channels playing sounds from this SoundContainer.
160-
/// </summary>
161-
/// <returns>The channels currently being used.</returns>
162-
std::unordered_set<unsigned short> *GetPlayingChannels() { return &m_PlayingChannels; }
163-
164-
/// <summary>
165-
/// Indicates whether any sound in this SoundContainer is currently being played.
166-
/// </summary>
167-
/// <returns>Whether any sounds are playing.</returns>
168-
bool IsBeingPlayed() const { return !m_PlayingChannels.empty(); }
169-
170-
/// <summary>
171-
/// Adds a channel index to the SoundContainer's collection of playing channels.
172-
/// </summary>
173-
/// <param name="channel">The channel index to add.</param>
174-
void AddPlayingChannel(unsigned short channel) { m_PlayingChannels.insert(channel); }
175-
176-
/// <summary>
177-
/// Removes a channel index from the SoundContainer's collection of playing channels.
178-
/// </summary>
179-
/// <param name="channel">The channel index to remove.</param>
180-
void RemovePlayingChannel(unsigned short channel) { m_PlayingChannels.erase(channel); }
181-
182167
/// <summary>
183168
/// Gets the current sound selection cycle mode, which is used to determine what SoundSet to select next time SelectNextSoundSet is called.
184169
/// </summary>
@@ -209,6 +194,42 @@ namespace RTE {
209194
/// <param name="sound">The FMOD::Sound to search for.</param>
210195
/// <returns>A pointer to the corresponding SoundData or a null pointer.</returns>
211196
const SoundData *GetSoundDataForSound(const FMOD::Sound *sound) const;
197+
198+
/// <summary>
199+
/// Gets the channels playing sounds from this SoundContainer.
200+
/// </summary>
201+
/// <returns>The channels currently being used.</returns>
202+
std::unordered_set<unsigned short> *GetPlayingChannels() { return &m_PlayingChannels; }
203+
204+
/// <summary>
205+
/// Indicates whether any sound in this SoundContainer is currently being played.
206+
/// </summary>
207+
/// <returns>Whether any sounds are playing.</returns>
208+
bool IsBeingPlayed() const { return !m_PlayingChannels.empty(); }
209+
210+
/// <summary>
211+
/// Adds a channel index to the SoundContainer's collection of playing channels.
212+
/// </summary>
213+
/// <param name="channel">The channel index to add.</param>
214+
void AddPlayingChannel(unsigned short channel) { m_PlayingChannels.insert(channel); }
215+
216+
/// <summary>
217+
/// Removes a channel index from the SoundContainer's collection of playing channels.
218+
/// </summary>
219+
/// <param name="channel">The channel index to remove.</param>
220+
void RemovePlayingChannel(unsigned short channel) { m_PlayingChannels.erase(channel); }
221+
222+
/// <summary>
223+
/// Gets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing.
224+
/// </summary>
225+
/// <returns>The SoundOverlapMode of this SoundContainer.</returns>
226+
SoundOverlapMode GetSoundOverlapMode() const { return m_SoundOverlapMode; }
227+
228+
/// <summary>
229+
/// Sets the SoundOverlapMode of this SoundContainer, which is used to determine how it should behave when it's told to play while already playing.
230+
/// </summary>
231+
/// <param name="newSoundOverlapMode">The new SoundOverlapMode this SoundContainer should use.</param>
232+
void SetSoundOverlapMode(SoundOverlapMode newSoundOverlapMode) { m_SoundOverlapMode = newSoundOverlapMode; }
212233
#pragma endregion
213234

214235
#pragma region Sound Property Getters and Setters
@@ -329,7 +350,7 @@ namespace RTE {
329350
/// </summary>
330351
/// <param name="player">The player to start playback of this SoundContainer's sounds for.</param>
331352
/// <returns>Whether there were sounds to play and they were able to be played.</returns>
332-
bool Play(int player) { return HasAnySounds() ? g_AudioMan.PlaySoundContainer(this, player) : false; }
353+
bool Play(int player);
333354

334355
/// <summary>
335356
/// Plays the next sound of this SoundContainer at the given position for all players.
@@ -359,6 +380,19 @@ namespace RTE {
359380
/// <returns>Whether this SoundContainer successfully stopped playing.</returns>
360381
bool Stop(int player) { return (HasAnySounds() && IsBeingPlayed()) ? g_AudioMan.StopSound(this, player) : false; }
361382

383+
/// <summary>
384+
/// Restarts playback of this SoundContainer for all players.
385+
/// </summary>
386+
/// <returns>Whether this SoundContainer successfully restarted its playback.</returns>
387+
bool Restart() { return Restart(-1); }
388+
389+
/// <summary>
390+
/// Restarts playback of this SoundContainer for a specific player.
391+
/// </summary>
392+
/// <param name="player">Player to restart playback of this SoundContainer for.</param>
393+
/// <returns>Whether this SoundContainer successfully restarted its playback.</returns>
394+
bool Restart(int player) { return (HasAnySounds() && IsBeingPlayed()) ? g_AudioMan.StopSound(this, player) && g_AudioMan.PlaySoundContainer(this, player) : false; }
395+
362396
/// <summary>
363397
/// Selects the next sounds of this SoundContainer to be played.
364398
/// </summary>
@@ -383,17 +417,19 @@ namespace RTE {
383417
protected:
384418

385419
static Entity::ClassInfo m_sClass; //!< ClassInfo for this class.
386-
static const std::unordered_map<std::string, SoundCycleMode> c_CycleModeMap; //!< A map of strings to CycleModes to support string parsing for the CycleMode enum. Populated in the implementing cpp file.
420+
static const std::unordered_map<std::string, SoundCycleMode> c_SoundCycleModeMap; //!< A map of strings to SoundCycleModes to support string parsing for the SoundCycleMode enum. Populated in the implementing cpp file.
421+
static const std::unordered_map<std::string, SoundOverlapMode> c_SoundOverlapModeMap; //!< A map of strings to SoundOverlapModes to support string parsing for the SoundOverlapMode enum. Populated in the implementing cpp file.
387422

388423
std::vector<std::vector<SoundData>> m_SoundSets; //The vector of SoundSets in this SoundContainer, wherein a SoundSet is a vector containing one or more SoundData structs.
389424
size_t m_SelectedSoundSet; //!< The selected SoundSet for this SoundContainer, used to determine what sounds will play when Play is called.
390-
SoundCycleMode m_SoundSelectionCycleMode; //!< The sound cycle mode for this sound container, used to determine what will play next, each time play is called.
425+
SoundCycleMode m_SoundSelectionCycleMode; //!< The SoundCycleMode for this SoundContainer, used to determine what will play next, each time play is called.
391426

392-
std::unordered_set<unsigned short> m_PlayingChannels; //!< The channels this SoundContainer is currently using
427+
std::unordered_set<unsigned short> m_PlayingChannels; //!< The channels this SoundContainer is currently using.
428+
SoundOverlapMode m_SoundOverlapMode; //!< The SoundOverlapMode for this SoundContainer, used to determine how it should handle overlapping play calls.
393429

394430
bool m_Immobile; //!< Whether this SoundContainer's sounds should be treated as immobile, i.e. not affected by 3D sound effects. Mostly used for GUI sounds and the like.
395431
float m_AttenuationStartDistance; //!< The distance away from the AudioSystem listener to start attenuating this sound. Attenuation follows FMOD 3D Inverse roll-off model.
396-
int m_Loops; //!< Number of loops (repeats) the SoundContainer's sounds should play when played. 0 means it plays once, -1 means it plays until stopped .
432+
int m_Loops; //!< Number of loops (repeats) the SoundContainer's sounds should play when played. 0 means it plays once, -1 means it plays until stopped.
397433
bool m_SoundPropertiesUpToDate = false; //!< Whether this SoundContainer's sounds' modes and properties are up to date. Used primarily to handle discrepancies that can occur when loading from ini if the line ordering isn't ideal.
398434

399435
int m_Priority; //!< The mixing priority of this SoundContainer's sounds. Higher values are more likely to be heard.

GUI/GUISound.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ namespace RTE {
8383
m_FundsChangedSound.AddSound("Base.rte/Sounds/GUIs/FundsChanged4.flac", true);
8484
m_FundsChangedSound.AddSound("Base.rte/Sounds/GUIs/FundsChanged5.flac", true);
8585
m_FundsChangedSound.AddSound("Base.rte/Sounds/GUIs/FundsChanged6.flac", true);
86+
m_FundsChangedSound.SetSoundOverlapMode(SoundContainer::SoundOverlapMode::MODE_RESTART);
8687

8788
m_ActorSwitchSound.Create("Base.rte/Sounds/GUIs/ActorSwitch.flac", true, false);
8889

Managers/LuaMan.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,17 @@ int LuaMan::Create() {
574574

575575
CONCRETELUABINDING(SoundContainer, Entity)
576576
.def(constructor<>())
577+
.enum_("SoundCycleMode")[
578+
value("MODE_RANDOM", SoundContainer::SoundCycleMode::MODE_RANDOM),
579+
value("MODE_FORWARDS", SoundContainer::SoundCycleMode::MODE_FORWARDS)
580+
]
581+
.enum_("SoundOverlapMode")[
582+
value("MODE_OVERLAP", SoundContainer::SoundOverlapMode::MODE_OVERLAP),
583+
value("MODE_RESTART", SoundContainer::SoundOverlapMode::MODE_RESTART),
584+
value("MODE_IGNORE_PLAY", SoundContainer::SoundOverlapMode::MODE_IGNORE_PLAY)
585+
]
586+
.property("SoundCycleMode", &SoundContainer::GetSoundSelectionCycleMode, &SoundContainer::SetSoundSelectionCycleMode)
587+
.property("SoundOverlapMode", &SoundContainer::GetSoundOverlapMode, &SoundContainer::SetSoundOverlapMode)
577588
.property("Immobile", &SoundContainer::IsImmobile, &SoundContainer::SetImmobile)
578589
.property("AttenuationStartDistance", &SoundContainer::GetAttenuationStartDistance, &SoundContainer::SetAttenuationStartDistance)
579590
.property("Loops", &SoundContainer::GetLoopSetting, &SoundContainer::SetLoopSetting)
@@ -590,6 +601,8 @@ int LuaMan::Create() {
590601
.def("Play", (bool (SoundContainer:: *)(const Vector &position, int player)) &SoundContainer::Play)
591602
.def("Stop", (bool (SoundContainer:: *)()) &SoundContainer::Stop)
592603
.def("Stop", (bool (SoundContainer:: *)(int player)) &SoundContainer::Stop)
604+
.def("Restart", (bool (SoundContainer:: *)()) &SoundContainer::Restart)
605+
.def("Restart", (bool (SoundContainer:: *)(int player)) &SoundContainer::Restart)
593606
.def("AddSound", (void (SoundContainer:: *)(std::string const &soundFilePath)) &SoundContainer::AddSound)
594607
.def("AddSound", (void (SoundContainer:: *)(std::string const &soundFilePath, const Vector &offset, float attenuationStartDistance, bool abortGameForInvalidSound)) &SoundContainer::AddSound)
595608
.def("AddSound", (void (SoundContainer:: *)(std::string const &soundFilePath, unsigned int soundSetIndex, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound)) &SoundContainer::AddSound)

0 commit comments

Comments
 (0)