@@ -5,13 +5,21 @@ namespace RTE {
5
5
6
6
CONCRETECLASSINFO (SoundContainer, Entity, 0 );
7
7
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
+
8
13
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
9
14
10
15
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
+
13
20
m_PlayingChannels.clear ();
14
- m_AttenuationStartDistance = 1 ;
21
+
22
+ m_AttenuationStartDistance = c_DefaultAttenuationStartDistance;
15
23
m_Loops = 0 ;
16
24
m_Priority = AudioMan::PRIORITY_NORMAL;
17
25
m_AffectedByGlobalPitch = true ;
@@ -25,10 +33,16 @@ namespace RTE {
25
33
int SoundContainer::Create (const SoundContainer &reference) {
26
34
Entity::Create (reference);
27
35
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);
30
42
}
31
- m_SelectedSounds = reference.m_SelectedSounds ;
43
+ m_SelectedSoundSet = reference.m_SelectedSoundSet ;
44
+ m_SoundSelectionCycleMode = reference.m_SoundSelectionCycleMode ;
45
+
32
46
m_PlayingChannels.clear ();
33
47
34
48
m_AttenuationStartDistance = reference.m_AttenuationStartDistance ;
@@ -43,12 +57,15 @@ namespace RTE {
43
57
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
44
58
45
59
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
+ }
52
69
} else if (propName == " AttenuationStartDistance" ) {
53
70
reader >> m_AttenuationStartDistance;
54
71
} else if (propName == " LoopSetting" ) {
@@ -68,66 +85,147 @@ namespace RTE {
68
85
return 0 ;
69
86
}
70
87
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
+
71
150
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
72
151
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]; }
75
155
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 ;
81
160
}
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 ;
82
167
}
83
168
84
169
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
85
170
86
- std::vector<size_t > SoundContainer::GetSelectedSoundHashes () {
171
+ std::vector<size_t > SoundContainer::GetSelectedSoundHashes () const {
87
172
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 ());
90
175
}
91
176
return soundHashes;
92
177
}
93
178
94
179
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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
+ }
100
188
}
101
- return soundObjects ;
189
+ return NULL ;
102
190
}
103
191
104
192
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105
193
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
+ };
112
215
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 ;
125
226
}
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." );
128
228
}
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
-
131
229
return true ;
132
230
}
133
231
@@ -136,16 +234,57 @@ namespace RTE {
136
234
FMOD_RESULT SoundContainer::UpdateSoundProperties () {
137
235
FMOD_RESULT result = FMOD_OK;
138
236
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);
142
254
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
+ }
146
261
}
147
262
m_AllSoundPropertiesUpToDate = result == FMOD_OK;
148
263
149
264
return result;
150
265
}
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
+ }
151
290
}
0 commit comments