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

Commit ad7a3cf

Browse files
authored
Merge pull request #100 from cortex-command-community/CF94-FMOD-Phase-2
Cf94 fmod phase 2
2 parents 864cbc6 + 8a1b9b1 commit ad7a3cf

18 files changed

+679
-228
lines changed

Entities/Actor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ ENTITYALLOCATION(Actor)
13191319
m_DeathSound.Reset();
13201320
} else {
13211321
SoundContainer newDeathSound;
1322-
newDeathSound.Create(samplePath, false);
1322+
newDeathSound.Create(samplePath);
13231323
m_DeathSound = newDeathSound;
13241324
}
13251325
}

Entities/SoundContainer.cpp

Lines changed: 195 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@ namespace RTE {
55

66
CONCRETECLASSINFO(SoundContainer, Entity, 0);
77

8+
const std::unordered_map<std::string, SoundContainer::SoundCycleMode> SoundContainer::c_CycleModeMap = {
9+
{"Random", SoundContainer::SoundCycleMode::MODE_RANDOM},
10+
{"Forwards", SoundContainer::SoundCycleMode::MODE_FORWARDS}
11+
};
12+
813
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
914

1015
void SoundContainer::Clear() {
11-
m_Sounds.clear();
12-
m_SelectedSounds.clear();
16+
m_SoundSets.clear();
17+
m_SelectedSoundSet = 0;
18+
m_SoundSelectionCycleMode = MODE_RANDOM;
19+
1320
m_PlayingChannels.clear();
14-
m_AttenuationStartDistance = 1;
21+
22+
m_AttenuationStartDistance = c_DefaultAttenuationStartDistance;
1523
m_Loops = 0;
1624
m_Priority = AudioMan::PRIORITY_NORMAL;
1725
m_AffectedByGlobalPitch = true;
@@ -25,10 +33,16 @@ namespace RTE {
2533
int SoundContainer::Create(const SoundContainer &reference) {
2634
Entity::Create(reference);
2735

28-
for (std::vector<std::pair<ContentFile, FMOD::Sound *>>::const_iterator itr = reference.m_Sounds.begin(); itr != reference.m_Sounds.end(); ++itr) {
29-
m_Sounds.push_back(*itr);
36+
for (const std::vector<SoundData> &referenceSoundSet : reference.m_SoundSets) {
37+
std::vector<SoundData> soundSet;
38+
for (const SoundData &referenceData : referenceSoundSet) {
39+
soundSet.push_back(SoundData {referenceData.SoundFile, referenceData.SoundObject, referenceData.Offset, referenceData.MinimumAudibleDistance, referenceData.AttenuationStartDistance});
40+
}
41+
m_SoundSets.push_back(soundSet);
3042
}
31-
m_SelectedSounds = reference.m_SelectedSounds;
43+
m_SelectedSoundSet = reference.m_SelectedSoundSet;
44+
m_SoundSelectionCycleMode = reference.m_SoundSelectionCycleMode;
45+
3246
m_PlayingChannels.clear();
3347

3448
m_AttenuationStartDistance = reference.m_AttenuationStartDistance;
@@ -43,12 +57,15 @@ namespace RTE {
4357
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4458

4559
int SoundContainer::ReadProperty(std::string propName, Reader &reader) {
46-
if (propName == "AddSound") {
47-
ContentFile newFile;
48-
reader >> newFile;
49-
FMOD::Sound *pNewSample = newFile.GetAsSample();
50-
if (!pNewSample) { reader.ReportError(std::string("Failed to load the sample from the file")); }
51-
m_Sounds.push_back(std::pair<ContentFile, FMOD::Sound *>(newFile, pNewSample));
60+
if (propName == "AddSound" || propName == "AddSoundSet") {
61+
return ReadSoundOrSoundSet(propName, reader);
62+
} else if (propName == "CycleMode") {
63+
std::string cycleModeString = reader.ReadPropValue();
64+
if (c_CycleModeMap.find(cycleModeString) != c_CycleModeMap.end()) {
65+
m_SoundSelectionCycleMode = c_CycleModeMap.find(cycleModeString)->second;
66+
} else {
67+
reader.ReportError("Cycle mode " + cycleModeString + " is invalid.");
68+
}
5269
} else if (propName == "AttenuationStartDistance") {
5370
reader >> m_AttenuationStartDistance;
5471
} else if (propName == "LoopSetting") {
@@ -68,66 +85,147 @@ namespace RTE {
6885
return 0;
6986
}
7087

88+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89+
90+
int SoundContainer::ReadSoundOrSoundSet(const std::string &propName, Reader &reader) {
91+
vector<SoundData> soundSet;
92+
if (propName == "AddSound") {
93+
soundSet.push_back(ReadSound(reader));
94+
} else if (propName == "AddSoundSet") {
95+
reader.ReadPropValue();
96+
while (reader.NextProperty()) {
97+
std::string soundSetSubPropertyName = reader.ReadPropName();
98+
if (soundSetSubPropertyName == "AddSound") {
99+
soundSet.push_back(ReadSound(reader));
100+
} else {
101+
reader.ReportError(soundSetSubPropertyName + " is not a valid property of SoundSets.");
102+
}
103+
}
104+
}
105+
106+
m_SoundSets.push_back(soundSet);
107+
return 0;
108+
}
109+
110+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
111+
112+
SoundContainer::SoundData SoundContainer::ReadSound(Reader &reader) {
113+
std::string propValue = reader.ReadPropValue();
114+
SoundData soundData;
115+
116+
/// <summary>
117+
/// Internal lambda function to load an audio file by path in as a ContentFile, which in turn loads it into FMOD, then returns SoundData for it in the outParam outSoundData.
118+
/// </summary>
119+
/// <param name="soundPath">The path to the sound file.</param>
120+
auto readSound = [&soundData, &reader](const std::string &soundPath) {
121+
ContentFile soundFile(soundPath.c_str());
122+
FMOD::Sound *soundObject = soundFile.GetAsSample();
123+
if (g_AudioMan.IsAudioEnabled() && !soundObject) { reader.ReportError(std::string("Failed to load the sound from the file")); }
124+
125+
soundData.SoundFile = soundFile;
126+
soundData.SoundObject = soundObject;
127+
};
128+
129+
if (propValue != "Sound" && propValue != "ContentFile") {
130+
readSound(propValue);
131+
return soundData;
132+
}
133+
134+
while (reader.NextProperty()) {
135+
std::string soundSubPropertyName = reader.ReadPropName();
136+
if (soundSubPropertyName == "FilePath" || soundSubPropertyName == "Path") {
137+
readSound(reader.ReadPropValue());
138+
} else if (soundSubPropertyName == "Offset") {
139+
reader >> soundData.Offset;
140+
} else if (soundSubPropertyName == "MinimumAudibleDistance") {
141+
reader >> soundData.MinimumAudibleDistance;
142+
} else if (soundSubPropertyName == "AttenuationStartDistance") {
143+
reader >> soundData.AttenuationStartDistance;
144+
}
145+
}
146+
147+
return soundData;
148+
}
149+
71150
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
72151

73-
void SoundContainer::AddSound(std::string const soundPath, bool abortGameForInvalidSound) {
74-
ContentFile newFile(soundPath.c_str());
152+
void SoundContainer::AddSound(const std::string &soundFilePath, unsigned int soundSetIndex, const Vector &offset, float minimumAudibleDistance, float attenuationStartDistance, bool abortGameForInvalidSound) {
153+
std::vector<SoundData> soundSet;
154+
if (soundSetIndex < m_SoundSets.size()) { soundSet = m_SoundSets[soundSetIndex]; }
75155

76-
FMOD::Sound *newSample = newFile.GetAsSample(abortGameForInvalidSound);
77-
if (newSample) {
78-
FMOD_RESULT result = newSample->setMode((m_Loops == 0) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL);
79-
result = (result == FMOD_OK) ? newSample->setLoopCount(m_Loops) : result;
80-
m_Sounds.push_back(std::pair<ContentFile, FMOD::Sound *>(newFile, newSample));
156+
ContentFile soundFile(soundFilePath.c_str());
157+
FMOD::Sound *soundObject = soundFile.GetAsSample(abortGameForInvalidSound);
158+
if (!soundObject) {
159+
return;
81160
}
161+
162+
attenuationStartDistance = (attenuationStartDistance < 0) ? c_DefaultAttenuationStartDistance : attenuationStartDistance;
163+
soundSet.push_back({soundFile, soundObject, offset, minimumAudibleDistance, attenuationStartDistance});
164+
if (soundSetIndex >= m_SoundSets.size()) { m_SoundSets.push_back(soundSet); }
165+
166+
m_AllSoundPropertiesUpToDate = false;
82167
}
83168

84169
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85170

86-
std::vector<size_t> SoundContainer::GetSelectedSoundHashes() {
171+
std::vector<size_t> SoundContainer::GetSelectedSoundHashes() const {
87172
std::vector<size_t> soundHashes;
88-
for (size_t selectedSoundIndex : m_SelectedSounds) {
89-
soundHashes.push_back(m_Sounds[selectedSoundIndex].first.GetHash());
173+
for (const SoundData &selectedSoundData : m_SoundSets[m_SelectedSoundSet]) {
174+
soundHashes.push_back(selectedSoundData.SoundFile.GetHash());
90175
}
91176
return soundHashes;
92177
}
93178

94179
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
95-
96-
std::vector<FMOD::Sound *> SoundContainer::GetSelectedSoundObjects() {
97-
std::vector<FMOD::Sound *> soundObjects;
98-
for (size_t selectedSoundIndex : m_SelectedSounds) {
99-
soundObjects.push_back(m_Sounds[selectedSoundIndex].second);
180+
181+
const SoundContainer::SoundData *SoundContainer::GetSoundDataForSound(const FMOD::Sound *sound) const {
182+
for (const std::vector<SoundData> &soundSet : m_SoundSets) {
183+
for (const SoundData &soundData : soundSet) {
184+
if (sound == soundData.SoundObject) {
185+
return &soundData;
186+
}
187+
}
100188
}
101-
return soundObjects;
189+
return NULL;
102190
}
103191

104192
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105193

106-
bool SoundContainer::SelectNextSounds() {
107-
int soundCount = GetSoundCount();
108-
if (soundCount == 0) {
109-
return false;
110-
}
111-
size_t previouslySelectedSound = (m_SelectedSounds.size() > 0) ? m_SelectedSounds[0] : 0;
194+
bool SoundContainer::SelectNextSoundSet() {
195+
int soundSetCount = m_SoundSets.size();
196+
switch (soundSetCount) {
197+
case 0:
198+
return false;
199+
case 1:
200+
return true;
201+
case 2:
202+
m_SelectedSoundSet = (m_SelectedSoundSet + 1) % 2;
203+
break;
204+
default:
205+
/// <summary>
206+
/// Internal lambda function to pick a random sound that's not the previously played sound. Done to avoid scoping issues inside the switch below.
207+
/// </summary>
208+
auto selectRandomSound = [&soundSetCount, this]() {
209+
size_t soundToSelect = std::floorf(static_cast<float>(soundSetCount) * PosRand());
210+
while (soundToSelect == m_SelectedSoundSet) {
211+
soundToSelect = std::floorf(static_cast<float>(soundSetCount) * PosRand());
212+
}
213+
m_SelectedSoundSet = soundToSelect;
214+
};
112215

113-
if (m_SelectedSounds.empty() || soundCount == 1) {
114-
m_SelectedSounds.clear();
115-
m_SelectedSounds.push_back(previouslySelectedSound);
116-
} else {
117-
m_SelectedSounds.clear();
118-
if (soundCount == 2) {
119-
m_SelectedSounds.push_back((previouslySelectedSound + 1) % soundCount);
120-
} else if (soundCount > 2) {
121-
size_t soundToSelect = floorf(static_cast<float>(soundCount) * PosRand());
122-
// Mix it up again if we got the same sound twice
123-
while (soundToSelect == previouslySelectedSound) {
124-
soundToSelect = floorf(static_cast<float>(soundCount) * PosRand());
216+
switch (m_SoundSelectionCycleMode) {
217+
case MODE_RANDOM:
218+
selectRandomSound();
219+
break;
220+
case MODE_FORWARDS:
221+
m_SelectedSoundSet = (m_SelectedSoundSet + 1) % soundSetCount;
222+
break;
223+
default:
224+
RTEAbort("Invalid sound cycle mode " + m_SoundSelectionCycleMode);
225+
break;
125226
}
126-
m_SelectedSounds.push_back(soundToSelect);
127-
}
227+
RTEAssert(m_SelectedSoundSet >= 0 && m_SelectedSoundSet < soundSetCount, "Failed to select next sound, either none was selected or the selected sound was invalid.");
128228
}
129-
RTEAssert(m_SelectedSounds.size() > 0 && m_SelectedSounds[0] >= 0 && m_SelectedSounds[0] < soundCount, "Failed to select next sound, either none was selected or the selected sound was invalid.");
130-
131229
return true;
132230
}
133231

@@ -136,16 +234,57 @@ namespace RTE {
136234
FMOD_RESULT SoundContainer::UpdateSoundProperties() {
137235
FMOD_RESULT result = FMOD_OK;
138236

139-
for (std::vector<std::pair<ContentFile, FMOD::Sound * >>::const_iterator itr = m_Sounds.begin(); itr != m_Sounds.end() && result == FMOD_OK; ++itr) {
140-
FMOD_MODE soundMode = (m_Loops == 0) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL;
141-
soundMode |= m_Immobile ? FMOD_3D_HEADRELATIVE : FMOD_3D_WORLDRELATIVE;
237+
for (std::vector<SoundData> &soundSet : m_SoundSets) {
238+
for (SoundData &soundData : soundSet) {
239+
FMOD_MODE soundMode = (m_Loops == 0) ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL;
240+
if (m_Immobile) {
241+
soundMode |= FMOD_3D_HEADRELATIVE;
242+
m_AttenuationStartDistance = c_SoundMaxAudibleDistance;
243+
} else {
244+
soundMode |= FMOD_3D_CUSTOMROLLOFF;
245+
}
246+
247+
result = (result == FMOD_OK) ? soundData.SoundObject->setMode(soundMode) : result;
248+
result = (result == FMOD_OK) ? soundData.SoundObject->setLoopCount(m_Loops) : result;
249+
if (m_Immobile) {
250+
result = (result == FMOD_OK) ? soundData.SoundObject->set3DMinMaxDistance(m_AttenuationStartDistance, c_SoundMaxAudibleDistance) : result;
251+
} else {
252+
//FMOD_VECTOR customRolloffPoints[10];
253+
//CalculateCustomRolloffPoints(soundData, customRolloffPoints, 10);
142254

143-
result = (result == FMOD_OK) ? itr->second->setMode(soundMode) : result;
144-
result = (result == FMOD_OK) ? itr->second->setLoopCount(m_Loops) : result;
145-
result = (result == FMOD_OK) ? itr->second->set3DMinMaxDistance(m_AttenuationStartDistance, 100000) : result;
255+
//TODO Consider replacing this with normal min and max distance (but keep custom rolloff mode) so we can save some pointing issues. Might need to store min audible distance in audioman if fmod is strict about min and max distance sizes wrt each other.
256+
soundData.CustomRolloffPoints[0] = FMOD_VECTOR{soundData.MinimumAudibleDistance, 0, 0};
257+
soundData.CustomRolloffPoints[1] = FMOD_VECTOR{(soundData.AttenuationStartDistance < 0) ? m_AttenuationStartDistance : soundData.AttenuationStartDistance, 1, 0};
258+
result = (result == FMOD_OK) ? soundData.SoundObject->set3DCustomRolloff(soundData.CustomRolloffPoints, 2) : result;
259+
}
260+
}
146261
}
147262
m_AllSoundPropertiesUpToDate = result == FMOD_OK;
148263

149264
return result;
150265
}
266+
267+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
268+
269+
//TODO this needs to be used or be deleted
270+
void SoundContainer::CalculateCustomRolloffPoints(const SoundData &soundDataToCalculateFor, FMOD_VECTOR *rolloffPoints, int numRolloffPoints) {
271+
int attenuationStartDistance = (soundDataToCalculateFor.AttenuationStartDistance < 0) ? m_AttenuationStartDistance : soundDataToCalculateFor.AttenuationStartDistance;
272+
float currentDistance = attenuationStartDistance;
273+
float currentVolumeLevel = 1;
274+
275+
int i = 0;
276+
if (soundDataToCalculateFor.MinimumAudibleDistance > 0) {
277+
i = 2;
278+
currentDistance += soundDataToCalculateFor.MinimumAudibleDistance;
279+
rolloffPoints[0] = FMOD_VECTOR{0, 0, 0};
280+
rolloffPoints[1] = FMOD_VECTOR{soundDataToCalculateFor.MinimumAudibleDistance - 0.1F, 0, 0};
281+
}
282+
283+
for (i = ((soundDataToCalculateFor.MinimumAudibleDistance > 0) ? 2 : 0); i < numRolloffPoints - 1; i++) {
284+
rolloffPoints[i] = FMOD_VECTOR{currentDistance, currentVolumeLevel, 0};
285+
currentDistance += attenuationStartDistance;
286+
currentVolumeLevel = (currentDistance < c_SoundMaxAudibleDistance) ? currentVolumeLevel * 0.5F : 0;
287+
}
288+
rolloffPoints[numRolloffPoints - 1] = FMOD_VECTOR{currentDistance, 0, 0};
289+
}
151290
}

0 commit comments

Comments
 (0)