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

Commit 30267b1

Browse files
committed
Added support for 3D Sounds and Panning
ContentFile now opens sounds in 3D Changed SoundContainer attenuation to a position Vector that gets used to handle panning. Added the ability to set a SoundContainer's attenuation start distance, i.e. the distance at which it will start attenuating away. Added immobile property for sounds that should always be played at the player's position (i.e. no 3D effects) Split SoundContainer getters and setters into 2 regions cause it was getting too big Added a flag for whether a SoundContainer's sounds are all up-to-date with its member variable settings, and method to update accordingly. The latter will be used in AudioMan::PlaySound and avoids problems when loading SoundContainers from ini
1 parent e5f513a commit 30267b1

File tree

3 files changed

+105
-30
lines changed

3 files changed

+105
-30
lines changed

Entities/SoundContainer.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ namespace RTE {
1111
m_Sounds.clear();
1212
m_SelectedSounds.clear();
1313
m_PlayingChannels.clear();
14+
m_AttenuationStartDistance = 0;
1415
m_Loops = 0;
1516
m_Priority = AudioMan::PRIORITY_NORMAL;
1617
m_AffectedByGlobalPitch = true;
18+
m_Immobile = false;
19+
20+
m_AllSoundPropertiesUpToDate = false;
1721
}
1822

1923
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -27,9 +31,12 @@ namespace RTE {
2731

2832
m_SelectedSounds = reference.m_SelectedSounds;
2933
m_PlayingChannels.clear();
34+
35+
m_AttenuationStartDistance = reference.m_AttenuationStartDistance;
3036
m_Loops = reference.m_Loops;
3137
m_Priority = reference.m_Priority;
3238
m_AffectedByGlobalPitch = reference.m_AffectedByGlobalPitch;
39+
m_Immobile = reference.m_Immobile;
3340

3441
return 0;
3542
}
@@ -40,12 +47,13 @@ namespace RTE {
4047
if (propName == "AddSample" || propName == "AddSound") {
4148
ContentFile newFile;
4249
reader >> newFile;
43-
4450
FMOD::Sound *pNewSample = newFile.GetAsSample();
4551
if (!pNewSample) {
4652
reader.ReportError(std::string("Failed to load the sample from the file"));
4753
}
4854
m_Sounds.push_back(std::pair<ContentFile, FMOD::Sound *>(newFile, pNewSample));
55+
} else if (propName == "AttenuationStartDistance") {
56+
reader >> m_AttenuationStartDistance;
4957
} else if (propName == "LoopSetting") {
5058
reader >> m_Loops;
5159
} else if (propName == "Priority") {
@@ -55,6 +63,8 @@ namespace RTE {
5563
}
5664
} else if (propName == "AffectedByGlobalPitch") {
5765
reader >> m_AffectedByGlobalPitch;
66+
} else if (propName == "Immobile") {
67+
reader >> m_Immobile;
5868
} else {
5969
// See if the base class(es) can find a match instead
6070
return Entity::ReadProperty(propName, reader);
@@ -96,16 +106,6 @@ namespace RTE {
96106
return soundObjects;
97107
}
98108

99-
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
100-
101-
void SoundContainer::SetLoopSetting(int loops) {
102-
m_Loops = loops;
103-
for (std::vector<std::pair<ContentFile, FMOD::Sound * >>::const_iterator itr = m_Sounds.begin(); itr != m_Sounds.end(); ++itr) {
104-
FMOD_RESULT result = itr->second->setMode(m_Loops == 0 ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL);
105-
result = itr->second->setLoopCount(m_Loops);
106-
}
107-
}
108-
109109
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
110110

111111
bool SoundContainer::SelectNextSounds() {
@@ -136,4 +136,22 @@ namespace RTE {
136136

137137
return true;
138138
}
139+
140+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
141+
142+
FMOD_RESULT SoundContainer::UpdateSoundProperties() {
143+
FMOD_RESULT result = FMOD_OK;
144+
145+
for (std::vector<std::pair<ContentFile, FMOD::Sound * >>::const_iterator itr = m_Sounds.begin(); itr != m_Sounds.end() && result == FMOD_OK; ++itr) {
146+
FMOD_MODE soundMode = m_Loops == 0 ? FMOD_LOOP_OFF : FMOD_LOOP_NORMAL;
147+
soundMode |= m_Immobile ? FMOD_3D_HEADRELATIVE : FMOD_3D_WORLDRELATIVE;
148+
149+
result = result == FMOD_OK ? itr->second->setMode(soundMode) : result;
150+
result = result == FMOD_OK ? itr->second->setLoopCount(m_Loops) : result;
151+
result = result == FMOD_OK ? itr->second->set3DMinMaxDistance(m_AttenuationStartDistance, 100000) : result;
152+
}
153+
m_AllSoundPropertiesUpToDate = result == FMOD_OK;
154+
155+
return result;
156+
}
139157
}

Entities/SoundContainer.h

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "AudioMan.h"
77

88
namespace RTE {
9+
class Vector;
910

1011
/// <summary>
1112
/// A container for sounds that represent a specific sound effect.
@@ -40,17 +41,21 @@ namespace RTE {
4041
/// </summary>
4142
/// <param name="loops">The number of times this SoundContainer's sounds will loop. 0 means play once. -1 means play infinitely until stopped.</param>
4243
/// <param name="affectedByGlobalPitch">Whether this SoundContainer's sounds' frequency will be affected by the global pitch.</param>
44+
/// <param name="attenuationStartDistance">The distance at which this SoundContainer's sounds should start attenuating away.</param>
45+
/// <param name="immobile">Whether this SoundContainer's sounds' positions will be treated as immobile, i.e. they won't be affected by 3D sound manipulation.</param>
4346
/// <returns>An error return value signaling success or any particular failure. Anything below 0 is an error signal.</returns>
44-
int Create(int loops = 0, bool affectedByGlobalPitch = true) { m_Loops = loops; m_AffectedByGlobalPitch = affectedByGlobalPitch; return 0; }
47+
int Create(int loops = 0, bool affectedByGlobalPitch = true, float attenuationStartDistance = 0, bool immobile = false) { SetLoopSetting(loops); m_AffectedByGlobalPitch = affectedByGlobalPitch; m_AttenuationStartDistance = attenuationStartDistance; m_Immobile = immobile; return 0; }
4548

4649
/// <summary>
4750
/// Creates a SoundContainer and gives it a path to its first sound.
4851
/// </summary>
4952
/// <param name="soundPath">A path to the sound for this sound to have.</param>
5053
/// <param name="loops">The number of times this SoundContainer's sounds will loop. 0 means play once. -1 means play infinitely until stopped.</param>
5154
/// <param name="affectedByGlobalPitch">Whether this SoundContainer's sounds' frequency will be affected by the global pitch.</param>
55+
/// <param name="attenuationStartDistance">The distance at which this SoundContainer's sounds should start attenuating away.</param>
56+
/// <param name="immobile">Whether this SoundContainer's sounds' positions will be treated as immobile, i.e. they won't be affected by 3D sound manipulation.</param>
5257
/// <returns>An error return value signaling success or any particular failure. Anything below 0 is an error signal.</returns>
53-
int Create(std::string const soundPath, int loops = 0, bool affectedByGlobalPitch = true) { int result = Create(loops, affectedByGlobalPitch); AddSound(soundPath); return result; }
58+
int Create(std::string const soundPath, int loops = 0, bool affectedByGlobalPitch = true, float attenuationStartDistance = 0, bool immobile = false) { int result = Create(loops, affectedByGlobalPitch, attenuationStartDistance, immobile); AddSound(soundPath); return result; }
5459

5560
/// <summary>
5661
/// Adds a new Sound to this SoundContainer, loaded from a file.
@@ -100,7 +105,7 @@ namespace RTE {
100105
virtual int Save(Writer &writer) const { return 0; }
101106
#pragma endregion
102107

103-
#pragma region Getters and Setters
108+
#pragma region Sound Management Getters and Setters
104109
/// <summary>
105110
/// Gets the current list of sounds in the SoundContainer.
106111
/// </summary>
@@ -160,13 +165,27 @@ namespace RTE {
160165
/// </summary>
161166
/// <param name="channel">The channel index to remove.</param>
162167
void RemovePlayingChannel(unsigned short int channel) { m_PlayingChannels.erase(channel); }
168+
#pragma endregion
163169

170+
#pragma region Sound Property Getters and Setters
164171
/// <summary>
165-
/// Updates the distance attenuation of the SoundContainer's sounds while they're playing.
172+
/// Updates the position of the SoundContainer's sounds while they're playing.
166173
/// </summary>
167-
/// <param name="attenuation">How much distance attenuation to apply to the sound. 0 = full volume 1.0 = max distant, but still won't be completely inaudible.</param>
174+
/// <param name="position">The new position to play the SoundContainer's sounds.</param>
168175
/// <returns>Whether this SoundContainer's attenuation setting was successful.</returns>
169-
bool UpdateAttenuation(float attenuation) { return g_AudioMan.SetSoundAttenuation(this, attenuation); }
176+
bool SetPosition(const Vector &position) { return g_AudioMan.SetSoundPosition(this, position); }
177+
178+
/// <summary>
179+
/// Gets the attenuation start distance of this SoundContainer.
180+
/// </summary>
181+
/// <returns>A float with the attenuation start distance.</returns>
182+
float GetAttenuationStartDistance() const { return m_AttenuationStartDistance; }
183+
184+
/// <summary>
185+
/// Sets the attenuation start distance of this SoundContainer.
186+
/// </summary>
187+
/// <param name="attenuationStartDistance">The new attenuation start distance.</param>
188+
void SetAttenuationStartDistance(float attenuationStartDistance) { m_AttenuationStartDistance = attenuationStartDistance; m_AllSoundPropertiesUpToDate = false; }
170189

171190
/// <summary>
172191
/// Gets the looping setting of this SoundContainer.
@@ -178,8 +197,8 @@ namespace RTE {
178197
/// Sets the looping setting of this SoundContainer.
179198
/// 0 means the sound is set to only play once. -1 means it loops indefinitely.
180199
/// </summary>
181-
/// <param name="loops">An int with the loop count.</param>
182-
void SetLoopSetting(int loops);
200+
/// <param name="loops">The new loop count.</param>
201+
void SetLoopSetting(int loops) { m_Loops = loops; m_AllSoundPropertiesUpToDate = false; }
183202

184203
/// <summary>
185204
/// Gets the current playback priority.
@@ -196,37 +215,62 @@ namespace RTE {
196215
/// <summary>
197216
/// Gets whether the sounds in this SoundContainer are affected by global pitch changes or not.
198217
/// </summary>
199-
/// <returns>Whether or not the Sounds in this SoundContainer affected by global pitch changes.</returns>
218+
/// <returns>Whether or not the sounds in this SoundContainer are affected by global pitch changes.</returns>
200219
bool IsAffectedByGlobalPitch() const { return m_AffectedByGlobalPitch; }
201220

202221
/// <summary>
203222
/// Sets whether the sounds in this SoundContainer are affected by global pitch changes or not.
204223
/// </summary>
205-
/// <param name="pitched">Whether the sounds in this SoundContainer should be affected by global pitch or not.</param>
224+
/// <param name="pitched">The new affected by global pitch setting.</param>
206225
void SetAffectedByGlobalPitch(bool affectedByGlobalPitch) { m_AffectedByGlobalPitch = affectedByGlobalPitch; }
226+
227+
/// <summary>
228+
/// Gets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position.
229+
/// </summary>
230+
/// <returns>Whether or not the sounds in this SoundContainer are immobile.</returns>
231+
bool IsImmobile() const { return m_Immobile; }
232+
233+
/// <summary>
234+
/// Sets whether the sounds in this SoundContainer should be considered immobile, i.e. always play at the listener's position.
235+
/// </summary>
236+
/// <param name="immobile">The new immobile setting.</param>
237+
void SetImmobile(bool immobile) { m_Immobile = immobile; m_AllSoundPropertiesUpToDate = false; }
238+
239+
/// <summary>
240+
/// Gets whether the sounds in this SoundContainer have all had all their properties set appropriately. Used to account for issues with ordering in INI loading.
241+
/// </summary>
242+
/// <returns>Whether or not the sounds in this SoundContainer have their properties set appropriately.</returns>
243+
bool AllSoundPropertiesUpToDate() { return m_AllSoundPropertiesUpToDate; }
207244
#pragma endregion
208245

209246
#pragma region Playback Controls
210247
/// <summary>
211-
/// Plays the next sound of this SoundContainer with default attenuation for all players.
248+
/// Plays the next sound of this SoundContainer with at (0, 0) for all players.
212249
/// </summary>
213250
/// <returns>Whether this SoundContainer successfully started playing on any channels.</returns>
214-
bool Play() { return Play(0, -1); }
251+
bool Play() { return Play(Vector(), -1); }
215252

216253
/// <summary>
217-
/// Plays the next sound of this SoundContainer with the given attenuation for all players.
254+
/// Plays the next sound of this SoundContainer at the given position for all players.
218255
/// </summary>
219-
/// <param name="attenuation">How much distance attenuation to apply to the SoundContainer.</param>
256+
/// <param name="position">The position at which to play the SoundContainer's sounds.</param>
220257
/// <returns>Whether this SoundContainer successfully started playing on any channels.</returns>
221-
bool Play(float attenuation) { return Play(attenuation, -1); }
258+
bool Play(const Vector &position) { return Play(position, -1); }
259+
260+
/// <summary>
261+
/// Plays the next sound of this container at (0, 0) for the given player. Mostly useful for immobile sounds.
262+
/// </summary>
263+
/// <param name="player">The player to start playback of this SoundContainer's sounds for.</param>
264+
/// <returns>Whether there were sounds to play and they were able to be played.</returns>
265+
bool Play(int player) { return Play(Vector(), player); }
222266

223267
/// <summary>
224268
/// Plays the next sound of this SoundContainer with the given attenuation for a specific player.
225269
/// </summary>
226-
/// <param name="attenuation">How much distance attenuation to apply to the SoundContainer.</param>
227-
/// <param name="player">Player to start playback of this SoundContainer for.</param>
270+
/// <param name="position">The position at which to play the SoundContainer's sounds.</param>
271+
/// <param name="player">The player to start playback of this SoundContainer's sounds for.</param>
228272
/// <returns>Whether this SoundContainer successfully started playing on any channels.</returns>
229-
bool Play(float attenuation, int player) { return HasAnySounds() ? g_AudioMan.PlaySound(this, attenuation, player) : false; }
273+
bool Play(const Vector &position, int player) { return HasAnySounds() ? g_AudioMan.PlaySound(this, (m_Immobile ? Vector() : position), player) : false; }
230274

231275
/// <summary>
232276
/// Stops playback of this SoundContainer for all players.
@@ -253,6 +297,15 @@ namespace RTE {
253297
void FadeOut(int fadeOutTime = 1000) { if (IsBeingPlayed()) { return g_AudioMan.FadeOutSound(this, fadeOutTime); } }
254298
#pragma endregion
255299

300+
#pragma region Miscellaneous
301+
/// <summary>
302+
/// Updates all sound properties to match this SoundContainer's settings.
303+
/// Necessary because sounds loaded from ini seem to be used directly instead of loaded from PresetMan, so their correctness can't be guaranteed when they're played.
304+
/// </summary>
305+
/// <returns>The FMOD_RESULT for updating all of the SoundContainer's sounds' properties. If it's not FMOD_OK, something went wrong.</returns>
306+
FMOD_RESULT UpdateSoundProperties();
307+
#pragma endregion
308+
256309
protected:
257310

258311
static Entity::ClassInfo m_sClass; //!< ClassInfo for this class.
@@ -262,9 +315,13 @@ namespace RTE {
262315

263316
std::unordered_set<unsigned short int> m_PlayingChannels; //!< The channels this SoundContainer is currently using
264317

318+
float m_AttenuationStartDistance; //!< The distance away from the AudioSystem listenter to start attenuating this sound. Attenuation follows FMOD 3D Inverse Rolloff model.
265319
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
266320
int m_Priority; //!< The mixing priority of this SoundContainer's sounds. Higher values are more likely to be heard
267321
bool m_AffectedByGlobalPitch; //!< Whether this SoundContainer's sounds should be able to be altered by global pitch changes
322+
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.
323+
324+
bool m_AllSoundPropertiesUpToDate; //!< 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.
268325

269326
private:
270327
/// <summary>

System/ContentFile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ namespace RTE {
250250
soundInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
251251
soundInfo.length = fileSize;
252252
//TODO Consider doing FMOD_CREATESAMPLE for dumping audio files into memory and FMOD_NONBLOCKING to async create sounds
253-
FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(pRawData, FMOD_OPENMEMORY, &soundInfo, &pReturnSample);
253+
FMOD_RESULT result = g_AudioMan.GetAudioSystem()->createSound(pRawData, FMOD_OPENMEMORY | FMOD_3D, &soundInfo, &pReturnSample);
254254

255255
if (result != FMOD_OK) {
256256
errorMessage = "Unable to create sound because of FMOD error " + std::string(FMOD_ErrorString(result)) + ". Path and name was: ";

0 commit comments

Comments
 (0)