aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Audio.Backends.SoundIo/Native
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Audio.Backends.SoundIo/Native')
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs178
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs13
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs75
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs107
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs8
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs49
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs22
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs11
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs25
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs164
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dllbin0 -> 85504 bytes
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylibbin0 -> 54976 bytes
-rw-r--r--src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.sobin0 -> 88584 bytes
13 files changed, 652 insertions, 0 deletions
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
new file mode 100644
index 00000000..9c3e686d
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
@@ -0,0 +1,178 @@
+using Ryujinx.Common.Memory;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public static partial class SoundIo
+ {
+ private const string LibraryName = "libsoundio";
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate void OnDeviceChangeNativeDelegate(IntPtr ctx);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate void OnEventsSignalDelegate(IntPtr ctx);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate void EmitRtPrioWarningDelegate();
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public unsafe delegate void JackCallbackDelegate(IntPtr msg);
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SoundIoStruct
+ {
+ public IntPtr UserData;
+ public IntPtr OnDeviceChange;
+ public IntPtr OnBackendDisconnected;
+ public IntPtr OnEventsSignal;
+ public SoundIoBackend CurrentBackend;
+ public IntPtr ApplicationName;
+ public IntPtr EmitRtPrioWarning;
+ public IntPtr JackInfoCallback;
+ public IntPtr JackErrorCallback;
+ }
+
+ public struct SoundIoChannelLayout
+ {
+ public IntPtr Name;
+ public int ChannelCount;
+ public Array24<SoundIoChannelId> Channels;
+
+ public static IntPtr GetDefault(int channelCount)
+ {
+ return soundio_channel_layout_get_default(channelCount);
+ }
+
+ public static unsafe SoundIoChannelLayout GetDefaultValue(int channelCount)
+ {
+ return Unsafe.AsRef<SoundIoChannelLayout>((SoundIoChannelLayout*)GetDefault(channelCount));
+ }
+ }
+
+ public struct SoundIoSampleRateRange
+ {
+ public int Min;
+ public int Max;
+ }
+
+ public struct SoundIoDevice
+ {
+ public IntPtr SoundIo;
+ public IntPtr Id;
+ public IntPtr Name;
+ public SoundIoDeviceAim Aim;
+ public IntPtr Layouts;
+ public int LayoutCount;
+ public SoundIoChannelLayout CurrentLayout;
+ public IntPtr Formats;
+ public int FormatCount;
+ public SoundIoFormat CurrentFormat;
+ public IntPtr SampleRates;
+ public int SampleRateCount;
+ public int SampleRateCurrent;
+ public double SoftwareLatencyMin;
+ public double SoftwareLatencyMax;
+ public double SoftwareLatencyCurrent;
+ public bool IsRaw;
+ public int RefCount;
+ public SoundIoError ProbeError;
+ }
+
+ public struct SoundIoOutStream
+ {
+ public IntPtr Device;
+ public SoundIoFormat Format;
+ public int SampleRate;
+ public SoundIoChannelLayout Layout;
+ public double SoftwareLatency;
+ public float Volume;
+ public IntPtr UserData;
+ public IntPtr WriteCallback;
+ public IntPtr UnderflowCallback;
+ public IntPtr ErrorCallback;
+ public IntPtr Name;
+ public bool NonTerminalHint;
+ public int BytesPerFrame;
+ public int BytesPerSample;
+ public SoundIoError LayoutError;
+ }
+
+ public struct SoundIoChannelArea
+ {
+ public IntPtr Pointer;
+ public int Step;
+ }
+
+ [LibraryImport(LibraryName)]
+ public static partial IntPtr soundio_create();
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_connect(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial void soundio_disconnect(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial void soundio_flush_events(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial int soundio_output_device_count(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial int soundio_default_output_device_index(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial IntPtr soundio_get_output_device(IntPtr ctx, int index);
+
+ [LibraryImport(LibraryName)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format);
+
+ [LibraryImport(LibraryName)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout);
+
+ [LibraryImport(LibraryName)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate);
+
+ [LibraryImport(LibraryName)]
+ public static partial IntPtr soundio_outstream_create(IntPtr devCtx);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause);
+
+ [LibraryImport(LibraryName)]
+ public static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume);
+
+ [LibraryImport(LibraryName)]
+ public static partial void soundio_outstream_destroy(IntPtr streamCtx);
+
+ [LibraryImport(LibraryName)]
+ public static partial void soundio_destroy(IntPtr ctx);
+
+ [LibraryImport(LibraryName)]
+ public static partial IntPtr soundio_channel_layout_get_default(int channelCount);
+
+ [LibraryImport(LibraryName)]
+ public static partial IntPtr soundio_strerror(SoundIoError err);
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
new file mode 100644
index 00000000..92f8ea37
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public enum SoundIoBackend : int
+ {
+ None = 0,
+ Jack = 1,
+ PulseAudio = 2,
+ Alsa = 3,
+ CoreAudio = 4,
+ Wasapi = 5,
+ Dummy = 6
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
new file mode 100644
index 00000000..70346e0b
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
@@ -0,0 +1,75 @@
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public enum SoundIoChannelId
+ {
+ Invalid = 0,
+ FrontLeft = 1,
+ FrontRight = 2,
+ FrontCenter = 3,
+ Lfe = 4,
+ BackLeft = 5,
+ BackRight = 6,
+ FrontLeftCenter = 7,
+ FrontRightCenter = 8,
+ BackCenter = 9,
+ SideLeft = 10,
+ SideRight = 11,
+ TopCenter = 12,
+ TopFrontLeft = 13,
+ TopFrontCenter = 14,
+ TopFrontRight = 15,
+ TopBackLeft = 16,
+ TopBackCenter = 17,
+ TopBackRight = 18,
+ BackLeftCenter = 19,
+ BackRightCenter = 20,
+ FrontLeftWide = 21,
+ FrontRightWide = 22,
+ FrontLeftHigh = 23,
+ FrontCenterHigh = 24,
+ FrontRightHigh = 25,
+ TopFrontLeftCenter = 26,
+ TopFrontRightCenter = 27,
+ TopSideLeft = 28,
+ TopSideRight = 29,
+ LeftLfe = 30,
+ RightLfe = 31,
+ Lfe2 = 32,
+ BottomCenter = 33,
+ BottomLeftCenter = 34,
+ BottomRightCenter = 35,
+ MsMid = 36,
+ MsSide = 37,
+ AmbisonicW = 38,
+ AmbisonicX = 39,
+ AmbisonicY = 40,
+ AmbisonicZ = 41,
+ XyX = 42,
+ XyY = 43,
+ HeadphonesLeft = 44,
+ HeadphonesRight = 45,
+ ClickTrack = 46,
+ ForeignLanguage = 47,
+ HearingImpaired = 48,
+ Narration = 49,
+ Haptic = 50,
+ DialogCentricMix = 51,
+ Aux = 52,
+ Aux0 = 53,
+ Aux1 = 54,
+ Aux2 = 55,
+ Aux3 = 56,
+ Aux4 = 57,
+ Aux5 = 58,
+ Aux6 = 59,
+ Aux7 = 60,
+ Aux8 = 61,
+ Aux9 = 62,
+ Aux10 = 63,
+ Aux11 = 64,
+ Aux12 = 65,
+ Aux13 = 66,
+ Aux14 = 67,
+ Aux15 = 68,
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
new file mode 100644
index 00000000..3744c2e6
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Reflection.Metadata;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
+
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public class SoundIoContext : IDisposable
+ {
+ private IntPtr _context;
+ private Action<SoundIoError> _onBackendDisconnect;
+ private OnBackendDisconnectedDelegate _onBackendDisconnectNative;
+
+ public IntPtr Context => _context;
+
+ internal SoundIoContext(IntPtr context)
+ {
+ _context = context;
+ _onBackendDisconnect = null;
+ _onBackendDisconnectNative = null;
+ }
+
+ public SoundIoError Connect() => soundio_connect(_context);
+ public void Disconnect() => soundio_disconnect(_context);
+
+ public void FlushEvents() => soundio_flush_events(_context);
+
+ public int OutputDeviceCount => soundio_output_device_count(_context);
+
+ public int DefaultOutputDeviceIndex => soundio_default_output_device_index(_context);
+
+ public Action<SoundIoError> OnBackendDisconnect
+ {
+ get { return _onBackendDisconnect; }
+ set
+ {
+ _onBackendDisconnect = value;
+
+ if (_onBackendDisconnect == null)
+ {
+ _onBackendDisconnectNative = null;
+ }
+ else
+ {
+ _onBackendDisconnectNative = (ctx, err) => _onBackendDisconnect(err);
+ }
+
+ GetContext().OnBackendDisconnected = Marshal.GetFunctionPointerForDelegate(_onBackendDisconnectNative);
+ }
+ }
+
+ private ref SoundIoStruct GetContext()
+ {
+ unsafe
+ {
+ return ref Unsafe.AsRef<SoundIoStruct>((SoundIoStruct*)_context);
+ }
+ }
+
+ public SoundIoDeviceContext GetOutputDevice(int index)
+ {
+ IntPtr deviceContext = soundio_get_output_device(_context, index);
+
+ if (deviceContext == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ return new SoundIoDeviceContext(deviceContext);
+ }
+
+ public static SoundIoContext Create()
+ {
+ IntPtr context = soundio_create();
+
+ if (context == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ return new SoundIoContext(context);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ IntPtr currentContext = Interlocked.Exchange(ref _context, IntPtr.Zero);
+
+ if (currentContext != IntPtr.Zero)
+ {
+ soundio_destroy(currentContext);
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~SoundIoContext()
+ {
+ Dispose(false);
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs
new file mode 100644
index 00000000..a0689d6d
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceAim.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public enum SoundIoDeviceAim
+ {
+ SoundIoDeviceAimInput = 0,
+ SoundIoDeviceAimOutput = 1
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs
new file mode 100644
index 00000000..42bcc6e3
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
+
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public class SoundIoDeviceContext
+ {
+ private readonly IntPtr _context;
+
+ public IntPtr Context => _context;
+
+ internal SoundIoDeviceContext(IntPtr context)
+ {
+ _context = context;
+ }
+
+ private ref SoundIoDevice GetDeviceContext()
+ {
+ unsafe
+ {
+ return ref Unsafe.AsRef<SoundIoDevice>((SoundIoDevice*)_context);
+ }
+ }
+
+ public bool IsRaw => GetDeviceContext().IsRaw;
+
+ public string Id => Marshal.PtrToStringAnsi(GetDeviceContext().Id);
+
+ public bool SupportsSampleRate(int sampleRate) => soundio_device_supports_sample_rate(_context, sampleRate);
+
+ public bool SupportsFormat(SoundIoFormat format) => soundio_device_supports_format(_context, format);
+
+ public bool SupportsChannelCount(int channelCount) => soundio_device_supports_layout(_context, SoundIoChannelLayout.GetDefault(channelCount));
+
+ public SoundIoOutStreamContext CreateOutStream()
+ {
+ IntPtr context = soundio_outstream_create(_context);
+
+ if (context == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ return new SoundIoOutStreamContext(context);
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
new file mode 100644
index 00000000..9e33fa19
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public enum SoundIoError
+ {
+ None = 0,
+ NoMem = 1,
+ InitAudioBackend = 2,
+ SystemResources = 3,
+ OpeningDevice = 4,
+ NoSuchDevice = 5,
+ Invalid = 6,
+ BackendUnavailable = 7,
+ Streaming = 8,
+ IncompatibleDevice = 9,
+ NoSuchClient = 10,
+ IncompatibleBackend = 11,
+ BackendDisconnected = 12,
+ Interrupted = 13,
+ Underflow = 14,
+ EncodingString = 15,
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
new file mode 100644
index 00000000..a033356e
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Runtime.InteropServices;
+using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
+
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ internal class SoundIoException : Exception
+ {
+ internal SoundIoException(SoundIoError error) : base(Marshal.PtrToStringAnsi(soundio_strerror(error))) { }
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
new file mode 100644
index 00000000..0eee9780
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public enum SoundIoFormat
+ {
+ Invalid = 0,
+ S8 = 1,
+ U8 = 2,
+ S16LE = 3,
+ S16BE = 4,
+ U16LE = 5,
+ U16BE = 6,
+ S24LE = 7,
+ S24BE = 8,
+ U24LE = 9,
+ U24BE = 10,
+ S32LE = 11,
+ S32BE = 12,
+ U32LE = 13,
+ U32BE = 14,
+ Float32LE = 15,
+ Float32BE = 16,
+ Float64LE = 17,
+ Float64BE = 18,
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
new file mode 100644
index 00000000..2e432b31
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
+
+namespace Ryujinx.Audio.Backends.SoundIo.Native
+{
+ public class SoundIoOutStreamContext : IDisposable
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private unsafe delegate void WriteCallbackDelegate(IntPtr ctx, int frameCountMin, int frameCountMax);
+
+ private IntPtr _context;
+ private IntPtr _nameStored;
+ private Action<int, int> _writeCallback;
+ private WriteCallbackDelegate _writeCallbackNative;
+
+ public IntPtr Context => _context;
+
+ internal SoundIoOutStreamContext(IntPtr context)
+ {
+ _context = context;
+ _nameStored = IntPtr.Zero;
+ _writeCallback = null;
+ _writeCallbackNative = null;
+ }
+
+ private ref SoundIoOutStream GetOutContext()
+ {
+ unsafe
+ {
+ return ref Unsafe.AsRef<SoundIoOutStream>((SoundIoOutStream*)_context);
+ }
+ }
+
+ public string Name
+ {
+ get => Marshal.PtrToStringAnsi(GetOutContext().Name);
+ set
+ {
+ var context = GetOutContext();
+
+ if (_nameStored != IntPtr.Zero && context.Name == _nameStored)
+ {
+ Marshal.FreeHGlobal(_nameStored);
+ }
+
+ _nameStored = Marshal.StringToHGlobalAnsi(value);
+ GetOutContext().Name = _nameStored;
+ }
+ }
+
+ public SoundIoChannelLayout Layout
+ {
+ get => GetOutContext().Layout;
+ set => GetOutContext().Layout = value;
+ }
+
+ public SoundIoFormat Format
+ {
+ get => GetOutContext().Format;
+ set => GetOutContext().Format = value;
+ }
+
+ public int SampleRate
+ {
+ get => GetOutContext().SampleRate;
+ set => GetOutContext().SampleRate = value;
+ }
+
+ public float Volume
+ {
+ get => GetOutContext().Volume;
+ set => GetOutContext().Volume = value;
+ }
+
+ public int BytesPerFrame
+ {
+ get => GetOutContext().BytesPerFrame;
+ set => GetOutContext().BytesPerFrame = value;
+ }
+
+ public int BytesPerSample
+ {
+ get => GetOutContext().BytesPerSample;
+ set => GetOutContext().BytesPerSample = value;
+ }
+
+ public Action<int, int> WriteCallback
+ {
+ get { return _writeCallback; }
+ set
+ {
+ _writeCallback = value;
+
+ if (_writeCallback == null)
+ {
+ _writeCallbackNative = null;
+ }
+ else
+ {
+ _writeCallbackNative = (ctx, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
+ }
+
+ GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
+ }
+ }
+
+ private static void CheckError(SoundIoError error)
+ {
+ if (error != SoundIoError.None)
+ {
+ throw new SoundIoException(error);
+ }
+ }
+
+ public void Open() => CheckError(soundio_outstream_open(_context));
+
+ public void Start() => CheckError(soundio_outstream_start(_context));
+
+ public void Pause(bool pause) => CheckError(soundio_outstream_pause(_context, pause));
+
+ public void SetVolume(double volume) => CheckError(soundio_outstream_set_volume(_context, volume));
+
+ public Span<SoundIoChannelArea> BeginWrite(ref int frameCount)
+ {
+ IntPtr arenas = default;
+ int nativeFrameCount = frameCount;
+
+ unsafe
+ {
+ var frameCountPtr = &nativeFrameCount;
+ var arenasPtr = &arenas;
+ CheckError(soundio_outstream_begin_write(_context, (IntPtr)arenasPtr, (IntPtr)frameCountPtr));
+
+ frameCount = *frameCountPtr;
+
+ return new Span<SoundIoChannelArea>((void*)arenas, Layout.ChannelCount);
+ }
+ }
+
+ public void EndWrite() => CheckError(soundio_outstream_end_write(_context));
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_context != IntPtr.Zero)
+ {
+ soundio_outstream_destroy(_context);
+ _context = IntPtr.Zero;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ~SoundIoOutStreamContext()
+ {
+ Dispose(false);
+ }
+ }
+}
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll
new file mode 100644
index 00000000..48804312
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dll
Binary files differ
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib
new file mode 100644
index 00000000..10171f4f
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.dylib
Binary files differ
diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so
new file mode 100644
index 00000000..87c8b506
--- /dev/null
+++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/libs/libsoundio.so
Binary files differ