From bc7cef4596fadf8e1a429574001ddd66966475c0 Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Thu, 29 Aug 2024 14:49:19 +0100 Subject: [PATCH] Try to fix OpenAL SoundEffectInstance Panning Issue Fixes #6876 Fixes #6543 So OpenAL has an outstanding issue where is does not support stereo panning (see https://github.com/kcat/openal-soft/issues/194) They do however have an extension `AL_EXT_STEREO_ANGLES` which can produce a similar effect. So lets make use of this extension if it is supported to support panning of stereo audio. --- .../MonoGame.Framework.Android.csproj | 19 +++--------- .../MonoGame.Framework.DesktopGL.csproj | 22 +------------ .../MonoGame.Framework.iOS.csproj | 4 +++ MonoGame.Framework/Platform/Audio/OpenAL.cs | 22 ++++++++----- .../Platform/Audio/OpenALSoundController.cs | 4 +++ .../Audio/SoundEffectInstance.OpenAL.cs | 31 +++++++++++++------ 6 files changed, 49 insertions(+), 53 deletions(-) diff --git a/MonoGame.Framework/MonoGame.Framework.Android.csproj b/MonoGame.Framework/MonoGame.Framework.Android.csproj index 8633e13db3a..f291a2a8da3 100644 --- a/MonoGame.Framework/MonoGame.Framework.Android.csproj +++ b/MonoGame.Framework/MonoGame.Framework.Android.csproj @@ -12,6 +12,10 @@ False + + + + @@ -78,21 +82,6 @@ - - - libs\armeabi-v7a\libopenal32.so - - - libs\arm64-v8a\libopenal32.so - - - libs\x86\libopenal32.so - - - libs\x86_64\libopenal32.so - - - diff --git a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj index 46b5fefb124..4965afb7636 100644 --- a/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj +++ b/MonoGame.Framework/MonoGame.Framework.DesktopGL.csproj @@ -18,6 +18,7 @@ all + @@ -88,27 +89,6 @@ - - x86\soft_oal.dll - runtimes\win-x86\native - PreserveNewest - - - x64\soft_oal.dll - runtimes\win-x64\native - PreserveNewest - - - x64\libopenal.so.1 - runtimes\linux-x64\native - PreserveNewest - - - libopenal.1.dylib - runtimes\osx\native - PreserveNewest - - diff --git a/MonoGame.Framework/MonoGame.Framework.iOS.csproj b/MonoGame.Framework/MonoGame.Framework.iOS.csproj index e98961d9b94..f9fff495204 100644 --- a/MonoGame.Framework/MonoGame.Framework.iOS.csproj +++ b/MonoGame.Framework/MonoGame.Framework.iOS.csproj @@ -12,6 +12,10 @@ 11.2 + + + + diff --git a/MonoGame.Framework/Platform/Audio/OpenAL.cs b/MonoGame.Framework/Platform/Audio/OpenAL.cs index 59d1798e0ad..87169b774b9 100644 --- a/MonoGame.Framework/Platform/Audio/OpenAL.cs +++ b/MonoGame.Framework/Platform/Audio/OpenAL.cs @@ -69,7 +69,8 @@ internal enum ALSourcef { Pitch = 0x1003, Gain = 0x100A, - ReferenceDistance = 0x1020 + ReferenceDistance = 0x1020, + StereoAngles = 0x1030 } internal enum ALGetSourcei @@ -197,28 +198,31 @@ private static IntPtr GetNativeLibrary() { #if DESKTOPGL if (CurrentPlatform.OS == OS.Windows) - return FuncLoader.LoadLibraryExt("soft_oal.dll"); + return FuncLoader.LoadLibraryExt("openal.dll"); else if (CurrentPlatform.OS == OS.Linux) - return FuncLoader.LoadLibraryExt("libopenal.so.1"); + return FuncLoader.LoadLibraryExt("libopenal.so"); else if (CurrentPlatform.OS == OS.MacOSX) - return FuncLoader.LoadLibraryExt("libopenal.1.dylib"); + return FuncLoader.LoadLibraryExt("libopenal.dylib"); else return FuncLoader.LoadLibraryExt("openal"); #elif ANDROID - var ret = FuncLoader.LoadLibrary("libopenal32.so"); + var ret = FuncLoader.LoadLibrary("libopenal.so"); if (ret == IntPtr.Zero) { var appFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal); var appDir = Path.GetDirectoryName(appFilesDir); - var lib = Path.Combine(appDir, "lib", "libopenal32.so"); + var lib = Path.Combine(appDir, "lib", "libopenal.so"); ret = FuncLoader.LoadLibrary(lib); } return ret; #else - return FuncLoader.LoadLibrary("/System/Library/Frameworks/OpenAL.framework/OpenAL"); + var ret = FuncLoader.LoadLibrary("libopenal.dylib"); + if (ret ==IntPtr.Zero) + ret = FuncLoader.LoadLibrary(Path.Combine (Path.GetDirectoryName (typeof (AL).Assembly.Location), "libopenal.dylib")); + return ret; #endif } @@ -422,6 +426,10 @@ internal static void Source(int sourceId, ALSourcef i, float dist) internal delegate void d_alsource3f(int sourceId, ALSource3f i, float x, float y, float z); internal static d_alsource3f alSource3f = FuncLoader.LoadFunction(NativeLibrary, "alSource3f"); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void d_alsourcefv(int sourceId, ALSourcef i, float[] values); + internal static d_alsourcefv alSourcefv = FuncLoader.LoadFunction(NativeLibrary, "alSourcefv"); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void d_algetsourcei(int sourceId, ALGetSourcei i, out int state); internal static d_algetsourcei GetSource = FuncLoader.LoadFunction(NativeLibrary, "alGetSourcei"); diff --git a/MonoGame.Framework/Platform/Audio/OpenALSoundController.cs b/MonoGame.Framework/Platform/Audio/OpenALSoundController.cs index 8cba5113241..844e6320abc 100644 --- a/MonoGame.Framework/Platform/Audio/OpenALSoundController.cs +++ b/MonoGame.Framework/Platform/Audio/OpenALSoundController.cs @@ -103,6 +103,8 @@ internal sealed class OpenALSoundController : IDisposable public bool SupportsEfx { get; private set; } public bool SupportsIeee { get; private set; } + public bool SupportsStereoAngles { get; private set;} + /// /// Sets up the hardware resources used by the controller. /// @@ -119,6 +121,8 @@ private OpenALSoundController() if (Alc.IsExtensionPresent(_device, "ALC_EXT_CAPTURE")) Microphone.PopulateCaptureDevices(); + SupportsStereoAngles = AL.IsExtensionPresent ("AL_EXT_STEREO_ANGLES"); + // We have hardware here and it is ready allSourcesArray = new int[MAX_NUMBER_OF_SOURCES]; diff --git a/MonoGame.Framework/Platform/Audio/SoundEffectInstance.OpenAL.cs b/MonoGame.Framework/Platform/Audio/SoundEffectInstance.OpenAL.cs index da226f350ca..db5407f220b 100644 --- a/MonoGame.Framework/Platform/Audio/SoundEffectInstance.OpenAL.cs +++ b/MonoGame.Framework/Platform/Audio/SoundEffectInstance.OpenAL.cs @@ -21,6 +21,8 @@ public partial class SoundEffectInstance : IDisposable float frequency; int pauseCount; + float[] panAngles = new float[2]; + internal readonly object sourceMutex = new object(); internal OpenALSoundController controller; @@ -123,20 +125,16 @@ private void PlatformPlay() AL.DistanceModel (ALDistanceModel.InverseDistanceClamped); ALHelper.CheckError("Failed set source distance."); // Pan - AL.Source (SourceId, ALSource3f.Position, _pan, 0f, 0f); - ALHelper.CheckError("Failed to set source pan."); + PlatformSetPan (_pan); // Velocity AL.Source (SourceId, ALSource3f.Velocity, 0f, 0f, 0f); ALHelper.CheckError("Failed to set source pan."); // Volume - AL.Source(SourceId, ALSourcef.Gain, _alVolume); - ALHelper.CheckError("Failed to set source volume."); + PlatformSetVolume (_alVolume); // Looping - AL.Source (SourceId, ALSourceb.Looping, IsLooped); - ALHelper.CheckError("Failed to set source loop state."); + PlatformSetIsLooped (IsLooped); // Pitch - AL.Source (SourceId, ALSourcef.Pitch, XnaPitchToAlPitch(_pitch)); - ALHelper.CheckError("Failed to set source pitch."); + PlatformSetPitch (_pitch); ApplyReverb (); ApplyFilter (); @@ -219,18 +217,31 @@ private bool PlatformGetIsLooped() private void PlatformSetPan(float value) { + _pan = value; + if (HasSourceId) { - AL.Source(SourceId, ALSource3f.Position, value, 0.0f, 0.1f); + AL.Source(SourceId, ALSource3f.Position, _pan, 0.0f, -1f); ALHelper.CheckError("Failed to set source pan."); + if (controller.SupportsStereoAngles) + { + // pan between -60 degrees when fully left (-1) and 60 degrees when fully right (1) + float angle = (float)Math.PI / 6f; + panAngles[0] = (1.0f - _pan) * angle; + panAngles[1]= (1.0f + _pan) * -angle; + AL.alSourcefv(SourceId, ALSourcef.StereoAngles, panAngles); + ALHelper.CheckError("Failed to set source position."); + } } } private void PlatformSetPitch(float value) { + _pitch = value; + if (HasSourceId) { - AL.Source(SourceId, ALSourcef.Pitch, XnaPitchToAlPitch(value)); + AL.Source(SourceId, ALSourcef.Pitch, XnaPitchToAlPitch(_pitch)); ALHelper.CheckError("Failed to set source pitch."); } }