diff --git a/OpenRa.Game/Sound.cs b/OpenRa.Game/Sound.cs index 1543f16239..6956d2a43f 100644 --- a/OpenRa.Game/Sound.cs +++ b/OpenRa.Game/Sound.cs @@ -4,6 +4,7 @@ using OpenRa.FileFormats; using OpenRa.Traits; using OpenRa.Support; using System.Runtime.InteropServices; +using System.Collections.Generic; namespace OpenRa { @@ -128,6 +129,8 @@ namespace OpenRa class OpenAlSoundEngine : ISoundEngine { float volume = 1f; + Dictionary sourcePool = new Dictionary(); + const int POOL_SIZE = 100; public OpenAlSoundEngine() { @@ -137,6 +140,46 @@ namespace OpenRa throw new InvalidOperationException("Can't create OpenAL device"); var ctx = OpenAlInterop.alcCreateContext(dev, IntPtr.Zero); OpenAlInterop.alcMakeContextCurrent(ctx); + + int[] sources = new int[POOL_SIZE]; + IntPtr pTemp = Marshal.AllocHGlobal(sizeof(int) * POOL_SIZE); + OpenAlInterop.alGenSources(POOL_SIZE, pTemp); + Marshal.Copy(pTemp, sources, 0, POOL_SIZE); + Marshal.FreeHGlobal(pTemp); + + foreach (int source in sources) + sourcePool.Add(source, false); + } + + int GetSourceFromPool() + { + foreach (var kvp in sourcePool) + { + if (!kvp.Value) + { + sourcePool[kvp.Key] = true; + return kvp.Key; + } + } + + List freeSources = new List(); + foreach (int key in sourcePool.Keys) + { + int state; + OpenAlInterop.alGetSourcei(key, OpenAlInterop.AL_SOURCE_STATE, out state); + if (state != OpenAlInterop.AL_PLAYING) + freeSources.Add(key); + } + + if (freeSources.Count == 0) + return -1; + + foreach (int i in freeSources) + sourcePool[i] = false; + + sourcePool[freeSources[0]] = true; + + return freeSources[0]; } public ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate) @@ -146,7 +189,8 @@ namespace OpenRa public ISound Play2D(ISoundSource sound, bool loop) { - return new OpenAlSound((sound as OpenAlSoundSource).buffer, loop); + int source = GetSourceFromPool(); + return new OpenAlSound(source, (sound as OpenAlSoundSource).buffer, loop); } public float Volume @@ -177,12 +221,13 @@ namespace OpenRa class OpenAlSound : ISound { - public readonly int source; + public readonly int source = -1; float volume = 1f; - public OpenAlSound(int buffer, bool looping) + public OpenAlSound(int source, int buffer, bool looping) { - OpenAlInterop.alGenSources(1, out source); + if (source == -1) return; + this.source = source; OpenAlInterop.alSourcef(source, OpenAlInterop.AL_PITCH, 1f); OpenAlInterop.alSourcef(source, OpenAlInterop.AL_GAIN, 1f); OpenAlInterop.alSource3f(source, OpenAlInterop.AL_POSITION, 0f, 0f, 0f); @@ -195,7 +240,11 @@ namespace OpenRa public float Volume { get { return volume; } - set { OpenAlInterop.alSourcef(source, OpenAlInterop.AL_GAIN, volume = value); } + set + { + if (source != -1) + OpenAlInterop.alSourcef(source, OpenAlInterop.AL_GAIN, volume = value); + } } } } diff --git a/OpenRa.Game/Support/OpenAlInterop.cs b/OpenRa.Game/Support/OpenAlInterop.cs index 73e4025425..85336d5e00 100644 --- a/OpenRa.Game/Support/OpenAlInterop.cs +++ b/OpenRa.Game/Support/OpenAlInterop.cs @@ -8,25 +8,30 @@ namespace OpenRa.Support { public static class OpenAlInterop { - public static readonly int AL_GAIN = 0x100A; + public const int AL_GAIN = 0x100A; - public static readonly int AL_FORMAT_MONO8 = 0x1100; - public static readonly int AL_FORMAT_MONO16 = 0x1101; - public static readonly int AL_FORMAT_STEREO8 = 0x1102; - public static readonly int AL_FORMAT_STEREO16 = 0x1103; + public const int AL_FORMAT_MONO8 = 0x1100; + public const int AL_FORMAT_MONO16 = 0x1101; + public const int AL_FORMAT_STEREO8 = 0x1102; + public const int AL_FORMAT_STEREO16 = 0x1103; - public static readonly int AL_PITCH = 0x1003; + public const int AL_PITCH = 0x1003; - public static readonly int AL_POSITION = 0x1004; - public static readonly int AL_DIRECTION = 0x1005; - public static readonly int AL_VELOCITY = 0x1006; + public const int AL_POSITION = 0x1004; + public const int AL_DIRECTION = 0x1005; + public const int AL_VELOCITY = 0x1006; - public static readonly int AL_LOOPING = 0x1007; - public static readonly int AL_BUFFER = 0x1009; + public const int AL_LOOPING = 0x1007; + public const int AL_BUFFER = 0x1009; - public static readonly int AL_TRUE = 1; - public static readonly int AL_FALSE = 0; + public const int AL_TRUE = 1; + public const int AL_FALSE = 0; + public const int AL_SOURCE_STATE = 0x1010; + public const int AL_INITIAL = 0x1011; + public const int AL_PLAYING = 0x1012; + public const int AL_PAUSED = 0x1013; + public const int AL_STOPPED = 0x1014; [DllImport("OpenAL32.dll")] public static extern IntPtr alcOpenDevice(IntPtr deviceName); @@ -44,11 +49,10 @@ namespace OpenRa.Support public static extern void alGenBuffers(int n, out int buffers); [DllImport("OpenAL32.dll")] - public static extern void alBufferData(int buffer, int format, - [MarshalAs(UnmanagedType.LPArray)] byte[] data, int size, int freq); + public static extern void alBufferData(int buffer, int format, byte[] data, int size, int freq); [DllImport("OpenAL32.dll")] - public static extern void alGenSources(int n, out int sources); + public static extern void alGenSources(int n, IntPtr sources); [DllImport("OpenAL32.dll")] public static extern void alSourcef(int source, int param, float value); @@ -59,6 +63,10 @@ namespace OpenRa.Support [DllImport("OpenAL32.dll")] public static extern void alSourcei(int source, int param, int value); + [DllImport("OpenAL32.dll")] + public static extern void alGetSourcei(int source, int param, out int value); + + [DllImport("OpenAL32.dll")] public static extern void alSourcePlay(int source); }