diff --git a/Framework/Audio/Audio.cs b/Framework/Audio/Audio.cs index aa1870c..ad45bea 100644 --- a/Framework/Audio/Audio.cs +++ b/Framework/Audio/Audio.cs @@ -1,23 +1,30 @@ using System; +using System.IO; namespace Foster.Framework { /// /// The Core Audio Module, used for playing sounds /// - public abstract class Audio : Module + public abstract class Audio : AppModule { public string ApiName { get; protected set; } = "Unknown"; public Version ApiVersion { get; protected set; } = new Version(0, 0, 0); - protected Audio() : base(400) - { + internal readonly AudioSourcePool AudioSourcePool = new AudioSourcePool(); - } + protected internal abstract AudioSource.Platform CreateAudioSource(); + + protected Audio() : base(400) { } + + /// + /// The Renderer this Audio Module implements + /// + public abstract Renderer Renderer { get; } - protected internal override void Startup() + protected internal sealed override void Update() { - Console.WriteLine($" - Audio {ApiName} {ApiVersion}"); + AudioSourcePool.Update(); } } -} +} \ No newline at end of file diff --git a/Framework/Audio/AudioChannel.cs b/Framework/Audio/AudioChannel.cs new file mode 100644 index 0000000..d59db33 --- /dev/null +++ b/Framework/Audio/AudioChannel.cs @@ -0,0 +1,9 @@ +namespace Foster.Framework +{ + public enum AudioChannel + { + None = 0, + Mono = 1, + Stereo = 2 + } +} \ No newline at end of file diff --git a/Framework/Audio/AudioException.cs b/Framework/Audio/AudioException.cs new file mode 100644 index 0000000..dcb0f2b --- /dev/null +++ b/Framework/Audio/AudioException.cs @@ -0,0 +1,13 @@ +using System; + +namespace Foster.Framework +{ + public class AudioException : Exception + { + public AudioException() { } + + public AudioException(string message) : base(message) { } + + public AudioException(string message, Exception innerException) : base(message, innerException) { } + } +} \ No newline at end of file diff --git a/Framework/Audio/AudioSource.cs b/Framework/Audio/AudioSource.cs new file mode 100644 index 0000000..6e9418a --- /dev/null +++ b/Framework/Audio/AudioSource.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; + +namespace Foster.Framework +{ + public class AudioSource : IDisposable + { + public abstract class Platform + { + protected internal abstract void Init(AudioSource source, Stream stream); + + protected internal abstract float Volume { get; set; } + protected internal abstract float Pitch { get; set; } + protected internal abstract bool Loop { get; set; } + + protected internal abstract AudioState GetState(); + + protected internal abstract void Pause(); + protected internal abstract void Play(); + protected internal abstract void Resume(); + + protected internal abstract void Rewind(); + + protected internal abstract void Stop(); + + + protected internal abstract void Dispose(); + } + + private readonly Platform implementation; + + private readonly Audio audio; + + internal bool IsPooled = false; + + public float Volume + { + get => implementation.Volume; + set => implementation.Volume = value; + } + + public float Pitch + { + get => implementation.Pitch; + set => implementation.Pitch = value; + } + + public bool Loop + { + get => implementation.Loop; + set => implementation.Loop = value; + } + + public AudioSource(Audio audio, Stream stream) + { + this.audio = audio; + implementation = audio.CreateAudioSource(); + implementation.Init(this, stream); + } + + public AudioSource(Stream stream) + : this(App.Audio, stream) + { + } + + public AudioSource(string fileName) + : this(App.Audio, File.Open(fileName, FileMode.Open)) + { + } + + public AudioState GetState() + { + return implementation.GetState(); + } + + public void Pause() + { + implementation.Pause(); + } + + public void Play() + { + + implementation.Play(); + } + + public void Resume() + { + implementation.Resume(); + } + + public void Stop() + { + implementation.Stop(); + } + + public void Dispose() + { + implementation.Dispose(); + } + } +} \ No newline at end of file diff --git a/Framework/Audio/AudioSourcePool.cs b/Framework/Audio/AudioSourcePool.cs new file mode 100644 index 0000000..2971e1f --- /dev/null +++ b/Framework/Audio/AudioSourcePool.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Concurrent; +using System.IO; + +namespace Foster.Framework +{ + internal class AudioSourcePool : IDisposable + { + private readonly ConcurrentStack availableSources; + private readonly ConcurrentHashSet playingSources; + + public int Count => availableSources.Count + playingSources.Count; + + public AudioSourcePool() + { + availableSources = new ConcurrentStack(); + playingSources = new ConcurrentHashSet(); + } + + public AudioSource Reserve(Audio audio, Stream stream) + { + if (!availableSources.TryPop(out var source)) + { + source = new AudioSource(audio, stream) + { + IsPooled = true + }; + } + + playingSources.Add(source); + return source; + } + + public void Free(AudioSource source) + { + if (!playingSources.TryRemove(source)) + { + Log.Error("Audio source is not pooled"); + } + } + + public void Update() + { + foreach (var source in playingSources) + { + if (source.GetState() == AudioState.Stopped) + { + if (playingSources.TryRemove(source)) + { + availableSources.Push(source); + } + else + { + Log.Message("Failed to remove source; possible race condition"); + } + } + } + } + + public void Dispose() + { + foreach (var source in playingSources) + { + source.Dispose(); + } + + foreach (var source in availableSources) + { + source.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/Framework/Audio/AudioState.cs b/Framework/Audio/AudioState.cs new file mode 100644 index 0000000..54cff3b --- /dev/null +++ b/Framework/Audio/AudioState.cs @@ -0,0 +1,10 @@ +namespace Foster.Framework +{ + public enum AudioState + { + Unknown, + Playing, + Paused, + Stopped + } +} \ No newline at end of file diff --git a/Framework/Audio/IAudioOpenAL.cs b/Framework/Audio/IAudioOpenAL.cs new file mode 100644 index 0000000..f3dab7f --- /dev/null +++ b/Framework/Audio/IAudioOpenAL.cs @@ -0,0 +1,10 @@ +namespace Foster.Framework +{ + /// + /// An Implementation of the Audio Module that supports the OpenAL API + /// + public interface IAudioOpenAL + { + + } +} \ No newline at end of file diff --git a/Framework/Audio/Wave.cs b/Framework/Audio/Wave.cs new file mode 100644 index 0000000..add7fbc --- /dev/null +++ b/Framework/Audio/Wave.cs @@ -0,0 +1,263 @@ +using System; +using System.IO; + +namespace Foster.Framework +{ + public static class Wave + { + public enum Format : short + { + None = 0, + PCM = 1, + MSADPCM = 2, + IEEE = 3, + IMA4 = 17 + } + + private static short ReadSample8(byte[] buffer, int index) + { + return (short)(((buffer[index] - sbyte.MaxValue) / (float)sbyte.MaxValue) * short.MaxValue); + } + + private static short ReadSample16(byte[] buffer, int i) + { + return (short)(buffer[i] | (buffer[i + 1] << 8)); + } + + private static short ReadSample24(byte[] buffer, int index) + { + var value = buffer[index] | (buffer[index + 1] << 8) | (buffer[index + 2] << 16); + + var normalized = value / (float)(1 << 23); + return (short)(normalized * short.MaxValue); + } + + private static short ReadSample32(byte[] buffer, int index) + { + var value = buffer[index] | + ((uint)buffer[index + 1] << 8) | + ((uint)buffer[index + 2] << 16) | + ((uint)buffer[index + 3] << 24); + + var normalized = value / (double)int.MaxValue; + return (short)(normalized * short.MaxValue); + } + + private static short ReadSample64(byte[] buffer, int index) + { + var value = buffer[index] | + ((long)buffer[index + 1] << 8) | + ((long)buffer[index + 2] << 16) | + ((long)buffer[index + 3] << 24) | + ((long)buffer[index + 4] << 32) | + ((long)buffer[index + 5] << 40) | + ((long)buffer[index + 6] << 48) | + ((long)buffer[index + 7] << 56); + + var normalized = value / (double)long.MaxValue; + return (short)(normalized * short.MaxValue); + } + + public static int GetSampleAlignment(Format format, AudioChannel channels, int blockAlignment) + { + return format switch + { + Format.IMA4 => ((((blockAlignment - (4 / (int)channels)) / 4) * 8) + 1), + Format.MSADPCM => ((((blockAlignment / (int)channels) - 7) * 2) + 2), + _ => 0 + }; + } + + public static bool TryLoad( + Stream stream, + out byte[] buffer, + out Format format, + out int frequency, + out AudioChannel channels, + out int blockAlignment, + out int bitsPerSample, + out int samplesPerBlock, + out int sampleCount) + { + buffer = Array.Empty(); + format = Format.None; + frequency = 0; + channels = AudioChannel.None; + blockAlignment = 0; + bitsPerSample = 0; + samplesPerBlock = 0; + sampleCount = 0; + + using var reader = new BinaryReader(stream); + + var signature = new string(reader.ReadChars(4)); + if (signature != "RIFF") + { + Log.Error("Stream is not a wave file"); + return false; + } + + reader.BaseStream.Position += 4; // Skip RIFF chunksize + + var waveFormat = new string(reader.ReadChars(4)); + if (waveFormat != "WAVE") + { + Log.Error("Stream is not a wave file"); + buffer = Array.Empty(); + return false; + } + + var streamLength = reader.BaseStream.Length; + var bufferFilled = false; + while (!bufferFilled) + { + var chunkId = new string(reader.ReadChars(4)); + var chunkSize = reader.ReadInt32(); + + if ((reader.BaseStream.Position + chunkSize) > streamLength) + { + Log.Error("Wave format header is invalid"); + return false; + } + + switch (chunkId) + { + case "fmt ": + { + format = (Format)reader.ReadInt16(); + chunkSize -= 2; + + if (!Enum.IsDefined(typeof(Format), format)) + { + Log.Error($"Wave format is not recognized: {format}"); + return false; + } + + channels = (AudioChannel)reader.ReadInt16(); + chunkSize -= 2; + + if ((channels != AudioChannel.Mono) && (channels != AudioChannel.Stereo)) + { + Log.Error($"Wave format does not support {channels} channels"); + return false; + } + + frequency = reader.ReadInt32(); + chunkSize -= 4; + + reader.BaseStream.Position += 4; // Skip fmt ByteRate + chunkSize -= 4; + + blockAlignment = reader.ReadInt16(); + chunkSize -= 2; + + bitsPerSample = reader.ReadInt16(); + chunkSize -= 2; + + if (chunkSize > 0) + { + if (format != Format.PCM) + { + var extraDataSize = reader.ReadInt16(); + if (format == Format.IMA4) + { + samplesPerBlock = reader.ReadInt16(); + extraDataSize -= 2; + } + + reader.BaseStream.Position += extraDataSize; + } + else + { + reader.BaseStream.Position += chunkSize; + } + } + + break; + } + case "fact": + { + if (format == Format.IMA4) + { + sampleCount = reader.ReadInt32() * (int)channels; + chunkSize -= 4; + } + + reader.BaseStream.Position += chunkSize; + break; + } + case "data": + { + buffer = reader.ReadBytes(chunkSize); + bufferFilled = true; + break; + } + default: + { + reader.BaseStream.Position += chunkSize; + break; + } + } + } + + if (samplesPerBlock == 0) + { + samplesPerBlock = GetSampleAlignment(format, channels, blockAlignment); + } + + if (sampleCount == 0) + { + switch (format) + { + case Format.IMA4: + case Format.MSADPCM: + { + sampleCount = ((buffer.Length / blockAlignment) * samplesPerBlock) + + GetSampleAlignment(format, channels, buffer.Length % blockAlignment); + + break; + } + case Format.PCM: + case Format.IEEE: + { + sampleCount = buffer.Length / (((int)channels * bitsPerSample) / 8); + break; + } + default: + { + Log.Error($"Wave format is not recognized: {format}"); + return false; + } + } + } + + return true; + } + + public static short[] ConvertPCM(byte[] buffer, int bitsPerSample) + { + if (bitsPerSample < 8 || bitsPerSample > 64 || (bitsPerSample & 7) > 0) + { + Log.Error($"Bitrate of {bitsPerSample.ToString()} is not supported by PCM"); + return Array.Empty(); + } + + var bytesPerSample = bitsPerSample / 8; + var outBuffer = new short[buffer.Length / bytesPerSample]; + for (var i = 0; i < outBuffer.Length; ++i) + { + outBuffer[i] = bitsPerSample switch + { + 8 => ReadSample8(buffer, i * bytesPerSample), + 16 => ReadSample16(buffer, i * bytesPerSample), + 24 => ReadSample24(buffer, i * bytesPerSample), + 32 => ReadSample32(buffer, i * bytesPerSample), + 64 => ReadSample64(buffer, i * bytesPerSample), + _ => 0 + }; + } + + return outBuffer; + } + } +} \ No newline at end of file diff --git a/Platforms/OpenAL/AL.cs b/Platforms/OpenAL/AL.cs new file mode 100644 index 0000000..e731425 --- /dev/null +++ b/Platforms/OpenAL/AL.cs @@ -0,0 +1,553 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + internal static class AL + { + private const string DLL = "openal32.dll"; + internal const CallingConvention ALCallingConvention = CallingConvention.Cdecl; + + [DllImport(DLL, EntryPoint = "alEnable", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Enable(ALCapability capability); + + [DllImport(DLL, EntryPoint = "alDisable", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Disable(ALCapability capability); + + [DllImport(DLL, EntryPoint = "alIsEnabled", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern bool IsEnabled(ALCapability capability); + + [DllImport(DLL, EntryPoint = "alGetString", ExactSpelling = true, CallingConvention = ALCallingConvention, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))] + public static extern string Get(ALGetString param); + + public static string GetErrorString(ALError param) => Get((ALGetString)param); + + [DllImport(DLL, EntryPoint = "alGetInteger", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern int Get(ALGetInteger param); + + [DllImport(DLL, EntryPoint = "alGetFloat", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern float Get(ALGetFloat param); + + [DllImport(DLL, EntryPoint = "alGetError", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern ALError GetError(); + + [DllImport(DLL, EntryPoint = "alIsExtensionPresent", ExactSpelling = true, CallingConvention = ALCallingConvention, CharSet = CharSet.Ansi)] + public static extern bool IsExtensionPresent([In] string extname); + + [DllImport(DLL, EntryPoint = "alGetProcAddress", ExactSpelling = true, CallingConvention = ALCallingConvention, CharSet = CharSet.Ansi)] + public static extern IntPtr GetProcAddress([In] string fname); + + [DllImport(DLL, EntryPoint = "alGetEnumValue", ExactSpelling = true, CallingConvention = ALCallingConvention, CharSet = CharSet.Ansi)] + public static extern int GetEnumValue([In] string ename); + + [DllImport(DLL, EntryPoint = "alListenerf", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Listener(ALListenerf param, float value); + + [DllImport(DLL, EntryPoint = "alListener3f", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Listener(ALListener3f param, float value1, float value2, float value3); + + public static void Listener(ALListener3f param, ref Vector3 values) + { + Listener(param, values.X, values.Y, values.Z); + } + + [DllImport(DLL, EntryPoint = "alListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void Listener(ALListenerfv param, float* values); + + [DllImport(DLL, EntryPoint = "alListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Listener(ALListenerfv param, ref float values); + + [DllImport(DLL, EntryPoint = "alListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Listener(ALListenerfv param, float[] values); + + public static void Listener(ALListenerfv param, ref Vector3 at, ref Vector3 up) + { + Span data = stackalloc float[6]; + + data[0] = at.X; + data[1] = at.Y; + data[2] = at.Z; + + data[3] = up.X; + data[4] = up.Y; + data[5] = up.Z; + + Listener(param, ref data[0]); + } + + [DllImport(DLL, EntryPoint = "alGetListenerf", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetListener(ALListenerf param, [Out] out float value); + + [DllImport(DLL, EntryPoint = "alGetListener3f", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetListener(ALListener3f param, [Out] out float value1, [Out] out float value2, [Out] out float value3); + + public static void GetListener(ALListener3f param, out Vector3 values) + { + GetListener(param, out values.X, out values.Y, out values.Z); + } + + [DllImport(DLL, EntryPoint = "alGetListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void GetListener(ALListenerfv param, float* values); + + [DllImport(DLL, EntryPoint = "alGetListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void GetListener(ALListenerfv param, ref float values); + + [DllImport(DLL, EntryPoint = "alGetListenerfv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetListener(ALListenerfv param, [In] float[] values); + + public static void GetListener(ALListenerfv param, out Vector3 at, out Vector3 up) + { + Span values = stackalloc float[6]; + GetListener(param, ref values[0]); + + at.X = values[0]; + at.Y = values[1]; + at.Z = values[2]; + + up.X = values[3]; + up.Y = values[4]; + up.Z = values[5]; + } + + + /* Source + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM buffer provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Pitch AL_PITCH ALfloat + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + + [DllImport(DLL, EntryPoint = "alGenSources", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void GenSources(int n, [In] int* sources); + + [DllImport(DLL, EntryPoint = "alGenSources", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GenSources(int n, ref int sources); + + [DllImport(DLL, EntryPoint = "alGenSources", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GenSources(int n, int[] sources); + + public static void GenSources(int[] sources) + { + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + GenSources(sources.Length, sources); + } + + public static void GenSources(Span sources) + { + GenSources(sources.Length, ref sources[0]); + } + + public static int[] GenSources(int n) + { + int[] sources = new int[n]; + GenSources(n, sources); + return sources; + } + + public static int GenSource() + { + int source = 0; + GenSources(1, ref source); + return source; + } + + public static void GenSource(out int source) + { + int newSource = 0; + GenSources(1, ref newSource); + source = newSource; + } + + [DllImport(DLL, EntryPoint = "alDeleteSources", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void DeleteSources(int n, [In] int* sources); + + [DllImport(DLL, EntryPoint = "alDeleteSources", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DeleteSources(int n, ref int sources); + + public static void DeleteSources(int[] sources) + { + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + DeleteSources(sources.Length, ref sources[0]); + } + + public static void DeleteSources(Span sources) + { + DeleteSources(sources.Length, ref sources[0]); + } + + public static void DeleteSource(int source) + { + DeleteSources(1, ref source); + } + + [DllImport(DLL, EntryPoint = "alIsSource", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern bool IsSource(int sid); + + [DllImport(DLL, EntryPoint = "alSourcef", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Source(int sid, ALSourcef param, float value); + + [DllImport(DLL, EntryPoint = "alSource3f", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Source(int sid, ALSource3f param, float value1, float value2, float value3); + + public static void Source(int sid, ALSource3f param, ref Vector3 values) + { + Source(sid, param, values.X, values.Y, values.Z); + } + + [DllImport(DLL, EntryPoint = "alSourcei", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Source(int sid, ALSourcei param, int value); + + [DllImport(DLL, EntryPoint = "alSourcei", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Source(int sid, ALSourceb param, bool value); + + public static void BindBufferToSource(int source, int buffer) + { + Source(source, ALSourcei.Buffer, buffer); + } + + [DllImport(DLL, EntryPoint = "alSource3i", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void Source(int sid, ALSource3i param, int value1, int value2, int value3); + + [DllImport(DLL, EntryPoint = "alGetSourcef", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetSource(int sid, ALSourcef param, out float value); + + [DllImport(DLL, EntryPoint = "alGetSource3f", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetSource(int sid, ALSource3f param, out float value1, out float value2, out float value3); + + public static void GetSource(int sid, ALSource3f param, out Vector3 values) + { + GetSource(sid, param, out values.X, out values.Y, out values.Z); + } + + [DllImport(DLL, EntryPoint = "alGetSource3i", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetSource(int sid, ALSource3i param, out int value1, out int value2, out int value3); + + public static void GetSource(int sid, ALSource3i param, out Vector3 values) + { + int x = 0; + int y = 0; + int z = 0; + + GetSource(sid, param, out x, out y, out z); + + values.X = x; + values.Y = y; + values.Z = z; + } + + [DllImport(DLL, EntryPoint = "alGetSourcei", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetSource(int sid, ALGetSourcei param, [Out] out int value); + + public static void GetSource(int sid, ALSourceb param, out bool value) + { + GetSource(sid, (ALGetSourcei)param, out int result); + value = result != 0; + } + + [DllImport(DLL, EntryPoint = "alSourcePlayv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourcePlay(int ns, [In] int* sids); + + [DllImport(DLL, EntryPoint = "alSourcePlayv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePlay(int ns, [In] ref int sids); + + [DllImport(DLL, EntryPoint = "alSourcePlayv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePlay(int ns, [In] int[] sids); + + public static void SourcePlay(Span sources) + { + SourcePlay(sources.Length, ref sources[0]); + } + + [DllImport(DLL, EntryPoint = "alSourceStopv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourceStop(int ns, [In] int* sids); + + [DllImport(DLL, EntryPoint = "alSourceStopv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceStop(int ns, ref int sids); + + [DllImport(DLL, EntryPoint = "alSourceStopv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceStop(int ns, int[] sids); + + public static void SourceStop(Span sources) + { + SourceStop(sources.Length, ref sources[0]); + } + + [DllImport(DLL, EntryPoint = "alSourceRewindv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourceRewind(int ns, [In] int* sids); + + [DllImport(DLL, EntryPoint = "alSourceRewindv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceRewind(int ns, ref int sids); + + [DllImport(DLL, EntryPoint = "alSourceRewindv", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceRewind(int ns, int[] sids); + + public static void SourceRewind(Span sources) + { + SourceRewind(sources.Length, ref sources[0]); + } + + [DllImport(DLL, EntryPoint = "alSourcePausev", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourcePause(int ns, [In] int* sids); + + [DllImport(DLL, EntryPoint = "alSourcePausev", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePause(int ns, ref int sids); + + [DllImport(DLL, EntryPoint = "alSourcePausev", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePause(int ns, int[] sids); + + [DllImport(DLL, EntryPoint = "alSourcePlay", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePlay(int sid); + + [DllImport(DLL, EntryPoint = "alSourceStop", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceStop(int sid); + + [DllImport(DLL, EntryPoint = "alSourceRewind", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceRewind(int sid); + + [DllImport(DLL, EntryPoint = "alSourcePause", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourcePause(int sid); + + [DllImport(DLL, EntryPoint = "alSourceQueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourceQueueBuffers(int sid, int numEntries, [In] int* bids); + + [DllImport(DLL, EntryPoint = "alSourceQueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceQueueBuffers(int sid, int numEntries, int[] bids); + + [DllImport(DLL, EntryPoint = "alSourceQueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceQueueBuffers(int sid, int numEntries, ref int bids); + public static void SourceQueueBuffers(int sid, Span buffers) + { + SourceQueueBuffers(sid, buffers.Length, ref buffers[0]); + } + + public static void SourceQueueBuffer(int source, int buffer) + { + SourceQueueBuffers(source, 1, ref buffer); + } + + [DllImport(DLL, EntryPoint = "alSourceUnqueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void SourceUnqueueBuffers(int sid, int numEntries, int* bids); + + [DllImport(DLL, EntryPoint = "alSourceUnqueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceUnqueueBuffers(int sid, int numEntries, int[] bids); + + [DllImport(DLL, EntryPoint = "alSourceUnqueueBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SourceUnqueueBuffers(int sid, int numEntries, ref int bids); + + public static void SourceUnqueueBuffers(int sid, int[] bids) + { + SourceUnqueueBuffers(sid, bids.Length, bids); + } + + public static void SourceUnqueueBuffers(int sid, Span bids) + { + SourceUnqueueBuffers(sid, bids.Length, ref bids[0]); + } + + public static int SourceUnqueueBuffer(int sid) + { + int buffer = 0; + SourceUnqueueBuffers(sid, 1, ref buffer); + return buffer; + } + + public static int[] SourceUnqueueBuffers(int sid, int numEntries) + { + if (numEntries <= 0) + { + throw new ArgumentOutOfRangeException(nameof(numEntries), "Must be greater than zero."); + } + int[] buf = new int[numEntries]; + SourceUnqueueBuffers(sid, numEntries, buf); + return buf; + } + + /* + * Buffer + * Buffer objects are storage space for sample buffer. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + + [DllImport(DLL, EntryPoint = "alGenBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void GenBuffers(int n, [Out] int* buffers); + + [DllImport(DLL, EntryPoint = "alGenBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GenBuffers(int n, ref int buffers); + + [DllImport(DLL, EntryPoint = "alGenBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GenBuffers(int n, [Out] int[] buffers); + + public static void GenBuffers(Span buffers) + { + GenBuffers(buffers.Length, ref buffers[0]); + } + + public static int[] GenBuffers(int n) + { + int[] buffers = new int[n]; + GenBuffers(buffers.Length, buffers); + return buffers; + } + + public static int GenBuffer() + { + int buffer = 0; + GenBuffers(1, ref buffer); + return buffer; + } + + public static void GenBuffer(out int buffer) + { + int newBuffer = 0; + GenBuffers(1, ref newBuffer); + buffer = newBuffer; + } + + [DllImport(DLL, EntryPoint = "alDeleteBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void DeleteBuffers(int n, [In] int* buffers); + + [DllImport(DLL, EntryPoint = "alDeleteBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DeleteBuffers(int n, [In] ref int buffers); + + [DllImport(DLL, EntryPoint = "alDeleteBuffers", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DeleteBuffers(int n, [In] int[] buffers); + + public static void DeleteBuffers(int[] buffers) + { + if (buffers == null) + { + throw new ArgumentNullException(nameof(buffers)); + } + DeleteBuffers(buffers.Length, buffers); + } + + public static void DeleteBuffers(Span buffers) + { + DeleteBuffers(buffers.Length, ref buffers[0]); + } + + public static void DeleteBuffer(int buffer) + { + DeleteBuffers(1, ref buffer); + } + + [DllImport(DLL, EntryPoint = "alIsBuffer", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern bool IsBuffer(int bid); + + [DllImport(DLL, EntryPoint = "alBufferData", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void BufferData(int bid, ALFormat format, IntPtr buffer, int size, int freq); + + [DllImport(DLL, EntryPoint = "alBufferData", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static unsafe extern void BufferData(int bid, ALFormat format, void* buffer, int size, int freq); + + [DllImport(DLL, EntryPoint = "alBufferData", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void BufferData(int bid, ALFormat format, ref byte buffer, int size, int freq); + + [DllImport(DLL, EntryPoint = "alBufferData", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void BufferData(int bid, ALFormat format, ref short buffer, int bytes, int freq); + + public static unsafe void BufferData(int bid, ALFormat format, TBuffer[] buffer, int freq) + where TBuffer : unmanaged + { + fixed (TBuffer* b = buffer) + { + BufferData(bid, format, b, buffer.Length * sizeof(TBuffer), freq); + } + } + + public static unsafe void BufferData(int bid, ALFormat format, Span buffer, int freq) + where TBuffer : unmanaged + { + fixed (TBuffer* b = buffer) + { + BufferData(bid, format, b, buffer.Length * sizeof(TBuffer), freq); + } + } + + + [DllImport(DLL, EntryPoint = "alGetBufferi", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void GetBuffer(int bid, ALGetBufferi param, [Out] out int value); + + [DllImport(DLL, EntryPoint = "alDopplerFactor", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DopplerFactor(float value); + + [DllImport(DLL, EntryPoint = "alDopplerVelocity", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DopplerVelocity(float value); + + [DllImport(DLL, EntryPoint = "alSpeedOfSound", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void SpeedOfSound(float value); + + [DllImport(DLL, EntryPoint = "alDistanceModel", ExactSpelling = true, CallingConvention = ALCallingConvention)] + public static extern void DistanceModel(ALDistanceModel distancemodel); + + public static ALDistanceModel GetDistanceModel() + { + return (ALDistanceModel)Get(ALGetInteger.DistanceModel); + } + + public static ALSourceState GetSourceState(int sid) + { + GetSource(sid, ALGetSourcei.SourceState, out int state); + return (ALSourceState)state; + } + + public static ALSourceType GetSourceType(int sid) + { + GetSource(sid, ALGetSourcei.SourceType, out int temp); + return (ALSourceType)temp; + } + } + + +} diff --git a/Platforms/OpenAL/ALC.cs b/Platforms/OpenAL/ALC.cs new file mode 100644 index 0000000..45618cb --- /dev/null +++ b/Platforms/OpenAL/ALC.cs @@ -0,0 +1,537 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + internal static class ALC + { + public static class Attributes + { + public static int MajorVersion = 0; + public static int MinorVersion = 0; + public static int AttributesSize = 0; + public static int AllAttributes = 0; + public static int CaptureSamples = 0; + public static int EfxMajorVersion = 0; + public static int EfxMinorVersion = 0; + public static int EfxMaxAuxiliarySends =0; + } + + private const string DLL = "openal32.dll"; + internal const CallingConvention AlcCallingConv = CallingConvention.Cdecl; + + public static void Init(AL_Audio audio, ISystemOpenAL system) + { + //bindings = new AL_Bindings(system); + //MajorVersion = ALC.g + //int[] attributes = ALC.GetAttributeArray() + } + + + /// This function creates a context using a specified device. + /// A pointer to a device. + /// A zero terminated array of a set of attributes: ALC_FREQUENCY, ALC_MONO_SOURCES, ALC_REFRESH, ALC_STEREO_SOURCES, ALC_SYNC. + /// Returns a pointer to the new context (NULL on failure). + /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. + [DllImport(DLL, EntryPoint = "alcCreateContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static unsafe extern ALContext CreateContext([In] ALDevice device, [In] int* attributeList); + // ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + + /// This function creates a context using a specified device. + /// A pointer to a device. + /// A zero terminated array of a set of attributes: ALC_FREQUENCY, ALC_MONO_SOURCES, ALC_REFRESH, ALC_STEREO_SOURCES, ALC_SYNC. + /// Returns a pointer to the new context (NULL on failure). + /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. + [DllImport(DLL, EntryPoint = "alcCreateContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern ALContext CreateContext([In] ALDevice device, [In] ref int attributeList); + // ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + + /// This function creates a context using a specified device. + /// A pointer to a device. + /// A zero terminated array of a set of attributes: ALC_FREQUENCY, ALC_MONO_SOURCES, ALC_REFRESH, ALC_STEREO_SOURCES, ALC_SYNC. + /// Returns a pointer to the new context (NULL on failure). + /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. + [DllImport(DLL, EntryPoint = "alcCreateContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern ALContext CreateContext([In] ALDevice device, [In] int[] attributeList); + // ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + + /// This function creates a context using a specified device. + /// A pointer to a device. + /// A zero terminated span of a set of attributes: ALC_FREQUENCY, ALC_MONO_SOURCES, ALC_REFRESH, ALC_STEREO_SOURCES, ALC_SYNC. + /// Returns a pointer to the new context (NULL on failure). + /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. + public static ALContext CreateContext(ALDevice device, Span attributeList) + { + return CreateContext(device, ref attributeList[0]); + } + + /// This function creates a context using a specified device. + /// A pointer to a device. + /// The AL_AudioContext attributes to request. + /// Returns a pointer to the new context (NULL on failure). + /// The attribute list can be NULL, or a zero terminated list of integer pairs composed of valid ALC attribute tokens and requested values. + public static ALContext CreateContext(ALDevice device, ALContextAttributes attributes) + { + return CreateContext(device, attributes.CreateAttributeArray()); + } + + /// This function makes a specified context the current context. + /// A pointer to the new context. + /// Returns True on success, or False on failure. + [DllImport(DLL, EntryPoint = "alcMakeContextCurrent", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern bool MakeContextCurrent(ALContext context); + // ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); + + /// This function tells a context to begin processing. When a context is suspended, changes in OpenAL state will be accepted but will not be processed. alcSuspendContext can be used to suspend a context, and then all the OpenAL state changes can be applied at once, followed by a call to alcProcessContext to apply all the state changes immediately. In some cases, this procedure may be more efficient than application of properties in a non-suspended state. In some implementations, process and suspend calls are each a NOP. + /// A pointer to the new context. + [DllImport(DLL, EntryPoint = "alcProcessContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void ProcessContext(ALContext context); + // ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); + + /// This function suspends processing on a specified context. When a context is suspended, changes in OpenAL state will be accepted but will not be processed. A typical use of alcSuspendContext would be to suspend a context, apply all the OpenAL state changes at once, and then call alcProcessContext to apply all the state changes at once. In some cases, this procedure may be more efficient than application of properties in a non-suspended state. In some implementations, process and suspend calls are each a NOP. + /// A pointer to the context to be suspended. + [DllImport(DLL, EntryPoint = "alcSuspendContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void SuspendContext(ALContext context); + // ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); + + /// This function destroys a context. + /// A pointer to the new context. + [DllImport(DLL, EntryPoint = "alcDestroyContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void DestroyContext(ALContext context); + // ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); + + /// This function retrieves the current context. + /// Returns a pointer to the current context. + [DllImport(DLL, EntryPoint = "alcGetCurrentContext", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern ALContext GetCurrentContext(); + // ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( void ); + + /// This function retrieves a context's device pointer. + /// A pointer to a context. + /// Returns a pointer to the specified context's device. + [DllImport(DLL, EntryPoint = "alcGetContextsDevice", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern ALDevice GetContextsDevice(ALContext context); + // ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); + + /// This function opens a device by name. + /// A null-terminated string describing a device. + /// Returns a pointer to the opened device. The return value will be NULL if there is an error. + [DllImport(DLL, EntryPoint = "alcOpenDevice", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern ALDevice OpenDevice([In] string devicename); + // ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); + + /// This function closes a device by name. + /// A pointer to an opened device. + /// True will be returned on success or False on failure. Closing a device will fail if the device contains any contexts or buffers. + [DllImport(DLL, EntryPoint = "alcCloseDevice", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern bool CloseDevice([In] ALDevice device); + // ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); + + /// This function retrieves the current context error state. + /// A pointer to the device to retrieve the error state from. + /// Errorcode Int32. + [DllImport(DLL, EntryPoint = "alcGetError", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern AlcError GetError([In] ALDevice device); + // ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); + + /// This function queries if a specified context extension is available. + /// A pointer to the device to be queried for an extension. + /// A null-terminated string describing the extension. + /// Returns True if the extension is available, False if the extension is not available. + [DllImport(DLL, EntryPoint = "alcIsExtensionPresent", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern bool IsExtensionPresent([In] ALDevice device, [In] string extname); + // ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + + /// This function queries if a specified context extension is available. + /// A pointer to the device to be queried for an extension. + /// A null-terminated string describing the extension. + /// Returns True if the extension is available, False if the extension is not available. + [DllImport(DLL, EntryPoint = "alcIsExtensionPresent", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern bool IsExtensionPresent([In] ALCaptureDevice device, [In] string extname); + // ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + + /// This function retrieves the address of a specified context extension function. + /// a pointer to the device to be queried for the function. + /// a null-terminated string describing the function. + /// Returns the address of the function, or NULL if it is not found. + [DllImport(DLL, EntryPoint = "alcGetProcAddress", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi),] + public static extern IntPtr GetProcAddress([In] ALDevice device, [In] string funcname); + // ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); + + /// This function retrieves the enum value for a specified enumeration name. + /// a pointer to the device to be queried. + /// a null terminated string describing the enum value. + /// Returns the enum value described by the enumName string. This is most often used for querying an enum value for an ALC extension. + [DllImport(DLL, EntryPoint = "alcGetEnumValue", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern int GetEnumValue([In] ALDevice device, [In] string enumname); + // ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); + + /// This strings related to the context. + /// + /// ALC_DEFAULT_DEVICE_SPECIFIER will return the name of the default output device. + /// ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER will return the name of the default capture device. + /// ALC_DEVICE_SPECIFIER will return the name of the specified output device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. A list is a pointer to a series of strings separated by NULL characters, with the list terminated by two NULL characters. See Enumeration Extension for more details. + /// ALC_CAPTURE_DEVICE_SPECIFIER will return the name of the specified capture device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. + /// ALC_EXTENSIONS returns a list of available context extensions, with each extension separated by a space and the list terminated by a NULL character. + /// + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_DEFAULT_DEVICE_SPECIFIER, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER, ALC_DEVICE_SPECIFIER, ALC_CAPTURE_DEVICE_SPECIFIER, ALC_EXTENSIONS. + /// A string containing the name of the Device. + [DllImport(DLL, EntryPoint = "alcGetString", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static unsafe extern byte* GetStringPtr([In] ALDevice device, AlcGetString param); + // ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); + + /// This strings related to the context. + /// + /// ALC_DEFAULT_DEVICE_SPECIFIER will return the name of the default output device. + /// ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER will return the name of the default capture device. + /// ALC_DEVICE_SPECIFIER will return the name of the specified output device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. A list is a pointer to a series of strings separated by NULL characters, with the list terminated by two NULL characters. See Enumeration Extension for more details. + /// ALC_CAPTURE_DEVICE_SPECIFIER will return the name of the specified capture device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. + /// ALC_EXTENSIONS returns a list of available context extensions, with each extension separated by a space and the list terminated by a NULL character. + /// + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_DEFAULT_DEVICE_SPECIFIER, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER, ALC_DEVICE_SPECIFIER, ALC_CAPTURE_DEVICE_SPECIFIER, ALC_EXTENSIONS. + /// A string containing the name of the Device. + [DllImport(DLL, EntryPoint = "alcGetString", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))] + public static extern string GetString([In] ALDevice device, AlcGetString param); + // ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); + + /// This function returns a List of strings related to the context. + /// + /// ALC_DEVICE_SPECIFIER will return the name of the specified output device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. A list is a pointer to a series of strings separated by NULL characters, with the list terminated by two NULL characters. See Enumeration Extension for more details. + /// ALC_CAPTURE_DEVICE_SPECIFIER will return the name of the specified capture device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. + /// ALC_EXTENSIONS returns a list of available context extensions, with each extension separated by a space and the list terminated by a NULL character. + /// + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_DEVICE_SPECIFIER, ALC_CAPTURE_DEVICE_SPECIFIER, ALC_ALL_DEVICES_SPECIFIER. + /// A List of strings containing the names of the Devices. + public static unsafe List GetString(ALDevice device, AlcGetStringList param) + { + byte* result = GetStringPtr(device, (AlcGetString)param); + return ALStringListToList(result); + } + + /// This function returns a List of strings related to the context. + /// + /// ALC_DEVICE_SPECIFIER will return the name of the specified output device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. A list is a pointer to a series of strings separated by NULL characters, with the list terminated by two NULL characters. See Enumeration Extension for more details. + /// ALC_CAPTURE_DEVICE_SPECIFIER will return the name of the specified capture device if a pointer is supplied, or will return a list of all available devices if a NULL device pointer is supplied. + /// ALC_EXTENSIONS returns a list of available context extensions, with each extension separated by a space and the list terminated by a NULL character. + /// + /// An attribute to be retrieved: ALC_DEVICE_SPECIFIER, ALC_CAPTURE_DEVICE_SPECIFIER, ALC_ALL_DEVICES_SPECIFIER. + /// A List of strings containing the names of the Devices. + public static List GetString(AlcGetStringList param) => GetString(ALDevice.Null, param); + + /// This function returns integers related to the context. + /// a pointer to the device to be queried. + /// an attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// the size of the destination buffer provided, in number of integers. + /// a pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static unsafe extern void GetInteger(ALDevice device, AlcGetInteger param, int size, int* data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// This function returns integers related to the context. + /// a pointer to the device to be queried. + /// an attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// the size of the destination buffer provided, in number of integers. + /// a pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern void GetInteger(ALDevice device, AlcGetInteger param, int size, int[] data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// The size of the destination buffer provided, in number of integers. + /// A pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern void GetInteger(ALDevice device, AlcGetInteger param, int size, out int data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// A pointer to the buffer to be returned. + public static void GetInteger(ALDevice device, AlcGetInteger param, out int data) + { + GetInteger(device, param, 1, out data); + } + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// The value returned. + public static int GetInteger(ALDevice device, AlcGetInteger param) + { + GetInteger(device, param, 1, out int data); + return data; + } + + /// + /// Returns a list of attributes for the current context of the specified device. + /// + /// The device to get attributes from. + /// A list of attributes for the device. + public static int[] GetAttributeArray(ALDevice device) + { + GetInteger(device, AlcGetInteger.AttributesSize, 1, out int size); + int[] attributes = new int[size]; + GetInteger(device, AlcGetInteger.AllAttributes, size, attributes); + return attributes; + } + + /// + /// Returns a list of attributes for the current context of the specified device. + /// + /// The device to get attributes from. + /// A list of attributes for the device. + public static ALContextAttributes GetContextAttributes(ALDevice device) + { + GetInteger(device, AlcGetInteger.AttributesSize, 1, out int size); + int[] attributes = new int[size]; + GetInteger(device, AlcGetInteger.AllAttributes, size, attributes); + return ALContextAttributes.FromArray(attributes); + } + + // -------- ALC_EXT_CAPTURE -------- + + /// + /// Checks to see that the ALC_EXT_CAPTURE extension is present. This will always be available in 1.1 devices or later. + /// + /// The device to check the extension is present for. + /// If the ALC_EXT_CAPTURE extension was present. + public static bool IsCaptureExtensionPresent(ALDevice device) + { + return IsExtensionPresent(device, "ALC_EXT_CAPTURE"); + } + + /// + /// Checks to see that the ALC_EXT_CAPTURE extension is present. This will always be available in 1.1 devices or later. + /// + /// The device to check the extension is present for. + /// If the ALC_EXT_CAPTURE extension was present. + public static bool IsCaptureExtensionPresent(ALCaptureDevice device) + { + return IsExtensionPresent(device, "ALC_EXT_CAPTURE"); + } + + /// This function opens a capture device by name. + /// A pointer to a device name string. + /// The frequency that the buffer should be captured at. + /// The requested capture buffer format. + /// The size of the capture buffer in samples, not bytes. + /// Returns the capture device pointer, or on failure. + [DllImport(DLL, EntryPoint = "alcCaptureOpenDevice", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern ALCaptureDevice CaptureOpenDevice(string devicename, uint frequency, ALFormat format, int buffersize); + // ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + + /// This function opens a capture device by name. + /// A pointer to a device name string. + /// The frequency that the buffer should be captured at. + /// The requested capture buffer format. + /// The size of the capture buffer in samples, not bytes. + /// Returns the capture device pointer, or on failure. + [DllImport(DLL, EntryPoint = "alcCaptureOpenDevice", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern ALCaptureDevice CaptureOpenDevice(string devicename, int frequency, ALFormat format, int buffersize); + // ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + + /// This function closes the specified capture device. + /// A pointer to a capture device. + /// Returns True if the close operation was successful, False on failure. + [DllImport(DLL, EntryPoint = "alcCaptureCloseDevice", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern bool CaptureCloseDevice([In] ALCaptureDevice device); + // ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); + + /// This function begins a capture operation. + /// alcCaptureStart will begin recording to an internal ring buffer of the size specified when opening the capture device. The application can then retrieve the number of samples currently available using the ALC_CAPTURE_SAPMPLES token with alcGetIntegerv. When the application determines that enough samples are available for processing, then it can obtain them with a call to alcCaptureSamples. + /// A pointer to a capture device. + [DllImport(DLL, EntryPoint = "alcCaptureStart", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void CaptureStart([In] ALCaptureDevice device); + // ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); + + /// This function stops a capture operation. + /// A pointer to a capture device. + [DllImport(DLL, EntryPoint = "alcCaptureStop", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void CaptureStop([In] ALCaptureDevice device); + // ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); + + /// This function completes a capture operation, and does not block. + /// A pointer to a capture device. + /// A pointer to a buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + [DllImport(DLL, EntryPoint = "alcCaptureSamples", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void CaptureSamples(ALCaptureDevice device, IntPtr buffer, int samples); + // ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + + /// This function completes a capture operation, and does not block. + /// A pointer to a capture device. + /// A pointer to a buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + [DllImport(DLL, EntryPoint = "alcCaptureSamples", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static unsafe extern void CaptureSamples(ALCaptureDevice device, void* buffer, int samples); + // ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + + /// This function completes a capture operation, and does not block. + /// A pointer to a capture device. + /// A pointer to a buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + [DllImport(DLL, EntryPoint = "alcCaptureSamples", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void CaptureSamples(ALCaptureDevice device, ref byte buffer, int samples); + // ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + + /// This function completes a capture operation, and does not block. + /// A pointer to a capture device. + /// A pointer to a buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + [DllImport(DLL, EntryPoint = "alcCaptureSamples", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static extern void CaptureSamples(ALCaptureDevice device, ref short buffer, int samples); + // ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + + /// This function completes a capture operation, and does not block. + /// The buffer datatype. + /// A pointer to a capture device. + /// A reference to a buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + public static unsafe void CaptureSamples(ALCaptureDevice device, ref T buffer, int samples) + where T : unmanaged + { + fixed (T* ptr = &buffer) + { + CaptureSamples(device, ptr, samples); + } + } + + /// This function completes a capture operation, and does not block. + /// The buffer datatype. + /// A pointer to a capture device. + /// A buffer, which must be large enough to accommodate the number of samples. + /// The number of samples to be retrieved. + public static void CaptureSamples(ALCaptureDevice device, T[] buffer, int samples) + where T : unmanaged + { + CaptureSamples(device, ref buffer[0], samples); + } + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// The size of the destination buffer provided, in number of integers. + /// A pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static unsafe extern void GetInteger(ALCaptureDevice device, AlcGetInteger param, int size, int* data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// The size of the destination buffer provided, in number of integers. + /// A pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern void GetInteger(ALCaptureDevice device, AlcGetInteger param, int size, int[] data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// This function returns integers related to the context. + /// A pointer to the device to be queried. + /// An attribute to be retrieved: ALC_MAJOR_VERSION, ALC_MINOR_VERSION, ALC_ATTRIBUTES_SIZE, ALC_ALL_ATTRIBUTES. + /// The size of the destination buffer provided, in number of integers. + /// A pointer to the buffer to be returned. + [DllImport(DLL, EntryPoint = "alcGetIntegerv", ExactSpelling = true, CallingConvention = AlcCallingConv, CharSet = CharSet.Ansi)] + public static extern void GetInteger(ALCaptureDevice device, AlcGetInteger param, int size, out int data); + // ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *buffer ); + + /// + /// Gets the current number of available capture samples. + /// + /// The device. + /// The number of capture samples available. + public static int GetAvailableSamples(ALCaptureDevice device) + { + GetInteger(device, AlcGetInteger.CaptureSamples, 1, out int result); + return result; + } + + // -------- ALC_ENUMERATION_EXT -------- + + /// + /// Checks to see that the ALC_ENUMERATION_EXT extension is present. This will always be available in 1.1 devices or later. + /// + /// The device to check the extension is present for. + /// If the ALC_ENUMERATION_EXT extension was present. + public static bool IsEnumerationExtensionPresent(ALDevice device) + { + return IsExtensionPresent(device, "ALC_ENUMERATION_EXT"); + } + + /// + /// Checks to see that the ALC_ENUMERATION_EXT extension is present. This will always be available in 1.1 devices or later. + /// + /// The device to check the extension is present for. + /// If the ALC_ENUMERATION_EXT extension was present. + public static bool IsEnumerationExtensionPresent(ALCaptureDevice device) + { + return IsExtensionPresent(device, "ALC_ENUMERATION_EXT"); + } + + /// + /// Gets a named property on the context. + /// + /// The device for the context. + /// The named property. + /// The value. + [DllImport(DLL, EntryPoint = "alcGetString", ExactSpelling = true, CallingConvention = AlcCallingConv)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))] + public static extern string GetString(ALDevice device, GetEnumerationString param); + + /// + /// Gets a named property on the context. + /// + /// The device for the context. + /// The named property. + /// The value. + [DllImport(DLL, EntryPoint = "alcGetString", ExactSpelling = true, CallingConvention = AlcCallingConv)] + public static unsafe extern byte* GetStringListPtr(ALDevice device, GetEnumerationStringList param); + + /// + public static unsafe IEnumerable GetStringList(GetEnumerationStringList param) + { + byte* result = GetStringListPtr(ALDevice.Null, param); + return ALStringListToList(result); + } + + /// + /// Used to convert a OpenAL string list to a C# List. + /// + /// A pointer to the AL list. Usually returned from GetStringList like AL functions. + /// The string list. + internal static unsafe List ALStringListToList(byte* alList) + { + if (alList == (byte*)0) + { + return new List(); + } + + var strings = new List(); + + byte* currentPos = alList; + while (true) + { + var currentString = Marshal.PtrToStringAnsi(new IntPtr(currentPos)); + if (string.IsNullOrEmpty(currentString)) + { + break; + } + + strings.Add(currentString); + currentPos += currentString.Length + 1; + } + + return strings; + } + } +} diff --git a/Platforms/OpenAL/ALCEnums.cs b/Platforms/OpenAL/ALCEnums.cs new file mode 100644 index 0000000..7f12934 --- /dev/null +++ b/Platforms/OpenAL/ALCEnums.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + public enum AlcContextAttributes : int + { + /// Followed by System.Int32 Hz + Frequency = 0x1007, + + /// Followed by System.Int32 Hz + Refresh = 0x1008, + + /// Followed by AlBoolean.True, or AlBoolean.False + Sync = 0x1009, + + /// Followed by System.Int32 Num of requested Mono (3D) Sources + MonoSources = 0x1010, + + /// Followed by System.Int32 Num of requested Stereo Sources + StereoSources = 0x1011, + + /// (EFX Extension) This Context property can be passed to OpenAL during Context creation (alcCreateContext) to request a maximum number of Auxiliary Sends desired on each Source. It is not guaranteed that the desired number of sends will be available, so an application should query this property after creating the context using alcGetIntergerv. Default: 2 + EfxMaxAuxiliarySends = 0x20003, + } + + /// + /// Defines OpenAL context errors. + /// + public enum AlcError : int + { + /// There is no current error. + NoError = 0, + + /// No Device. The device handle or specifier names an inaccessible driver/server. + InvalidDevice = 0xA001, + + /// Invalid context ID. The Context argument does not name a valid context. + InvalidContext = 0xA002, + + /// Bad enum. A token used is not valid, or not applicable. + InvalidEnum = 0xA003, + + /// Bad value. A value (e.g. Attribute) is not valid, or not applicable. + InvalidValue = 0xA004, + + /// Out of memory. Unable to allocate memory. + OutOfMemory = 0xA005, + } + + /// + /// Defines available parameters for . + /// + public enum AlcGetString : int + { + /// The specifier string for the default device. + DefaultDeviceSpecifier = 0x1004, + + /// A list of available context extensions separated by spaces. + Extensions = 0x1006, + + /// The name of the default capture device + CaptureDefaultDeviceSpecifier = 0x311, // ALC_EXT_CAPTURE extension. + + /// a list of the default devices. + DefaultAllDevicesSpecifier = 0x1012, + + // duplicates from AlcGetStringList: + + /// Will only return the first Device, not a list. Use AlcGetStringList.CaptureDeviceSpecifier. ALC_EXT_CAPTURE_EXT + CaptureDeviceSpecifier = 0x310, + + /// Will only return the first Device, not a list. Use AlcGetStringList.DeviceSpecifier + DeviceSpecifier = 0x1005, + + /// Will only return the first Device, not a list. Use AlcGetStringList.AllDevicesSpecifier + AllDevicesSpecifier = 0x1013, + } + + /// + /// Defines available parameters for . + /// + public enum AlcGetStringList : int + { + /// The name of the specified capture device, or a list of all available capture devices if no capture device is specified. ALC_EXT_CAPTURE_EXT + CaptureDeviceSpecifier = 0x310, + + /// The specifier strings for all available devices. ALC_ENUMERATION_EXT + DeviceSpecifier = 0x1005, + + /// The specifier strings for all available devices. ALC_ENUMERATE_ALL_EXT + AllDevicesSpecifier = 0x1013, + } + + /// + /// Defines available parameters for . + /// + public enum AlcGetInteger : int + { + /// The specification revision for this implementation (major version). NULL is an acceptable device. + MajorVersion = 0x1000, + + /// The specification revision for this implementation (minor version). NULL is an acceptable device. + MinorVersion = 0x1001, + + /// The size (number of ALCint values) required for a zero-terminated attributes list, for the current context. NULL is an invalid device. + AttributesSize = 0x1002, + + /// Expects a destination of ALC_ATTRIBUTES_SIZE, and provides an attribute list for the current context of the specified device. NULL is an invalid device. + AllAttributes = 0x1003, + + /// The number of capture samples available. NULL is an invalid device. + CaptureSamples = 0x312, + + /// (EFX Extension) This property can be used by the application to retrieve the Major version number of the Effects Extension supported by this OpenAL implementation. As this is a Context property is should be retrieved using alcGetIntegerv. + EfxMajorVersion = 0x20001, + + /// (EFX Extension) This property can be used by the application to retrieve the Minor version number of the Effects Extension supported by this OpenAL implementation. As this is a Context property is should be retrieved using alcGetIntegerv. + EfxMinorVersion = 0x20002, + + /// (EFX Extension) This Context property can be passed to OpenAL during Context creation (alcCreateContext) to request a maximum number of Auxiliary Sends desired on each Source. It is not guaranteed that the desired number of sends will be available, so an application should query this property after creating the context using alcGetIntergerv. Default: 2 + EfxMaxAuxiliarySends = 0x20003, + } + + /// + /// Defines available parameters for . + /// + public enum GetEnumerationString + { + /// + /// Gets the specifier for the default device. ALC_ENUMERATION_EXT + /// + DefaultDeviceSpecifier = 0x1004, + + /// + /// Gets a specific output device's specifier. + /// Can also be used without a device to get a list of all available output devices, see . ALC_ENUMERATION_EXT + /// + DeviceSpecifier = 0x1005, + + /// + /// Gets the specifier for the default capture device. ALC_ENUMERATION_EXT + /// + DefaultCaptureDeviceSpecifier = 0x311, + + /// + /// Gets a specific capture device's specifier. + /// Can also be used without a device to get a list of all available capture devices, see . ALC_ENUMERATION_EXT + /// + CaptureDeviceSpecifier = 0x310, + } + + /// + /// Defines available parameters for . + /// + public enum GetEnumerationStringList + { + /// + /// Gets the specifier strings for all available output devices. + /// Can also be used to get the specifier for a specific device, see . ALC_ENUMERATION_EXT + /// + DeviceSpecifier = 0x1005, + + /// + /// Gets the specifier strings for all available capture devices. + /// Can also be used to get the specifier for a specific capture device, see . ALC_ENUMERATION_EXT + /// + CaptureDeviceSpecifier = 0x310, + } +} diff --git a/Platforms/OpenAL/ALCaptureDevice.cs b/Platforms/OpenAL/ALCaptureDevice.cs new file mode 100644 index 0000000..8baabc7 --- /dev/null +++ b/Platforms/OpenAL/ALCaptureDevice.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + public struct ALCaptureDevice + { + public static readonly ALCaptureDevice Null = new ALCaptureDevice(IntPtr.Zero); + + public IntPtr Handle; + + public ALCaptureDevice(IntPtr handle) + { + Handle = handle; + } + } +} \ No newline at end of file diff --git a/Platforms/OpenAL/ALContext.cs b/Platforms/OpenAL/ALContext.cs new file mode 100644 index 0000000..9a28e57 --- /dev/null +++ b/Platforms/OpenAL/ALContext.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + public struct ALContext : IEquatable + { + public static readonly ALContext Null = new ALContext(IntPtr.Zero); + + public IntPtr Handle; + + public ALContext(IntPtr handle) + { + Handle = handle; + } + + public override bool Equals(object obj) + { + return obj is ALContext handle && Equals(handle); + } + + public bool Equals([AllowNull] ALContext other) + { + return Handle.Equals(other.Handle); + } + + public override int GetHashCode() + { + return HashCode.Combine(Handle); + } + + public static bool operator ==(ALContext left, ALContext right) + { + return left.Equals(right); + } + + public static bool operator !=(ALContext left, ALContext right) + { + return !(left == right); + } + + public static implicit operator IntPtr(ALContext context) => context.Handle; + } +} diff --git a/Platforms/OpenAL/ALContextAttributes.cs b/Platforms/OpenAL/ALContextAttributes.cs new file mode 100644 index 0000000..ccf8d49 --- /dev/null +++ b/Platforms/OpenAL/ALContextAttributes.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + public class ALContextAttributes + { + /// + /// Gets or sets the output buffer frequency in Hz. + /// This does not actually change any AL state. To apply these attributes see . + /// + public int? Frequency { get; set; } + + /// + /// Gets or sets the number of mono sources. + /// This does not actually change any AL state. To apply these attributes see . + /// Not guaranteed to get exact number of mono sources when creating a context. + /// + public int? MonoSources { get; set; } + + /// + /// Gets or sets the number of stereo sources. + /// This does not actually change any AL state. To apply these attributes see . + /// Not guaranteed to get exact number of mono sources when creating a context. + /// + public int? StereoSources { get; set; } + + /// + /// Gets or sets the refrash interval in Hz. + /// This does not actually change any AL state. To apply these attributes see . + /// + public int? Refresh { get; set; } + + /// + /// Gets or sets if the context is synchronous. + /// This does not actually change any AL state. To apply these attributes see . + /// + public bool? Sync { get; set; } + + /// + /// Gets or sets additional attributes. + /// Will usually be the major and minor version numbers of the context. // FIXME: This needs verification. Docs say nothing about this. + /// + public int[] AdditionalAttributes { get; set; } + + /// + /// Initializes a new instance of the class. + /// Leaving all attributes to the driver implementation default values. + /// + public ALContextAttributes() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The mixing output buffer frequency in Hz. + /// The number of mono sources available. Not guaranteed. + /// The number of stereo sources available. Not guaranteed. + /// The refresh interval in Hz. + /// If the context is synchronous. + public ALContextAttributes(int? frequency, int? monoSources, int? stereoSources, int? refresh, bool? sync) + { + Frequency = frequency; + MonoSources = monoSources; + StereoSources = stereoSources; + Refresh = refresh; + Sync = sync; + } + + /// + /// Converts these context attributes to a compatible list. + /// Alternativly, consider using the more convenient overload. + /// + /// The attibute list in the form of a span. + public int[] CreateAttributeArray() + { + // The number of members * 2 + AdditionalAttributes + int[] attributeList = new int[(5 * 2) + (AdditionalAttributes?.Length ?? 0) + 1]; + int index = 0; + + void AddAttribute(int? value, AlcContextAttributes attribute) + { + if (value != null) + { + attributeList[index++] = (int)attribute; + attributeList[index++] = value ?? default; + } + } + + AddAttribute(Frequency, AlcContextAttributes.Frequency); + AddAttribute(MonoSources, AlcContextAttributes.MonoSources); + AddAttribute(StereoSources, AlcContextAttributes.StereoSources); + AddAttribute(Refresh, AlcContextAttributes.Refresh); + if (Sync != null) + { + AddAttribute((Sync ?? false) ? 1 : 0, AlcContextAttributes.Sync); + } + + if (AdditionalAttributes != null) + { + Array.Copy(AdditionalAttributes, 0, attributeList, index, AdditionalAttributes.Length); + index += AdditionalAttributes.Length; + } + + // Add the trailing null byte. + attributeList[index++] = 0; + + return attributeList; + } + + /// + /// Parses a AL attribute list. + /// + /// The AL context attribute list. + /// The parsed object. + internal static ALContextAttributes FromArray(int[] attributeArray) + { + List extra = new List(); + ALContextAttributes attributes = new ALContextAttributes(); + + void ParseAttribute(int @enum, int value) + { + switch (@enum) + { + case (int)AlcContextAttributes.Frequency: + attributes.Frequency = value; + break; + case (int)AlcContextAttributes.MonoSources: + attributes.MonoSources = value; + break; + case (int)AlcContextAttributes.StereoSources: + attributes.StereoSources = value; + break; + case (int)AlcContextAttributes.Refresh: + attributes.Refresh = value; + break; + case (int)AlcContextAttributes.Sync: + attributes.Sync = value == 1; + break; + default: + extra.Add(@enum); extra.Add(value); + break; + } + } + + for (int i = 0; i < attributeArray.Length - 1; i += 2) + { + ParseAttribute(attributeArray[i], attributeArray[i + 1]); + } + + attributes.AdditionalAttributes = extra.ToArray(); + + return attributes; + } + + // Used for ToString. + private string GetOptionalString(string title, T? value) + where T : unmanaged + { + if (value == null) + { + return null; + } + else + { + return $"{title}: {value}"; + } + } + + /// + /// Converts the attributes to a string representation. + /// + /// The string representation of the attributes. + public override string ToString() + { + return $"{GetOptionalString(nameof(Frequency), Frequency)}, " + + $"{GetOptionalString(nameof(MonoSources), MonoSources)}, " + + $"{GetOptionalString(nameof(StereoSources), StereoSources)}, " + + $"{GetOptionalString(nameof(Refresh), Refresh)}, " + + $"{GetOptionalString(nameof(Sync), Sync)}" + + $"{((AdditionalAttributes != null) ? ", " + string.Join(", ", AdditionalAttributes) : string.Empty)}"; + } + } +} diff --git a/Platforms/OpenAL/ALDevice.cs b/Platforms/OpenAL/ALDevice.cs new file mode 100644 index 0000000..710a120 --- /dev/null +++ b/Platforms/OpenAL/ALDevice.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + public struct ALDevice : IEquatable + { + public static readonly ALDevice Null = new ALDevice(IntPtr.Zero); + + public IntPtr Handle; + + public ALDevice(IntPtr handle) + { + Handle = handle; + } + + public override bool Equals(object obj) + { + return obj is ALDevice device && Equals(device); + } + + public bool Equals([AllowNull] ALDevice other) + { + return Handle.Equals(other.Handle); + } + + public override int GetHashCode() + { + return HashCode.Combine(Handle); + } + + public static bool operator ==(ALDevice left, ALDevice right) + { + return left.Equals(right); + } + + public static bool operator !=(ALDevice left, ALDevice right) + { + return !(left == right); + } + + public static implicit operator IntPtr(ALDevice device) => device.Handle; + } +} diff --git a/Platforms/OpenAL/AL_Audio.cs b/Platforms/OpenAL/AL_Audio.cs new file mode 100644 index 0000000..9a6c285 --- /dev/null +++ b/Platforms/OpenAL/AL_Audio.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + public class AL_Audio : Audio + { + + // Background Context can be null up until Startup, at which point they never are again + internal ISystemOpenAL.Context BackgroundContext = null!; + + public override Renderer Renderer => Renderer.OpenAL; + + protected override void ApplicationStarted() + { + ApiName = "OpenAL"; + } + + // various resources waiting to be deleted + internal List BuffersToDelete = new List(); + internal List SourcesToDelete = new List(); + + private ALContext context; + private ALDevice device; + + // stored delegates for deleting graphics resources + private delegate void DeleteResource(int id); + + protected override void FirstWindowCreated() + { + Console.WriteLine("Hello!"); + var devices = ALC.GetStringList(GetEnumerationStringList.DeviceSpecifier); + Console.WriteLine($"Devices: {string.Join(", ", devices)}"); + + // Get the default device, then go though all devices and select the AL soft device if it exists. + string deviceName = ALC.GetString(ALDevice.Null, AlcGetString.DefaultDeviceSpecifier); + foreach (var d in devices) + { + if (d.Contains("OpenAL Soft")) + { + deviceName = d; + } + } + + // var allDevices = Extensions.Creative.EnumerateAll.EnumerateAll.GetStringList(Extensions.Creative.EnumerateAll.GetEnumerateAllContextStringList.AllDevicesSpecifier); + // Console.WriteLine($"All Devices: {string.Join(", ", allDevices)}"); + + device = ALC.OpenDevice(deviceName); + context = ALC.CreateContext(device, (int[])null); + ALC.MakeContextCurrent(context); + + //AL.Init(this, System); + //ALC.Init(this, System); + + ALC.GetInteger(device, AlcGetInteger.MajorVersion, 1, out int alcMajorVersion); + ALC.GetInteger(device, AlcGetInteger.MinorVersion, 1, out int alcMinorVersion); + string alcExts = ALC.GetString(device, AlcGetString.Extensions); + + ALC.Attributes.MajorVersion = alcMajorVersion; + ALC.Attributes.MinorVersion = alcMinorVersion; + + var attrs = ALC.GetContextAttributes(device); + Console.WriteLine($"Attributes: {attrs}"); + + string exts = AL.Get(ALGetString.Extensions); + string rend = AL.Get(ALGetString.Renderer); + string vend = AL.Get(ALGetString.Vendor); + string vers = AL.Get(ALGetString.Version); + + + ApiVersion = new Version(ALC.Attributes.MajorVersion, ALC.Attributes.MinorVersion); + // ApiName = AL.GetString(ALEnum.Renderer); + + // BackgroundContext = System.CreateALContext(); + } + + + protected override AudioSource.Platform CreateAudioSource() + { + return new AL_AudioSource(this); + } + + + protected override void Shutdown() + { + //BackgroundContext.Dispose(); + + ALC.MakeContextCurrent(ALContext.Null); + ALC.DestroyContext(context); + ALC.CloseDevice(device); + } + } +} diff --git a/Platforms/OpenAL/AL_AudioSource.cs b/Platforms/OpenAL/AL_AudioSource.cs new file mode 100644 index 0000000..c786ab5 --- /dev/null +++ b/Platforms/OpenAL/AL_AudioSource.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + internal class AL_AudioSource : AudioSource.Platform + { + public int BufferID { get; private set; } + + public int SourceID { get; private set; } + + private float volume; + protected override float Volume + { + get { return volume; } + set + { + AL.Source(this.SourceID, ALSourcef.Gain, volume = value); + } + } + + private float pitch; + protected override float Pitch + { + get { return pitch; } + set + { + AL.Source(this.SourceID, ALSourcef.Pitch, value); + } + + } + + private bool loop; + protected override bool Loop + { + get { return loop; } + set + { + AL.Source(this.SourceID, ALSourceb.Looping, value); + } + } + + + private readonly AL_Audio audio; + private AudioSource audioSource; + + internal AL_AudioSource(AL_Audio audio) + { + this.audio = audio; + audioSource = null!; + } + + ~AL_AudioSource() + { + Dispose(); + } + + protected override void Init(AudioSource audioSource, Stream stream) + { + this.audioSource = audioSource; + Initialize(stream); + } + + void Initialize(Stream stream) + { + int bits; + int rate; + int frequency; + int sampleCount; + byte[] soundData; + Wave.Format format; + AudioChannel audioChannel; + + this.BufferID = AL.GenBuffer(); + int chunkSize; + + Wave.TryLoad(stream, out soundData, out format, out frequency, out audioChannel, out bits, out rate, out chunkSize, out sampleCount); + AL.BufferData(this.BufferID, GetSoundFormat(audioChannel, bits), soundData, frequency); + + ALError error = AL.GetError(); + if (error != ALError.NoError) + { + Console.WriteLine("error loading buffer: " + error); + } + + + AL.Listener(ALListenerf.Gain, 0.1f); + + this.SourceID = AL.GenSource(); + + AL.Listener(ALListener3f.Position, 0, 0, 0); + AL.Source(this.SourceID, ALSourcef.Gain, 1f); + AL.Source(this.SourceID, ALSourcei.Buffer, this.BufferID); + } + + private ALFormat GetSoundFormat(AudioChannel channels, int bitsPerSample) + { + switch (channels) + { + case AudioChannel.Mono: return bitsPerSample == 8 ? ALFormat.Mono8 : ALFormat.Mono16; + case AudioChannel.Stereo: return bitsPerSample == 8 ? ALFormat.Stereo8 : ALFormat.Stereo16; + default: throw new NotSupportedException("The specified sound format is not supported."); + } + } + + protected override AudioState GetState() + { + switch (AL.GetSourceState(this.SourceID)) + { + case ALSourceState.Playing: { return AudioState.Playing; } + case ALSourceState.Stopped: { return AudioState.Stopped; } + case ALSourceState.Paused: { return AudioState.Paused; } + } + + return AudioState.Unknown; + } + + protected override void Stop() + { + AL.SourceStop(this.SourceID); + } + + protected override void Play() + { + AL.SourcePlay(this.SourceID); + } + + protected override void Pause() + { + AL.SourcePause(this.SourceID); + } + + protected override void Rewind() + { + AL.SourceRewind(this.SourceID); + } + + protected override void Resume() + { + AL.SourcePlay(this.SourceID); + } + + protected override void Dispose() + { + if (this.BufferID != 0) + { + this.audio.BuffersToDelete.Add(this.BufferID); + this.BufferID = 0; + } + if (this.SourceID != 0) + { + this.audio.SourcesToDelete.Add(this.SourceID); + this.SourceID = 0; + } + } + + } +} diff --git a/Platforms/OpenAL/AL_Backup.cs b/Platforms/OpenAL/AL_Backup.cs new file mode 100644 index 0000000..002e484 --- /dev/null +++ b/Platforms/OpenAL/AL_Backup.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Foster.Framework; + +namespace Foster.OpenAL +{ + internal static class ALBackup + { + public static int MajorVersion; + public static int MinorVersion; + public static int AttributesSize; + public static int AllAttributes; + public static int CaptureSamples; + public static int EfxMajorVersion; + public static int EfxMinorVersion; + public static int EfxMaxAuxiliarySends; + +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. + private static AL_Bindings bindings; + private static OnError onErrorRef; +#pragma warning restore CS8618 + + public static void Init(AL_Audio audio, ISystemOpenAL system) + { + bindings = new AL_Bindings(system); + + GetIntegerv((ALEnum)0x1000, out MajorVersion); + GetIntegerv((ALEnum)0x1001, out MinorVersion); + GetIntegerv((ALEnum)0x1002, out AttributesSize); + GetIntegerv((ALEnum)0x1003, out AllAttributes); + GetIntegerv((ALEnum)0x312, out CaptureSamples); + GetIntegerv((ALEnum)0x20001, out EfxMajorVersion); + GetIntegerv((ALEnum)0x20002, out EfxMinorVersion); + GetIntegerv((ALEnum)0x20003, out EfxMaxAuxiliarySends); +#if DEBUG + +#endif + } + + private delegate void OnError(ALEnum source, ALEnum type, uint id, ALEnum severity, uint length, IntPtr message, IntPtr userParam); + + public static unsafe string GetString(ALEnum name) + { + return Marshal.PtrToStringAnsi(bindings.alGetString(name)) ?? ""; + } + + public static void Enable(ALEnum name) => bindings.alEnable(name); + + public static void Disable(ALEnum name) => bindings.alDisable(name); + + public static void GetIntegerv(ALEnum name, out int data) => bindings.alGetIntegerv(name, out data); + + public static bool IsEnabled(ALEnum name) => bindings.alIsEnabled(name); + + public static unsafe string GetErrorString(ALEnum name) + { + return Marshal.PtrToStringAnsi(bindings.alGetErrorString(name)); + } + + public static uint GetInteger(ALEnum name) => bindings.alGetInteger(name); + + public static float GetFloat(ALEnum name) => bindings.alGetFloat(name); + + public static ALEnum GetError() => bindings.alGetError(); + + public static bool IsExtensionPresent(string extName) => bindings.alIsExtensionPresent(extName); + + + public static unsafe IntPtr GetProcAddress(string fname) + { + return bindings.alGetProcAddress(fname); + } + + public static uint GetEnumValue(string ename) => bindings.alGetEnumValue(ename); + + public static void Listenerf(ALEnum parm, float value) => bindings.alListenerf(parm, value); + + public static void Listener3f(ALEnum parm, float value1, float value2, float value3) => bindings.alListener3f(parm, value1, value2, value3); + + public unsafe static void Listenerfv(ALEnum parm, float* value) => bindings.alListenerfv(parm, value); + + public static void Getlistenerf(ALEnum parm, out float value) => bindings.alGetListenerf(parm, out value); + + public static void Getlistenerf(ALEnum parm, out float value1, out float value2, out float value3) => bindings.alGetListener3f(parm, out value1, out value2, out value3); + + public static void GetListenerfv(ALEnum parm, ref float value) => bindings.alGetListenerfv(parm, ref value); + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void GetListenerfv(ALEnum param, float[] values); + //public GetListenerfv alGetListenerfv; + + //public unsafe static void GenSources(int n, IntPtr sources) => bindings.alGenSources(n, sources); + public unsafe static void GenSources(int n, IntPtr sources) => bindings.alGenSources(n, sources); + + public unsafe static uint GenSource() + { + uint id; + bindings.alGenSources(1, new IntPtr(&id)); + return id; + } + + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void GenSources(int n, ref uint sources); + //public GenSources alGenSources; + + //public unsafe static void DeleteSources(int n, IntPtr sources) => bindings.alDeleteSources(n, sources); + public unsafe static void DeleteSources(int n, IntPtr sources) => bindings.alDeleteSources(n, sources); + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void DeleteSources(int n, ref int sources); + //public DeleteSources alDeleteSources; + + public static bool IsSource(uint sid) => bindings.alIsSource(sid); + + public static void Sourcef(uint sid, ALEnum param, float value) => bindings.alSourcef(sid, param, value); + + public static void Source3f(uint sid, ALEnum param, float value1, float value2, float value3) => bindings.alSource3f(sid, param, value1, value2, value3); + + public static void Sourcei(uint sid, ALEnum param, uint value) => bindings.alSourcef(sid, param, value); + + public static void Source3i(uint sid, ALEnum param, uint value1, uint value2, uint value3) => bindings.alSource3i(sid, param, value1, value2, value3); + + public unsafe static void GetSourcef(uint sid, ALEnum param, out float value) => bindings.alGetSourcef(sid, param, out value); + + public unsafe static void GetSource3f(uint sid, ALEnum param, out float value1, out float value2, out float value3) => bindings.alGetSource3f(sid, param, out value1, out value2, out value3); + + public unsafe static void GetSourcei(uint sid, ALEnum param, out uint value) => bindings.alGetSourcei(sid, param, out value); + + public unsafe static void GetSource3i(uint sid, ALEnum param, out uint value1, out uint value2, out uint value3) => bindings.alGetSource3i(sid, param, out value1, out value2, out value3); + + public unsafe static void SourcePlayv(int ns, IntPtr sids) => bindings.alSourcePlayv(ns, sids); + + public unsafe static void SourceStopv(int ns, IntPtr sids) => bindings.alSourceStopv(ns, sids); + + public unsafe static void SourceRewindv(int ns, IntPtr sids) => bindings.alSourceRewindv(ns, sids); + + public unsafe static void SourcePausev(int ns, IntPtr sids) => bindings.alSourcePausev(ns, sids); + + public unsafe static void SourcePlay(uint sid) => bindings.alSourcePlay(sid); + + public unsafe static void SourceStop(uint sid) => bindings.alSourceStop(sid); + + public unsafe static void SourceRewind(uint sid) => bindings.alSourceRewind(sid); + + public unsafe static void SourcePause(uint sid) => bindings.alSourcePause(sid); + + public unsafe static void SourceQueueBuffers(uint sid, uint numEntries, IntPtr bids) => bindings.alSourceQueueBuffers(sid, numEntries, bids); + + public unsafe static void SourceUnqueueBuffers(uint sid, uint numEntries, IntPtr bids) => bindings.alSourceUnqueueBuffers(sid, numEntries, bids); + + public unsafe static void GenBuffers(uint n, IntPtr buffers) => bindings.alGenBuffers(n, buffers); + + public unsafe static uint GenBuffer() + { + uint id; + bindings.alGenBuffers(1, new IntPtr(&id)); + return id; + } + + public unsafe static void DeleteBuffers(uint n, [Out] IntPtr buffers) => bindings.alDeleteBuffers(n, buffers); + + public static bool IsBuffer(uint bid) => bindings.alIsBuffer(bid); + + public static void BufferData(uint bid, ALEnum format, IntPtr buffer, uint size, uint freq) => bindings.alBufferData(bid, format, buffer, size, freq); + + public unsafe static void GetBufferi(uint bid, ALEnum param, [Out] out uint value) => bindings.alGetBufferi(bid, param, out value); + + public static uint GetBufferi(ALEnum param) + { + uint value = 0; + bindings.alGetBufferi(1, param, out value); + return value; + } + + public static void DopplerFactor(float value) => bindings.alDopplerFactor(value); + + public static void DopplerVelocity(float value) => bindings.alDopplerVelocity(value); + + public static void SpeedOfSound(float value) => bindings.alSpeedOfSound(value); + + public static void DistanceModel(ALEnum distanceModel) => bindings.alDistanceModel(distanceModel); + } + + + internal enum ALEnum + { + //Capability + Invalid = -1, + + //Listener + Gain = 0x100A, + EfxMetersPerUnit = 0x20004, + + //Vector3 Listener + Position = 0x1004, + Velocity = 0x1006, + + //Float[] Listener + Orientation = 0x100F, + + //Float Source + ReferenceDistance = 0x1020, + MaxDistance = 0x1023, + RolloffFactor = 0x1021, + Pitch = 0x1003, + //Gain = 0x100A, + MinGain = 0x100D, + MaxGain = 0x100E, + ConeInnerAngle = 0x1001, + ConeOuterAngle = 0x1002, + ConeOuterGain = 0x1022, + SecOffset = 0x1024, // AL_EXT_OFFSET extension. + EfxAirAbsorptionFactor = 0x20007, + EfxRoomRolloffFactor = 0x20008, + EfxConeOuterGainHighFrequency = 0x20009, + + //Vector3 Source + Direction = 0x1005, + + //8-Bit Boolean Source + SourceRelative = 0x202, + Looping = 0x1007, + EfxDirectFilterGainHighFrequencyAuto = 0x2000A, + EfxAuxiliarySendFilterGainAuto = 0x2000B, + EfxAuxiliarySendFilterGainHighFrequencyAuto = 0x2000C, + + //Int32 Source + ByteOffset = 0x1026, // AL_EXT_OFFSET extension. + SampleOffset = 0x1025, // AL_EXT_OFFSET extension. + Buffer = 0x1009, + BuffersQueued = 0x1015, + BuffersProcessed = 0x1016, + SourceType = 0x1027, + SourceState = 0x1010, + EfxDirectFilter = 0x20005, + + //3x Int32 Source + //Position = 0x1004, + //Velocity = 0x1006, + //Direction = 0x1005, + + /// Deprecated. Specify the channel mask. (Creative) Type: uint Range: [0 - 255] + //ChannelMask = 0x3000, + + //Source State + Initial = 0x1011, + Playing = 0x1012, + Paused = 0x1013, + Stopped = 0x1014, + + //Source Type + Static = 0x1028, + Streaming = 0x1029, + Undetermined = 0x1030, + + //Format + Mono8 = 0x1100, + Mono16 = 0x1101, + Stereo8 = 0x1102, + Stereo16 = 0x1103, + MonoALawExt = 0x10016, + StereoALawExt = 0x10017, + MonoMuLawExt = 0x10014, + StereoMuLawExt = 0x10015, + VorbisExt = 0x10003, + Mp3Ext = 0x10020, + MonoIma4Ext = 0x1300, + StereoIma4Ext = 0x1301, + MonoFloat32Ext = 0x10010, + StereoFloat32Ext = 0x10011, + MonoDoubleExt = 0x10012, + StereoDoubleExt = 0x10013, + Multi51Chn16Ext = 0x120B, + Multi51Chn32Ext = 0x120C, + Multi51Chn8Ext = 0x120A, + Multi61Chn16Ext = 0x120E, + Multi61Chn32Ext = 0x120F, + Multi61Chn8Ext = 0x120D, + Multi71Chn16Ext = 0x1211, + Multi71Chn32Ext = 0x1212, + Multi71Chn8Ext = 0x1210, + MultiQuad16Ext = 0x1205, + MultiQuad32Ext = 0x1206, + MultiQuad8Ext = 0x1204, + MultiRear16Ext = 0x1208, + MultiRear32Ext = 0x1209, + MultiRear8Ext = 0x1207, + + //Get Buffers Int32 + Frequency = 0x2001, + Bits = 0x2002, + Channels = 0x2003, + Size = 0x2004, + + //Buffer State + Unused = 0x2010, + Pending = 0x2011, + Processed = 0x2012, + + //Errors + NoError = 0, + InvalidName = 0xA001, + IllegalEnum = 0xA002, + InvalidEnum = 0xA002, + InvalidValue = 0xA003, + IllegalCommand = 0xA004, + InvalidOperation = 0xA004, + OutOfMemory = 0xA005, + + //Get String + Vendor = 0xB001, + Version = 0xB002, + Renderer = 0xB003, + Extensions = 0xB004, + + //Get Float + DopplerFactor = 0xC000, + DopplerVelocity = 0xC001, + SpeedOfSound = 0xC003, + + //Get Int + DistanceModel = 0xD000, + + //Get Distance Model + None = 0, + InverseDistance = 0xD001, + InverseDistanceClamped = 0xD002, + LinearDistance = 0xD003, + LinearDistanceClamped = 0xD004, + ExponentDistance = 0xD005, + ExponentDistanceClamped = 0xD006, + } +} diff --git a/Platforms/OpenAL/AL_Bindings.cs b/Platforms/OpenAL/AL_Bindings.cs new file mode 100644 index 0000000..163952e --- /dev/null +++ b/Platforms/OpenAL/AL_Bindings.cs @@ -0,0 +1,293 @@ +using Foster.Framework; +using System; +using System.Runtime.InteropServices; + + +namespace Foster.OpenAL +{ + internal class AL_Bindings + { + private readonly ISystemOpenAL system; + + public AL_Bindings(ISystemOpenAL system) + { + this.system = system ?? throw new Exception("AL Module requires a System that implements ProcAddress"); + + CreateDelegate(ref alGetString!, "alGetString"); + CreateDelegate(ref alEnable!, "alEnable"); + CreateDelegate(ref alDisable!, "alDisable"); + CreateDelegate(ref alGetIntegerv!, "alGetIntegerv"); + CreateDelegate(ref alIsEnabled!, "alIsEnabled"); + CreateDelegate(ref alGetErrorString!, "alGetErrorString"); + CreateDelegate(ref alGetString!, "alGetString"); + CreateDelegate(ref alGetInteger!, "alGetInteger"); + CreateDelegate(ref alGetFloat!, "alGetFloat"); + CreateDelegate(ref alGetError!, "alGetError"); + CreateDelegate(ref alIsExtensionPresent!, "alIsExtensionPresent"); + CreateDelegate(ref alGetProcAddress!, "alGetProcAddress"); + CreateDelegate(ref alGetEnumValue!, "alGetEnumValue"); + CreateDelegate(ref alListenerf!, "alListenerf"); + CreateDelegate(ref alListener3f!, "alListener3f"); + CreateDelegate(ref alListenerfv!, "alListenerfv"); + CreateDelegate(ref alGetListenerf!, "alGetListenerf"); + CreateDelegate(ref alGetListener3f!, "alGetListener3f"); + CreateDelegate(ref alGetListenerfv!, "alGetListenerfv"); + CreateDelegate(ref alGenSources!, "alGenSources"); + CreateDelegate(ref alDeleteSources!, "alDeleteSources"); + CreateDelegate(ref alIsSource!, "alIsSource"); + CreateDelegate(ref alSourcef!, "alSourcef"); + CreateDelegate(ref alSource3f!, "alSource3f"); + CreateDelegate(ref alSourcei!, "alSourcei"); + CreateDelegate(ref alSource3i!, "alSource3i"); + CreateDelegate(ref alGetSourcef!, "alGetSourcef"); + CreateDelegate(ref alGetSourcei!, "alGetSourcei"); + CreateDelegate(ref alGetSource3f!, "alGetSource3f"); + CreateDelegate(ref alGetSource3i!, "alGetSource3i"); + CreateDelegate(ref alSourcePlayv!, "alSourcePlayv"); + CreateDelegate(ref alSourceStopv!, "alSourceStopv"); + CreateDelegate(ref alSourceRewindv!, "alSourceRewindv"); + CreateDelegate(ref alSourcePausev!, "alSourcePausev"); + CreateDelegate(ref alSourcePlay!, "alSourcePlay"); + CreateDelegate(ref alSourceStop!, "alSourceStop"); + CreateDelegate(ref alSourceRewind!, "alSourceRewind"); + CreateDelegate(ref alSourcePause!, "alSourcePause"); + CreateDelegate(ref alSourceQueueBuffers!, "alSourceQueueBuffers"); + CreateDelegate(ref alSourceUnqueueBuffers!, "alSourceUnqueueBuffers"); + CreateDelegate(ref alGenBuffers!, "alGenBuffers"); + CreateDelegate(ref alDeleteBuffers!, "alDeleteBuffers"); + CreateDelegate(ref alIsBuffer!, "alIsBuffer"); + CreateDelegate(ref alBufferData!, "alBufferData"); + CreateDelegate(ref alGetBufferi!, "alGetBufferi"); + CreateDelegate(ref alDopplerFactor!, "alDopplerFactor"); + CreateDelegate(ref alDopplerVelocity!, "alDopplerVelocity"); + CreateDelegate(ref alSpeedOfSound!, "alSpeedOfSound"); + CreateDelegate(ref alDistanceModel!, "alDistanceModel"); + } + + private void CreateDelegate(ref T def, string name) where T : class + { + var addr = system.GetALProcAddress(name); + if (addr != IntPtr.Zero && (Marshal.GetDelegateForFunctionPointer(addr, typeof(T)) is T del)) + def = del; + } + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate IntPtr GetString(ALEnum name); + public GetString alGetString; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Enable(ALEnum mode); + public Enable alEnable; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Disable(ALEnum mode); + public Disable alDisable; + + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetIntegerv(ALEnum name, out int data); + public GetIntegerv alGetIntegerv; + + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool IsEnabled(ALEnum name); + public IsEnabled alIsEnabled; + + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate IntPtr GetErrorString(ALEnum name); + public GetErrorString alGetErrorString; + + ///// This function retrieves an OpenAL string property. + ///// The human-readable errorstring to be returned. + ///// Returns a pointer to a null-terminated string. + //public static string GetErrorString(ALError param) => Get((ALGetString)param); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint GetInteger(ALEnum name); + public GetInteger alGetInteger; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate float GetFloat(ALEnum name); + public GetFloat alGetFloat; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate ALEnum GetError(); + public GetError alGetError; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool IsExtensionPresent(string extName); + public IsExtensionPresent alIsExtensionPresent; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate IntPtr GetProcAddress(string fName); + public GetProcAddress alGetProcAddress; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint GetEnumValue(string ename); + public GetEnumValue alGetEnumValue; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Listenerf(ALEnum param, float value); + public Listenerf alListenerf; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Listener3f(ALEnum param, float value1, float value2, float value3); + public Listener3f alListener3f; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void Listenerfv(ALEnum param, [Out] float* values); + public Listenerfv alListenerfv; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetListenerf(ALEnum param, out float value); + public GetListenerf alGetListenerf; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetListener3f(ALEnum param, out float value1, out float value2, out float value3); + public GetListener3f alGetListener3f; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetListenerfv(ALEnum param, ref float values); + public GetListenerfv alGetListenerfv; + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void GetListenerfv(ALEnum param, float[] values); + //public GetListenerfv alGetListenerfv; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void GenSources(int n, IntPtr sources); + public GenSources alGenSources; + + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void GenSources(int n, ref uint sources); + //public GenSources alGenSources; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void DeleteSources(int n, IntPtr sources); + public DeleteSources alDeleteSources; + + //[UnmanagedFunctionPointer(CallingConvention.StdCall)] + //public delegate void DeleteSources(int n, ref uint sources); + //public DeleteSources alDeleteSources; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool IsSource (uint sid); + public IsSource alIsSource; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Sourcef (uint sid, ALEnum param, float value); + public Sourcef alSourcef; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Source3f(uint sid, ALEnum param, float value1, float value2, float value3); + public Source3f alSource3f; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Sourcei(uint sid, ALEnum param, uint value); + public Sourcei alSourcei; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void Source3i(uint sid, ALEnum param, uint value1, uint value2, uint value3); + public Source3i alSource3i; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetSourcef(uint sid, ALEnum param, out float value); + public GetSourcef alGetSourcef; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetSource3f(uint sid, ALEnum param, out float value1, out float value2, out float value3); + public GetSource3f alGetSource3f; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetSourcei(uint sid, ALEnum param, out uint value); + public GetSourcei alGetSourcei; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetSource3i(uint sid, ALEnum param, out uint value1, out uint value2, out uint value3); + public GetSource3i alGetSource3i; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourcePlayv(int ns, IntPtr sids); + public SourcePlayv alSourcePlayv; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourceStopv(int ns, IntPtr sids); + public SourceStopv alSourceStopv; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourceRewindv(int ns, IntPtr sids); + public SourceRewindv alSourceRewindv; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourcePausev(int ns, IntPtr sids); + public SourcePausev alSourcePausev; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void SourcePlay(uint sid); + public SourcePlay alSourcePlay; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void SourceStop(uint sid); + public SourceStop alSourceStop; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void SourceRewind(uint sid); + public SourceRewind alSourceRewind; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void SourcePause(uint sid); + public SourcePause alSourcePause; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourceQueueBuffers(uint sid, uint numEntries, IntPtr bids); + public SourceQueueBuffers alSourceQueueBuffers; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void SourceUnqueueBuffers(uint sid, uint numEntries, IntPtr bids); + public SourceUnqueueBuffers alSourceUnqueueBuffers; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void GenBuffers(uint n, IntPtr buffers); + public GenBuffers alGenBuffers; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate void DeleteBuffers(uint n, IntPtr buffers); + public DeleteBuffers alDeleteBuffers; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate bool IsBuffer(uint bid); + public IsBuffer alIsBuffer; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void BufferData(uint bid, ALEnum format, IntPtr buffer, uint size, uint freq); + public BufferData alBufferData; + + // AL_API void AL_APIENTRY alGetBufferf( ALuint bid, ALenum param, ALfloat* value ); + // AL_API void AL_APIENTRY alGetBuffer3f( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + // AL_API void AL_APIENTRY alGetBufferfv( ALuint bid, ALenum param, ALfloat* values ); + // AL_API void AL_APIENTRY alGetBuffer3i( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + // AL_API void AL_APIENTRY alGetBufferiv( ALuint bid, ALenum param, ALint* values ); + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void GetBufferi(uint bid, ALEnum param, [Out] out uint value); + public GetBufferi alGetBufferi; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void DopplerFactor(float value); + public DopplerFactor alDopplerFactor; + + /// This function is deprecated and should not be used. + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void DopplerVelocity(float value); + public DopplerVelocity alDopplerVelocity; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void SpeedOfSound(float value); + public SpeedOfSound alSpeedOfSound; + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void DistanceModel(ALEnum distanceModel); + public DistanceModel alDistanceModel; + } +} \ No newline at end of file diff --git a/Platforms/OpenAL/AL_Enums.cs b/Platforms/OpenAL/AL_Enums.cs new file mode 100644 index 0000000..b0cf937 --- /dev/null +++ b/Platforms/OpenAL/AL_Enums.cs @@ -0,0 +1,430 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + public enum ALCapability : int + { + /// Currently no state toggles exist for vanilla OpenAL and no Extension uses it. + Invalid = -1, + } + + /// A list of valid 32-bit Float Listener/GetListener parameters. + public enum ALListenerf : int + { + /// Indicate the gain (Volume amplification) applied. Type: float Range: [0.0f - ? ] A value of 1.0 means un-attenuated/unchanged. Each division by 2 equals an attenuation of -6dB. Each multiplicaton with 2 equals an amplification of +6dB. A value of 0.0f is interpreted as zero volume and the channel is effectively disabled. + Gain = 0x100A, + + /// (EFX Extension) This setting is critical if Air Absorption effects are enabled because the amount of Air Absorption applied is directly related to the real-world distance between the Source and the Listener. centimeters 0.01f meters 1.0f kilometers 1000.0f Range [float.MinValue .. float.MaxValue] Default: 1.0f + EfxMetersPerUnit = 0x20004, + } + + /// A list of valid Math.Vector3 Listener/GetListener parameters. + public enum ALListener3f : int + { + /// Specify the current location in three dimensional space. OpenAL, like OpenGL, uses a right handed coordinate system, where in a frontal default view X (thumb) points right, Y points up (index finger), and Z points towards the viewer/camera (middle finger). To switch from a left handed coordinate system, flip the sign on the Z coordinate. Listener position is always in the world coordinate system. + Position = 0x1004, + + /// Specify the current velocity in three dimensional space. + Velocity = 0x1006, + } + + /// A list of valid float[] Listener/GetListener parameters. + public enum ALListenerfv : int + { + /// Indicate Listener orientation. Expects two Vector3, At followed by Up. + Orientation = 0x100F, + } + + /// A list of valid 32-bit Float Source/GetSource parameters. + public enum ALSourcef : int + { + /// Source specific reference distance. Type: float Range: [0.0f - float.PositiveInfinity] At 0.0f, no distance attenuation occurs. Type: float Default: 1.0f. + ReferenceDistance = 0x1020, + + /// Indicate distance above which Sources are not attenuated using the inverse clamped distance model. Default: float.PositiveInfinity Type: float Range: [0.0f - float.PositiveInfinity] + MaxDistance = 0x1023, + + /// Source specific rolloff factor. Type: float Range: [0.0f - float.PositiveInfinity] + RolloffFactor = 0x1021, + + /// Specify the pitch to be applied, either at Source, or on mixer results, at Listener. Range: [0.5f - 2.0f] Default: 1.0f + Pitch = 0x1003, + + /// Indicate the gain (volume amplification) applied. Type: float. Range: [0.0f - ? ] A value of 1.0 means un-attenuated/unchanged. Each division by 2 equals an attenuation of -6dB. Each multiplicaton with 2 equals an amplification of +6dB. A value of 0.0f is meaningless with respect to a logarithmic scale; it is interpreted as zero volume - the channel is effectively disabled. + Gain = 0x100A, + + /// Indicate minimum Source attenuation. Type: float Range: [0.0f - 1.0f] (Logarthmic) + MinGain = 0x100D, + + /// Indicate maximum Source attenuation. Type: float Range: [0.0f - 1.0f] (Logarthmic) + MaxGain = 0x100E, + + /// Directional Source, inner cone angle, in degrees. Range: [0-360] Default: 360 + ConeInnerAngle = 0x1001, + + /// Directional Source, outer cone angle, in degrees. Range: [0-360] Default: 360 + ConeOuterAngle = 0x1002, + + /// Directional Source, outer cone gain. Default: 0.0f Range: [0.0f - 1.0] (Logarithmic) + ConeOuterGain = 0x1022, + + /// The playback position, expressed in seconds. + SecOffset = 0x1024, // AL_EXT_OFFSET extension. + + /// (EFX Extension) This property is a multiplier on the amount of Air Absorption applied to the Source. The AL_AIR_ABSORPTION_FACTOR is multiplied by an internal Air Absorption Gain HF value of 0.994 (-0.05dB) per meter which represents normal atmospheric humidity and temperature. Range [0.0f .. 10.0f] Default: 0.0f + EfxAirAbsorptionFactor = 0x20007, + + /// (EFX Extension) This property is defined the same way as the Reverb Room Rolloff property: it is one of two methods available in the Effect Extension to attenuate the reflected sound (early reflections and reverberation) according to source-listener distance. Range [0.0f .. 10.0f] Default: 0.0f + EfxRoomRolloffFactor = 0x20008, + + /// (EFX Extension) A directed Source points in a specified direction. The Source sounds at full volume when the listener is directly in front of the source; it is attenuated as the listener circles the Source away from the front. Range [0.0f .. 1.0f] Default: 1.0f + EfxConeOuterGainHighFrequency = 0x20009, + } + + /// A list of valid Math.Vector3 Source/GetSource parameters. + public enum ALSource3f : int + { + /// Specify the current location in three dimensional space. OpenAL, like OpenGL, uses a right handed coordinate system, where in a frontal default view X (thumb) points right, Y points up (index finger), and Z points towards the viewer/camera (middle finger). To switch from a left handed coordinate system, flip the sign on the Z coordinate. Listener position is always in the world coordinate system. + Position = 0x1004, + + /// Specify the current velocity in three dimensional space. + Velocity = 0x1006, + + /// Specify the current direction vector. + Direction = 0x1005, + } + + /// A list of valid 8-bit boolean Source/GetSource parameters. + public enum ALSourceb : int + { + /// Indicate that the Source has relative coordinates. Type: bool Range: [True, False] + SourceRelative = 0x202, + + /// Indicate whether the Source is looping. Type: bool Range: [True, False] Default: False. + Looping = 0x1007, + + /// (EFX Extension) If this Source property is set to True, this Source’s direct-path is automatically filtered according to the orientation of the source relative to the listener and the setting of the Source property Sourcef.ConeOuterGainHF. Type: bool Range [False, True] Default: True + EfxDirectFilterGainHighFrequencyAuto = 0x2000A, + + /// (EFX Extension) If this Source property is set to True, the intensity of this Source’s reflected sound is automatically attenuated according to source-listener distance and source directivity (as determined by the cone parameters). If it is False, the reflected sound is not attenuated according to distance and directivity. Type: bool Range [False, True] Default: True + EfxAuxiliarySendFilterGainAuto = 0x2000B, + + /// (EFX Extension) If this Source property is AL_TRUE (its default value), the intensity of this Source’s reflected sound at high frequencies will be automatically attenuated according to the high-frequency source directivity as set by the Sourcef.ConeOuterGainHF property. If this property is AL_FALSE, the Source’s reflected sound is not filtered at all according to the Source’s directivity. Type: bool Range [False, True] Default: True + EfxAuxiliarySendFilterGainHighFrequencyAuto = 0x2000C, + } + + /// A list of valid Int32 Source parameters. + public enum ALSourcei : int + { + /// The playback position, expressed in bytes. + ByteOffset = 0x1026, // AL_EXT_OFFSET extension. + + /// The playback position, expressed in samples. + SampleOffset = 0x1025, // AL_EXT_OFFSET extension. + + /// Indicate the Buffer to provide sound samples. Type: uint Range: any valid Buffer Handle. + Buffer = 0x1009, + + /// Source type (Static, Streaming or undetermined). Use enum AlSourceType for comparison + SourceType = 0x1027, + + /// (EFX Extension) This Source property is used to apply filtering on the direct-path (dry signal) of a Source. + EfxDirectFilter = 0x20005, + } + + /// A list of valid 3x Int32 Source/GetSource parameters. + public enum ALSource3i : int + { + /// Specify the current location in three dimensional space. OpenAL, like OpenGL, uses a right handed coordinate system, where in a frontal default view X (thumb) points right, Y points up (index finger), and Z points towards the viewer/camera (middle finger). To switch from a left handed coordinate system, flip the sign on the Z coordinate. Listener position is always in the world coordinate system. + Position = 0x1004, + + /// Specify the current velocity in three dimensional space. + Velocity = 0x1006, + + /// Specify the current direction vector. + Direction = 0x1005, + } + + /// A list of valid Int32 GetSource parameters. + public enum ALGetSourcei : int + { + /// The playback position, expressed in bytes. AL_EXT_OFFSET Extension. + ByteOffset = 0x1026, + + /// The playback position, expressed in samples. AL_EXT_OFFSET Extension. + SampleOffset = 0x1025, + + /// Indicate the Buffer to provide sound samples. Type: uint Range: any valid Buffer Handle. + Buffer = 0x1009, + + /// The state of the source (Stopped, Playing, etc.) Use the enum AlSourceState for comparison. + SourceState = 0x1010, + + /// The number of buffers queued on this source. + BuffersQueued = 0x1015, + + /// The number of buffers in the queue that have been processed. + BuffersProcessed = 0x1016, + + /// Source type (Static, Streaming or undetermined). Use enum AlSourceType for comparison. + SourceType = 0x1027, + } + + /* + public enum ALDeprecated : int + { + /// Deprecated. Specify the channel mask. (Creative) Type: uint Range: [0 - 255] + ChannelMask = 0x3000, + } + */ + + /// Source state information, can be retrieved by AL.Source() with ALSourcei.SourceState. + public enum ALSourceState : int + { + /// Default State when loaded, can be manually set with AL.SourceRewind(). + Initial = 0x1011, + + /// The source is currently playing. + Playing = 0x1012, + + /// The source has paused playback. + Paused = 0x1013, + + /// The source is not playing. + Stopped = 0x1014, + } + + /// Source type information, can be retrieved by AL.Source() with ALSourcei.SourceType. + public enum ALSourceType : int + { + /// Source is Static if a Buffer has been attached using AL.Source with the parameter Sourcei.Buffer. + Static = 0x1028, + + /// Source is Streaming if one or more Buffers have been attached using AL.SourceQueueBuffers + Streaming = 0x1029, + + /// Source is undetermined when it has a null Buffer attached + Undetermined = 0x1030, + } + + /// Sound samples: Format specifier. + public enum ALFormat : int + { + /// 1 Channel, 8 bits per sample. + Mono8 = 0x1100, + + /// 1 Channel, 16 bits per sample. + Mono16 = 0x1101, + + /// 2 Channels, 8 bits per sample each. + Stereo8 = 0x1102, + + /// 2 Channels, 16 bits per sample each. + Stereo16 = 0x1103, + + /// 1 Channel, A-law encoded data. Requires Extension: AL_EXT_ALAW + MonoALawExt = 0x10016, + + /// 2 Channels, A-law encoded data. Requires Extension: AL_EXT_ALAW + StereoALawExt = 0x10017, + + /// 1 Channel, µ-law encoded data. Requires Extension: AL_EXT_MULAW + MonoMuLawExt = 0x10014, + + /// 2 Channels, µ-law encoded data. Requires Extension: AL_EXT_MULAW + StereoMuLawExt = 0x10015, + + /// Ogg Vorbis encoded data. Requires Extension: AL_EXT_vorbis + VorbisExt = 0x10003, + + /// MP3 encoded data. Requires Extension: AL_EXT_mp3 + Mp3Ext = 0x10020, + + /// 1 Channel, IMA4 ADPCM encoded data. Requires Extension: AL_EXT_IMA4 + MonoIma4Ext = 0x1300, + + /// 2 Channels, IMA4 ADPCM encoded data. Requires Extension: AL_EXT_IMA4 + StereoIma4Ext = 0x1301, + + /// 1 Channel, single-precision floating-point data. Requires Extension: AL_EXT_float32 + MonoFloat32Ext = 0x10010, + + /// 2 Channels, single-precision floating-point data. Requires Extension: AL_EXT_float32 + StereoFloat32Ext = 0x10011, + + /// 1 Channel, double-precision floating-point data. Requires Extension: AL_EXT_double + MonoDoubleExt = 0x10012, + + /// 2 Channels, double-precision floating-point data. Requires Extension: AL_EXT_double + StereoDoubleExt = 0x10013, + + /// Multichannel 5.1, 16-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi51Chn16Ext = 0x120B, + + /// Multichannel 5.1, 32-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi51Chn32Ext = 0x120C, + + /// Multichannel 5.1, 8-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi51Chn8Ext = 0x120A, + + /// Multichannel 6.1, 16-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi61Chn16Ext = 0x120E, + + /// Multichannel 6.1, 32-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi61Chn32Ext = 0x120F, + + /// Multichannel 6.1, 8-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi61Chn8Ext = 0x120D, + + /// Multichannel 7.1, 16-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi71Chn16Ext = 0x1211, + + /// Multichannel 7.1, 32-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi71Chn32Ext = 0x1212, + + /// Multichannel 7.1, 8-bit data. Requires Extension: AL_EXT_MCFORMATS + Multi71Chn8Ext = 0x1210, + + /// Multichannel 4.0, 16-bit data. Requires Extension: AL_EXT_MCFORMATS + MultiQuad16Ext = 0x1205, + + /// Multichannel 4.0, 32-bit data. Requires Extension: AL_EXT_MCFORMATS + MultiQuad32Ext = 0x1206, + + /// Multichannel 4.0, 8-bit data. Requires Extension: AL_EXT_MCFORMATS + MultiQuad8Ext = 0x1204, + + /// 1 Channel rear speaker, 16-bit data. See Quadrophonic setups. Requires Extension: AL_EXT_MCFORMATS + MultiRear16Ext = 0x1208, + + /// 1 Channel rear speaker, 32-bit data. See Quadrophonic setups. Requires Extension: AL_EXT_MCFORMATS + MultiRear32Ext = 0x1209, + + /// 1 Channel rear speaker, 8-bit data. See Quadrophonic setups. Requires Extension: AL_EXT_MCFORMATS + MultiRear8Ext = 0x1207, + } + + /// A list of valid Int32 GetBuffer parameters. + public enum ALGetBufferi : int + { + /// Sound sample's frequency, in units of hertz [Hz]. This is the number of samples per second. Half of the sample frequency marks the maximum significant frequency component. + Frequency = 0x2001, + + /// Bit depth of the buffer. Should be 8 or 16. + Bits = 0x2002, + + /// Number of channels in buffer. > 1 is valid, but buffer won’t be positioned when played. 1 for Mono, 2 for Stereo. + Channels = 0x2003, + + /// size of the Buffer in bytes. + Size = 0x2004, + + // Deprecated: From Manual, not in header: AL_DATA ( i, iv ) original location where buffer was copied from generally useless, as was probably freed after buffer creation + } + + /// Buffer state. Not supported for public use (yet). + public enum ALBufferState : int + { + /// Buffer state. Not supported for public use (yet). + Unused = 0x2010, + + /// Buffer state. Not supported for public use (yet). + Pending = 0x2011, + + /// Buffer state. Not supported for public use (yet). + Processed = 0x2012, + } + + /// Returned by AL.GetError. + public enum ALError : int + { + /// No OpenAL Error. + NoError = 0, + + /// Invalid Name paramater passed to OpenAL call. + InvalidName = 0xA001, + + /// Invalid parameter passed to OpenAL call. + IllegalEnum = 0xA002, + + /// Invalid parameter passed to OpenAL call. + InvalidEnum = 0xA002, + + /// Invalid OpenAL enum parameter value. + InvalidValue = 0xA003, + + /// Illegal OpenAL call. + IllegalCommand = 0xA004, + + /// Illegal OpenAL call. + InvalidOperation = 0xA004, + + /// No OpenAL memory left. + OutOfMemory = 0xA005, + } + + /// A list of valid string AL.Get() parameters. + public enum ALGetString : int + { + /// Gets the Vendor name. + Vendor = 0xB001, + + /// Gets the driver version. + Version = 0xB002, + + /// Gets the renderer mode. + Renderer = 0xB003, + + /// Gets a list of all available Extensions, separated with spaces. + Extensions = 0xB004, + } + + /// A list of valid 32-bit Float AL.Get() parameters. + public enum ALGetFloat : int + { + /// Doppler scale. Default 1.0f + DopplerFactor = 0xC000, + + /// Tweaks speed of propagation. This functionality is deprecated. + DopplerVelocity = 0xC001, + + /// Speed of Sound in units per second. Default: 343.3f + SpeedOfSound = 0xC003, + } + + /// A list of valid Int32 AL.Get() parameters. + public enum ALGetInteger : int + { + /// See enum ALDistanceModel. + DistanceModel = 0xD000, + } + + /// Used by AL.DistanceModel(), the distance model can be retrieved by AL.Get() with ALGetInteger.DistanceModel. + public enum ALDistanceModel : int + { + /// Bypasses all distance attenuation calculation for all Sources. + None = 0, + + /// InverseDistance is equivalent to the IASIG I3DL2 model with the exception that ALSourcef.ReferenceDistance does not imply any clamping. + InverseDistance = 0xD001, + + /// InverseDistanceClamped is the IASIG I3DL2 model, with ALSourcef.ReferenceDistance indicating both the reference distance and the distance below which gain will be clamped. + InverseDistanceClamped = 0xD002, + + /// AL_EXT_LINEAR_DISTANCE extension. + LinearDistance = 0xD003, + + /// AL_EXT_LINEAR_DISTANCE extension. + LinearDistanceClamped = 0xD004, + + /// AL_EXT_EXPONENT_DISTANCE extension. + ExponentDistance = 0xD005, + + /// AL_EXT_EXPONENT_DISTANCE extension. + ExponentDistanceClamped = 0xD006, + } +} diff --git a/Platforms/OpenAL/ConstCharPtrMarshaler.cs b/Platforms/OpenAL/ConstCharPtrMarshaler.cs new file mode 100644 index 0000000..9366c7a --- /dev/null +++ b/Platforms/OpenAL/ConstCharPtrMarshaler.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.OpenAL +{ + internal class ConstCharPtrMarshaler : ICustomMarshaler + { + private static readonly ConstCharPtrMarshaler Instance = new ConstCharPtrMarshaler(); + + public void CleanUpManagedData(object ManagedObj) + { + } + + public void CleanUpNativeData(IntPtr pNativeData) + { + } + + public int GetNativeDataSize() + { + return IntPtr.Size; + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + switch (ManagedObj) + { + case string str: + return Marshal.StringToHGlobalAnsi(str); + default: + throw new ArgumentException($"{nameof(ConstCharPtrMarshaler)} only supports marshaling of strings. Got '{ManagedObj.GetType()}'"); + } + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + return Marshal.PtrToStringAnsi(pNativeData); + } + + // See https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.custommarshalers.typetotypeinfomarshaler.getinstance +#pragma warning disable IDE0060 // Remove unused parameter + public static ICustomMarshaler GetInstance(string cookie) => Instance; +#pragma warning restore IDE0060 // Remove unused parameter + } +} diff --git a/Platforms/OpenAL/Foster.OpenAL.csproj b/Platforms/OpenAL/Foster.OpenAL.csproj new file mode 100644 index 0000000..e75f1d2 --- /dev/null +++ b/Platforms/OpenAL/Foster.OpenAL.csproj @@ -0,0 +1,20 @@ + + + + net5.0 + + + + true + + + + + + + + + + + + diff --git a/Platforms/OpenAL/OpenAL32.dll b/Platforms/OpenAL/OpenAL32.dll new file mode 100644 index 0000000..a7e3dac Binary files /dev/null and b/Platforms/OpenAL/OpenAL32.dll differ