aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs')
-rw-r--r--Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs331
1 files changed, 331 insertions, 0 deletions
diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs
new file mode 100644
index 00000000..432ca42b
--- /dev/null
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOOutStream.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace SoundIOSharp
+{
+ public class SoundIOOutStream : IDisposable
+ {
+ internal SoundIOOutStream (Pointer<SoundIoOutStream> handle)
+ {
+ this.handle = handle;
+ }
+
+ Pointer<SoundIoOutStream> handle;
+
+ public void Dispose ()
+ {
+ Natives.soundio_outstream_destroy (handle);
+ }
+ // Equality (based on handle)
+
+ public override bool Equals (object other)
+ {
+ var d = other as SoundIOOutStream;
+
+ return d != null && (this.handle == d.handle);
+ }
+
+ public override int GetHashCode ()
+ {
+ return (int)(IntPtr)handle;
+ }
+
+ public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2)
+ {
+ return obj1 is null ? obj2 is null : obj1.Equals(obj2);
+ }
+
+ public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2)
+ {
+ return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
+ }
+
+ // fields
+
+ public SoundIODevice Device
+ {
+ get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
+ }
+
+ static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("device");
+
+ public SoundIOFormat Format
+ {
+ get { return (SoundIOFormat) Marshal.ReadInt32(handle, format_offset); }
+ set { Marshal.WriteInt32(handle, format_offset, (int) value); }
+ }
+
+ static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("format");
+
+ public int SampleRate
+ {
+ get { return Marshal.ReadInt32(handle, sample_rate_offset); }
+ set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
+ }
+
+ static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("sample_rate");
+
+ public SoundIOChannelLayout Layout
+ {
+ get { unsafe { return new SoundIOChannelLayout((IntPtr) (void*)((IntPtr)handle + layout_offset)); } }
+ set
+ {
+ unsafe
+ {
+ Buffer.MemoryCopy((void*)value.Handle, (void*)((IntPtr)handle + layout_offset), Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
+ }
+ }
+ }
+ static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("layout");
+
+ public double SoftwareLatency
+ {
+ get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
+ set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
+ }
+
+ static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("software_latency");
+
+ public float Volume
+ {
+ get { return MarshalEx.ReadFloat(handle, volume_offset); }
+ set { MarshalEx.WriteFloat(handle, volume_offset, value); }
+ }
+
+ static readonly int volume_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("volume");
+
+ // error_callback
+ public Action ErrorCallback
+ {
+ get { return error_callback; }
+ set
+ {
+ error_callback = value;
+ if (value == null)
+ {
+ error_callback_native = null;
+ }
+ else
+ {
+ error_callback_native = stream => error_callback();
+ }
+
+ var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
+ Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
+ }
+ }
+
+ static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("error_callback");
+
+ Action error_callback;
+ delegate void error_callback_delegate (IntPtr handle);
+ error_callback_delegate error_callback_native;
+
+ // write_callback
+ public Action<int, int> WriteCallback
+ {
+ get { return write_callback; }
+ set
+ {
+ write_callback = value;
+ if (value == null)
+ {
+ write_callback_native = null;
+ }
+ else
+ {
+ write_callback_native = (h, frame_count_min, frame_count_max) => write_callback(frame_count_min, frame_count_max);
+ }
+
+ var ptr = Marshal.GetFunctionPointerForDelegate (write_callback_native);
+ Marshal.WriteIntPtr (handle, write_callback_offset, ptr);
+ }
+ }
+
+ static readonly int write_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("write_callback");
+
+ Action<int, int> write_callback;
+ delegate void write_callback_delegate(IntPtr handle, int min, int max);
+ write_callback_delegate write_callback_native;
+
+ // underflow_callback
+ public Action UnderflowCallback
+ {
+ get { return underflow_callback; }
+ set
+ {
+ underflow_callback = value;
+ if (value == null)
+ {
+ underflow_callback_native = null;
+ }
+ else
+ {
+ underflow_callback_native = h => underflow_callback();
+ }
+
+ var ptr = Marshal.GetFunctionPointerForDelegate (underflow_callback_native);
+ Marshal.WriteIntPtr (handle, underflow_callback_offset, ptr);
+ }
+ }
+
+ static readonly int underflow_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("underflow_callback");
+
+ Action underflow_callback;
+ delegate void underflow_callback_delegate(IntPtr handle);
+ underflow_callback_delegate underflow_callback_native;
+
+ // FIXME: this should be taken care in more centralized/decent manner... we don't want to write
+ // this kind of code anywhere we need string marshaling.
+ List<IntPtr> allocated_hglobals = new List<IntPtr>();
+
+ public string Name {
+ get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
+ set
+ {
+ unsafe
+ {
+ var existing = Marshal.ReadIntPtr(handle, name_offset);
+ if (allocated_hglobals.Contains(existing))
+ {
+ allocated_hglobals.Remove(existing);
+ Marshal.FreeHGlobal(existing);
+ }
+
+ var ptr = Marshal.StringToHGlobalAnsi(value);
+ Marshal.WriteIntPtr(handle, name_offset, ptr);
+ allocated_hglobals.Add(ptr);
+ }
+ }
+ }
+
+ static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("name");
+
+ public bool NonTerminalHint
+ {
+ get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
+ }
+
+ static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("non_terminal_hint");
+
+ public int BytesPerFrame
+ {
+ get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
+ }
+
+ static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_frame");
+
+ public int BytesPerSample
+ {
+ get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
+ }
+
+ static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_sample");
+
+ public string LayoutErrorMessage
+ {
+ get
+ {
+ var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
+
+ return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
+ }
+ }
+
+ static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("layout_error");
+
+ // functions
+
+ public void Open ()
+ {
+ var ret = (SoundIoError)Natives.soundio_outstream_open(handle);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+ }
+
+ public void Start ()
+ {
+ var ret = (SoundIoError)Natives.soundio_outstream_start(handle);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+ }
+
+ public SoundIOChannelAreas BeginWrite(ref int frameCount)
+ {
+ IntPtr ptrs = default;
+ int nativeFrameCount = frameCount;
+
+ unsafe
+ {
+ var frameCountPtr = &nativeFrameCount;
+ var ptrptr = &ptrs;
+ var ret = (SoundIoError)Natives.soundio_outstream_begin_write(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
+
+ frameCount = *frameCountPtr;
+
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+
+ return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
+ }
+ }
+
+ public void EndWrite ()
+ {
+ var ret = (SoundIoError)Natives.soundio_outstream_end_write(handle);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+ }
+
+ public void ClearBuffer ()
+ {
+ _ = Natives.soundio_outstream_clear_buffer(handle);
+ }
+
+ public void Pause (bool pause)
+ {
+ var ret = (SoundIoError)Natives.soundio_outstream_pause(handle, pause);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+ }
+
+ public double GetLatency ()
+ {
+ unsafe
+ {
+ double* dptr = null;
+ IntPtr p = new IntPtr(dptr);
+
+ var ret = (SoundIoError)Natives.soundio_outstream_get_latency(handle, p);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+
+ dptr = (double*)p;
+
+ return *dptr;
+ }
+ }
+
+ public void SetVolume (double volume)
+ {
+ var ret = (SoundIoError)Natives.soundio_outstream_set_volume(handle, volume);
+ if (ret != SoundIoError.SoundIoErrorNone)
+ {
+ throw new SoundIOException(ret);
+ }
+ }
+ }
+}