This is a problem I have never managed to solve for a 2D game I have ported to MonoGame 3.7.1 from XNA 4.0. The project is set up as DesktopGL, and uses .NET Framework 4.8.
In the XNA version, 3D audio worked perfectly and you could tell straight away where the origin of the sound was located spatially. With MonoGame, every sound seems to be coming from the center.
The relevant code is exactly the same in both instances:
public void setSound(SoundEffectInstance sound, object owner, AudioListener listener, AudioEmitter emitter, double startTime, bool looping = false, float volumeModifier = 1)
{
currentSound = sound;
soundOwner = owner;
startTimeOfCurrentSound = startTime;
initialVolume = MathHelper.Clamp(AudioSettings.SFX_VOLUME * volumeModifier, 0, 1);
targetVolume = initialVolume;
transitionStartVolume = initialVolume;
currentSound.Volume = targetVolume;
currentSound.IsLooped = looping;
if (listener != null && emitter != null)
{
if (Vector3.Distance(listener.Position, emitter.Position) > MIN_DISTANCE_SCREEN_UNITS)
{
currentSound.Apply3D(listener, emitter);
//Console.WriteLine("over threshold! listener = " + listener.Position + ", emitter = " + emitter.Position + ", distance = " + Vector3.Distance(listener.Position, emitter.Position));
}
else
{
Vector3 savedPosition = emitter.Position;
emitter.Position = new Vector3(listener.Position.X, listener.Position.Y, ConvertUnits.ToDisplayUnits(5));
currentSound.Apply3D(listener, emitter);
//Console.WriteLine("under threshold! listener = " + listener.Position + ", emitter = " + emitter.Position + ", distance = " + Vector3.Distance(listener.Position, emitter.Position));
emitter.Position = savedPosition;
}
}
currentSound.Play();
}
Notes:
SFX to be played: audio/sfx/environment/sfx_enemy_alert
over threshold! listener = {X:19 Y:182,8 Z:0}, emitter = {X:115,5864 Y:147,08 Z:0}, distance = 102,9799
SFX to be played: audio/sfx/wpn/sfx_gun_shot_001
under threshold! listener = {X:19 Y:148,08 Z:0}, emitter = {X:19 Y:148,08 Z:80}, distance = 80
SFX to be played: audio/sfx/characters/generic/sfx_bullethit
over threshold! listener = {X:19 Y:160,3001 Z:0}, emitter = {X:115,5864 Y:147,08 Z:0}, distance = 97,48696
SFX to be played: audio/sfx/wpn/sfx_gun_shot_002
over threshold! listener = {X:19 Y:195,0798 Z:0}, emitter = {X:104,25 Y:140 Z:0}, distance = 101,4956
EDIT: Forgot to mention that SoundEffect.DistanceScale was set to 100, but I tried different values (ranging from 0.00001 to 100000) with nothing changing.
What I also tried:
I am aware that MonoGame, unlike XNA, requires the sound effect files (which are in .ogg format, not sure if relevant) to be mono, and I have verified they indeed are mono (1 channel at 48kHz).
I have also heard there may be some issue with the scaling if the distances are above 100 units (which is about the average value I get when I uncomment the console output, which again was fine with XNA), but I have also tried rescaling the positions to make them smaller. Nothing changed.
Could anybody help? Would be really appreciated, thank you!
I figured it out by checking the source code of MonoGame 3.7.1.
The call to Apply3D for OpenAL returns immediately if the HasSourceId property for the SoundEffectInstance is false. So I checked with the debugger and in my case, indeed, it was, so Apply3D was not doing anything.
Earlier today I had stumbled upon an unrelated issue which mentioned that SoundEffectInstances were only given a SourceId when played, and not at their creation. So I just moved the call to Play() before Apply3D() and BAM, it worked.
Hope this helps anybody else dealing with the same issue.