diff --git a/.gitattributes b/.gitattributes
index 55ed421..2a578f4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
Binding/SharpLSL.Header.cs text eol=lf
+Source/SharpLSL/Interop/*.g.cs text eol=lf
diff --git a/Binding/generate.ps1 b/Binding/generate.ps1
index 4202acd..fe65430 100644
--- a/Binding/generate.ps1
+++ b/Binding/generate.ps1
@@ -9,6 +9,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName Common `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop
Move-Item -Path ../Source/SharpLSL/Interop/Common.cs -Destination ../Source/SharpLSL/Interop/Common.g.cs -Force
@@ -29,6 +30,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop/Common.g.cs `
--remap `
sbyte*=IntPtr `
@@ -49,6 +51,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop/Inlet.g.cs `
--remap `
lsl_inlet=IntPtr `
@@ -65,6 +68,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop/Outlet.g.cs `
--remap `
lsl_outlet=IntPtr `
@@ -81,6 +85,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop/Resolver.g.cs `
--remap `
lsl_continuous_resolver=IntPtr `
@@ -97,6 +102,7 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
+ --additional -m64 `
--output ../Source/SharpLSL/Interop/StreamInfo.g.cs `
--remap `
lsl_streaminfo=IntPtr `
@@ -113,7 +119,8 @@ ClangSharpPInvokeGenerator `
--language c++ `
--methodClassName LSL `
--namespace SharpLSL.Interop `
- --output ../Source/SharpLSL/Interop/Xml.g.cs `
+ --additional -m64 `
+ --output ../Source/SharpLSL/Interop/XML.g.cs `
--remap `
lsl_xml_ptr=IntPtr
diff --git a/Examples/LSLVer/LSLVer.csproj b/Examples/LSLVer/LSLVer.csproj
index 988bdb9..8c6c0f6 100644
--- a/Examples/LSLVer/LSLVer.csproj
+++ b/Examples/LSLVer/LSLVer.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/Examples/LSLVer/Program.cs b/Examples/LSLVer/Program.cs
index 29ef49c..e2db962 100644
--- a/Examples/LSLVer/Program.cs
+++ b/Examples/LSLVer/Program.cs
@@ -1,6 +1,5 @@
// Port of https://github.com/sccn/liblsl/blob/master/testing/lslver.c
using SharpLSL;
-using SharpLSL.Interop;
namespace LSLVer
{
@@ -8,9 +7,9 @@ internal class Program
{
static void Main(string[] args)
{
- Console.WriteLine($"LSL version: {LSL.lsl_library_version()}");
- Console.WriteLine(Lsl.GetLibraryInfo());
- Console.WriteLine(Lsl.GetLocalClock());
+ Console.WriteLine($"LSL version: {LSL.GetLibraryVersion()}");
+ Console.WriteLine(LSL.GetLibraryInfo());
+ Console.WriteLine(LSL.GetLocalClock());
}
}
}
\ No newline at end of file
diff --git a/Source/SharpLSL/ChannelFormat.cs b/Source/SharpLSL/ChannelFormat.cs
new file mode 100644
index 0000000..dac6171
--- /dev/null
+++ b/Source/SharpLSL/ChannelFormat.cs
@@ -0,0 +1,96 @@
+using SharpLSL.Interop;
+
+namespace SharpLSL
+{
+ ///
+ /// Data format of a channel.
+ ///
+ ///
+ /// This enumeration specifies the format of data transmitted for each channel.
+ /// Each transmitted sample contains an array of channels, and this enum describes
+ /// the format of the data sent over the wire.
+ ///
+ ///
+ public enum ChannelFormat : int
+ {
+ ///
+ /// Represents a 32-bit floating-point format.
+ ///
+ ///
+ /// This format is used for measurements that require up to 24-bit precision,
+ /// such as physical quantities measured in microvolts. Integers within the
+ /// range of -16,777,216 to 16,777,216 are represented accurately using this
+ /// format.
+ ///
+ Float = lsl_channel_format_t.cft_float32,
+
+ ///
+ /// Represents a 64-bit double-precision floating-point format.
+ ///
+ ///
+ /// This format is used for representing numerical data with high precision
+ /// for universal numeric data as long as permitted by network & disk budget.
+ /// The largest representable integer is 53-bit.
+ ///
+ Double = lsl_channel_format_t.cft_double64,
+
+ ///
+ /// Represents variable-length ASCII strings or data blobs.
+ ///
+ ///
+ /// This format is suitable for data that cannot be easily represented as
+ /// numeric values, such as video frames, or complex event descriptions.
+ ///
+ String = lsl_channel_format_t.cft_string,
+
+ ///
+ /// Represents a 32-bit signed integer format.
+ ///
+ ///
+ /// This format is used for transmitting data that requires 32-bit integer
+ /// precision. It is suitable for high-rate digitized formats and cases
+ /// where the data is represented as discrete numeric values, such as
+ /// application event codes or other coded data.
+ ///
+ Int32 = lsl_channel_format_t.cft_int32,
+
+ ///
+ /// Represents a 16-bit signed integer format.
+ ///
+ ///
+ /// This format is used for transmitting data with 16-bit integer precision.
+ /// It is ideal for very high-rate signals (40kHz+) or consumer-grade audio.
+ /// For professional audio, is recommended.
+ ///
+ Int16 = lsl_channel_format_t.cft_int16,
+
+ ///
+ /// Represents an 8-bit signed integer format.
+ ///
+ ///
+ /// This format is used for transmitting data with 8-bit integer precision.
+ /// It is suitable for binary signals or other coded data. It is not
+ /// recommended for encoding string data.
+ ///
+ Int8 = lsl_channel_format_t.cft_int8,
+
+ ///
+ /// Represents a 64-bit signed integer format.
+ ///
+ ///
+ /// This format is used for transmitting data that requires 64-bit integer
+ /// precision. Note that support for `Int64` is not yet exposed in all languages.
+ /// Also, some builds of liblsl will not be able to send or receive data of this type.
+ ///
+ Int64 = lsl_channel_format_t.cft_int64,
+
+ ///
+ /// Represents an undefined or unsupported data format.
+ ///
+ ///
+ /// This format indicates that the data format is either not defined or
+ /// not supported for transmission.
+ ///
+ Undefined = lsl_channel_format_t.cft_undefined,
+ }
+}
diff --git a/Source/SharpLSL/ContinuousResolver.cs b/Source/SharpLSL/ContinuousResolver.cs
new file mode 100644
index 0000000..ce87dfd
--- /dev/null
+++ b/Source/SharpLSL/ContinuousResolver.cs
@@ -0,0 +1,91 @@
+using System;
+
+using static SharpLSL.Interop.LSL;
+using static SharpLSL.LSL;
+
+namespace SharpLSL
+{
+ public class ContinuousResolver : LSLObject
+ {
+ public ContinuousResolver(double forgetAfter = 5.0)
+ : base(lsl_create_continuous_resolver(forgetAfter))
+ {
+ }
+
+ public ContinuousResolver(string property, string value, double forgetAfter = 5.0)
+ : base(lsl_create_continuous_resolver_byprop(property, value, forgetAfter))
+ {
+ }
+
+ public ContinuousResolver(string predicate, double forgetAfter = 5.0)
+ : base(lsl_create_continuous_resolver_bypred(predicate, forgetAfter))
+ {
+ }
+
+ public ContinuousResolver(IntPtr handle, bool ownsHandle = true)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ public StreamInfo[] Resolve(int maxCount = 1024)
+ {
+ var streamInfoPointers = new IntPtr[maxCount];
+
+ var result = lsl_resolver_results(handle, streamInfoPointers, (uint)streamInfoPointers.Length);
+ CheckError(result);
+
+ var streamInfos = new StreamInfo[result];
+ for (int i = 0; i < result; ++i)
+ streamInfos[i] = new StreamInfo(streamInfoPointers[i], true);
+
+ return streamInfos;
+ }
+
+ public StreamInfo[] ResolveAll(int maxCount = 1024, double waitTime = 1.0)
+ {
+ var streamInfoPointers = new IntPtr[maxCount];
+
+ var result = lsl_resolve_all(streamInfoPointers, (uint)streamInfoPointers.Length, waitTime);
+ CheckError(result);
+
+ var streamInfos = new StreamInfo[result];
+ for (int i = 0; i < result; ++i)
+ streamInfos[i] = new StreamInfo(streamInfoPointers[i], true);
+
+ return streamInfos;
+ }
+
+ public StreamInfo[] Resolve(string property, string value, int minCount, int maxCount = 1024, double timeout = Forever)
+ {
+ var streamInfoPointers = new IntPtr[maxCount];
+
+ var result = lsl_resolve_byprop(streamInfoPointers, (uint)streamInfoPointers.Length, property, value, minCount, timeout);
+ CheckError(result);
+
+ var streamInfos = new StreamInfo[result];
+ for (int i = 0; i < result; ++i)
+ streamInfos[i] = new StreamInfo(streamInfoPointers[i], true);
+
+ return streamInfos;
+ }
+
+ public StreamInfo[] Resolve(string predicate, int minCount, int maxCount = 1024, double timeout = Forever)
+ {
+ var streamInfoPointers = new IntPtr[maxCount];
+
+ var result = lsl_resolve_bypred(streamInfoPointers, (uint)streamInfoPointers.Length, predicate, minCount, timeout);
+ CheckError(result);
+
+ var streamInfos = new StreamInfo[result];
+ for (int i = 0; i < result; ++i)
+ streamInfos[i] = new StreamInfo(streamInfoPointers[i], true);
+
+ return streamInfos;
+ }
+
+ protected override void DestroyLSLObject()
+ {
+ lsl_destroy_continuous_resolver(handle);
+ }
+ }
+}
diff --git a/Source/SharpLSL/Interop/Inlet.Interop.cs b/Source/SharpLSL/Interop/Inlet.Interop.cs
new file mode 100644
index 0000000..2770831
--- /dev/null
+++ b/Source/SharpLSL/Interop/Inlet.Interop.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Runtime.InteropServices;
+
+using lsl_inlet = System.IntPtr;
+
+namespace SharpLSL.Interop
+{
+ public static unsafe partial class LSL
+ {
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_inlet lsl_get_fullinfo(lsl_inlet @in, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern void lsl_open_stream(lsl_inlet @in, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_time_correction(lsl_inlet @in, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_time_correction_ex(lsl_inlet @in, ref double remote_time, ref double uncertainty, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_c(lsl_inlet @in, sbyte[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_c(lsl_inlet @in, byte[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_s(lsl_inlet @in, short[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_i(lsl_inlet @in, int[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_l(lsl_inlet @in, long[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_f(lsl_inlet @in, float[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_d(lsl_inlet @in, double[] buffer, int buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_str(lsl_inlet @in, IntPtr[] buffer, int buffer_elements, double timeout, ref int ec);
+
+
+
+
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_f([NativeTypeName("lsl_inlet")] IntPtr @in, float[] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_d([NativeTypeName("lsl_inlet")] IntPtr @in, double[] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_l([NativeTypeName("lsl_inlet")] IntPtr @in, long[] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_i([NativeTypeName("lsl_inlet")] IntPtr @in, int[] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_s([NativeTypeName("lsl_inlet")] IntPtr @in, short[] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_f([NativeTypeName("lsl_inlet")] IntPtr @in, float[,] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_d([NativeTypeName("lsl_inlet")] IntPtr @in, double[,] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_l([NativeTypeName("lsl_inlet")] IntPtr @in, long[,] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_i([NativeTypeName("lsl_inlet")] IntPtr @in, int[,] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern uint lsl_pull_chunk_s([NativeTypeName("lsl_inlet")] IntPtr @in, short[,] data_buffer, double[] timestamp_buffer, uint data_buffer_elements, uint timestamp_buffer_elements, double timeout, ref int ec);
+
+ /*
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("lsl_inlet")]
+ public static extern IntPtr lsl_create_inlet([NativeTypeName("lsl_streaminfo")] IntPtr info, [NativeTypeName("int32_t")] int max_buflen, [NativeTypeName("int32_t")] int max_chunklen, [NativeTypeName("int32_t")] int recover);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("lsl_streaminfo")]
+ public static extern IntPtr lsl_get_fullinfo([NativeTypeName("lsl_inlet")] IntPtr @in, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_c([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char *")] sbyte* buffer, [NativeTypeName("int32_t")] int buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_str([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char **")] sbyte** buffer, [NativeTypeName("int32_t")] int buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_buf([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char **")] sbyte** buffer, [NativeTypeName("uint32_t *")] uint* buffer_lengths, [NativeTypeName("int32_t")] int buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern double lsl_pull_sample_v([NativeTypeName("lsl_inlet")] IntPtr @in, void* buffer, [NativeTypeName("int32_t")] int buffer_bytes, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("unsigned long")]
+ public static extern uint lsl_pull_chunk_c([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char *")] sbyte* data_buffer, double* timestamp_buffer, [NativeTypeName("unsigned long")] uint data_buffer_elements, [NativeTypeName("unsigned long")] uint timestamp_buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("unsigned long")]
+ public static extern uint lsl_pull_chunk_str([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char **")] sbyte** data_buffer, double* timestamp_buffer, [NativeTypeName("unsigned long")] uint data_buffer_elements, [NativeTypeName("unsigned long")] uint timestamp_buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("unsigned long")]
+ public static extern uint lsl_pull_chunk_buf([NativeTypeName("lsl_inlet")] IntPtr @in, [NativeTypeName("char **")] sbyte** data_buffer, [NativeTypeName("uint32_t *")] uint* lengths_buffer, double* timestamp_buffer, [NativeTypeName("unsigned long")] uint data_buffer_elements, [NativeTypeName("unsigned long")] uint timestamp_buffer_elements, double timeout, [NativeTypeName("int32_t *")] int* ec);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("uint32_t")]
+ public static extern uint lsl_samples_available([NativeTypeName("lsl_inlet")] IntPtr @in);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("uint32_t")]
+ public static extern uint lsl_inlet_flush([NativeTypeName("lsl_inlet")] IntPtr @in);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("uint32_t")]
+ public static extern uint lsl_was_clock_reset([NativeTypeName("lsl_inlet")] IntPtr @in);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_smoothing_halftime([NativeTypeName("lsl_inlet")] IntPtr @in, float value);
+ */
+ }
+}
diff --git a/Source/SharpLSL/Interop/Outlet.Interop.cs b/Source/SharpLSL/Interop/Outlet.Interop.cs
new file mode 100644
index 0000000..1e26cec
--- /dev/null
+++ b/Source/SharpLSL/Interop/Outlet.Interop.cs
@@ -0,0 +1,326 @@
+using System.Runtime.InteropServices;
+
+using lsl_outlet = System.IntPtr;
+
+namespace SharpLSL.Interop
+{
+ public static unsafe partial class LSL
+ {
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_f(lsl_outlet @out, float[] data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_d(lsl_outlet @out, double[] data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_l(lsl_outlet @out, long[] data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_i(lsl_outlet @out, int[] data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_s(lsl_outlet @out, short[] data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_ft(lsl_outlet @out, float[] data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_dt(lsl_outlet @out, double[] data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_lt(lsl_outlet @out, long[] data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_it(lsl_outlet @out, int[] data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_st(lsl_outlet @out, short[] data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_ftp(lsl_outlet @out, float[] data, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_dtp(lsl_outlet @out, double[] data, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_ltp(lsl_outlet @out, long[] data, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_itp(lsl_outlet @out, int[] data, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_sample_stp(lsl_outlet @out, short[] data, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_f(lsl_outlet @out, float[] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_d(lsl_outlet @out, double[] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_l(lsl_outlet @out, long[] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_i(lsl_outlet @out, int[] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_s(lsl_outlet @out, short[] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_f(lsl_outlet @out, float[,] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_d(lsl_outlet @out, double[,] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_l(lsl_outlet @out, long[,] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_i(lsl_outlet @out, int[,] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_s(lsl_outlet @out, short[,] data, uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ft(lsl_outlet @out, float[] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dt(lsl_outlet @out, double[] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_lt(lsl_outlet @out, long[] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_it(lsl_outlet @out, int[] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_st(lsl_outlet @out, short[] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ft(lsl_outlet @out, float[,] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dt(lsl_outlet @out, double[,] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_lt(lsl_outlet @out, long[,] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_it(lsl_outlet @out, int[,] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_st(lsl_outlet @out, short[,] data, uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftp(lsl_outlet @out, float[] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtp(lsl_outlet @out, double[] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltp(lsl_outlet @out, long[] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itp(lsl_outlet @out, int[] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stp(lsl_outlet @out, short[] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftp(lsl_outlet @out, float[,] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtp(lsl_outlet @out, double[,] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltp(lsl_outlet @out, long[,] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itp(lsl_outlet @out, int[,] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stp(lsl_outlet @out, short[,] data, uint data_elements, double timestamp, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftn(lsl_outlet @out, float[] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtn(lsl_outlet @out, double[] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltn(lsl_outlet @out, long[] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itn(lsl_outlet @out, int[] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stn(lsl_outlet @out, short[] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftn(lsl_outlet @out, float[,] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtn(lsl_outlet @out, double[,] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltn(lsl_outlet @out, long[,] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itn(lsl_outlet @out, int[,] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stn(lsl_outlet @out, short[,] data, uint data_elements, double[] timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftnp(lsl_outlet @out, float[] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtnp(lsl_outlet @out, double[] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltnp(lsl_outlet @out, long[] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itnp(lsl_outlet @out, int[] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stnp(lsl_outlet @out, short[] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ftnp(lsl_outlet @out, float[,] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_dtnp(lsl_outlet @out, double[,] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_ltnp(lsl_outlet @out, long[,] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_itnp(lsl_outlet @out, int[,] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_push_chunk_stnp(lsl_outlet @out, short[,] data, uint data_elements, double[] timestamps, int pushthrough);
+
+ /*
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_c(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_str(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_v(lsl_outlet @out, [NativeTypeName("const void *")] void* data);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_ct(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_strt(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_vt(lsl_outlet @out, [NativeTypeName("const void *")] void* data, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_ctp(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_strtp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_vtp(lsl_outlet @out, [NativeTypeName("const void *")] void* data, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_buf(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_buft(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_sample_buftp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_c(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, [NativeTypeName("unsigned long")] uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_str(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("unsigned long")] uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_ct(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, [NativeTypeName("unsigned long")] uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_strt(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("unsigned long")] uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_ctp(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, [NativeTypeName("unsigned long")] uint data_elements, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_strtp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("unsigned long")] uint data_elements, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_ctn(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_strtn(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_ctnp(lsl_outlet @out, [NativeTypeName("const char *")] sbyte* data, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_strtnp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_buf(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, [NativeTypeName("unsigned long")] uint data_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_buft(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, [NativeTypeName("unsigned long")] uint data_elements, double timestamp);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_buftp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, [NativeTypeName("unsigned long")] uint data_elements, double timestamp, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_buftn(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_push_chunk_buftnp(lsl_outlet @out, [NativeTypeName("const char **")] sbyte** data, [NativeTypeName("const uint32_t *")] uint* lengths, [NativeTypeName("unsigned long")] uint data_elements, [NativeTypeName("const double *")] double* timestamps, [NativeTypeName("int32_t")] int pushthrough);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_have_consumers(lsl_outlet @out);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("int32_t")]
+ public static extern int lsl_wait_for_consumers(lsl_outlet @out, double timeout);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ [return: NativeTypeName("lsl_streaminfo")]
+ public static extern IntPtr lsl_get_info(lsl_outlet @out);
+ */
+ }
+}
diff --git a/Source/SharpLSL/Interop/Resolver.Interop.cs b/Source/SharpLSL/Interop/Resolver.Interop.cs
new file mode 100644
index 0000000..39ebac8
--- /dev/null
+++ b/Source/SharpLSL/Interop/Resolver.Interop.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SharpLSL.Interop
+{
+ public static unsafe partial class LSL
+ {
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern IntPtr lsl_create_continuous_resolver_byprop(string prop, string value, double forget_after);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern IntPtr lsl_create_continuous_resolver_bypred(string pred, double forget_after);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_resolver_results([NativeTypeName("lsl_continuous_resolver")] IntPtr res, IntPtr[] buffer, [NativeTypeName("uint32_t")] uint buffer_elements);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_resolve_all(IntPtr[] buffer, [NativeTypeName("uint32_t")] uint buffer_elements, double wait_time);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_resolve_byprop(IntPtr[] buffer, uint buffer_elements, string prop, string value, int minimum, double timeout);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_resolve_bypred(IntPtr[] buffer, uint buffer_elements, string pred, int minimum, double timeout);
+ }
+}
diff --git a/Source/SharpLSL/Interop/StreamInfo.Interop.cs b/Source/SharpLSL/Interop/StreamInfo.Interop.cs
new file mode 100644
index 0000000..e914db3
--- /dev/null
+++ b/Source/SharpLSL/Interop/StreamInfo.Interop.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Runtime.InteropServices;
+
+using lsl_streaminfo = System.IntPtr;
+
+namespace SharpLSL.Interop
+{
+ public static partial class LSL
+ {
+ // TODO: Encoding
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_streaminfo lsl_create_streaminfo(
+ [MarshalAs(UnmanagedType.LPStr)] string name,
+ [MarshalAs(UnmanagedType.LPStr)] string type,
+ int channel_count,
+ double nominal_srate,
+ ChannelFormat channel_format,
+ [MarshalAs(UnmanagedType.LPStr)] string source_id);
+
+ // TODO: Encoding
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_streaminfo lsl_streaminfo_from_xml(
+ [MarshalAs(UnmanagedType.LPStr)] string xml);
+
+ // TODO: Encoding
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_stream_info_matches_query(
+ lsl_streaminfo info,
+ [MarshalAs(UnmanagedType.LPStr)] string query);
+ }
+}
+
+
+// References:
+// [Refactor your code using alias any type](https://devblogs.microsoft.com/dotnet/refactor-your-code-using-alias-any-type/)
diff --git a/Source/SharpLSL/Interop/XML.Interop.cs b/Source/SharpLSL/Interop/XML.Interop.cs
new file mode 100644
index 0000000..7d65561
--- /dev/null
+++ b/Source/SharpLSL/Interop/XML.Interop.cs
@@ -0,0 +1,58 @@
+using System.Runtime.InteropServices;
+
+using lsl_xml_ptr = System.IntPtr;
+
+namespace SharpLSL.Interop
+{
+ // TODO: Encoding
+ public static unsafe partial class LSL
+ {
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_set_name(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string rhs);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_set_value(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string rhs);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_child(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_child_value_n(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern int lsl_set_child_value(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_next_sibling_n(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_previous_sibling_n(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_append_child(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_append_child_value(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_prepend_child(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern lsl_xml_ptr lsl_prepend_child_value(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value);
+
+ [DllImport("lsl", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ public static extern void lsl_remove_child_n(
+ lsl_xml_ptr e, [MarshalAs(UnmanagedType.LPStr)] string name);
+ }
+}
diff --git a/Source/SharpLSL/Interop/Xml.g.cs b/Source/SharpLSL/Interop/XML.g.cs
similarity index 100%
rename from Source/SharpLSL/Interop/Xml.g.cs
rename to Source/SharpLSL/Interop/XML.g.cs
diff --git a/Source/SharpLSL/LSL.cs b/Source/SharpLSL/LSL.cs
new file mode 100644
index 0000000..2a44ccc
--- /dev/null
+++ b/Source/SharpLSL/LSL.cs
@@ -0,0 +1,205 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using SharpLSL.Interop;
+using static SharpLSL.Interop.LSL;
+
+namespace SharpLSL
+{
+ public static class LSL
+ {
+ ///
+ /// Constant to indicate that a stream has variable sampling rate.
+ ///
+ ///
+ public const double IrregularRate = LSL_IRREGULAR_RATE;
+
+ ///
+ /// Constant to indicate that a sample has the next successive timestamp.
+ ///
+ ///
+ /// This is an optional optimization to transmit less data per sample.
+ /// The timestamp is then deduced from the preceding one according to the stream's
+ /// sampling rate. If the sampling rate is irregular, the time stamp will be assumed
+ /// to be the same as the previous sample's timestamp.
+ ///
+ public const double DeducedTimestamp = LSL_DEDUCED_TIMESTAMP;
+
+ ///
+ /// Constant to indicate a very large time value, approximately equivalent to 1 year.
+ /// This constant can be used for specifying timeouts where you want to effectively
+ /// indicate an indefinite or very long duration.
+ ///
+ public const double Forever = LSL_FOREVER;
+
+ ///
+ /// Constant to indicate that there is no preference about how a data stream shall be
+ /// chunked for transmission.
+ ///
+ ///
+ /// This constant can be used for the chunking parameters in the inlet or the outlet.
+ ///
+ public const int NoPreference = LSL_NO_PREFERENCE;
+
+ ///
+ /// Constant to indicate the LSL version the binary was compiled against.
+ ///
+ ///
+ /// This constant is used either to check if the same version is used:
+ ///
+ /// if (LSL.GetProtocolVersion() != LSL.CompileHeaderVersion)
+ /// {
+ /// // Do stuff...
+ /// }
+ ///
+ /// or to require a certain set of features::
+ ///
+ /// if (LSL.CompileHeaderVersion > 113)
+ /// {
+ /// // Do stuff...
+ /// }
+ ///
+ ///
+ ///
+ public const int CompileHeaderVersion = LIBLSL_COMPILE_HEADER_VERSION;
+
+ ///
+ /// Gets the last error message from the LSL (Lab Streaming Layer) library.
+ ///
+ ///
+ /// A string containing the most recent error message from the LSL library.
+ ///
+ public static string GetLastError() => PtrToString(lsl_last_error());
+
+ ///
+ /// Gets the LSL protocol version encoded as a single integer.
+ ///
+ /// The LSL protocol version encoded as a single integer.
+ ///
+ /// The protocol version is encoded as a single integer, where:
+ ///
+ /// - The major version can be obtained by dividing the protocol version by 100 (i.e., LSL.GetProtocolVersion() / 100).
+ /// - The minor version can be obtained by taking the remainder of the protocol version divided by 100 (i.e., LSL.GetProtocolVersion() % 100).
+ ///
+ /// Clients with different minor versions are considered protocol-compatible,
+ /// while clients with different major versions are not compatible and will
+ /// refuse to work together.
+ ///
+ ///
+ ///
+ public static int GetProtocolVersion() => lsl_protocol_version();
+
+ ///
+ /// Gets the underlying liblsl version number encoded as a single integer.
+ ///
+ /// The liblsl version number encoded as a single integer.
+ ///
+ /// The liblsl version number is encoded as a single integer, where:
+ ///
+ /// - The major version can be obtained by dividing the version by 100 (i.e., LSL.GetLibraryVersion() / 100).
+ /// - The minor version can be obtained by taking the remainder of the version divided by 100 (i.e., LSL.GetLibraryVersion() % 100).
+ ///
+ ///
+ public static int GetLibraryVersion() => lsl_library_version();
+
+ ///
+ /// Gets a string containing library information.
+ ///
+ /// The string containing library information.
+ ///
+ /// The format of the string shouldn't be used for anything important except
+ /// giving a debugging person a good idea which exact library version is used.
+ ///
+ public static string GetLibraryInfo() => PtrToString(lsl_library_info());
+
+ ///
+ /// Gets the current local system timestamp in seconds with high resolution.
+ ///
+ /// The current local system timestamp in seconds.
+ ///
+ /// This function returns a local system timestamp in seconds which has a
+ /// high resolution better than a milliseconds. The returned timestamp can
+ /// be used to assign timestamps to samples as they are being acquired.
+ /// If the "age" of a sample is known at a particular time (e.g., from USB
+ /// transmission delays), it can be used as an offset to
+ /// to obtain a better estimate of when a sample was actually captured.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ // TODO: Update seealso
+ public static double GetLocalClock() => lsl_local_clock();
+
+
+ // TODO: Encoding
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ internal static string PtrToString(IntPtr ptr)
+ {
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+
+ // TODO: Encoding
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ internal static string PtrToXmlString(IntPtr ptr)
+ {
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ internal static void CheckError(int errorCode)
+ {
+ if (errorCode < 0)
+ {
+ switch ((lsl_error_code_t)errorCode)
+ {
+ //case lsl_error_code_t.lsl_no_error:
+ // break;
+ case lsl_error_code_t.lsl_timeout_error:
+ throw new TimeoutException("The operation failed due to a timeout.");
+ case lsl_error_code_t.lsl_lost_error:
+ throw new StreamLostException("The stream has been lost.");
+ case lsl_error_code_t.lsl_argument_error:
+ throw new ArgumentException("An argument was incorrectly specified.");
+ case lsl_error_code_t.lsl_internal_error:
+ throw new LSLInternalException("An internal error has occurred.");
+ default:
+ throw new LSLException("An unknown error has occurred.");
+ }
+ }
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ internal static void CheckChannelCount(int expected, int actual)
+ {
+ if (actual != expected)
+ throw new ArgumentException($"Provided buffer's channel count ({actual}) doesn't match the stream's channel count ({expected}).");
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ internal static void CheckSampleBuffer(T[] sample, int channelCount)
+ {
+ if (sample == null)
+ throw new ArgumentNullException(nameof(sample));
+
+ CheckChannelCount(channelCount, sample.Length);
+ }
+ }
+}
diff --git a/Source/SharpLSL/LSLException.cs b/Source/SharpLSL/LSLException.cs
new file mode 100644
index 0000000..ef88a92
--- /dev/null
+++ b/Source/SharpLSL/LSLException.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents errors that occur within the LSL (Lab Streaming Layer) framework.
+ ///
+ ///
+ /// This exception is used to signal issues specific to LSL operations, such as
+ /// problems with data streams or communication errors. It extends the base
+ /// class to provide additional context for LSL-related
+ /// exceptions.
+ ///
+ public class LSLException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LSLException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ public LSLException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message and a reference to the inner exception.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null
+ /// reference if no inner exception is specified.
+ ///
+ public LSLException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/Source/SharpLSL/LSLInternalException.cs b/Source/SharpLSL/LSLInternalException.cs
new file mode 100644
index 0000000..6c53ea8
--- /dev/null
+++ b/Source/SharpLSL/LSLInternalException.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents an internal error within the LSL (Lab Streaming Layer) framework.
+ ///
+ ///
+ /// This exception is used to indicate errors that occur within the internal of LSL.
+ /// This class extends to provide a more specific exception
+ /// type for internal LSL errors.
+ ///
+ public class LSLInternalException : LSLException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LSLInternalException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ public LSLInternalException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message and a reference to the inner exception.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null
+ /// reference if no inner exception is specified.
+ ///
+ public LSLInternalException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/Source/SharpLSL/LSLObject.cs b/Source/SharpLSL/LSLObject.cs
new file mode 100644
index 0000000..851fc44
--- /dev/null
+++ b/Source/SharpLSL/LSLObject.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents a base class for LSL (Lab Streaming Layer) objects, which wraps
+ /// a native handle to manage the underlying native resources.
+ ///
+ ///
+ /// This abstract class extends to provide a safe and
+ /// managed way to interact with LSL objects. It ensures that the native resources
+ /// associated with the LSL objects are properly released when no longer needed.
+ /// Derived classes should implement specific functionality for different types
+ /// of LSL objects, such as inlets or outlets.
+ ///
+ public abstract class LSLObject : SafeHandle
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Speciies whether the wrapped handle should be released during the finalization
+ /// phase.
+ ///
+ protected LSLObject(bool ownsHandle = true)
+ : base(IntPtr.Zero, ownsHandle)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the
+ /// specified LSL native handle value.
+ ///
+ ///
+ /// Specifies the handle to be wrapped.
+ ///
+ ///
+ /// Speciies whether the wrapped handle should be released during the finalization phase.
+ ///
+ ///
+ /// Thrown if the handle to be wrapped is invalid.
+ ///
+ protected LSLObject(IntPtr handle, bool ownsHandle = true)
+ : base(IntPtr.Zero, ownsHandle)
+ {
+ if (handle == IntPtr.Zero)
+ throw new LSLException($"Failed to create {GetType().Name} object: {LSL.GetLastError()}.");
+
+ SetHandle(handle);
+ }
+
+ ///
+ /// Gets a value indicating whether the handle value is invalid.
+ ///
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ ///
+ /// Frees the wrapped LSL native handle by calling .
+ ///
+ /// A value indicates if the handle is released successfully.
+ protected override bool ReleaseHandle()
+ {
+ DestroyLSLObject();
+ // Without this line, IsInvalid == false;
+ // SetHandleAsInvalid() won't change the value of handle.
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ ///
+ /// When overridden in a derived class, ensures that the appropriate LSL
+ /// function is called to release the handle of the corresponding LSL object.
+ ///
+ ///
+ protected abstract void DestroyLSLObject();
+
+ ///
+ /// Throws an if the handle is invalid.
+ ///
+ ///
+ /// Thrown when the handle is invalid (i.e., is true).
+ ///
+ protected void ThrowIfInvalid()
+ {
+ if (IsInvalid)
+ throw new InvalidOperationException("The handle is invalid.");
+ }
+ }
+}
+
+
+// References:
+// [SafeHandle Class](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle?view=net-8.0)
+// [System.Runtime.InteropServices.SafeHandle class](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-runtime-interopservices-safehandle)
+// [SafeHandle.SetHandleAsInvalid Method](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.safehandle.sethandleasinvalid?view=net-8.0)
+// > As with the SetHandle method, use SetHandleAsInvalid only if you need to support a pre-existing handle.
+// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
+// https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Net/DebugSafeHandleZeroOrMinusOneIsInvalid.cs
+// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs
+// https://github.com/dotnet/runtime/blob/main/src/tests/Interop/PInvoke/SafeHandles/SafeHandles.cs
+// https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Microsoft/Win32/SafeHandles/SafeCertStoreHandle.cs
+// [Cannot pass SafeHandle instance in ReleaseHandle to native method](https://stackoverflow.com/questions/47934248/cannot-pass-safehandle-instance-in-releasehandle-to-native-method)
+// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.cs
+// > SetHandle(...);
+// https://github.com/dotnet/runtime/tree/main/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles
+// [SafeHandle used to wrap a non-owned pointer](https://stackoverflow.com/questions/31691183/safehandle-used-to-wrap-a-non-owned-pointer)
+// [Are there any benefits for using SafeFileHandle with FileStream constructor](https://stackoverflow.com/questions/58576214/are-there-any-benefits-for-using-safefilehandle-with-filestream-constructor)
+// [What is SafeFileHandle in C# and when should I use it?](https://stackoverflow.com/questions/58568415/what-is-safefilehandle-in-c-sharp-and-when-should-i-use-it)
+// [How to Close SafeFile Handle properly](https://stackoverflow.com/questions/20827222/how-to-close-safefile-handle-properly)
+// https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs
+// > SetHandle(IntPtr.Zero);
+// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicSafeHandle.cs
+// > SetHandle(IntPtr.Zero);
diff --git a/Source/SharpLSL/Lsl.cs b/Source/SharpLSL/Lsl.cs
deleted file mode 100644
index a299d53..0000000
--- a/Source/SharpLSL/Lsl.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-using SharpLSL.Interop;
-
-namespace SharpLSL
-{
- public static class Lsl
- {
- public static string GetLastError()
- {
- var ptr = LSL.lsl_last_error();
- return Marshal.PtrToStringAnsi(ptr);
- }
-
- public static int GetProtocolVersion() => LSL.lsl_protocol_version();
-
- public static int GetLibraryVersion() => LSL.lsl_library_version();
-
- public static string GetLibraryInfo()
- {
- var ptr = LSL.lsl_library_info();
- return Marshal.PtrToStringAnsi(ptr);
- }
-
- public static double GetLocalClock() => LSL.lsl_local_clock();
-
- public static void DestroyString(IntPtr str) => LSL.lsl_destroy_string(str);
- }
-}
diff --git a/Source/SharpLSL/LslChannelFormat.cs b/Source/SharpLSL/LslChannelFormat.cs
deleted file mode 100644
index 9866579..0000000
--- a/Source/SharpLSL/LslChannelFormat.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using SharpLSL.Interop;
-
-namespace SharpLSL
-{
- public enum LslChannelFormat : int
- {
- Float = lsl_channel_format_t.cft_float32,
- Double = lsl_channel_format_t.cft_double64,
- String = lsl_channel_format_t.cft_string,
- Int32 = lsl_channel_format_t.cft_int32,
- Int16 = lsl_channel_format_t.cft_int16,
- Int8 = lsl_channel_format_t.cft_int8,
- Int64 = lsl_channel_format_t.cft_int64,
- Undefined = lsl_channel_format_t.cft_undefined,
- }
-}
diff --git a/Source/SharpLSL/LslException.cs b/Source/SharpLSL/LslException.cs
deleted file mode 100644
index 80b9f7a..0000000
--- a/Source/SharpLSL/LslException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace SharpLSL
-{
- public class LSLException : Exception
- {
- public LSLException()
- {
- }
-
- public LSLException(string message)
- : base(message)
- {
- }
-
- public LSLException(string message, Exception innerException)
- : base(message, innerException)
- {
- }
- }
-}
diff --git a/Source/SharpLSL/LslObject.cs b/Source/SharpLSL/LslObject.cs
deleted file mode 100644
index 5147e0d..0000000
--- a/Source/SharpLSL/LslObject.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace SharpLSL
-{
- public abstract class LslObject : SafeHandle
- {
- protected LslObject(IntPtr lslHandle, bool ownsHandle = true)
- : base(IntPtr.Zero, ownsHandle)
- {
- if (lslHandle == IntPtr.Zero)
- throw new LSLException($"Failed to create {GetType().Name} object: {Lsl.GetLastError()}.");
-
- SetHandle(lslHandle);
- }
-
- public override bool IsInvalid => handle != IntPtr.Zero;
- }
-}
diff --git a/Source/SharpLSL/LslProcessingOptions.cs b/Source/SharpLSL/LslProcessingOptions.cs
deleted file mode 100644
index 1e61e7d..0000000
--- a/Source/SharpLSL/LslProcessingOptions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-
-using SharpLSL.Interop;
-
-namespace SharpLSL
-{
- [Flags]
- public enum LslProcessingOptions
- {
- None = lsl_processing_options_t.proc_none,
- ClockSync = lsl_processing_options_t.proc_clocksync,
- Dejitter = lsl_processing_options_t.proc_dejitter,
- Monotonize = lsl_processing_options_t.proc_monotonize,
- ThreadSafe = lsl_processing_options_t.proc_threadsafe,
- All = ClockSync | Dejitter | Monotonize | ThreadSafe,
- }
-}
diff --git a/Source/SharpLSL/LslStreamInfo.cs b/Source/SharpLSL/LslStreamInfo.cs
deleted file mode 100644
index b802cb2..0000000
--- a/Source/SharpLSL/LslStreamInfo.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-using SharpLSL.Interop;
-
-namespace SharpLSL
-{
- public class LslStreamInfo : LslObject
- {
- public LslStreamInfo(
- string name,
- string type,
- int channelCount = 1,
- double nominalSrate = LSL.LSL_IRREGULAR_RATE,
- LslChannelFormat channelFormat = LslChannelFormat.Float,
- string sourceId = "")
- : this(CreateStreamInfo(
- name,
- type,
- channelCount,
- nominalSrate,
- channelFormat,
- sourceId), true)
- {
- }
-
- public LslStreamInfo(IntPtr handle, bool ownsHandle = true)
- : base(handle, ownsHandle)
- {
- }
-
- protected override bool ReleaseHandle()
- {
- LSL.lsl_destroy_streaminfo(handle);
- return true;
- }
-
- private static IntPtr CreateStreamInfo(
- string name,
- string type,
- int channelCount,
- double nominalSrate,
- LslChannelFormat channelFormat,
- string sourceId)
- {
- var namePtr = IntPtr.Zero;
- var typePtr = IntPtr.Zero;
- var sourceIdPtr = IntPtr.Zero;
-
- try
- {
- namePtr = Marshal.StringToHGlobalAnsi(name);
- typePtr = Marshal.StringToHGlobalAnsi(type);
- sourceIdPtr = Marshal.StringToHGlobalAnsi(sourceId);
-
- return LSL.lsl_create_streaminfo(
- namePtr,
- typePtr,
- channelCount,
- nominalSrate,
- (lsl_channel_format_t)channelFormat,
- sourceIdPtr);
- }
- finally
- {
- if (namePtr != IntPtr.Zero)
- Marshal.FreeHGlobal(namePtr);
-
- if (typePtr != IntPtr.Zero)
- Marshal.FreeHGlobal(typePtr);
-
- if (sourceIdPtr != IntPtr.Zero)
- Marshal.FreeHGlobal(sourceIdPtr);
- }
- }
- }
-}
-
-
-// References:
-// https://github.com/labstreaminglayer/liblsl-Csharp/blob/master/LSL.cs
-// [make IntPtr in C#.NET point to string value](https://stackoverflow.com/questions/11090427/make-intptr-in-c-net-point-to-string-value)
diff --git a/Source/SharpLSL/LslTransportOptions.cs b/Source/SharpLSL/LslTransportOptions.cs
deleted file mode 100644
index 680ef6c..0000000
--- a/Source/SharpLSL/LslTransportOptions.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SharpLSL.Interop;
-
-namespace SharpLSL
-{
- public enum LslTransportOptions
- {
- Default = lsl_transport_options_t.transp_default,
- BufferSizeSamples = lsl_transport_options_t.transp_bufsize_samples,
- BufferSizeThousandths = lsl_transport_options_t.transp_bufsize_thousandths,
- }
-}
diff --git a/Source/SharpLSL/PostProcessingOptions.cs b/Source/SharpLSL/PostProcessingOptions.cs
new file mode 100644
index 0000000..f03845e
--- /dev/null
+++ b/Source/SharpLSL/PostProcessingOptions.cs
@@ -0,0 +1,68 @@
+using System;
+
+using SharpLSL.Interop;
+
+namespace SharpLSL
+{
+ ///
+ /// Post-processing options for stream inlets.
+ ///
+ ///
+ [Flags]
+ public enum PostProcessingOptions
+ {
+ ///
+ /// No automatic post-processing is applied.
+ ///
+ ///
+ /// This option returns the ground-truth timestamps as they are, allowing
+ /// for manual post-processing. It is the default behavior of the inlet.
+ ///
+ None = lsl_processing_options_t.proc_none,
+
+ ///
+ /// Performs clock synchronization automatically.
+ ///
+ ///
+ /// This option indicates a time correction to the timestamps will be performed
+ /// automatically. It is equivalent to manually adding the
+ /// value to the received timestamps.
+ ///
+ ///
+ ///
+ ClockSync = lsl_processing_options_t.proc_clocksync,
+
+ ///
+ /// Removes jitter from timestamps.
+ ///
+ ///
+ /// This will apply a smoothing algorithm to the received timestamps; the
+ /// smoothing needs to see a minimum number of samples (30-120 seconds
+ /// worst-case) until the remaining jitter is consistently below 1ms.
+ ///
+ Dejitter = lsl_processing_options_t.proc_dejitter,
+
+ ///
+ /// Forces the timestamps to be monotonically ascending.
+ ///
+ ///
+ /// This option only makes sense if timestamps are dejittered.
+ ///
+ Monotonize = lsl_processing_options_t.proc_monotonize,
+
+ ///
+ /// Indicates post-processing is thread-safe.
+ ///
+ ///
+ /// This option ensures that post-processing is thread-safe, allowing multiple
+ /// threads to read from the same inlet concurrently. Note that this flag may
+ /// increase CPU usage.
+ ///
+ ThreadSafe = lsl_processing_options_t.proc_threadsafe,
+
+ ///
+ /// The combination of all possible post-processing options.
+ ///
+ All = ClockSync | Dejitter | Monotonize | ThreadSafe,
+ }
+}
diff --git a/Source/SharpLSL/StreamInfo.cs b/Source/SharpLSL/StreamInfo.cs
new file mode 100644
index 0000000..509a191
--- /dev/null
+++ b/Source/SharpLSL/StreamInfo.cs
@@ -0,0 +1,531 @@
+using System;
+
+using static SharpLSL.Interop.LSL;
+using static SharpLSL.LSL;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents a `lsl_streaminfo` object which keeps a stream's meta data and
+ /// connection settings.
+ ///
+ ///
+ /// Whenever a program wants to provide a new stream on the lab network it will
+ /// typically first create a object to describe its
+ /// properties and then construct a with it to create
+ /// the stream on the network. Recipients who discover the outlet can query the
+ /// ; it is also written to disk when recording the stream
+ /// (playing a similar role as a file header).
+ ///
+ public class StreamInfo : LSLObject
+ {
+ ///
+ /// Constructs a new instance of object.
+ ///
+ ///
+ /// The stream name.
+ ///
+ /// The stream name describes the device (or product series) that this stream
+ /// makes available (for use by programs, experimenters or data analysts).
+ /// The stream name cannot be empty.
+ ///
+ ///
+ ///
+ /// The content type of the stream.
+ ///
+ /// Please see https://github.com/sccn/xdf/wiki/Meta-Data for pre-defined
+ /// content-type names. Note that you can also make up your own stream type
+ /// The stream content type is the preferred way to find streams (as opposed
+ /// to searching stream by name).
+ ///
+ ///
+ ///
+ /// Number of channels per sample.
+ ///
+ /// This stays constant during the lifetime of the stream.
+ ///
+ ///
+ ///
+ /// The nominal sampling rate(in Hz) as advertised by the data source,
+ /// if regular(otherwise set to ).
+ ///
+ ///
+ /// The data format of each channel.
+ ///
+ /// If your channels have different formats, consider supplying multiple
+ /// streams or use the largest type that can hold them all(such as ).
+ ///
+ ///
+ ///
+ /// Unique identifier of the device or source of the data, if available
+ /// (such as the serial number).
+ ///
+ /// This is critical for system robustness since it allows recipients to
+ /// recover from failure even after the serving app, device or computer
+ /// crashes(just by finding a stream with the same source id on the network
+ /// again). Therefore, it is highly recommended to always try to provide
+ /// whatever information can uniquely identify the data source itself.
+ ///
+ ///
+ ///
+ /// Thrown when creating a new instance of fails.
+ ///
+ public StreamInfo(
+ string name,
+ string type,
+ int channelCount = 1,
+ double nominalSrate = IrregularRate,
+ ChannelFormat channelFormat = ChannelFormat.Float,
+ string sourceId = "")
+ : this(lsl_create_streaminfo(
+ name,
+ type,
+ channelCount,
+ nominalSrate,
+ channelFormat,
+ sourceId), true)
+ {
+ }
+
+ ///
+ /// Constructs a new instance of object which wraps
+ /// a pre-existing `lsl_streaminfo` handle.
+ ///
+ ///
+ /// Specifies the handle to be wrapped.
+ ///
+ ///
+ /// Speciies whether the wrapped handle should be released during the finalization phase.
+ ///
+ ///
+ /// Thrown if the handle is invalid.
+ ///
+ public StreamInfo(IntPtr handle, bool ownsHandle = true)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ ///
+ /// Gets the name of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ ///
+ /// The stream name is a human-readable name.
+ ///
+ ///
+ /// For streams offered by device modules, it refers to the type of device
+ /// or product series that is generating the data of the stream. If the
+ /// source is an application, the name may be a more generic or specific
+ /// identifier. Multiple streams with the same name can coexist, though
+ /// potentially at the cost of ambiguity (for the recording app or experimenter).
+ ///
+ ///
+ public string Name
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_name(handle));
+ }
+ }
+
+ ///
+ /// Gets the content type of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// The content type is a short string such as "EEG", "Gaze" which describes
+ /// the content carried by the channel (if known). If a stream contains mixed
+ /// content this value need not be assigned but may instead be stored in the
+ /// description of channel types. To be useful to applications and automated
+ /// processing systems using the recommended content types is preferred. Content
+ /// types usually follow those pre-defined in the [wiki](https://github.com/sccn/xdf/wiki/Meta-Data).
+ ///
+ public string Type
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_type(handle));
+ }
+ }
+
+ ///
+ /// Gets number of channels of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// A stream has at least one channels, and the channel count stays constant
+ /// for all samples.
+ ///
+ public int ChannelCount
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_channel_count(handle);
+ }
+ }
+
+ ///
+ /// Gets the nominal sampling rate (in Hz) announced by the data source.
+ /// If the stream is irregularly sampled, this should be set to .
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Note that no data will be lost even if this sampling rate is incorrect
+ /// or if a device has temporary hiccups, since all samples will be recorded
+ /// anyway (except for those dropped by the device itself). However, when
+ /// the recording is imported into an application, a good importer may correct
+ /// such errors more accurately if the advertised sampling rate was close to
+ /// the specs of the device.
+ ///
+ public double NominalSrate
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_nominal_srate(handle);
+ }
+ }
+
+ ///
+ /// Gets the channel format of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// All channels in a stream have the same format. However, a device might
+ /// offer multiple time-synched streams each with its own format.
+ ///
+ ///
+ public ChannelFormat ChannelFormat
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return (ChannelFormat)lsl_get_channel_format(handle);
+ }
+ }
+
+ ///
+ /// Gets number of bytes occupied by a channel (0 for string-typed channels).
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ public int ChannelBytes
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_channel_bytes(handle);
+ }
+ }
+
+ ///
+ /// Gets number of bytes occupied by a sample (0 for string-typed channels).
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ public int SampleBytes
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_sample_bytes(handle);
+ }
+ }
+
+ ///
+ /// Gets the unique identifier of the stream's source, if available.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// The unique source (or device) identifier is an optional piece of
+ /// information that, if available, allows that endpoints (such as the
+ /// recording program) can re-acquire a stream automatically once it is
+ /// back online.
+ ///
+ public string SourceId
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_source_id(handle));
+ }
+ }
+
+ ///
+ /// Gets the protocol version used to deliver the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ public int Version
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_version(handle);
+ }
+ }
+
+ ///
+ /// Gets the creation timestamp of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// This is the time stamp when the stream was first created(as determined
+ /// via on the providing machine).
+ ///
+ public double CreatedAt
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return lsl_get_created_at(handle);
+ }
+ }
+
+ ///
+ /// Gets the unique ID of the stream outlet (once assigned).
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// This is a unique identifier of the stream outlet, and is guaranteed
+ /// to be different across multiple instantiations of the same outlet
+ /// (e.g., after a re-start).
+ ///
+ public string Uid
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_uid(handle));
+ }
+ }
+
+ ///
+ /// Gets the session ID of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// The session id is an optional human-assigned identifier of the recording
+ /// session. While it is rarely used, it can be used to prevent concurrent
+ /// recording activitites on the same sub-network (e.g., in multiple experiment
+ /// areas) from seeing each other's streams (assigned via a configuration file
+ /// by the experimenter, see Network Connectivity in the LSL wiki).
+ ///
+ public string SessionId
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_session_id(handle));
+ }
+ }
+
+ ///
+ /// Gets the host name of providing machine (once bound to an outlet).
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ public string HostName
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return PtrToString(lsl_get_hostname(handle));
+ }
+ }
+
+ ///
+ /// Gets the extended description of the stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if getting extended description fails.
+ ///
+ ///
+ ///
+ /// The extended description may contain extra meta-data of the stream, such as:
+ /// channel labels, amplifier settings, measurement units, setup information,
+ /// subject information, etc.
+ ///
+ ///
+ /// Meta-data recommendations follow the XDF file format project. See:
+ /// https://github.com/sccn/xdf/wiki/Meta-Data for more details.
+ ///
+ ///
+ /// If you use a stream content type for which meta-data recommendations
+ /// exist, please try to lay out your meta-data in agreement with these
+ /// recommendations for compatibility with other applications.
+ ///
+ ///
+ ///
+ public XMLElement Description
+ {
+ get
+ {
+ ThrowIfInvalid();
+ // TODO: memory free?
+ return new XMLElement(lsl_get_desc(handle));
+ }
+ }
+
+ ///
+ /// Creates a copy of the current instance.
+ ///
+ ///
+ /// A new instance of that is a copy of the current instance.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown when creating a copy of current instance of fails.
+ ///
+ ///
+ /// This method is rarely used.
+ ///
+ public StreamInfo Clone()
+ {
+ ThrowIfInvalid();
+ return new StreamInfo(lsl_copy_streaminfo(handle), true);
+ }
+
+ ///
+ /// Tests whether current object matches the given
+ /// query string.
+ ///
+ /// The query string.
+ /// Whether stream info is matched by the query string.
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the query string is null or empty.
+ ///
+ ///
+ /// The represents an XPath 1.0 query string.
+ /// Here are some examples:
+ ///
+ /// channel_count>5 and type='EEG'
+ /// type='TestStream' or contains(name,'Brain')
+ /// name='ExampleStream'
+ ///
+ ///
+ public bool MatchesQuery(string query)
+ {
+ ThrowIfInvalid();
+
+ if (string.IsNullOrEmpty(query))
+ throw new ArgumentException(nameof(query));
+
+ return Convert.ToBoolean(lsl_stream_info_matches_query(handle, query));
+ }
+
+ ///
+ /// Retrieves the entire in XML format.
+ ///
+ ///
+ /// The XML representation of the object as a string.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown when retrieving the entire in XML format fails.
+ ///
+ ///
+ /// This yields an XML document (in string form) whose top-level element is
+ /// ``. element contains one element for each field of the streaminfo
+ /// class, including:
+ /// - the core elements ``, ``, ``, ``,
+ /// ``, ``
+ /// - the misc elements ``, ``, ``, ``,
+ /// ``, ``, ``, ``,
+ /// ``, ``
+ /// - the extended description element `` with user-defined sub-elements.
+ ///
+ ///
+ ///
+ public string ToXML()
+ {
+ ThrowIfInvalid();
+
+ var xml = lsl_get_xml(handle);
+ if (xml != IntPtr.Zero)
+ {
+ var xmlString = PtrToXmlString(xml);
+ lsl_destroy_string(xml);
+ return xmlString;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Constructs a new instance of object from an
+ /// XML representation.
+ ///
+ ///
+ /// The XML representation of the object as a string.
+ ///
+ ///
+ /// A new instance of corresponding to the XML
+ /// representation.
+ ///
+ ///
+ /// Thrown if the is null or empty.
+ ///
+ ///
+ /// Thrown when creating a new instance of fails.
+ ///
+ ///
+ public static StreamInfo FromXML(string xml)
+ {
+ if (string.IsNullOrEmpty(xml))
+ throw new ArgumentException(nameof(xml));
+
+ return new StreamInfo(lsl_streaminfo_from_xml(xml), true);
+ }
+
+ ///
+ /// Destroys the underlying `lsl_streaminfo` handle associated with this instance.
+ ///
+ protected override void DestroyLSLObject()
+ {
+ lsl_destroy_streaminfo(handle);
+ }
+ }
+}
+
+
+// References:
+// https://github.com/labstreaminglayer/liblsl-Csharp/blob/master/LSL.cs
+// [make IntPtr in C#.NET point to string value](https://stackoverflow.com/questions/11090427/make-intptr-in-c-net-point-to-string-value)
diff --git a/Source/SharpLSL/StreamInlet.cs b/Source/SharpLSL/StreamInlet.cs
new file mode 100644
index 0000000..5e753a9
--- /dev/null
+++ b/Source/SharpLSL/StreamInlet.cs
@@ -0,0 +1,816 @@
+using System;
+using System.Collections.Generic;
+
+using SharpLSL.Interop;
+
+using static SharpLSL.Interop.LSL;
+using static SharpLSL.LSL;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents a stream inlet object for receiving streaming data (and meta-data)
+ /// from the lab network.
+ ///
+ public class StreamInlet : LSLObject
+ {
+ ///
+ /// Constructs a new stream inlet from a resolved stream info.
+ ///
+ ///
+ ///
+ /// A resolved object (as coming from one of the
+ /// resolver functions).
+ ///
+ ///
+ /// The inlet will makes a copy of the object
+ /// at its construction.
+ ///
+ ///
+ /// The may also be constructed with a fully specified
+ /// , if the desired channel format and count is already
+ /// known up-front, but this is strongly discouraged and should only ever be done
+ /// if there is no time to resolve the stream up-front (e.g., due to limitations
+ /// in the client program).
+ ///
+ ///
+ ///
+ ///
+ /// Specifies the maximum amount of the data to buffer (in seconds if there
+ /// is a nominal sampling rate, otherwise x100 in samples).
+ ///
+ ///
+ /// Recording applications want to use a fairly large buffer size here, while
+ /// real-time applications would only buffer as much as they need to perform
+ /// their next calculation. A good default is 360, which corresponds to 6
+ /// minutes of data.
+ ///
+ ///
+ ///
+ /// Specifies the maximum size, in samples, at which chunks are transmitted.
+ /// If set as 0, the chunk sizes preferred by the sender are used. Recording
+ /// applications can use a generous size here (leaving it to the network how
+ /// to pack things), while real time applications may want a finer (perhaps
+ /// 1-sample) granularity.
+ ///
+ ///
+ /// Specifies whether try to silently recover lost streams that are recoverable
+ /// (those that have a source id set). In all other cases (recover is false or
+ /// the stream is not recoverable) functions may throw a
+ /// if the stream's source is lost (e.g., due to an app or computer crash).
+ ///
+ ///
+ /// Specifies the transport options.
+ ///
+ ///
+ /// Thrown when creating a new instance of fails.
+ ///
+ public StreamInlet(
+ StreamInfo streamInfo, int maxBufferLength = 360, int maxChunkLength = 0,
+ bool recover = true, TransportOptions transportOptions = TransportOptions.Default)
+ : base(lsl_create_inlet_ex(
+ streamInfo.DangerousGetHandle(),
+ maxBufferLength, maxChunkLength,
+ recover ? 1 : 0,
+ (lsl_transport_options_t)transportOptions))
+ {
+ ChannelCount = streamInfo.ChannelCount;
+ }
+
+ ///
+ /// Constructs a new instance of object which wraps
+ /// a pre-existing stream inlet handle.
+ ///
+ ///
+ /// Specifies the handle to be wrapped.
+ ///
+ ///
+ /// Speciies whether the wrapped handle should be released during the finalization phase.
+ ///
+ ///
+ /// Thrown if the handle is invalid.
+ ///
+ public StreamInlet(IntPtr handle, bool ownsHandle = true)
+ : base(handle, ownsHandle)
+ {
+ var streamInfo = GetStreamInfo();
+ ChannelCount = streamInfo.ChannelCount;
+ }
+
+ ///
+ /// Gets number of channels of the stream.
+ ///
+ public int ChannelCount { get; }
+
+ ///
+ /// Retrieves the complete information of the given stream, including the
+ /// extended description.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds. Default value is
+ /// which indicates no timeout.
+ ///
+ ///
+ /// An instance of containing detailed information
+ /// about the stream, including its extended description.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// Thrown if the timeout has expired.
+ ///
+ ///
+ /// This method can be invoked at any time of the stream's lifetime.
+ ///
+ public StreamInfo GetStreamInfo(double timeout = Forever)
+ {
+ ThrowIfInvalid();
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var streamInfo = lsl_get_fullinfo(handle, timeout, ref errorCode);
+#if false
+ if (streamInfo != IntPtr.Zero)
+ return new StreamInfo(streamInfo, true);
+
+ CheckError(errorCode);
+ // [Is there something like [[noreturn]] in C# to indicate the compiler that the method will never return a value?](https://stackoverflow.com/questions/48232105/is-there-something-like-noreturn-in-c-sharp-to-indicate-the-compiler-that-th)
+#else
+ CheckError(errorCode);
+ return new StreamInfo(streamInfo, true);
+#endif
+ }
+
+ ///
+ /// Subscribes to the data stream.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds. Default value is
+ /// which indicates no timeout.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// Thrown if the timeout has expired.
+ ///
+ ///
+ /// All samples pushed in at the other end from this moment onwards will be
+ /// queued and eventually be delivered in response to PullSample() or
+ /// PullChunk() calls. Pulling a sample without some preceding OpenStream()
+ /// is permitted (the stream will then be opened implicitly).
+ ///
+ public void OpenStream(double timeout = Forever)
+ {
+ ThrowIfInvalid();
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ lsl_open_stream(handle, timeout, ref errorCode);
+ CheckError(errorCode);
+ }
+
+ ///
+ /// Drops the current data stream.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// All samples that are still buffered or in flight will be dropped and
+ /// transmission and buffering of data for this inlet will be stopped. If
+ /// an application stops being interested in data from a source (temporarily
+ /// or not) but keeps the outlet alive, it should call CloseStream() to not
+ /// waste unnecessary system and network resources.
+ ///
+ public void CloseStream()
+ {
+ ThrowIfInvalid();
+
+ lsl_close_stream(handle);
+ }
+
+ ///
+ /// Retrieves an estimated time correction offset for the given stream.
+ ///
+ ///
+ /// Specifies the timeout to acquire the first time correction estimate
+ /// in seconds. Default value is which indicates
+ /// no timeout.
+ ///
+ ///
+ /// The time correction estimate. This is the number that needs to be added
+ /// to a timestamp that was remotely generated via
+ /// to map it into the local clock domain of this machine.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// Thrown if the timeout has expired.
+ ///
+ ///
+ ///
+ /// The first call to this function takes several milliseconds until a reliable
+ /// first estimate is obtained. Subsequent calls are instantaneous (and rely
+ /// on periodic background updates). On a well-behaved network, the precision
+ /// of these estimates should be below 1 ms(empirically it is within +/-0.2 ms).
+ ///
+ ///
+ /// To get a measure of whether the network is well-behaved, use the extended
+ /// version and
+ /// check uncertainty (i.e. the round-trip-time).
+ ///
+ ///
+ /// 0.2 ms is typical of wired networks. 2 ms is typical of wireless networks.
+ /// The number can be much higher on poor networks.
+ ///
+ ///
+ ///
+ public double TimeCorrection(double timeout = Forever)
+ {
+ ThrowIfInvalid();
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_time_correction(handle, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ ///
+ /// Retrieves an estimated time correction offset for the given stream.
+ ///
+ ///
+ /// The current time of the remote computer that was used to generate this
+ /// time correction. If desired, the client can fit time correction vs remote
+ /// time to improve the real-time time correction further.
+ ///
+ ///
+ /// The maximum uncertainty of the given time correction.
+ ///
+ ///
+ /// Specifies the timeout to acquire the first time correction estimate
+ /// in seconds. Default value is which indicates
+ /// no timeout.
+ ///
+ ///
+ /// The time correction estimate. This is the number that needs to be added
+ /// to a timestamp that was remotely generated via
+ /// to map it into the local clock domain of this machine.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// Thrown if the timeout has expired.
+ ///
+ ///
+ public double TimeCorrection(ref double remoteTime, ref double uncertainty, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_time_correction_ex(handle, ref remoteTime, ref uncertainty, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ ///
+ /// Sets post-processing flags to use.
+ ///
+ ///
+ /// The post-processing flags to use, this is the result of bitwise OR'ing
+ /// one or more options from together;
+ /// a good setting is to use .
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the specified post-processing flags are invalid.
+ ///
+ ///
+ ///
+ /// By default, the inlet performs NO post-processing and returns the ground
+ /// truth timestamps, which can then be manually synchronized using ,
+ /// and then smoothed/dejittered if desired.
+ ///
+ ///
+ /// This function allows automating these two and possibly more operations.
+ ///
+ ///
+ /// When you enable this, you will no longer receive or be able to recover
+ /// the original timestamps.
+ ///
+ ///
+ public void SetPostProcessingOptions(
+ PostProcessingOptions postProcessingOptions = PostProcessingOptions.All)
+ {
+ ThrowIfInvalid();
+
+ CheckError(lsl_set_postprocessing(handle, (uint)postProcessingOptions));
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(byte[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_c(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(sbyte[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_c(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(short[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_s(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(int[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_i(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(long[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_l(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(float[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_f(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(double[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var timestamp = lsl_pull_sample_d(handle, sample, sample.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return timestamp;
+ }
+
+ ///
+ /// Pulls a sample from the inlet and read it into an array of values. Handles
+ /// type checking & conversion if necessary.
+ ///
+ ///
+ /// The buffer to hold the resulting values.
+ ///
+ ///
+ /// Specifies the timeout of the operation in seconds, if any. Default value
+ /// is which indicates no timeout. Use 0.0 to make a
+ /// non-blocking call, in this case a sample is only returned if one is
+ /// currently buffered.
+ ///
+ ///
+ /// The capture time of the sample on the remote machine. Returns 0.0 if no
+ /// new sample was available or the timeout expires. To remap this timestamp
+ /// to the local clock, add the value returned by
+ /// to it.
+ ///
+ ///
+ /// Thrown if this object is invalid.
+ ///
+ ///
+ /// Thrown if the provided buffer is null.
+ ///
+ ///
+ /// Thrown if size of the provided buffer does not
+ /// the channel count () of the stream inlet.
+ ///
+ ///
+ /// Thrown if the stream source has been lost.
+ ///
+ ///
+ /// If the timeout expires before a new sample was received, the function
+ /// returns 0.0; this case is not considered an error condition.
+ ///
+ public double PullSample(string[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var buffer = new IntPtr[ChannelCount];
+ var timestamp = lsl_pull_sample_str(handle, buffer, buffer.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+
+ for (int i = 0; i < buffer.Length; ++i)
+ {
+ sample[i] = PtrToString(buffer[i]);
+ lsl_destroy_string(buffer[i]);
+ }
+
+ return timestamp;
+ }
+
+ public double PullSample(List[] sample, double timeout = Forever)
+ {
+ ThrowIfInvalid();
+ CheckSampleBuffer(sample, ChannelCount);
+
+ return 0.0;
+ }
+
+ public uint PullChunk(short[] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_s(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(int[] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_i(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(long[] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_l(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(float[] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_f(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(double[] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_d(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(short[,] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_s(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(int[,] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_i(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(long[,] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_l(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(float[,] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_f(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public uint PullChunk(double[,] chunk, double[] timestamps, double timeout)
+ {
+ var errorCode = (int)lsl_error_code_t.lsl_no_error;
+ var result = lsl_pull_chunk_d(handle, chunk, timestamps, (uint)chunk.Length, (uint)timestamps.Length, timeout, ref errorCode);
+ CheckError(errorCode);
+ return result;
+ }
+
+ public bool SamplesAvailable() => lsl_samples_available(handle) > 0;
+
+ public uint Flush() => lsl_inlet_flush(handle);
+
+ public uint WasClockReset() => lsl_was_clock_reset(handle);
+
+ public void SetSmoothingHalfTime(float value)
+ {
+ CheckError(lsl_smoothing_halftime(handle, value));
+ }
+
+ ///
+ /// Disconnects the inlet and destroys the underlying native resource.
+ ///
+ protected override void DestroyLSLObject()
+ {
+ lsl_destroy_inlet(handle);
+ }
+ }
+}
diff --git a/Source/SharpLSL/StreamLostException.cs b/Source/SharpLSL/StreamLostException.cs
new file mode 100644
index 0000000..232cc40
--- /dev/null
+++ b/Source/SharpLSL/StreamLostException.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents an error that occurs when a LSL stream is unexpectedly lost or disconnected.
+ ///
+ ///
+ /// This exception is thrown when an application attempts to access a LSL stream
+ /// that is no longer available. It indicates that the connection to the stream
+ /// has been lost, which may be due to network issues, stream termination, or other
+ /// unforeseen circumstances. This exception extends the base class
+ /// to provide specific information about stream loss errors.
+ ///
+ public class StreamLostException : LSLException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StreamLostException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ public StreamLostException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with
+ /// a specified error message and a reference to the inner exception.
+ ///
+ ///
+ /// The error message that describes the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception, or a null
+ /// reference if no inner exception is specified.
+ ///
+ public StreamLostException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/Source/SharpLSL/StreamOutlet.cs b/Source/SharpLSL/StreamOutlet.cs
new file mode 100644
index 0000000..682e357
--- /dev/null
+++ b/Source/SharpLSL/StreamOutlet.cs
@@ -0,0 +1,461 @@
+using System;
+using System.Runtime.CompilerServices;
+
+using SharpLSL.Interop;
+
+using static SharpLSL.Interop.LSL;
+using static SharpLSL.LSL;
+
+namespace SharpLSL
+{
+ public class StreamOutlet : LSLObject
+ {
+ public StreamOutlet(StreamInfo streamInfo, int chunkSize = 0, int maxBuffered = 360, TransportOptions transportOptions = TransportOptions.Default)
+ : base(lsl_create_outlet_ex(streamInfo.DangerousGetHandle(), chunkSize, maxBuffered, (lsl_transport_options_t)transportOptions))
+ {
+ channelCount_ = streamInfo.ChannelCount;
+ }
+
+ public StreamOutlet(IntPtr handle, bool ownsHandle = true)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ public void PushSample(short[] sampleData)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_s(handle, sampleData));
+ }
+
+ public void PushSample(int[] sampleData)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_i(handle, sampleData));
+ }
+
+ public void PushSample(long[] sampleData)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_l(handle, sampleData));
+ }
+
+ public void PushSample(float[] sampleData)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_f(handle, sampleData));
+ }
+
+ public void PushSample(double[] sampleData)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_d(handle, sampleData));
+ }
+
+ public void PushSample(short[] sampleData, double timestamp)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_st(handle, sampleData, timestamp));
+ }
+
+ public void PushSample(int[] sampleData, double timestamp)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_it(handle, sampleData, timestamp));
+ }
+
+ public void PushSample(long[] sampleData, double timestamp)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_lt(handle, sampleData, timestamp));
+ }
+
+ public void PushSample(float[] sampleData, double timestamp)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_ft(handle, sampleData, timestamp));
+ }
+
+ public void PushSample(double[] sampleData, double timestamp)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_dt(handle, sampleData, timestamp));
+ }
+
+ public void PushSample(short[] sampleData, double timestamp, bool pushThrough)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_stp(handle, sampleData, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushSample(int[] sampleData, double timestamp, bool pushThrough)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_itp(handle, sampleData, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushSample(long[] sampleData, double timestamp, bool pushThrough)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_ltp(handle, sampleData, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushSample(float[] sampleData, double timestamp, bool pushThrough)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_ftp(handle, sampleData, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushSample(double[] sampleData, double timestamp, bool pushThrough)
+ {
+ CheckSampleData(sampleData);
+ CheckError(lsl_push_sample_dtp(handle, sampleData, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(short[] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_s(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(int[] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_i(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(long[] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_l(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(float[] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_f(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(double[] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_d(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(short[,] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_s(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(int[,] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_i(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(long[,] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_l(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(float[,] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_f(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(double[,] data)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_d(handle, data, (uint)data.Length));
+ }
+
+ public void PushChunk(short[] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_st(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(int[] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_it(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(long[] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_lt(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(float[] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ft(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(double[] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dt(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(short[,] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_st(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(int[,] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_it(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(long[,] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_lt(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(float[,] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ft(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(double[,] data, double timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dt(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(short[] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(int[] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(long[] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(float[] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(double[] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(short[,] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(int[,] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(long[,] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(float[,] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(double[,] data, double timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(short[] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(int[] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(long[] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(float[] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(double[] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(short[,] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(int[,] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(long[,] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(float[,] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(double[,] data, double[] timestamp)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtn(handle, data, (uint)data.Length, timestamp));
+ }
+
+ public void PushChunk(short[] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(int[] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(long[] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(float[] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(double[] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(short[,] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_stnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(int[,] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_itnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(long[,] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ltnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(float[,] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_ftnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public void PushChunk(double[,] data, double[] timestamp, bool pushThrough)
+ {
+ CheckChunkData(data);
+ CheckError(lsl_push_chunk_dtnp(handle, data, (uint)data.Length, timestamp, pushThrough ? 1 : 0));
+ }
+
+ public bool HaveConsumers() => lsl_have_consumers(handle) > 0;
+
+ public bool WaitForConsumers(double timeout) => lsl_wait_for_consumers(handle, timeout) > 0;
+
+ protected override void DestroyLSLObject()
+ {
+ lsl_destroy_outlet(handle);
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CheckSampleData(T[] sampleData) =>
+ CheckSampleBuffer(sampleData, channelCount_);
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CheckChunkData(T[] chunkData)
+ {
+ if (chunkData == null)
+ throw new ArgumentNullException(nameof(chunkData));
+
+ if (chunkData.Length == 0 || chunkData.Length % channelCount_ != 0)
+ throw new ArgumentException(nameof(chunkData));
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CheckChunkData(T[,] chunkData)
+ {
+ if (chunkData == null)
+ throw new ArgumentNullException(nameof(chunkData));
+
+ if (chunkData.GetLength(0) == 0 || chunkData.GetLength(1) != channelCount_)
+ throw new ArgumentException(nameof(chunkData));
+ }
+
+#if !NET35
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ private void CheckChannelCount(int channelCount) =>
+ LSL.CheckChannelCount(channelCount_, channelCount);
+
+ private readonly int channelCount_;
+ }
+}
diff --git a/Source/SharpLSL/TransportOptions.cs b/Source/SharpLSL/TransportOptions.cs
new file mode 100644
index 0000000..b8b9aca
--- /dev/null
+++ b/Source/SharpLSL/TransportOptions.cs
@@ -0,0 +1,31 @@
+using System;
+
+using SharpLSL.Interop;
+
+namespace SharpLSL
+{
+ ///
+ /// Specifies the transport options for stream inlets and outlets.
+ ///
+ [Flags]
+ public enum TransportOptions
+ {
+ // TODO: argument name & see also
+ ///
+ /// Default behavior: `max_buffered` / `max_buflen` is interpreted as time in seconds, and asynchronous transfer is used.
+ ///
+ Default = lsl_transport_options_t.transp_default,
+
+ // TODO: argument name & see also
+ ///
+ /// Specifies the supplied `max_buf` value is in samples.
+ ///
+ BufferSizeInSamples = lsl_transport_options_t.transp_bufsize_samples,
+
+ // TODO: argument name & see also
+ ///
+ /// Specifies the supplied `max_buf` value should be scaled by 0.001.
+ ///
+ BufferSizeThousandths = lsl_transport_options_t.transp_bufsize_thousandths,
+ }
+}
diff --git a/Source/SharpLSL/XMLElement.cs b/Source/SharpLSL/XMLElement.cs
new file mode 100644
index 0000000..3c49b9c
--- /dev/null
+++ b/Source/SharpLSL/XMLElement.cs
@@ -0,0 +1,578 @@
+using System;
+using System.Diagnostics;
+
+using static SharpLSL.Interop.LSL;
+using static SharpLSL.LSL;
+
+namespace SharpLSL
+{
+ ///
+ /// Represents a lightweight XML element tree, modeling the
+ /// field of .
+ ///
+ ///
+ /// Each element has a name and can contain multiple named child elements or
+ /// have text content as its value; attributes are omitted. The interface is
+ /// modeled after a subset of pugixml's node type and is compatible with it.
+ /// See https://pugixml.org/docs/manual.html#access for more details.
+ ///
+ public class XMLElement // TODO: IEnumerable, ToString, C# XML libraries
+ {
+ ///
+ /// Constructs a new instance of the class which
+ /// wraps a pre-existing native XML node handle.
+ ///
+ ///
+ /// Specifies the handle to be wrapped.
+ ///
+ internal XMLElement(IntPtr handle)
+ {
+ handle_ = handle;
+ }
+
+ ///
+ /// Gets a value indicating whether the element is null.
+ ///
+ ///
+ public bool IsNull => handle_ == IntPtr.Zero;
+
+ ///
+ /// Gets a value indicating whether the element is empty.
+ ///
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ public bool IsEmpty
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return Convert.ToBoolean(lsl_empty(handle_));
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether this element is a text body (instead of
+ /// an XML element). True both for plain char data and CData.
+ ///
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ public bool IsText
+ {
+ get
+ {
+ ThrowIfInvalid();
+ return Convert.ToBoolean(lsl_is_text(handle_));
+ }
+ }
+
+ ///
+ /// Gets or sets the name of the element.
+ ///
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ ///
+ /// Thrown if setting name of the element fails.
+ ///
+ public string Name
+ {
+ get
+ {
+ ThrowIfInvalid();
+
+ unsafe
+ {
+ return PtrToXmlString((IntPtr)lsl_name(handle_));
+ }
+ }
+
+ set
+ {
+ ThrowIfInvalid();
+
+ var result = Convert.ToBoolean(lsl_set_name(handle_, value));
+ if (!result)
+ throw new LSLException($"Failed to set name of element to {value}.");
+ }
+ }
+
+ ///
+ /// Gets or sets the value of the element.
+ ///
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ ///
+ /// Thrown if setting value of the element fails.
+ ///
+ public string Value
+ {
+ get
+ {
+ ThrowIfInvalid();
+
+ unsafe
+ {
+ return PtrToXmlString((IntPtr)lsl_value(handle_));
+ }
+ }
+
+ set
+ {
+ ThrowIfInvalid();
+
+ var result = Convert.ToBoolean(lsl_set_value(handle_, value));
+ if (!result)
+ throw new LSLException($"Failed to set value of element to {value}.");
+ }
+ }
+
+ ///
+ /// Gets the parent node of the element.
+ ///
+ ///
+ /// The parent node of the element, or if the parent node
+ /// doesn't exist.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement GetParent()
+ {
+ ThrowIfInvalid();
+
+ var parent = lsl_parent(handle_);
+ if (parent != IntPtr.Zero)
+ return new XMLElement(parent);
+
+ return Null;
+ }
+
+ ///
+ /// Finds the first child of the element.
+ ///
+ ///
+ /// The first child of the element, or if the element has
+ /// no children.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindFirstChild()
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_first_child(handle_);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Finds the last child of the element.
+ ///
+ ///
+ /// The last child of the element, or if the element has
+ /// no children.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindLastChild()
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_last_child(handle_);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Finds a child of the element with the specified name.
+ ///
+ /// The name of the child.
+ ///
+ /// A child of the element with the specified name, or
+ /// if no such child exists.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindChild(string name)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_child(handle_, name);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Gets the value of the first child that is text.
+ ///
+ ///
+ /// The value of the first child that is text, or null if no such child exists.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public string GetChildValue()
+ {
+ ThrowIfInvalid();
+
+ unsafe
+ {
+ var str = (IntPtr)lsl_child_value(handle_);
+ if (str != IntPtr.Zero) // TODO:
+ return PtrToXmlString(str);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Gets the value of the first child element with the specified name that
+ /// contains text.
+ ///
+ /// The name of the child.
+ ///
+ /// The value of the first child that is text, or null if no such child exists.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public string GetChildValue(string name)
+ {
+ ThrowIfInvalid();
+
+ var str = lsl_child_value_n(handle_, name);
+ if (str != IntPtr.Zero) // TODO:
+ return PtrToXmlString(str);
+
+ return null;
+ }
+
+ ///
+ /// Sets the text value of the (nameless) plain-text child of a named child node.
+ ///
+ /// The name of the child.
+ /// The value of the child.
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ ///
+ /// Thrown if setting child value fails.
+ ///
+ public void SetChildValue(string name, string value)
+ {
+ ThrowIfInvalid();
+
+ var result = Convert.ToBoolean(lsl_set_child_value(handle_, name, value));
+ if (!result)
+ throw new LSLException($"Failed to set child value: {name}={value}.");
+ }
+
+ ///
+ /// Finds the next sibling of the element.
+ ///
+ ///
+ /// The next sibling of the element, or if the element
+ /// is the last node in the list.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindNextSibling()
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_next_sibling(handle_);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Finds the next sibling of the element with the specified name.
+ ///
+ /// The name of the sibling.
+ ///
+ /// The next sibling of the element with the specified name, or
+ /// if no such sibling exists.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindNextSibling(string name)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_next_sibling_n(handle_, name);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Finds the previous sibling of the element.
+ ///
+ ///
+ /// The previous sibling of the element, or if the element
+ /// is the first node in the list.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindPreviousSibling()
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_previous_sibling(handle_);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Finds the previous sibling of the element with the specified name.
+ ///
+ /// The name of the sibling.
+ ///
+ /// The previous sibling of the element with the specified name, or
+ /// if no such sibling exists.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement FindPreviousSibling(string name)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_previous_sibling_n(handle_, name);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ return Null;
+ }
+
+ ///
+ /// Appends a new child element with the specified name to the current element.
+ ///
+ /// The name of the child.
+ ///
+ /// An representing the newly created child element.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement AppendChild(string name)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_append_child(handle_, name);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ throw new LSLException($"Failed to append child with name {name}.");
+ }
+
+ ///
+ /// Appends a child element with a given name, which has a (nameless) plain-text
+ /// child with the given text value.
+ ///
+ /// The name of the child.
+ /// The value of the child.
+ /// The current element.
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement AppendChild(string name, string value)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_append_child_value(handle_, name, value);
+ Debug.Assert(node == handle_); // TODO:
+ return this; // TODO:
+ }
+
+ ///
+ /// Appends a copy of the specified element as a child.
+ ///
+ /// The original element to be copied.
+ ///
+ /// An representing the newly created child element.
+ ///
+ ///
+ /// Thrown if the element to be copied is null.
+ ///
+ ///
+ /// Thrown if current element or the element to be copied is invalid.
+ ///
+ ///
+ /// Thrown if appending a copy of the specified element as child fails.
+ ///
+ public XMLElement AppendChild(XMLElement element)
+ {
+ ThrowIfInvalid();
+
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ element.ThrowIfInvalid();
+
+ var node = lsl_append_copy(handle_, element.handle_);
+ if (node != IntPtr.Zero) // TODO:
+ return new XMLElement(node);
+
+ throw new LSLException("Failed to append a copy of the specified element as a child.");
+ }
+
+ ///
+ /// Prepends a new child element with the specified name to the current element.
+ ///
+ /// The name of the child.
+ ///
+ /// An representing the newly created child element.
+ ///
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement PrependChild(string name)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_prepend_child(handle_, name);
+ if (node != IntPtr.Zero)
+ return new XMLElement(node);
+
+ throw new LSLException($"Failed to prepend child with name {name}.");
+ }
+
+ ///
+ /// Prepends a child element with a given name, which has a (nameless) plain-text
+ /// child with the given text value.
+ ///
+ /// The name of the child.
+ /// The value of the child.
+ /// The current element.
+ ///
+ /// Thrown if current element is invalid.
+ ///
+ public XMLElement PrependChild(string name, string value)
+ {
+ ThrowIfInvalid();
+
+ var node = lsl_prepend_child_value(handle_, name, value);
+ Debug.Assert(node == handle_); // TODO:
+ return this; // TODO:
+ }
+
+ ///
+ /// Prepends a copy of the specified element as a child.
+ ///
+ /// The original element to be copied.
+ ///
+ /// An representing the newly created child element.
+ ///
+ ///
+ /// Thrown if the element to be copied is null.
+ ///
+ ///
+ /// Thrown if current element or the element to be copied is invalid.
+ ///
+ ///
+ /// Thrown if prepending a copy of the specified element as child fails.
+ ///
+ public XMLElement PrependChild(XMLElement element)
+ {
+ ThrowIfInvalid();
+
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ element.ThrowIfInvalid();
+
+ var node = lsl_prepend_copy(handle_, element.handle_);
+ if (node != IntPtr.Zero) // TODO:
+ return new XMLElement(node);
+
+ throw new LSLException("Failed to prepend a copy of the specified element as a child.");
+ }
+
+ ///
+ /// Removes a child of the element with the specified name.
+ ///
+ /// The name of the child.
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ public void RemoveChild(string name)
+ {
+ ThrowIfInvalid();
+
+ lsl_remove_child_n(handle_, name);
+ }
+
+ ///
+ /// Remove a specified child element.
+ ///
+ /// The child element to be removed.
+ ///
+ /// Thrown if the element to be removed is null.
+ ///
+ ///
+ /// Thrown if current element or the element to be removed is invalid.
+ ///
+ public void RemoveChild(XMLElement element)
+ {
+ ThrowIfInvalid();
+
+ if (element == null)
+ throw new ArgumentNullException(nameof(element));
+
+ element.ThrowIfInvalid();
+
+ lsl_remove_child(handle_, element.handle_);
+ }
+
+ ///
+ /// Represents a null XML element.
+ ///
+ ///
+ /// The traversal functions provided by may return
+ /// this value if no element is found.
+ ///
+ ///
+ public static readonly XMLElement Null = new XMLElement(IntPtr.Zero);
+
+ ///
+ /// Throws an if the handle is invalid.
+ ///
+ ///
+ /// Thrown when the handle is invalid.
+ ///
+ protected void ThrowIfInvalid()
+ {
+ if (handle_ == IntPtr.Zero)
+ throw new InvalidOperationException("The XML node handle is invalid.");
+ }
+
+ private readonly IntPtr handle_;
+ }
+}
+
+
+// References:
+// https://github.com/labstreaminglayer/liblsl-Csharp/blob/master/LSL.cs
+// https://pugixml.org/docs/manual.html#access
+// https://github.com/zeux/pugixml/blob/master/src/pugixml.hpp
diff --git a/Tests/SharpLSL.Test/CommonTest.cs b/Tests/SharpLSL.Test/CommonTest.cs
index 4ecb136..3b04c06 100644
--- a/Tests/SharpLSL.Test/CommonTest.cs
+++ b/Tests/SharpLSL.Test/CommonTest.cs
@@ -1,15 +1,20 @@
using Xunit;
-using SharpLSL.Interop;
+using static SharpLSL.Interop.LSL;
namespace SharpLSL.Test;
public class CommonTest
{
[Fact]
- public void Given_When_Then()
+ public void TestVersion()
{
- Assert.Equal(4, sizeof(LslChannelFormat));
- Assert.Equal(114, LSL.LIBLSL_COMPILE_HEADER_VERSION);
+ Assert.Equal(114, LIBLSL_COMPILE_HEADER_VERSION);
+ }
+
+ [Fact]
+ public void TestChannelFormat()
+ {
+ Assert.Equal(4, sizeof(ChannelFormat));
}
}
diff --git a/Tests/SharpLSL.Test/ResolverTest.cs b/Tests/SharpLSL.Test/ResolverTest.cs
new file mode 100644
index 0000000..67f9bc1
--- /dev/null
+++ b/Tests/SharpLSL.Test/ResolverTest.cs
@@ -0,0 +1,19 @@
+using Xunit;
+
+namespace SharpLSL.Test;
+
+public class ResolverTest
+{
+ [Fact]
+ public void TestDispose()
+ {
+ var resolver = new ContinuousResolver();
+ Assert.False(resolver.IsInvalid);
+
+ resolver.Dispose();
+ Assert.True(resolver.IsInvalid);
+ }
+}
+
+// References:
+// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Runtime/tests/System.Runtime.Tests/Microsoft/Win32/SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs
diff --git a/Tests/SharpLSL.Test/SharpLSL.Test.csproj b/Tests/SharpLSL.Test/SharpLSL.Test.csproj
index 543e563..2889be3 100644
--- a/Tests/SharpLSL.Test/SharpLSL.Test.csproj
+++ b/Tests/SharpLSL.Test/SharpLSL.Test.csproj
@@ -5,6 +5,9 @@
+
@@ -14,6 +17,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive