audio: Cleanup Ryujinx.Audio and fix OpenAL issue (#1746)
* audio: Cleanup SoundIO and fix OpenAL issue * fix tabs by spaces * Fix extra spaces * Fix SoundIO.cs * Fix ContainsAudioOutBuffer
This commit is contained in:
parent
0108004691
commit
7b66cb0d90
@ -1,91 +0,0 @@
|
|||||||
namespace Ryujinx.Audio.Adpcm
|
|
||||||
{
|
|
||||||
public static class AdpcmDecoder
|
|
||||||
{
|
|
||||||
private const int SamplesPerFrame = 14;
|
|
||||||
private const int BytesPerFrame = 8;
|
|
||||||
|
|
||||||
public static int[] Decode(byte[] Buffer, AdpcmDecoderContext Context)
|
|
||||||
{
|
|
||||||
int Samples = GetSamplesCountFromSize(Buffer.Length);
|
|
||||||
|
|
||||||
int[] Pcm = new int[Samples * 2];
|
|
||||||
|
|
||||||
short History0 = Context.History0;
|
|
||||||
short History1 = Context.History1;
|
|
||||||
|
|
||||||
int InputOffset = 0;
|
|
||||||
int OutputOffset = 0;
|
|
||||||
|
|
||||||
while (InputOffset < Buffer.Length)
|
|
||||||
{
|
|
||||||
byte Header = Buffer[InputOffset++];
|
|
||||||
|
|
||||||
int Scale = 0x800 << (Header & 0xf);
|
|
||||||
|
|
||||||
int CoeffIndex = (Header >> 4) & 7;
|
|
||||||
|
|
||||||
short Coeff0 = Context.Coefficients[CoeffIndex * 2 + 0];
|
|
||||||
short Coeff1 = Context.Coefficients[CoeffIndex * 2 + 1];
|
|
||||||
|
|
||||||
int FrameSamples = SamplesPerFrame;
|
|
||||||
|
|
||||||
if (FrameSamples > Samples)
|
|
||||||
{
|
|
||||||
FrameSamples = Samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Value = 0;
|
|
||||||
|
|
||||||
for (int SampleIndex = 0; SampleIndex < FrameSamples; SampleIndex++)
|
|
||||||
{
|
|
||||||
int Sample;
|
|
||||||
|
|
||||||
if ((SampleIndex & 1) == 0)
|
|
||||||
{
|
|
||||||
Value = Buffer[InputOffset++];
|
|
||||||
|
|
||||||
Sample = (Value << 24) >> 28;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Sample = (Value << 28) >> 28;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Prediction = Coeff0 * History0 + Coeff1 * History1;
|
|
||||||
|
|
||||||
Sample = (Sample * Scale + Prediction + 0x400) >> 11;
|
|
||||||
|
|
||||||
short SaturatedSample = DspUtils.Saturate(Sample);
|
|
||||||
|
|
||||||
History1 = History0;
|
|
||||||
History0 = SaturatedSample;
|
|
||||||
|
|
||||||
Pcm[OutputOffset++] = SaturatedSample;
|
|
||||||
Pcm[OutputOffset++] = SaturatedSample;
|
|
||||||
}
|
|
||||||
|
|
||||||
Samples -= FrameSamples;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.History0 = History0;
|
|
||||||
Context.History1 = History1;
|
|
||||||
|
|
||||||
return Pcm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long GetSizeFromSamplesCount(int SamplesCount)
|
|
||||||
{
|
|
||||||
int Frames = SamplesCount / SamplesPerFrame;
|
|
||||||
|
|
||||||
return Frames * BytesPerFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetSamplesCountFromSize(long Size)
|
|
||||||
{
|
|
||||||
int Frames = (int)(Size / BytesPerFrame);
|
|
||||||
|
|
||||||
return Frames * SamplesPerFrame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
namespace Ryujinx.Audio.Adpcm
|
|
||||||
{
|
|
||||||
public class AdpcmDecoderContext
|
|
||||||
{
|
|
||||||
public short[] Coefficients;
|
|
||||||
|
|
||||||
public short History0;
|
|
||||||
public short History1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,27 +24,31 @@ namespace Ryujinx.Audio
|
|||||||
public short Right;
|
public short Right;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int Q15Bits = 16;
|
private const int Q15Bits = 16;
|
||||||
private const int RawQ15One = 1 << Q15Bits;
|
private const int RawQ15One = 1 << Q15Bits;
|
||||||
private const int RawQ15HalfOne = (int)(0.5f * RawQ15One);
|
private const int RawQ15HalfOne = (int)(0.5f * RawQ15One);
|
||||||
private const int Minus3dBInQ15 = (int)(0.707f * RawQ15One);
|
private const int Minus3dBInQ15 = (int)(0.707f * RawQ15One);
|
||||||
private const int Minus6dBInQ15 = (int)(0.501f * RawQ15One);
|
private const int Minus6dBInQ15 = (int)(0.501f * RawQ15One);
|
||||||
private const int Minus12dBInQ15 = (int)(0.251f * RawQ15One);
|
private const int Minus12dBInQ15 = (int)(0.251f * RawQ15One);
|
||||||
|
|
||||||
private static int[] DefaultSurroundToStereoCoefficients = new int[4]
|
private static readonly int[] DefaultSurroundToStereoCoefficients = new int[4]
|
||||||
{
|
{
|
||||||
RawQ15One,
|
RawQ15One,
|
||||||
Minus3dBInQ15,
|
Minus3dBInQ15,
|
||||||
Minus12dBInQ15,
|
Minus12dBInQ15,
|
||||||
Minus3dBInQ15,
|
Minus3dBInQ15
|
||||||
};
|
};
|
||||||
|
|
||||||
private static int[] DefaultStereoToMonoCoefficients = new int[2]
|
private static readonly int[] DefaultStereoToMonoCoefficients = new int[2]
|
||||||
{
|
{
|
||||||
Minus6dBInQ15,
|
Minus6dBInQ15,
|
||||||
Minus6dBInQ15,
|
Minus6dBInQ15
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private const int SurroundChannelCount = 6;
|
||||||
|
private const int StereoChannelCount = 2;
|
||||||
|
private const int MonoChannelCount = 1;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static ReadOnlySpan<Channel51FormatPCM16> GetSurroundBuffer(ReadOnlySpan<short> data)
|
private static ReadOnlySpan<Channel51FormatPCM16> GetSurroundBuffer(ReadOnlySpan<short> data)
|
||||||
{
|
{
|
||||||
@ -72,9 +76,6 @@ namespace Ryujinx.Audio
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static short[] DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
private static short[] DownMixSurroundToStereo(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
||||||
{
|
{
|
||||||
const int SurroundChannelCount = 6;
|
|
||||||
const int StereoChannelCount = 2;
|
|
||||||
|
|
||||||
int samplePerChannelCount = data.Length / SurroundChannelCount;
|
int samplePerChannelCount = data.Length / SurroundChannelCount;
|
||||||
|
|
||||||
short[] downmixedBuffer = new short[samplePerChannelCount * StereoChannelCount];
|
short[] downmixedBuffer = new short[samplePerChannelCount * StereoChannelCount];
|
||||||
@ -85,7 +86,7 @@ namespace Ryujinx.Audio
|
|||||||
{
|
{
|
||||||
Channel51FormatPCM16 channel = channels[i];
|
Channel51FormatPCM16 channel = channels[i];
|
||||||
|
|
||||||
downmixedBuffer[i * 2] = DownMixSurroundToStereo(coefficients, channel.BackLeft, channel.LowFrequency, channel.FrontCenter, channel.FrontLeft);
|
downmixedBuffer[i * 2] = DownMixSurroundToStereo(coefficients, channel.BackLeft, channel.LowFrequency, channel.FrontCenter, channel.FrontLeft);
|
||||||
downmixedBuffer[i * 2 + 1] = DownMixSurroundToStereo(coefficients, channel.BackRight, channel.LowFrequency, channel.FrontCenter, channel.FrontRight);
|
downmixedBuffer[i * 2 + 1] = DownMixSurroundToStereo(coefficients, channel.BackRight, channel.LowFrequency, channel.FrontCenter, channel.FrontRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,9 +96,6 @@ namespace Ryujinx.Audio
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static short[] DownMixStereoToMono(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
private static short[] DownMixStereoToMono(ReadOnlySpan<int> coefficients, ReadOnlySpan<short> data)
|
||||||
{
|
{
|
||||||
const int StereoChannelCount = 2;
|
|
||||||
const int MonoChannelCount = 1;
|
|
||||||
|
|
||||||
int samplePerChannelCount = data.Length / StereoChannelCount;
|
int samplePerChannelCount = data.Length / StereoChannelCount;
|
||||||
|
|
||||||
short[] downmixedBuffer = new short[samplePerChannelCount * MonoChannelCount];
|
short[] downmixedBuffer = new short[samplePerChannelCount * MonoChannelCount];
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
namespace Ryujinx.Audio
|
|
||||||
{
|
|
||||||
public static class DspUtils
|
|
||||||
{
|
|
||||||
public static short Saturate(int Value)
|
|
||||||
{
|
|
||||||
if (Value > short.MaxValue)
|
|
||||||
Value = short.MaxValue;
|
|
||||||
|
|
||||||
if (Value < short.MinValue)
|
|
||||||
Value = short.MinValue;
|
|
||||||
|
|
||||||
return (short)Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,17 +13,13 @@ namespace Ryujinx.Audio
|
|||||||
return targetChannelCount;
|
return targetChannelCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (targetChannelCount)
|
return targetChannelCount switch
|
||||||
{
|
{
|
||||||
case 6:
|
6 => SelectHardwareChannelCount(2),
|
||||||
return SelectHardwareChannelCount(2);
|
2 => SelectHardwareChannelCount(1),
|
||||||
case 2:
|
1 => throw new ArgumentException("No valid channel configuration found!"),
|
||||||
return SelectHardwareChannelCount(1);
|
_ => throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}"),
|
||||||
case 1:
|
};
|
||||||
throw new ArgumentException("No valid channel configuration found!");
|
|
||||||
default:
|
|
||||||
throw new ArgumentException($"Invalid targetChannelCount {targetChannelCount}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int OpenTrack(int sampleRate, int channels, ReleaseCallback callback)
|
int OpenTrack(int sampleRate, int channels, ReleaseCallback callback)
|
||||||
|
@ -4,308 +4,383 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIO : IDisposable
|
public class SoundIO : IDisposable
|
||||||
{
|
{
|
||||||
Pointer<SoundIo> handle;
|
Pointer<SoundIo> handle;
|
||||||
|
|
||||||
public SoundIO ()
|
public SoundIO()
|
||||||
{
|
{
|
||||||
handle = Natives.soundio_create ();
|
handle = Natives.soundio_create();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SoundIO (Pointer<SoundIo> handle)
|
internal SoundIO(Pointer<SoundIo> handle)
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose ()
|
public void Dispose ()
|
||||||
{
|
{
|
||||||
foreach (var h in allocated_hglobals)
|
foreach (var h in allocated_hglobals)
|
||||||
Marshal.FreeHGlobal (h);
|
{
|
||||||
Natives.soundio_destroy (handle);
|
Marshal.FreeHGlobal(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equality (based on handle)
|
Natives.soundio_destroy(handle);
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals (object other)
|
// Equality (based on handle)
|
||||||
{
|
|
||||||
var d = other as SoundIO;
|
|
||||||
return d != null && this.handle == d.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
public override bool Equals(object other)
|
||||||
{
|
{
|
||||||
return (int) (IntPtr) handle;
|
var d = other as SoundIO;
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIO obj1, SoundIO obj2)
|
return d != null && this.handle == d.handle;
|
||||||
{
|
}
|
||||||
return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIO obj1, SoundIO obj2)
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2);
|
return (int)(IntPtr)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields
|
public static bool operator == (SoundIO obj1, SoundIO obj2)
|
||||||
|
{
|
||||||
|
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
public static bool operator != (SoundIO obj1, SoundIO obj2)
|
||||||
// this kind of code anywhere we need string marshaling.
|
{
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr> ();
|
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
public string ApplicationName {
|
// fields
|
||||||
get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, app_name_offset)); }
|
|
||||||
set {
|
|
||||||
unsafe {
|
|
||||||
var existing = Marshal.ReadIntPtr (handle, app_name_offset);
|
|
||||||
if (allocated_hglobals.Contains (existing)) {
|
|
||||||
allocated_hglobals.Remove (existing);
|
|
||||||
Marshal.FreeHGlobal (existing);
|
|
||||||
}
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi (value);
|
|
||||||
Marshal.WriteIntPtr (handle, app_name_offset, ptr);
|
|
||||||
allocated_hglobals.Add (ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int app_name_offset = (int)Marshal.OffsetOf<SoundIo> ("app_name");
|
|
||||||
|
|
||||||
public SoundIOBackend CurrentBackend {
|
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
||||||
get { return (SoundIOBackend) Marshal.ReadInt32 (handle, current_backend_offset); }
|
// this kind of code anywhere we need string marshaling.
|
||||||
}
|
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
||||||
static readonly int current_backend_offset = (int)Marshal.OffsetOf<SoundIo> ("current_backend");
|
|
||||||
|
|
||||||
// emit_rtprio_warning
|
public string ApplicationName {
|
||||||
public Action EmitRealtimePriorityWarning {
|
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, app_name_offset)); }
|
||||||
get { return emit_rtprio_warning; }
|
set
|
||||||
set {
|
{
|
||||||
emit_rtprio_warning = value;
|
unsafe
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (on_devices_change);
|
{
|
||||||
Marshal.WriteIntPtr (handle, emit_rtprio_warning_offset, ptr);
|
var existing = Marshal.ReadIntPtr(handle, app_name_offset);
|
||||||
}
|
if (allocated_hglobals.Contains (existing))
|
||||||
}
|
{
|
||||||
static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf<SoundIo> ("emit_rtprio_warning");
|
allocated_hglobals.Remove(existing);
|
||||||
Action emit_rtprio_warning;
|
Marshal.FreeHGlobal(existing);
|
||||||
|
}
|
||||||
|
|
||||||
// jack_error_callback
|
var ptr = Marshal.StringToHGlobalAnsi(value);
|
||||||
public Action<string> JackErrorCallback {
|
Marshal.WriteIntPtr(handle, app_name_offset, ptr);
|
||||||
get { return jack_error_callback; }
|
allocated_hglobals.Add(ptr);
|
||||||
set {
|
}
|
||||||
jack_error_callback = value;
|
}
|
||||||
if (value == null)
|
}
|
||||||
jack_error_callback = null;
|
|
||||||
else
|
|
||||||
jack_error_callback_native = msg => jack_error_callback (msg);
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (jack_error_callback_native);
|
|
||||||
Marshal.WriteIntPtr (handle, jack_error_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf<SoundIo> ("jack_error_callback");
|
|
||||||
Action<string> jack_error_callback;
|
|
||||||
delegate void jack_error_delegate (string message);
|
|
||||||
jack_error_delegate jack_error_callback_native;
|
|
||||||
|
|
||||||
// jack_info_callback
|
static readonly int app_name_offset = (int)Marshal.OffsetOf<SoundIo>("app_name");
|
||||||
public Action<string> JackInfoCallback {
|
|
||||||
get { return jack_info_callback; }
|
|
||||||
set {
|
|
||||||
jack_info_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
jack_info_callback = null;
|
|
||||||
else
|
|
||||||
jack_info_callback_native = msg => jack_info_callback (msg);
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (jack_info_callback_native);
|
|
||||||
Marshal.WriteIntPtr (handle, jack_info_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf<SoundIo> ("jack_info_callback");
|
|
||||||
Action<string> jack_info_callback;
|
|
||||||
delegate void jack_info_delegate (string message);
|
|
||||||
jack_info_delegate jack_info_callback_native;
|
|
||||||
|
|
||||||
// on_backend_disconnect
|
public SoundIOBackend CurrentBackend
|
||||||
public Action<int> OnBackendDisconnect {
|
{
|
||||||
get { return on_backend_disconnect; }
|
get { return (SoundIOBackend)Marshal.ReadInt32(handle, current_backend_offset); }
|
||||||
set {
|
}
|
||||||
on_backend_disconnect = value;
|
|
||||||
if (value == null)
|
|
||||||
on_backend_disconnect_native = null;
|
|
||||||
else
|
|
||||||
on_backend_disconnect_native = (sio, err) => on_backend_disconnect (err);
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (on_backend_disconnect_native);
|
|
||||||
Marshal.WriteIntPtr (handle, on_backend_disconnect_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf<SoundIo> ("on_backend_disconnect");
|
|
||||||
Action<int> on_backend_disconnect;
|
|
||||||
delegate void on_backend_disconnect_delegate (IntPtr handle, int errorCode);
|
|
||||||
on_backend_disconnect_delegate on_backend_disconnect_native;
|
|
||||||
|
|
||||||
// on_devices_change
|
static readonly int current_backend_offset = (int)Marshal.OffsetOf<SoundIo>("current_backend");
|
||||||
public Action OnDevicesChange {
|
|
||||||
get { return on_devices_change; }
|
|
||||||
set {
|
|
||||||
on_devices_change = value;
|
|
||||||
if (value == null)
|
|
||||||
on_devices_change_native = null;
|
|
||||||
else
|
|
||||||
on_devices_change_native = sio => on_devices_change ();
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (on_devices_change_native);
|
|
||||||
Marshal.WriteIntPtr (handle, on_devices_change_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int on_devices_change_offset = (int)Marshal.OffsetOf<SoundIo> ("on_devices_change");
|
|
||||||
Action on_devices_change;
|
|
||||||
delegate void on_devices_change_delegate (IntPtr handle);
|
|
||||||
on_devices_change_delegate on_devices_change_native;
|
|
||||||
|
|
||||||
// on_events_signal
|
// emit_rtprio_warning
|
||||||
public Action OnEventsSignal {
|
public Action EmitRealtimePriorityWarning
|
||||||
get { return on_events_signal; }
|
{
|
||||||
set {
|
get { return emit_rtprio_warning; }
|
||||||
on_events_signal = value;
|
set
|
||||||
if (value == null)
|
{
|
||||||
on_events_signal_native = null;
|
emit_rtprio_warning = value;
|
||||||
else
|
|
||||||
on_events_signal_native = sio => on_events_signal ();
|
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change);
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (on_events_signal_native);
|
|
||||||
Marshal.WriteIntPtr (handle, on_events_signal_offset, ptr);
|
Marshal.WriteIntPtr(handle, emit_rtprio_warning_offset, ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static readonly int on_events_signal_offset = (int)Marshal.OffsetOf<SoundIo> ("on_events_signal");
|
|
||||||
Action on_events_signal;
|
static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf<SoundIo>("emit_rtprio_warning");
|
||||||
delegate void on_events_signal_delegate (IntPtr handle);
|
|
||||||
on_events_signal_delegate on_events_signal_native;
|
Action emit_rtprio_warning;
|
||||||
|
|
||||||
|
// jack_error_callback
|
||||||
|
public Action<string> JackErrorCallback
|
||||||
|
{
|
||||||
|
get { return jack_error_callback; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
jack_error_callback = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
jack_error_callback = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jack_error_callback_native = msg => jack_error_callback(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Marshal.GetFunctionPointerForDelegate(jack_error_callback_native);
|
||||||
|
Marshal.WriteIntPtr(handle, jack_error_callback_offset, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_error_callback");
|
||||||
|
|
||||||
|
Action<string> jack_error_callback;
|
||||||
|
delegate void jack_error_delegate(string message);
|
||||||
|
jack_error_delegate jack_error_callback_native;
|
||||||
|
|
||||||
|
// jack_info_callback
|
||||||
|
public Action<string> JackInfoCallback
|
||||||
|
{
|
||||||
|
get { return jack_info_callback; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
jack_info_callback = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
jack_info_callback = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jack_info_callback_native = msg => jack_info_callback(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Marshal.GetFunctionPointerForDelegate(jack_info_callback_native);
|
||||||
|
Marshal.WriteIntPtr(handle, jack_info_callback_offset, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_info_callback");
|
||||||
|
|
||||||
|
Action<string> jack_info_callback;
|
||||||
|
delegate void jack_info_delegate(string message);
|
||||||
|
jack_info_delegate jack_info_callback_native;
|
||||||
|
|
||||||
|
// on_backend_disconnect
|
||||||
|
public Action<int> OnBackendDisconnect
|
||||||
|
{
|
||||||
|
get { return on_backend_disconnect; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
on_backend_disconnect = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
on_backend_disconnect_native = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
on_backend_disconnect_native = (sio, err) => on_backend_disconnect(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Marshal.GetFunctionPointerForDelegate(on_backend_disconnect_native);
|
||||||
|
Marshal.WriteIntPtr(handle, on_backend_disconnect_offset, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf<SoundIo>("on_backend_disconnect");
|
||||||
|
|
||||||
|
Action<int> on_backend_disconnect;
|
||||||
|
delegate void on_backend_disconnect_delegate(IntPtr handle, int errorCode);
|
||||||
|
on_backend_disconnect_delegate on_backend_disconnect_native;
|
||||||
|
|
||||||
|
// on_devices_change
|
||||||
|
public Action OnDevicesChange
|
||||||
|
{
|
||||||
|
get { return on_devices_change; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
on_devices_change = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
on_devices_change_native = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
on_devices_change_native = sio => on_devices_change();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change_native);
|
||||||
|
Marshal.WriteIntPtr(handle, on_devices_change_offset, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int on_devices_change_offset = (int)Marshal.OffsetOf<SoundIo>("on_devices_change");
|
||||||
|
|
||||||
|
Action on_devices_change;
|
||||||
|
delegate void on_devices_change_delegate(IntPtr handle);
|
||||||
|
on_devices_change_delegate on_devices_change_native;
|
||||||
|
|
||||||
|
// on_events_signal
|
||||||
|
public Action OnEventsSignal
|
||||||
|
{
|
||||||
|
get { return on_events_signal; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
on_events_signal = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
on_events_signal_native = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
on_events_signal_native = sio => on_events_signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptr = Marshal.GetFunctionPointerForDelegate(on_events_signal_native);
|
||||||
|
Marshal.WriteIntPtr(handle, on_events_signal_offset, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int on_events_signal_offset = (int)Marshal.OffsetOf<SoundIo>("on_events_signal");
|
||||||
|
|
||||||
|
Action on_events_signal;
|
||||||
|
delegate void on_events_signal_delegate(IntPtr handle);
|
||||||
|
on_events_signal_delegate on_events_signal_native;
|
||||||
|
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
|
|
||||||
public int BackendCount {
|
public int BackendCount
|
||||||
get { return Natives.soundio_backend_count (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_backend_count(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public int InputDeviceCount {
|
public int InputDeviceCount
|
||||||
get { return Natives.soundio_input_device_count (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_input_device_count(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public int OutputDeviceCount {
|
public int OutputDeviceCount
|
||||||
get { return Natives.soundio_output_device_count (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_output_device_count(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public int DefaultInputDeviceIndex {
|
public int DefaultInputDeviceIndex
|
||||||
get { return Natives.soundio_default_input_device_index (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_default_input_device_index(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public int DefaultOutputDeviceIndex {
|
public int DefaultOutputDeviceIndex
|
||||||
get { return Natives.soundio_default_output_device_index (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_default_output_device_index(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIOBackend GetBackend (int index)
|
public SoundIOBackend GetBackend(int index)
|
||||||
{
|
{
|
||||||
return (SoundIOBackend) Natives.soundio_get_backend (handle, index);
|
return (SoundIOBackend)Natives.soundio_get_backend(handle, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundIODevice GetInputDevice (int index)
|
public SoundIODevice GetInputDevice(int index)
|
||||||
{
|
{
|
||||||
return new SoundIODevice (Natives.soundio_get_input_device (handle, index));
|
return new SoundIODevice(Natives.soundio_get_input_device(handle, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundIODevice GetOutputDevice (int index)
|
public SoundIODevice GetOutputDevice(int index)
|
||||||
{
|
{
|
||||||
return new SoundIODevice (Natives.soundio_get_output_device (handle, index));
|
return new SoundIODevice(Natives.soundio_get_output_device(handle, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Connect ()
|
public void Connect()
|
||||||
{
|
{
|
||||||
var ret = (SoundIoError) Natives.soundio_connect (handle);
|
var ret = (SoundIoError)Natives.soundio_connect(handle);
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
throw new SoundIOException (ret);
|
{
|
||||||
}
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ConnectBackend (SoundIOBackend backend)
|
public void ConnectBackend(SoundIOBackend backend)
|
||||||
{
|
{
|
||||||
var ret = (SoundIoError) Natives.soundio_connect_backend (handle, (SoundIoBackend) backend);
|
var ret = (SoundIoError)Natives.soundio_connect_backend(handle, (SoundIoBackend)backend);
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
throw new SoundIOException (ret);
|
{
|
||||||
}
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Disconnect ()
|
public void Disconnect()
|
||||||
{
|
{
|
||||||
Natives.soundio_disconnect (handle);
|
Natives.soundio_disconnect(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FlushEvents ()
|
public void FlushEvents()
|
||||||
{
|
{
|
||||||
Natives.soundio_flush_events (handle);
|
Natives.soundio_flush_events(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitEvents ()
|
public void WaitEvents()
|
||||||
{
|
{
|
||||||
Natives.soundio_wait_events (handle);
|
Natives.soundio_wait_events(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Wakeup ()
|
public void Wakeup()
|
||||||
{
|
{
|
||||||
Natives.soundio_wakeup (handle);
|
Natives.soundio_wakeup(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceDeviceScan ()
|
public void ForceDeviceScan()
|
||||||
{
|
{
|
||||||
Natives.soundio_force_device_scan (handle);
|
Natives.soundio_force_device_scan(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundIORingBuffer CreateRingBuffer (int capacity)
|
public SoundIORingBuffer CreateRingBuffer(int capacity)
|
||||||
{
|
{
|
||||||
return new SoundIORingBuffer (Natives.soundio_ring_buffer_create (handle, capacity));
|
return new SoundIORingBuffer(Natives.soundio_ring_buffer_create(handle, capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static methods
|
// static methods
|
||||||
|
|
||||||
public static string VersionString {
|
public static string VersionString
|
||||||
get { return Marshal.PtrToStringAnsi (Natives.soundio_version_string ()); }
|
{
|
||||||
}
|
get { return Marshal.PtrToStringAnsi(Natives.soundio_version_string()); }
|
||||||
|
}
|
||||||
|
|
||||||
public static int VersionMajor {
|
public static int VersionMajor
|
||||||
get { return Natives.soundio_version_major (); }
|
{
|
||||||
}
|
get { return Natives.soundio_version_major(); }
|
||||||
|
}
|
||||||
|
|
||||||
public static int VersionMinor {
|
public static int VersionMinor
|
||||||
get { return Natives.soundio_version_minor (); }
|
{
|
||||||
}
|
get { return Natives.soundio_version_minor(); }
|
||||||
|
}
|
||||||
|
|
||||||
public static int VersionPatch {
|
public static int VersionPatch
|
||||||
get { return Natives.soundio_version_patch (); }
|
{
|
||||||
}
|
get { return Natives.soundio_version_patch(); }
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetBackendName (SoundIOBackend backend)
|
public static string GetBackendName(SoundIOBackend backend)
|
||||||
{
|
{
|
||||||
return Marshal.PtrToStringAnsi (Natives.soundio_backend_name ((SoundIoBackend) backend));
|
return Marshal.PtrToStringAnsi(Natives.soundio_backend_name((SoundIoBackend)backend));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool HaveBackend (SoundIOBackend backend)
|
public static bool HaveBackend(SoundIOBackend backend)
|
||||||
{
|
{
|
||||||
return Natives.soundio_have_backend ((SoundIoBackend) backend);
|
return Natives.soundio_have_backend((SoundIoBackend)backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetBytesPerSample (SoundIOFormat format)
|
public static int GetBytesPerSample(SoundIOFormat format)
|
||||||
{
|
{
|
||||||
return Natives.soundio_get_bytes_per_sample ((SoundIoFormat) format);
|
return Natives.soundio_get_bytes_per_sample((SoundIoFormat)format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetBytesPerFrame (SoundIOFormat format, int channelCount)
|
public static int GetBytesPerFrame(SoundIOFormat format, int channelCount)
|
||||||
{
|
{
|
||||||
return Natives.soundio_get_bytes_per_frame ((SoundIoFormat) format, channelCount);
|
return Natives.soundio_get_bytes_per_frame((SoundIoFormat)format, channelCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetBytesPerSecond (SoundIOFormat format, int channelCount, int sampleRate)
|
public static int GetBytesPerSecond(SoundIOFormat format, int channelCount, int sampleRate)
|
||||||
{
|
{
|
||||||
return Natives.soundio_get_bytes_per_second ((SoundIoFormat) format, channelCount, sampleRate);
|
return Natives.soundio_get_bytes_per_second((SoundIoFormat)format, channelCount, sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetSoundFormatName (SoundIOFormat format)
|
public static string GetSoundFormatName(SoundIOFormat format)
|
||||||
{
|
{
|
||||||
return Marshal.PtrToStringAnsi (Natives.soundio_format_string ((SoundIoFormat) format));
|
return Marshal.PtrToStringAnsi(Natives.soundio_format_string((SoundIoFormat)format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,13 @@
|
|||||||
using System;
|
namespace SoundIOSharp
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
{
|
||||||
public enum SoundIOBackend
|
public enum SoundIOBackend
|
||||||
{
|
{
|
||||||
None = 0,
|
None,
|
||||||
Jack = 1,
|
Jack,
|
||||||
PulseAudio = 2,
|
PulseAudio,
|
||||||
Alsa = 3,
|
Alsa,
|
||||||
CoreAudio = 4,
|
CoreAudio,
|
||||||
Wasapi = 5,
|
Wasapi,
|
||||||
Dummy = 6,
|
Dummy
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -3,24 +3,28 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public struct SoundIOChannelArea
|
public struct SoundIOChannelArea
|
||||||
{
|
{
|
||||||
internal SoundIOChannelArea (Pointer<SoundIoChannelArea> handle)
|
internal SoundIOChannelArea(Pointer<SoundIoChannelArea> handle)
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer<SoundIoChannelArea> handle;
|
Pointer<SoundIoChannelArea> handle;
|
||||||
|
|
||||||
public IntPtr Pointer {
|
public IntPtr Pointer
|
||||||
get { return Marshal.ReadIntPtr (handle, ptr_offset); }
|
{
|
||||||
set { Marshal.WriteIntPtr (handle, ptr_offset, value); }
|
get { return Marshal.ReadIntPtr(handle, ptr_offset); }
|
||||||
}
|
set { Marshal.WriteIntPtr(handle, ptr_offset, value); }
|
||||||
static readonly int ptr_offset = (int) Marshal.OffsetOf<SoundIoChannelArea> ("ptr");
|
}
|
||||||
|
|
||||||
public int Step {
|
static readonly int ptr_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("ptr");
|
||||||
get { return Marshal.ReadInt32 (handle, step_offset); }
|
|
||||||
}
|
public int Step
|
||||||
static readonly int step_offset = (int)Marshal.OffsetOf<SoundIoChannelArea> ("step");
|
{
|
||||||
}
|
get { return Marshal.ReadInt32(handle, step_offset); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int step_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("step");
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,31 +3,32 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public struct SoundIOChannelAreas
|
public struct SoundIOChannelAreas
|
||||||
{
|
{
|
||||||
static readonly int native_size = Marshal.SizeOf<SoundIoChannelArea> ();
|
static readonly int native_size = Marshal.SizeOf<SoundIoChannelArea>();
|
||||||
|
|
||||||
internal SoundIOChannelAreas (IntPtr head, int channelCount, int frameCount)
|
internal SoundIOChannelAreas(IntPtr head, int channelCount, int frameCount)
|
||||||
{
|
{
|
||||||
this.head = head;
|
this.head = head;
|
||||||
this.channel_count = channelCount;
|
this.channel_count = channelCount;
|
||||||
this.frame_count = frameCount;
|
this.frame_count = frameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntPtr head;
|
IntPtr head;
|
||||||
int channel_count;
|
int channel_count;
|
||||||
int frame_count;
|
int frame_count;
|
||||||
|
|
||||||
public bool IsEmpty {
|
public bool IsEmpty
|
||||||
get { return head == IntPtr.Zero; }
|
{
|
||||||
}
|
get { return head == IntPtr.Zero; }
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIOChannelArea GetArea (int channel)
|
public SoundIOChannelArea GetArea(int channel)
|
||||||
{
|
{
|
||||||
return new SoundIOChannelArea (head + native_size * channel);
|
return new SoundIOChannelArea(head + native_size * channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ChannelCount => channel_count;
|
public int ChannelCount => channel_count;
|
||||||
public int FrameCount => frame_count;
|
public int FrameCount => frame_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,77 +1,75 @@
|
|||||||
using System;
|
namespace SoundIOSharp
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
{
|
||||||
|
public enum SoundIOChannelId
|
||||||
public enum SoundIOChannelId
|
{
|
||||||
{
|
Invalid,
|
||||||
Invalid = 0,
|
FrontLeft,
|
||||||
FrontLeft = 1,
|
FrontRight,
|
||||||
FrontRight = 2,
|
FrontCenter,
|
||||||
FrontCenter = 3,
|
Lfe,
|
||||||
Lfe = 4,
|
BackLeft,
|
||||||
BackLeft = 5,
|
BackRight,
|
||||||
BackRight = 6,
|
FrontLeftCenter,
|
||||||
FrontLeftCenter = 7,
|
FrontRightCenter,
|
||||||
FrontRightCenter = 8,
|
BackCenter,
|
||||||
BackCenter = 9,
|
SideLeft,
|
||||||
SideLeft = 10,
|
SideRight,
|
||||||
SideRight = 11,
|
TopCenter,
|
||||||
TopCenter = 12,
|
TopFrontLeft,
|
||||||
TopFrontLeft = 13,
|
TopFrontCenter,
|
||||||
TopFrontCenter = 14,
|
TopFrontRight,
|
||||||
TopFrontRight = 15,
|
TopBackLeft,
|
||||||
TopBackLeft = 16,
|
TopBackCenter,
|
||||||
TopBackCenter = 17,
|
TopBackRight,
|
||||||
TopBackRight = 18,
|
BackLeftCenter,
|
||||||
BackLeftCenter = 19,
|
BackRightCenter,
|
||||||
BackRightCenter = 20,
|
FrontLeftWide,
|
||||||
FrontLeftWide = 21,
|
FrontRightWide,
|
||||||
FrontRightWide = 22,
|
FrontLeftHigh,
|
||||||
FrontLeftHigh = 23,
|
FrontCenterHigh,
|
||||||
FrontCenterHigh = 24,
|
FrontRightHigh,
|
||||||
FrontRightHigh = 25,
|
TopFrontLeftCenter,
|
||||||
TopFrontLeftCenter = 26,
|
TopFrontRightCenter,
|
||||||
TopFrontRightCenter = 27,
|
TopSideLeft,
|
||||||
TopSideLeft = 28,
|
TopSideRight,
|
||||||
TopSideRight = 29,
|
LeftLfe,
|
||||||
LeftLfe = 30,
|
RightLfe,
|
||||||
RightLfe = 31,
|
Lfe2,
|
||||||
Lfe2 = 32,
|
BottomCenter,
|
||||||
BottomCenter = 33,
|
BottomLeftCenter,
|
||||||
BottomLeftCenter = 34,
|
BottomRightCenter,
|
||||||
BottomRightCenter = 35,
|
MsMid,
|
||||||
MsMid = 36,
|
MsSide,
|
||||||
MsSide = 37,
|
AmbisonicW,
|
||||||
AmbisonicW = 38,
|
AmbisonicX,
|
||||||
AmbisonicX = 39,
|
AmbisonicY,
|
||||||
AmbisonicY = 40,
|
AmbisonicZ,
|
||||||
AmbisonicZ = 41,
|
XyX,
|
||||||
XyX = 42,
|
XyY,
|
||||||
XyY = 43,
|
HeadphonesLeft,
|
||||||
HeadphonesLeft = 44,
|
HeadphonesRight,
|
||||||
HeadphonesRight = 45,
|
ClickTrack,
|
||||||
ClickTrack = 46,
|
ForeignLanguage,
|
||||||
ForeignLanguage = 47,
|
HearingImpaired,
|
||||||
HearingImpaired = 48,
|
Narration,
|
||||||
Narration = 49,
|
Haptic,
|
||||||
Haptic = 50,
|
DialogCentricMix,
|
||||||
DialogCentricMix = 51,
|
Aux,
|
||||||
Aux = 52,
|
Aux0,
|
||||||
Aux0 = 53,
|
Aux1,
|
||||||
Aux1 = 54,
|
Aux2,
|
||||||
Aux2 = 55,
|
Aux3,
|
||||||
Aux3 = 56,
|
Aux4,
|
||||||
Aux4 = 57,
|
Aux5,
|
||||||
Aux5 = 58,
|
Aux6,
|
||||||
Aux6 = 59,
|
Aux7,
|
||||||
Aux7 = 60,
|
Aux8,
|
||||||
Aux8 = 61,
|
Aux9,
|
||||||
Aux9 = 62,
|
Aux10,
|
||||||
Aux10 = 63,
|
Aux11,
|
||||||
Aux11 = 64,
|
Aux12,
|
||||||
Aux12 = 65,
|
Aux13,
|
||||||
Aux13 = 66,
|
Aux14,
|
||||||
Aux14 = 67,
|
Aux15
|
||||||
Aux15 = 68,
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,99 +1,116 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public struct SoundIOChannelLayout
|
public struct SoundIOChannelLayout
|
||||||
{
|
{
|
||||||
public static int BuiltInCount {
|
public static int BuiltInCount
|
||||||
get { return Natives.soundio_channel_layout_builtin_count (); }
|
{
|
||||||
}
|
get { return Natives.soundio_channel_layout_builtin_count(); }
|
||||||
|
}
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetBuiltIn (int index)
|
public static SoundIOChannelLayout GetBuiltIn(int index)
|
||||||
{
|
{
|
||||||
return new SoundIOChannelLayout (Natives.soundio_channel_layout_get_builtin (index));
|
return new SoundIOChannelLayout(Natives.soundio_channel_layout_get_builtin(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetDefault (int channelCount)
|
public static SoundIOChannelLayout GetDefault(int channelCount)
|
||||||
{
|
{
|
||||||
var handle = Natives.soundio_channel_layout_get_default (channelCount);
|
var handle = Natives.soundio_channel_layout_get_default(channelCount);
|
||||||
return new SoundIOChannelLayout (handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelId ParseChannelId (string name)
|
return new SoundIOChannelLayout (handle);
|
||||||
{
|
}
|
||||||
var ptr = Marshal.StringToHGlobalAnsi (name);
|
|
||||||
try {
|
|
||||||
return (SoundIOChannelId)Natives.soundio_parse_channel_id (ptr, name.Length);
|
|
||||||
} finally {
|
|
||||||
Marshal.FreeHGlobal (ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// instance members
|
public static SoundIOChannelId ParseChannelId(string name)
|
||||||
|
{
|
||||||
|
var ptr = Marshal.StringToHGlobalAnsi(name);
|
||||||
|
|
||||||
internal SoundIOChannelLayout (Pointer<SoundIoChannelLayout> handle)
|
try
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
return (SoundIOChannelId)Natives.soundio_parse_channel_id(ptr, name.Length);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
readonly Pointer<SoundIoChannelLayout> handle;
|
// instance members
|
||||||
|
|
||||||
public bool IsNull {
|
internal SoundIOChannelLayout(Pointer<SoundIoChannelLayout> handle)
|
||||||
get { return handle.Handle == IntPtr.Zero; }
|
{
|
||||||
}
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
internal IntPtr Handle {
|
readonly Pointer<SoundIoChannelLayout> handle;
|
||||||
get { return handle; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ChannelCount {
|
public bool IsNull
|
||||||
get { return IsNull ? 0 : Marshal.ReadInt32 ((IntPtr) handle + channel_count_offset); }
|
{
|
||||||
}
|
get { return handle.Handle == IntPtr.Zero; }
|
||||||
static readonly int channel_count_offset = (int) Marshal.OffsetOf<SoundIoChannelLayout> ("channel_count");
|
}
|
||||||
|
|
||||||
public string Name {
|
internal IntPtr Handle
|
||||||
get { return IsNull ? null : Marshal.PtrToStringAnsi (Marshal.ReadIntPtr ((IntPtr) handle + name_offset)); }
|
{
|
||||||
}
|
get { return handle; }
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout> ("name");
|
}
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelId> Channels {
|
public int ChannelCount
|
||||||
get {
|
{
|
||||||
if (IsNull)
|
get { return IsNull ? 0 : Marshal.ReadInt32((IntPtr)handle + channel_count_offset); }
|
||||||
yield break;
|
}
|
||||||
for (int i = 0; i < 24; i++)
|
|
||||||
yield return (SoundIOChannelId) Marshal.ReadInt32 ((IntPtr) handle + channels_offset + sizeof (SoundIoChannelId) * i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int channels_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout> ("channels");
|
|
||||||
|
|
||||||
public override bool Equals (object other)
|
static readonly int channel_count_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channel_count");
|
||||||
{
|
|
||||||
if (!(other is SoundIOChannelLayout))
|
|
||||||
return false;
|
|
||||||
var s = (SoundIOChannelLayout) other;
|
|
||||||
return handle == s.handle || Natives.soundio_channel_layout_equal (handle, s.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
public string Name
|
||||||
{
|
{
|
||||||
return handle.GetHashCode ();
|
get { return IsNull ? null : Marshal.PtrToStringAnsi(Marshal.ReadIntPtr((IntPtr)handle + name_offset)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DetectBuiltInName ()
|
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("name");
|
||||||
{
|
|
||||||
if (IsNull)
|
|
||||||
throw new InvalidOperationException ();
|
|
||||||
return Natives.soundio_channel_layout_detect_builtin (handle) ? Name : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FindChannel (SoundIOChannelId channel)
|
public IEnumerable<SoundIOChannelId> Channels
|
||||||
{
|
{
|
||||||
if (IsNull)
|
get
|
||||||
throw new InvalidOperationException ();
|
{
|
||||||
return Natives.soundio_channel_layout_find_channel (handle, (SoundIoChannelId) channel);
|
if (IsNull) yield break;
|
||||||
}
|
|
||||||
}
|
for (int i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
yield return (SoundIOChannelId)Marshal.ReadInt32((IntPtr)handle + channels_offset + sizeof(SoundIoChannelId) * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int channels_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channels");
|
||||||
|
|
||||||
|
public override bool Equals(object other)
|
||||||
|
{
|
||||||
|
if (!(other is SoundIOChannelLayout)) return false;
|
||||||
|
|
||||||
|
var s = (SoundIOChannelLayout) other;
|
||||||
|
|
||||||
|
return handle == s.handle || Natives.soundio_channel_layout_equal(handle, s.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return handle.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DetectBuiltInName()
|
||||||
|
{
|
||||||
|
if (IsNull) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
return Natives.soundio_channel_layout_detect_builtin(handle) ? Name : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FindChannel(SoundIOChannelId channel)
|
||||||
|
{
|
||||||
|
if (IsNull) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
return Natives.soundio_channel_layout_find_channel(handle, (SoundIoChannelId)channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,217 +4,264 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIODevice
|
public class SoundIODevice
|
||||||
{
|
{
|
||||||
public static SoundIOChannelLayout BestMatchingChannelLayout (SoundIODevice device1, SoundIODevice device2)
|
public static SoundIOChannelLayout BestMatchingChannelLayout(SoundIODevice device1, SoundIODevice device2)
|
||||||
{
|
{
|
||||||
var ptr1 = Marshal.ReadIntPtr (device1.handle, layouts_offset);
|
var ptr1 = Marshal.ReadIntPtr(device1.handle, layouts_offset);
|
||||||
var ptr2 = Marshal.ReadIntPtr (device2.handle, layouts_offset);
|
var ptr2 = Marshal.ReadIntPtr(device2.handle, layouts_offset);
|
||||||
return new SoundIOChannelLayout (Natives.soundio_best_matching_channel_layout (ptr1, device1.LayoutCount, ptr2, device2.LayoutCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SoundIODevice (Pointer<SoundIoDevice> handle)
|
return new SoundIOChannelLayout(Natives.soundio_best_matching_channel_layout(ptr1, device1.LayoutCount, ptr2, device2.LayoutCount));
|
||||||
{
|
}
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Pointer<SoundIoDevice> handle;
|
internal SoundIODevice(Pointer<SoundIoDevice> handle)
|
||||||
|
{
|
||||||
|
this.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
// Equality (based on handle and native func)
|
readonly Pointer<SoundIoDevice> handle;
|
||||||
|
|
||||||
public override bool Equals (object other)
|
// Equality (based on handle and native func)
|
||||||
{
|
|
||||||
var d = other as SoundIODevice;
|
|
||||||
return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
public override bool Equals(object other)
|
||||||
{
|
{
|
||||||
return (int) (IntPtr) handle;
|
var d = other as SoundIODevice;
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIODevice obj1, SoundIODevice obj2)
|
return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle));
|
||||||
{
|
}
|
||||||
return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIODevice obj1, SoundIODevice obj2)
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object) obj2 != null : !obj1.Equals (obj2);
|
return (int)(IntPtr)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields
|
public static bool operator == (SoundIODevice obj1, SoundIODevice obj2)
|
||||||
|
{
|
||||||
|
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIODeviceAim Aim {
|
public static bool operator != (SoundIODevice obj1, SoundIODevice obj2)
|
||||||
get { return (SoundIODeviceAim) Marshal.ReadInt32 (handle, aim_offset); }
|
{
|
||||||
}
|
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||||
static readonly int aim_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("aim");
|
}
|
||||||
|
|
||||||
public SoundIOFormat CurrentFormat {
|
// fields
|
||||||
get { return (SoundIOFormat) Marshal.ReadInt32 (handle, current_format_offset); }
|
|
||||||
}
|
|
||||||
static readonly int current_format_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("current_format");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout CurrentLayout {
|
public SoundIODeviceAim Aim
|
||||||
get { return new SoundIOChannelLayout ((IntPtr) handle + current_layout_offset);
|
{
|
||||||
}
|
get { return (SoundIODeviceAim)Marshal.ReadInt32(handle, aim_offset); }
|
||||||
}
|
}
|
||||||
static readonly int current_layout_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("current_layout");
|
|
||||||
|
|
||||||
public int FormatCount {
|
static readonly int aim_offset = (int)Marshal.OffsetOf<SoundIoDevice>("aim");
|
||||||
get { return Marshal.ReadInt32 (handle, format_count_offset); }
|
|
||||||
}
|
|
||||||
static readonly int format_count_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("format_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOFormat> Formats {
|
public SoundIOFormat CurrentFormat
|
||||||
get {
|
{
|
||||||
var ptr = Marshal.ReadIntPtr (handle, formats_offset);
|
get { return (SoundIOFormat)Marshal.ReadInt32(handle, current_format_offset); }
|
||||||
for (int i = 0; i < FormatCount; i++)
|
}
|
||||||
yield return (SoundIOFormat) Marshal.ReadInt32 (ptr, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int formats_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("formats");
|
|
||||||
|
|
||||||
public string Id {
|
static readonly int current_format_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_format");
|
||||||
get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, id_offset)); }
|
|
||||||
}
|
|
||||||
static readonly int id_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("id");
|
|
||||||
|
|
||||||
public bool IsRaw {
|
public SoundIOChannelLayout CurrentLayout
|
||||||
get { return Marshal.ReadInt32 (handle, is_raw_offset) != 0; }
|
{
|
||||||
}
|
get { return new SoundIOChannelLayout((IntPtr)handle + current_layout_offset); }
|
||||||
static readonly int is_raw_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("is_raw");
|
}
|
||||||
|
|
||||||
public int LayoutCount {
|
static readonly int current_layout_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_layout");
|
||||||
get { return Marshal.ReadInt32 (handle, layout_count_offset); }
|
|
||||||
}
|
|
||||||
static readonly int layout_count_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("layout_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelLayout> Layouts {
|
public int FormatCount
|
||||||
get {
|
{
|
||||||
var ptr = Marshal.ReadIntPtr (handle, layouts_offset);
|
get { return Marshal.ReadInt32(handle, format_count_offset); }
|
||||||
for (int i = 0; i < LayoutCount; i++)
|
}
|
||||||
yield return new SoundIOChannelLayout (ptr + i * Marshal.SizeOf<SoundIoChannelLayout> ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int layouts_offset = (int) Marshal.OffsetOf<SoundIoDevice> ("layouts");
|
|
||||||
|
|
||||||
public string Name {
|
static readonly int format_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("format_count");
|
||||||
get { return Marshal.PtrToStringAnsi (Marshal.ReadIntPtr (handle, name_offset)); }
|
|
||||||
}
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("name");
|
|
||||||
|
|
||||||
public int ProbeError {
|
public IEnumerable<SoundIOFormat> Formats
|
||||||
get { return Marshal.ReadInt32 (handle, probe_error_offset); }
|
{
|
||||||
}
|
get
|
||||||
static readonly int probe_error_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("probe_error");
|
{
|
||||||
|
var ptr = Marshal.ReadIntPtr(handle, formats_offset);
|
||||||
|
|
||||||
public int ReferenceCount {
|
for (int i = 0; i < FormatCount; i++)
|
||||||
get { return Marshal.ReadInt32 (handle, ref_count_offset); }
|
{
|
||||||
}
|
yield return (SoundIOFormat)Marshal.ReadInt32(ptr, i);
|
||||||
static readonly int ref_count_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("ref_count");
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int SampleRateCount {
|
static readonly int formats_offset = (int)Marshal.OffsetOf<SoundIoDevice>("formats");
|
||||||
get { return Marshal.ReadInt32 (handle, sample_rate_count_offset); }
|
|
||||||
}
|
|
||||||
static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("sample_rate_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOSampleRateRange> SampleRates {
|
public string Id
|
||||||
get {
|
{
|
||||||
var ptr = Marshal.ReadIntPtr (handle, sample_rates_offset);
|
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, id_offset)); }
|
||||||
for (int i = 0; i < SampleRateCount; i++)
|
}
|
||||||
yield return new SoundIOSampleRateRange (
|
|
||||||
Marshal.ReadInt32 (ptr, i * 2),
|
|
||||||
Marshal.ReadInt32 (ptr, i * 2 + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int sample_rates_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("sample_rates");
|
|
||||||
|
|
||||||
public double SoftwareLatencyCurrent {
|
static readonly int id_offset = (int)Marshal.OffsetOf<SoundIoDevice>("id");
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_current_offset); }
|
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_current_offset, value); }
|
|
||||||
}
|
|
||||||
static readonly int software_latency_current_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("software_latency_current");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMin {
|
public bool IsRaw
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_min_offset); }
|
{
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_min_offset, value); }
|
get { return Marshal.ReadInt32(handle, is_raw_offset) != 0; }
|
||||||
}
|
}
|
||||||
static readonly int software_latency_min_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("software_latency_min");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMax {
|
static readonly int is_raw_offset = (int)Marshal.OffsetOf<SoundIoDevice>("is_raw");
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_max_offset); }
|
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_max_offset, value); }
|
|
||||||
}
|
|
||||||
static readonly int software_latency_max_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("software_latency_max");
|
|
||||||
|
|
||||||
public SoundIO SoundIO {
|
public int LayoutCount
|
||||||
get { return new SoundIO (Marshal.ReadIntPtr (handle, soundio_offset)); }
|
{
|
||||||
}
|
get { return Marshal.ReadInt32(handle, layout_count_offset); }
|
||||||
static readonly int soundio_offset = (int)Marshal.OffsetOf<SoundIoDevice> ("soundio");
|
}
|
||||||
|
|
||||||
// functions
|
static readonly int layout_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layout_count");
|
||||||
|
|
||||||
public void AddReference ()
|
public IEnumerable<SoundIOChannelLayout> Layouts
|
||||||
{
|
{
|
||||||
Natives.soundio_device_ref (handle);
|
get
|
||||||
}
|
{
|
||||||
|
var ptr = Marshal.ReadIntPtr (handle, layouts_offset);
|
||||||
|
|
||||||
public void RemoveReference ()
|
for (int i = 0; i < LayoutCount; i++)
|
||||||
{
|
{
|
||||||
Natives.soundio_device_unref (handle);
|
yield return new SoundIOChannelLayout(ptr + i * Marshal.SizeOf<SoundIoChannelLayout>());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SortDeviceChannelLayouts ()
|
static readonly int layouts_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layouts");
|
||||||
{
|
|
||||||
Natives.soundio_device_sort_channel_layouts (handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
public string Name
|
||||||
public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
{
|
||||||
public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
||||||
public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
}
|
||||||
public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
|
||||||
public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
|
||||||
public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
|
||||||
public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
|
||||||
public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
|
|
||||||
public bool SupportsFormat (SoundIOFormat format)
|
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoDevice>("name");
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_format (handle, (SoundIoFormat) format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsSampleRate (int sampleRate)
|
public int ProbeError
|
||||||
{
|
{
|
||||||
return Natives.soundio_device_supports_sample_rate (handle, sampleRate);
|
get { return Marshal.ReadInt32(handle, probe_error_offset); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SupportsChannelCount(int channelCount)
|
static readonly int probe_error_offset = (int)Marshal.OffsetOf<SoundIoDevice>("probe_error");
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_layout(handle, SoundIOChannelLayout.GetDefault(channelCount).Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetNearestSampleRate (int sampleRate)
|
public int ReferenceCount
|
||||||
{
|
{
|
||||||
return Natives.soundio_device_nearest_sample_rate (handle, sampleRate);
|
get { return Marshal.ReadInt32(handle, ref_count_offset); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundIOInStream CreateInStream ()
|
static readonly int ref_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("ref_count");
|
||||||
{
|
|
||||||
return new SoundIOInStream (Natives.soundio_instream_create (handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOOutStream CreateOutStream ()
|
public int SampleRateCount
|
||||||
{
|
{
|
||||||
return new SoundIOOutStream (Natives.soundio_outstream_create (handle));
|
get { return Marshal.ReadInt32(handle, sample_rate_count_offset); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rate_count");
|
||||||
|
|
||||||
|
public IEnumerable<SoundIOSampleRateRange> SampleRates
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var ptr = Marshal.ReadIntPtr(handle, sample_rates_offset);
|
||||||
|
|
||||||
|
for (int i = 0; i < SampleRateCount; i++)
|
||||||
|
{
|
||||||
|
yield return new SoundIOSampleRateRange(Marshal.ReadInt32(ptr, i * 2), Marshal.ReadInt32(ptr, i * 2 + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int sample_rates_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rates");
|
||||||
|
|
||||||
|
public double SoftwareLatencyCurrent
|
||||||
|
{
|
||||||
|
get { return MarshalEx.ReadDouble(handle, software_latency_current_offset); }
|
||||||
|
set { MarshalEx.WriteDouble(handle, software_latency_current_offset, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int software_latency_current_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_current");
|
||||||
|
|
||||||
|
public double SoftwareLatencyMin
|
||||||
|
{
|
||||||
|
get { return MarshalEx.ReadDouble(handle, software_latency_min_offset); }
|
||||||
|
set { MarshalEx.WriteDouble(handle, software_latency_min_offset, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int software_latency_min_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_min");
|
||||||
|
|
||||||
|
public double SoftwareLatencyMax
|
||||||
|
{
|
||||||
|
get { return MarshalEx.ReadDouble(handle, software_latency_max_offset); }
|
||||||
|
set { MarshalEx.WriteDouble(handle, software_latency_max_offset, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int software_latency_max_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_max");
|
||||||
|
|
||||||
|
public SoundIO SoundIO
|
||||||
|
{
|
||||||
|
get { return new SoundIO(Marshal.ReadIntPtr(handle, soundio_offset)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int soundio_offset = (int)Marshal.OffsetOf<SoundIoDevice>("soundio");
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
public void AddReference()
|
||||||
|
{
|
||||||
|
Natives.soundio_device_ref(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveReference()
|
||||||
|
{
|
||||||
|
Natives.soundio_device_unref(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SortDeviceChannelLayouts()
|
||||||
|
{
|
||||||
|
Natives.soundio_device_sort_channel_layouts(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
||||||
|
public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
||||||
|
public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
||||||
|
public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
||||||
|
public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
||||||
|
public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
||||||
|
public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
||||||
|
public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
||||||
|
public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
||||||
|
public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
||||||
|
public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
||||||
|
public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
||||||
|
public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
||||||
|
public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
||||||
|
public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
||||||
|
public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
||||||
|
|
||||||
|
public bool SupportsFormat(SoundIOFormat format)
|
||||||
|
{
|
||||||
|
return Natives.soundio_device_supports_format(handle, (SoundIoFormat)format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsSampleRate(int sampleRate)
|
||||||
|
{
|
||||||
|
return Natives.soundio_device_supports_sample_rate(handle, sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SupportsChannelCount(int channelCount)
|
||||||
|
{
|
||||||
|
return Natives.soundio_device_supports_layout(handle, SoundIOChannelLayout.GetDefault(channelCount).Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetNearestSampleRate(int sampleRate)
|
||||||
|
{
|
||||||
|
return Natives.soundio_device_nearest_sample_rate(handle, sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIOInStream CreateInStream()
|
||||||
|
{
|
||||||
|
return new SoundIOInStream(Natives.soundio_instream_create(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIOOutStream CreateOutStream()
|
||||||
|
{
|
||||||
|
return new SoundIOOutStream(Natives.soundio_outstream_create(handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,8 @@
|
|||||||
using System;
|
namespace SoundIOSharp
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
{
|
||||||
public enum SoundIODeviceAim // soundio.h (228, 6)
|
public enum SoundIODeviceAim // soundio.h (228, 6)
|
||||||
{
|
{
|
||||||
Input = 0,
|
Input,
|
||||||
Output = 1,
|
Output
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,11 +3,8 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIOException : Exception
|
public class SoundIOException : Exception
|
||||||
{
|
{
|
||||||
internal SoundIOException (SoundIoError errorCode)
|
internal SoundIOException(SoundIoError errorCode) : base (Marshal.PtrToStringAnsi(Natives.soundio_strerror((int) errorCode))) { }
|
||||||
: base (Marshal.PtrToStringAnsi (Natives.soundio_strerror ((int) errorCode)))
|
}
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,26 +1,25 @@
|
|||||||
using System;
|
namespace SoundIOSharp
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
{
|
||||||
public enum SoundIOFormat
|
public enum SoundIOFormat
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
Invalid,
|
||||||
S8 = 1,
|
S8,
|
||||||
U8 = 2,
|
U8,
|
||||||
S16LE = 3,
|
S16LE,
|
||||||
S16BE = 4,
|
S16BE,
|
||||||
U16LE = 5,
|
U16LE,
|
||||||
U16BE = 6,
|
U16BE,
|
||||||
S24LE = 7,
|
S24LE,
|
||||||
S24BE = 8,
|
S24BE,
|
||||||
U24LE = 9,
|
U24LE,
|
||||||
U24BE = 10,
|
U24BE,
|
||||||
S32LE = 11,
|
S32LE,
|
||||||
S32BE = 12,
|
S32BE,
|
||||||
U32LE = 13,
|
U32LE,
|
||||||
U32BE = 14,
|
U32BE,
|
||||||
Float32LE = 15,
|
Float32LE,
|
||||||
Float32BE = 16,
|
Float32BE,
|
||||||
Float64LE = 17,
|
Float64LE,
|
||||||
Float64BE = 18,
|
Float64BE
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,225 +4,290 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIOInStream : IDisposable
|
public class SoundIOInStream : IDisposable
|
||||||
{
|
{
|
||||||
internal SoundIOInStream (Pointer<SoundIoInStream> handle)
|
internal SoundIOInStream(Pointer<SoundIoInStream> handle)
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer<SoundIoInStream> handle;
|
Pointer<SoundIoInStream> handle;
|
||||||
|
|
||||||
public void Dispose ()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Natives.soundio_instream_destroy (handle);
|
Natives.soundio_instream_destroy(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equality (based on handle)
|
// Equality (based on handle)
|
||||||
|
|
||||||
public override bool Equals (object other)
|
public override bool Equals(object other)
|
||||||
{
|
{
|
||||||
var d = other as SoundIOInStream;
|
var d = other as SoundIOInStream;
|
||||||
return d != null && (this.handle == d.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
return d != null && (this.handle == d.handle);
|
||||||
{
|
}
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2)
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2);
|
return (int)(IntPtr)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2)
|
public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2)
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2);
|
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields
|
public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2)
|
||||||
|
{
|
||||||
|
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIODevice Device {
|
// fields
|
||||||
get { return new SoundIODevice (Marshal.ReadIntPtr (handle, device_offset)); }
|
|
||||||
}
|
|
||||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("device");
|
|
||||||
|
|
||||||
public SoundIOFormat Format {
|
public SoundIODevice Device
|
||||||
get { return (SoundIOFormat) Marshal.ReadInt32 (handle, format_offset); }
|
{
|
||||||
set { Marshal.WriteInt32 (handle, format_offset, (int) value); }
|
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
||||||
}
|
}
|
||||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("format");
|
|
||||||
|
|
||||||
public int SampleRate {
|
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoInStream>("device");
|
||||||
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<SoundIoInStream> ("sample_rate");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout Layout {
|
public SoundIOFormat Format
|
||||||
get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); }
|
{
|
||||||
set {
|
get { return (SoundIOFormat)Marshal.ReadInt32(handle, format_offset); }
|
||||||
unsafe {
|
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
||||||
Buffer.MemoryCopy ((void*) ((IntPtr) handle + layout_offset), (void*)value.Handle,
|
}
|
||||||
Marshal.SizeOf<SoundIoChannelLayout> (), Marshal.SizeOf<SoundIoChannelLayout> ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("layout");
|
|
||||||
|
|
||||||
|
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoInStream>("format");
|
||||||
|
|
||||||
public double SoftwareLatency {
|
public int SampleRate
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
|
{
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
|
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
||||||
}
|
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
||||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("software_latency");
|
}
|
||||||
|
|
||||||
// error_callback
|
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoInStream>("sample_rate");
|
||||||
public Action ErrorCallback {
|
|
||||||
get { return error_callback; }
|
|
||||||
set {
|
|
||||||
error_callback = value;
|
|
||||||
error_callback_native = _ => 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<SoundIoInStream> ("error_callback");
|
|
||||||
Action error_callback;
|
|
||||||
delegate void error_callback_delegate (IntPtr handle);
|
|
||||||
error_callback_delegate error_callback_native;
|
|
||||||
|
|
||||||
// read_callback
|
public SoundIOChannelLayout Layout
|
||||||
public Action<int,int> ReadCallback {
|
{
|
||||||
get { return read_callback; }
|
get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); }
|
||||||
set {
|
set
|
||||||
read_callback = value;
|
{
|
||||||
read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback (minFrameCount, maxFrameCount);
|
unsafe
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (read_callback_native);
|
{
|
||||||
Marshal.WriteIntPtr (handle, read_callback_offset, ptr);
|
Buffer.MemoryCopy((void*)((IntPtr)handle + layout_offset), (void*)value.Handle, Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static readonly int read_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("read_callback");
|
}
|
||||||
Action<int, int> read_callback;
|
|
||||||
delegate void read_callback_delegate (IntPtr handle, int min, int max);
|
|
||||||
read_callback_delegate read_callback_native;
|
|
||||||
|
|
||||||
// overflow_callback
|
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout");
|
||||||
public Action OverflowCallback {
|
|
||||||
get { return overflow_callback; }
|
|
||||||
set {
|
|
||||||
overflow_callback = value;
|
|
||||||
overflow_callback_native = _ => overflow_callback ();
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (overflow_callback_native);
|
|
||||||
Marshal.WriteIntPtr (handle, overflow_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int overflow_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("overflow_callback");
|
|
||||||
Action overflow_callback;
|
|
||||||
delegate void overflow_callback_delegate (IntPtr handle);
|
|
||||||
overflow_callback_delegate overflow_callback_native;
|
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
public double SoftwareLatency
|
||||||
// this kind of code anywhere we need string marshaling.
|
{
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr> ();
|
get { return MarshalEx.ReadDouble(handle, software_latency_offset); }
|
||||||
|
set { MarshalEx.WriteDouble(handle, software_latency_offset, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public string Name {
|
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoInStream>("software_latency");
|
||||||
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<SoundIoInStream> ("name");
|
|
||||||
|
|
||||||
public bool NonTerminalHint {
|
// error_callback
|
||||||
get { return Marshal.ReadInt32 (handle, non_terminal_hint_offset) != 0; }
|
public Action ErrorCallback
|
||||||
}
|
{
|
||||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("non_terminal_hint");
|
get { return error_callback; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
error_callback = value;
|
||||||
|
error_callback_native = _ => error_callback();
|
||||||
|
|
||||||
public int BytesPerFrame {
|
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
||||||
get { return Marshal.ReadInt32 (handle, bytes_per_frame_offset); }
|
|
||||||
}
|
|
||||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("bytes_per_frame");
|
|
||||||
|
|
||||||
public int BytesPerSample {
|
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
||||||
get { return Marshal.ReadInt32 (handle, bytes_per_sample_offset); }
|
}
|
||||||
}
|
}
|
||||||
static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoInStream> ("bytes_per_sample");
|
|
||||||
|
|
||||||
public string LayoutErrorMessage {
|
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("error_callback");
|
||||||
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<SoundIoInStream> ("layout_error");
|
|
||||||
|
|
||||||
// functions
|
Action error_callback;
|
||||||
|
delegate void error_callback_delegate(IntPtr handle);
|
||||||
|
error_callback_delegate error_callback_native;
|
||||||
|
|
||||||
public void Open ()
|
// read_callback
|
||||||
{
|
public Action<int,int> ReadCallback
|
||||||
var ret = (SoundIoError) Natives.soundio_instream_open (handle);
|
{
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
get { return read_callback; }
|
||||||
throw new SoundIOException (ret);
|
set
|
||||||
}
|
{
|
||||||
|
read_callback = value;
|
||||||
|
read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback(minFrameCount, maxFrameCount);
|
||||||
|
|
||||||
public void Start ()
|
var ptr = Marshal.GetFunctionPointerForDelegate(read_callback_native);
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_start (handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelAreas BeginRead (ref int frameCount)
|
Marshal.WriteIntPtr(handle, read_callback_offset, ptr);
|
||||||
{
|
}
|
||||||
IntPtr ptrs = default (IntPtr);
|
}
|
||||||
int nativeFrameCount = frameCount;
|
|
||||||
unsafe {
|
|
||||||
var frameCountPtr = &nativeFrameCount;
|
|
||||||
var ptrptr = &ptrs;
|
|
||||||
var ret = (SoundIoError) Natives.soundio_instream_begin_read (handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
|
||||||
frameCount = *frameCountPtr;
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
return new SoundIOChannelAreas (ptrs, Layout.ChannelCount, frameCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndRead ()
|
static readonly int read_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("read_callback");
|
||||||
{
|
|
||||||
var ret = (SoundIoError) Natives.soundio_instream_end_read (handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause (bool pause)
|
Action<int, int> read_callback;
|
||||||
{
|
delegate void read_callback_delegate(IntPtr handle, int min, int max);
|
||||||
var ret = (SoundIoError) Natives.soundio_instream_pause (handle, pause);
|
read_callback_delegate read_callback_native;
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double GetLatency ()
|
// overflow_callback
|
||||||
{
|
public Action OverflowCallback
|
||||||
unsafe {
|
{
|
||||||
double* dptr = null;
|
get { return overflow_callback; }
|
||||||
IntPtr p = new IntPtr (dptr);
|
set
|
||||||
var ret = (SoundIoError) Natives.soundio_instream_get_latency (handle, p);
|
{
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
overflow_callback = value;
|
||||||
throw new SoundIOException (ret);
|
overflow_callback_native = _ => overflow_callback();
|
||||||
dptr = (double*) p;
|
|
||||||
return *dptr;
|
var ptr = Marshal.GetFunctionPointerForDelegate(overflow_callback_native);
|
||||||
}
|
|
||||||
}
|
Marshal.WriteIntPtr(handle, overflow_callback_offset, ptr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
static readonly int overflow_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("overflow_callback");
|
||||||
|
|
||||||
|
Action overflow_callback;
|
||||||
|
delegate void overflow_callback_delegate(IntPtr handle);
|
||||||
|
overflow_callback_delegate overflow_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<SoundIoInStream>("name");
|
||||||
|
|
||||||
|
public bool NonTerminalHint
|
||||||
|
{
|
||||||
|
get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoInStream>("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<SoundIoInStream>("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<SoundIoInStream>("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<SoundIoInStream>("layout_error");
|
||||||
|
|
||||||
|
// functions
|
||||||
|
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
var ret = (SoundIoError)Natives.soundio_instream_open(handle);
|
||||||
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
|
{
|
||||||
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
var ret = (SoundIoError)Natives.soundio_instream_start(handle);
|
||||||
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
|
{
|
||||||
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIOChannelAreas BeginRead(ref int frameCount)
|
||||||
|
{
|
||||||
|
IntPtr ptrs = default;
|
||||||
|
int nativeFrameCount = frameCount;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var frameCountPtr = &nativeFrameCount;
|
||||||
|
var ptrptr = &ptrs;
|
||||||
|
var ret = (SoundIoError)Natives.soundio_instream_begin_read(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
||||||
|
|
||||||
|
frameCount = *frameCountPtr;
|
||||||
|
|
||||||
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
|
{
|
||||||
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndRead()
|
||||||
|
{
|
||||||
|
var ret = (SoundIoError)Natives.soundio_instream_end_read(handle);
|
||||||
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
|
{
|
||||||
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pause(bool pause)
|
||||||
|
{
|
||||||
|
var ret = (SoundIoError)Natives.soundio_instream_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_instream_get_latency(handle, p);
|
||||||
|
if (ret != SoundIoError.SoundIoErrorNone)
|
||||||
|
{
|
||||||
|
throw new SoundIOException(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
dptr = (double*)p;
|
||||||
|
|
||||||
|
return *dptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,251 +4,328 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIOOutStream : IDisposable
|
public class SoundIOOutStream : IDisposable
|
||||||
{
|
{
|
||||||
internal SoundIOOutStream (Pointer<SoundIoOutStream> handle)
|
internal SoundIOOutStream (Pointer<SoundIoOutStream> handle)
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer<SoundIoOutStream> handle;
|
Pointer<SoundIoOutStream> handle;
|
||||||
|
|
||||||
public void Dispose ()
|
public void Dispose ()
|
||||||
{
|
{
|
||||||
Natives.soundio_outstream_destroy (handle);
|
Natives.soundio_outstream_destroy (handle);
|
||||||
}
|
}
|
||||||
// Equality (based on handle)
|
// Equality (based on handle)
|
||||||
|
|
||||||
public override bool Equals (object other)
|
public override bool Equals (object other)
|
||||||
{
|
{
|
||||||
var d = other as SoundIOOutStream;
|
var d = other as SoundIOOutStream;
|
||||||
return d != null && (this.handle == d.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
return d != null && (this.handle == d.handle);
|
||||||
{
|
}
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
public override int GetHashCode ()
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object)obj2 == null : obj1.Equals (obj2);
|
return (int)(IntPtr)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
||||||
{
|
{
|
||||||
return (object)obj1 == null ? (object)obj2 != null : !obj1.Equals (obj2);
|
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fields
|
public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
||||||
|
{
|
||||||
|
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIODevice Device {
|
// fields
|
||||||
get { return new SoundIODevice (Marshal.ReadIntPtr (handle, device_offset)); }
|
|
||||||
}
|
|
||||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("device");
|
|
||||||
|
|
||||||
public SoundIOFormat Format {
|
public SoundIODevice Device
|
||||||
get { return (SoundIOFormat) Marshal.ReadInt32 (handle, format_offset); }
|
{
|
||||||
set { Marshal.WriteInt32 (handle, format_offset, (int) value); }
|
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
||||||
}
|
}
|
||||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("format");
|
|
||||||
|
|
||||||
public int SampleRate {
|
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("device");
|
||||||
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 SoundIOFormat Format
|
||||||
|
{
|
||||||
|
get { return (SoundIOFormat) Marshal.ReadInt32(handle, format_offset); }
|
||||||
|
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
||||||
|
}
|
||||||
|
|
||||||
public SoundIOChannelLayout Layout {
|
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("format");
|
||||||
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 {
|
public int SampleRate
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
|
{
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
|
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
||||||
}
|
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
||||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("software_latency");
|
}
|
||||||
|
|
||||||
public float Volume {
|
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("sample_rate");
|
||||||
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 SoundIOChannelLayout Layout
|
||||||
public Action ErrorCallback {
|
{
|
||||||
get { return error_callback; }
|
get { unsafe { return new SoundIOChannelLayout((IntPtr) (void*)((IntPtr)handle + layout_offset)); } }
|
||||||
set {
|
set
|
||||||
error_callback = value;
|
{
|
||||||
if (value == null)
|
unsafe
|
||||||
error_callback_native = null;
|
{
|
||||||
else
|
Buffer.MemoryCopy((void*)value.Handle, (void*)((IntPtr)handle + layout_offset), Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
||||||
error_callback_native = stream => error_callback ();
|
}
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (error_callback_native);
|
}
|
||||||
Marshal.WriteIntPtr (handle, error_callback_offset, ptr);
|
}
|
||||||
}
|
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("layout");
|
||||||
}
|
|
||||||
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 double SoftwareLatency
|
||||||
public Action<int, int> WriteCallback {
|
{
|
||||||
get { return write_callback; }
|
get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
|
||||||
set {
|
set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
|
||||||
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
|
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("software_latency");
|
||||||
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
|
public float Volume
|
||||||
// this kind of code anywhere we need string marshaling.
|
{
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr> ();
|
get { return MarshalEx.ReadFloat(handle, volume_offset); }
|
||||||
|
set { MarshalEx.WriteFloat(handle, volume_offset, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public string Name {
|
static readonly int volume_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("volume");
|
||||||
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 {
|
// error_callback
|
||||||
get { return Marshal.ReadInt32 (handle, non_terminal_hint_offset) != 0; }
|
public Action ErrorCallback
|
||||||
}
|
{
|
||||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("non_terminal_hint");
|
get { return error_callback; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
error_callback = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
error_callback_native = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error_callback_native = stream => error_callback();
|
||||||
|
}
|
||||||
|
|
||||||
public int BytesPerFrame {
|
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
||||||
get { return Marshal.ReadInt32 (handle, bytes_per_frame_offset); }
|
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
||||||
}
|
}
|
||||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("bytes_per_frame");
|
}
|
||||||
|
|
||||||
public int BytesPerSample {
|
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("error_callback");
|
||||||
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 {
|
Action error_callback;
|
||||||
get {
|
delegate void error_callback_delegate (IntPtr handle);
|
||||||
var code = (SoundIoError) Marshal.ReadInt32 (handle, layout_error_offset);
|
error_callback_delegate error_callback_native;
|
||||||
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
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
public void Open ()
|
var ptr = Marshal.GetFunctionPointerForDelegate (write_callback_native);
|
||||||
{
|
Marshal.WriteIntPtr (handle, write_callback_offset, ptr);
|
||||||
var ret = (SoundIoError) Natives.soundio_outstream_open (handle);
|
}
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
}
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start ()
|
static readonly int write_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("write_callback");
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_start (handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelAreas BeginWrite (ref int frameCount)
|
Action<int, int> write_callback;
|
||||||
{
|
delegate void write_callback_delegate(IntPtr handle, int min, int max);
|
||||||
IntPtr ptrs = default (IntPtr);
|
write_callback_delegate write_callback_native;
|
||||||
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 ()
|
// underflow_callback
|
||||||
{
|
public Action UnderflowCallback
|
||||||
var ret = (SoundIoError) Natives.soundio_outstream_end_write (handle);
|
{
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
get { return underflow_callback; }
|
||||||
throw new SoundIOException (ret);
|
set
|
||||||
}
|
{
|
||||||
|
underflow_callback = value;
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
underflow_callback_native = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
underflow_callback_native = h => underflow_callback();
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearBuffer ()
|
var ptr = Marshal.GetFunctionPointerForDelegate (underflow_callback_native);
|
||||||
{
|
Marshal.WriteIntPtr (handle, underflow_callback_offset, ptr);
|
||||||
Natives.soundio_outstream_clear_buffer (handle);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Pause (bool pause)
|
static readonly int underflow_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("underflow_callback");
|
||||||
{
|
|
||||||
var ret = (SoundIoError) Natives.soundio_outstream_pause (handle, pause);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double GetLatency ()
|
Action underflow_callback;
|
||||||
{
|
delegate void underflow_callback_delegate(IntPtr handle);
|
||||||
unsafe {
|
underflow_callback_delegate underflow_callback_native;
|
||||||
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)
|
// 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.
|
||||||
var ret = (SoundIoError) Natives.soundio_outstream_set_volume (handle, volume);
|
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
throw new SoundIOException (ret);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,58 @@
|
|||||||
using System;
|
using System;
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public class SoundIORingBuffer : IDisposable
|
public class SoundIORingBuffer : IDisposable
|
||||||
{
|
{
|
||||||
internal SoundIORingBuffer (IntPtr handle)
|
internal SoundIORingBuffer(IntPtr handle)
|
||||||
{
|
{
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
IntPtr handle;
|
IntPtr handle;
|
||||||
|
|
||||||
public int Capacity {
|
public int Capacity
|
||||||
get { return Natives.soundio_ring_buffer_capacity (handle); }
|
{
|
||||||
}
|
get { return Natives.soundio_ring_buffer_capacity(handle); }
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear ()
|
public void Clear()
|
||||||
{
|
{
|
||||||
Natives.soundio_ring_buffer_clear (handle);
|
Natives.soundio_ring_buffer_clear(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose ()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Natives.soundio_ring_buffer_destroy (handle);
|
Natives.soundio_ring_buffer_destroy(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FillCount {
|
public int FillCount
|
||||||
get {
|
{
|
||||||
return Natives.soundio_ring_buffer_fill_count (handle);
|
get { return Natives.soundio_ring_buffer_fill_count(handle); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int FreeCount {
|
public int FreeCount
|
||||||
get {
|
{
|
||||||
return Natives.soundio_ring_buffer_free_count (handle);
|
get { return Natives.soundio_ring_buffer_free_count(handle); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr ReadPointer {
|
public IntPtr ReadPointer
|
||||||
get {
|
{
|
||||||
return Natives.soundio_ring_buffer_read_ptr (handle);
|
get { return Natives.soundio_ring_buffer_read_ptr(handle); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr WritePointer {
|
public IntPtr WritePointer
|
||||||
get {
|
{
|
||||||
return Natives.soundio_ring_buffer_write_ptr (handle);
|
get { return Natives.soundio_ring_buffer_write_ptr(handle); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceReadPointer (int count)
|
public void AdvanceReadPointer(int count)
|
||||||
{
|
{
|
||||||
Natives.soundio_ring_buffer_advance_read_ptr (handle, count);
|
Natives.soundio_ring_buffer_advance_read_ptr(handle, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AdvanceWritePointer (int count)
|
public void AdvanceWritePointer(int count)
|
||||||
{
|
{
|
||||||
Natives.soundio_ring_buffer_advance_write_ptr (handle, count);
|
Natives.soundio_ring_buffer_advance_write_ptr(handle, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
namespace SoundIOSharp
|
namespace SoundIOSharp
|
||||||
{
|
{
|
||||||
public struct SoundIOSampleRateRange
|
public struct SoundIOSampleRateRange
|
||||||
{
|
{
|
||||||
internal SoundIOSampleRateRange (int min, int max)
|
internal SoundIOSampleRateRange(int min, int max)
|
||||||
{
|
{
|
||||||
Min = min;
|
Min = min;
|
||||||
Max = max;
|
Max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly int Min;
|
public readonly int Min;
|
||||||
public readonly int Max;
|
public readonly int Max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,7 +367,7 @@ namespace Ryujinx.Audio
|
|||||||
{
|
{
|
||||||
lock (track)
|
lock (track)
|
||||||
{
|
{
|
||||||
return track.Volume;
|
return track.GetVolume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ namespace Ryujinx.Audio
|
|||||||
public int SampleRate { get; private set; }
|
public int SampleRate { get; private set; }
|
||||||
public ALFormat Format { get; private set; }
|
public ALFormat Format { get; private set; }
|
||||||
public PlaybackState State { get; set; }
|
public PlaybackState State { get; set; }
|
||||||
public float Volume { get; private set; }
|
|
||||||
|
|
||||||
public int HardwareChannels { get; }
|
public int HardwareChannels { get; }
|
||||||
public int VirtualChannels { get; }
|
public int VirtualChannels { get; }
|
||||||
@ -151,9 +150,14 @@ namespace Ryujinx.Audio
|
|||||||
|
|
||||||
public void SetVolume(float volume)
|
public void SetVolume(float volume)
|
||||||
{
|
{
|
||||||
Volume = volume;
|
AL.Source(SourceId, ALSourcef.Gain, volume);
|
||||||
|
}
|
||||||
|
|
||||||
AL.Source(SourceId, ALSourcef.Gain, Volume);
|
public float GetVolume()
|
||||||
|
{
|
||||||
|
AL.GetSource(SourceId, ALSourcef.Gain, out float volume);
|
||||||
|
|
||||||
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Ryujinx.Audio.SoundIo;
|
using Ryujinx.Audio.SoundIo;
|
||||||
using SoundIOSharp;
|
using SoundIOSharp;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Audio
|
namespace Ryujinx.Audio
|
||||||
|
@ -85,16 +85,16 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
|
|||||||
{
|
{
|
||||||
long tag = context.RequestData.ReadInt64();
|
long tag = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag) ? 1 : 0);
|
context.ResponseData.Write(_audioOut.ContainsBuffer(_track, tag));
|
||||||
|
|
||||||
return 0;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(7)] // 3.0.0+
|
[Command(7)] // 3.0.0+
|
||||||
// AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
|
// AppendAudioOutBufferAuto(u64 tag, buffer<nn::audio::AudioOutBuffer, 0x21>)
|
||||||
public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
|
public ResultCode AppendAudioOutBufferAuto(ServiceCtx context)
|
||||||
{
|
{
|
||||||
(long position, long size) = context.Request.GetBufferType0x21();
|
(long position, _) = context.Request.GetBufferType0x21();
|
||||||
|
|
||||||
return AppendAudioOutBufferImpl(context, position);
|
return AppendAudioOutBufferImpl(context, position);
|
||||||
}
|
}
|
||||||
@ -103,9 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioOutManager
|
|||||||
{
|
{
|
||||||
long tag = context.RequestData.ReadInt64();
|
long tag = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
AudioOutData data = MemoryHelper.Read<AudioOutData>(
|
AudioOutData data = MemoryHelper.Read<AudioOutData>(context.Memory, position);
|
||||||
context.Memory,
|
|
||||||
position);
|
|
||||||
|
|
||||||
// NOTE: Assume PCM16 all the time, change if new format are found.
|
// NOTE: Assume PCM16 all the time, change if new format are found.
|
||||||
short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
|
short[] buffer = new short[data.SampleBufferSize / sizeof(short)];
|
||||||
|
@ -20,10 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
// ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
|
// ListAudioOuts() -> (u32 count, buffer<bytes, 6>)
|
||||||
public ResultCode ListAudioOuts(ServiceCtx context)
|
public ResultCode ListAudioOuts(ServiceCtx context)
|
||||||
{
|
{
|
||||||
return ListAudioOutsImpl(
|
return ListAudioOutsImpl(context, context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
|
||||||
context,
|
|
||||||
context.Request.ReceiveBuff[0].Position,
|
|
||||||
context.Request.ReceiveBuff[0].Size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(1)]
|
[Command(1)]
|
||||||
@ -31,12 +28,8 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
// -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
|
// -> (u32 sample_rate, u32 channel_count, u32 pcm_format, u32, object<nn::audio::detail::IAudioOut>, buffer<bytes, 6> name_out)
|
||||||
public ResultCode OpenAudioOut(ServiceCtx context)
|
public ResultCode OpenAudioOut(ServiceCtx context)
|
||||||
{
|
{
|
||||||
return OpenAudioOutImpl(
|
return OpenAudioOutImpl(context, context.Request.SendBuff[0].Position, context.Request.SendBuff[0].Size,
|
||||||
context,
|
context.Request.ReceiveBuff[0].Position, context.Request.ReceiveBuff[0].Size);
|
||||||
context.Request.SendBuff[0].Position,
|
|
||||||
context.Request.SendBuff[0].Size,
|
|
||||||
context.Request.ReceiveBuff[0].Position,
|
|
||||||
context.Request.ReceiveBuff[0].Size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command(2)] // 3.0.0+
|
[Command(2)] // 3.0.0+
|
||||||
@ -56,12 +49,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
(long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
|
(long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
|
||||||
(long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
|
(long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
|
||||||
|
|
||||||
return OpenAudioOutImpl(
|
return OpenAudioOutImpl(context, sendPosition, sendSize, recvPosition, recvSize);
|
||||||
context,
|
|
||||||
sendPosition,
|
|
||||||
sendSize,
|
|
||||||
recvPosition,
|
|
||||||
recvSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode ListAudioOutsImpl(ServiceCtx context, long position, long size)
|
private ResultCode ListAudioOutsImpl(ServiceCtx context, long position, long size)
|
||||||
@ -88,10 +76,7 @@ namespace Ryujinx.HLE.HOS.Services.Audio
|
|||||||
|
|
||||||
private ResultCode OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
|
private ResultCode OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
|
||||||
{
|
{
|
||||||
string deviceName = MemoryHelper.ReadAsciiString(
|
string deviceName = MemoryHelper.ReadAsciiString(context.Memory, sendPosition, sendSize);
|
||||||
context.Memory,
|
|
||||||
sendPosition,
|
|
||||||
sendSize);
|
|
||||||
|
|
||||||
if (deviceName == string.Empty)
|
if (deviceName == string.Empty)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user