audio2dxnamonogamexna-4.0

3D audio not working in 2D game after switching from XNA to MonoGame


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:

Could anybody help? Would be really appreciated, thank you!


Solution

  • 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.