diff --git a/Editor/LSLEditorWindow.cs b/Editor/LSLEditorWindow.cs index b73fe457f832c3ba955207d2d0a0a359a9a6e82e..bdd312bf5f83b9e6185d73fb7e7e4c781646ea27 100644 --- a/Editor/LSLEditorWindow.cs +++ b/Editor/LSLEditorWindow.cs @@ -19,15 +19,15 @@ public class LSLShowStreamsWindow : EditorWindow private Vector2 scrollVector; private string streamLookUpResult; - private liblsl.ContinuousResolver resolver; + private ContinuousResolver resolver; private string lslVersionInfos; public void Init() { - resolver = new liblsl.ContinuousResolver(); + resolver = new ContinuousResolver(); - int libVersion = liblsl.LibraryVersion(); - int protocolVersion = liblsl.ProtocolVersion(); + int libVersion = LSL.LibraryVersion(); + int protocolVersion = LSL.ProtocolVersion(); int libMajor = libVersion / 100; int libMinor = libVersion % 100; @@ -39,7 +39,7 @@ public class LSLShowStreamsWindow : EditorWindow titleContent = new GUIContent("LSL Utility"); } - private liblsl.StreamInfo[] streamInfos = null; + private StreamInfo[] streamInfos = null; private void OnGUI() { @@ -101,7 +101,7 @@ public class LSLShowStreamsWindow : EditorWindow if (streamInfos.Length == 0) { streamLookUpResult = NO_STREAMS_FOUND; } else { - foreach (liblsl.StreamInfo item in streamInfos) { namesOfStreams.Add($"{item.Name()} {item.Type()} {item.Hostname()} {item.Sampling()}"); } + foreach (StreamInfo item in streamInfos) { namesOfStreams.Add($"{item.Name()} {item.Type()} {item.Hostname()} {item.Sampling()}"); } streamLookUpResult = namesOfStreams.Count + N_STREAMS_FOUND; } } diff --git a/LSL.cs b/LSL.cs index de2ae745d959aca5a5c1cb77f91b4ee47b22db37..b87215c4e842f9395200546b875b1cef65ac8856 100644 --- a/LSL.cs +++ b/LSL.cs @@ -3,22 +3,84 @@ // C# API for the lab streaming layer. // // The lab streaming layer provides a set of functions to make instrument data accessible in real time within a lab network. -// From there, streams can be picked up by recording programs, +// From there, streams can be picked up by recording programs, // // The API covers two areas: -// - The "push API" allows to create stream outlets and to push data (regular or irregular measurement +// - The "push API" allows to create stream outlets and to push data (regular or irregular measurement // time series, event data, coded audio/video frames, etc.) into them. -// - The "pull API" allows to create stream inlets and read time-synched experiment data from them +// - The "pull API" allows to create stream inlets and read time-synched experiment data from them // (for recording, viewing or experiment control). // //------------------------------------------------------------------------------------------------- using System.Runtime.InteropServices; using IntPtr = System.IntPtr; +using Exception = System.Exception; +using SerializationInfo = System.Runtime.Serialization.SerializationInfo; +using StreamingContext = System.Runtime.Serialization.StreamingContext; namespace LSL4Unity { -public class liblsl +#region Enums + +/// <summary> Data format of a channel (each transmitted sample holds an array of channels). </summary> +public enum ChannelFormat +{ + /// <summary> For up to 24-bit precision measurements in the appropriate physical unit. </summary> + Float32 = 1, + + /// <summary> For universal numeric data as long as permitted by network and disk budget. + /// (e.g., microvolts). Integers from -16777216 to 16777216 are represented accurately. </summary> + Double64 = 2, + + /// <summary> For variable-length ASCII strings or data blobs, such as video frames, + /// the largest representable integer is 53-bit. </summary> + Str = 3, + + /// <summary> For high-rate digitized formats that require 32-bit precision. Depends critically on complex event descriptions, etc. </summary> + Int32 = 4, + + /// <summary> For very high rate signals (40Khz+) or consumer-grade audio meta-data to represent meaningful units. + /// Useful for application event codes or other coded data. </summary> + Int16 = 5, + + /// <summary> For binary signals or other coded data. (for professional audio float is recommended). </summary> + Int8 = 6, + + /// <summary> For now only for future compatibility. Support for this type is not yet exposed in all languages. + /// Not recommended for encoding string data. </summary> + Int64 = 7, + + /// <summary> Can not be transmitted. Also, some builds of liblsl will not be able to send or receive data of this type. </summary> + Undefined = 0 +} + +/// <summary> Post-processing options for stream inlets. </summary> +public enum ProcessingOptions +{ + /// <summary> No automatic post-processing; return the ground-truth time stamps for manual post-processing. </summary> + None = 0, + + /// <summary> Perform automatic clock synchronization; equivalent to manually adding the TimeCorrection() value (this is the default behavior of the inlet). </summary> + ClockSync = 1, + + /// <summary> Remove jitter from time stamps. This will apply a smoothing algorithm to the received time stamps; to the received time stamps. </summary> + Dejitter = 2, + + // the smoothing needs to see a minimum number of samples (30-120 seconds worst-case) until the remaining jitter is consistently below 1ms. + /// <summary> Force the time-stamps to be monotonically ascending (only makes sense if timestamps are dejittered). </summary> + Monotonize = 4, + + /// <summary> Post-processing is thread-safe (same inlet can be read from by multiple threads); uses somewhat more CPU. </summary> + Threadsafe = 8, + + /// <summary> The combination of all possible post-processing options. </summary> + All = 1 | 2 | 4 | 8 +} + +#endregion + +public static class LSL { /// <summary> Constant to indicate that a stream has variable sampling rate. </summary> /// <value> Indicate that a stream has variable sampling rate. </value> @@ -37,59 +99,11 @@ public class liblsl /// <value> A very large time duration (> 1 year). </value> public const double FOREVER = 32000000.0; - /// <summary> Data format of a channel (each transmitted sample holds an array of channels). </summary> - public enum channel_format_t : byte - { - /// <summary> For up to 24-bit precision measurements in the appropriate physical unit. </summary> - cf_float32 = 1, - - /// <summary> For universal numeric data as long as permitted by network and disk budget. - /// (e.g., microvolts). Integers from -16777216 to 16777216 are represented accurately. </summary> - cf_double64 = 2, - - /// <summary> For variable-length ASCII strings or data blobs, such as video frames, - /// the largest representable integer is 53-bit. </summary> - cf_string = 3, - - /// <summary> For high-rate digitized formats that require 32-bit precision. Depends critically on complex event descriptions, etc. </summary> - cf_int32 = 4, - - /// <summary> For very high rate signals (40Khz+) or consumer-grade audio meta-data to represent meaningful units. - /// Useful for application event codes or other coded data. </summary> - cf_int16 = 5, - - /// <summary> For binary signals or other coded data. (for professional audio float is recommended). </summary> - cf_int8 = 6, - - /// <summary> For now only for future compatibility. Support for this type is not yet exposed in all languages. - /// Not recommended for encoding string data. </summary> - cf_int64 = 7, - - /// <summary> Can not be transmitted. Also, some builds of liblsl will not be able to send or receive data of this type. </summary> - cf_undefined = 0 - } - - /// <summary> Post-processing options for stream inlets. </summary> - public enum processing_options_t : byte + /// <summary> Helper method to dispose a StreamInfo[] array </summary> + /// <param name="arr">Array to be disposed</param> + public static void DisposeArray(this StreamInfo[] array) { - /// <summary> No automatic post-processing; return the ground-truth time stamps for manual post-processing. </summary> - post_none = 0, - - /// <summary> Perform automatic clock synchronization; equivalent to manually adding the TimeCorrection() value (this is the default behavior of the inlet). </summary> - post_clocksync = 1, - - /// <summary> Remove jitter from time stamps. This will apply a smoothing algorithm to the received time stamps; to the received time stamps. </summary> - post_dejitter = 2, - - // the smoothing needs to see a minimum number of samples (30-120 seconds worst-case) until the remaining jitter is consistently below 1ms. - /// <summary> Force the time-stamps to be monotonically ascending (only makes sense if timestamps are dejittered). </summary> - post_monotonize = 4, - - /// <summary> Post-processing is thread-safe (same inlet can be read from by multiple threads); uses somewhat more CPU. </summary> - post_threadsafe = 8, - - /// <summary> The combination of all possible post-processing options. </summary> - post_ALL = 1 | 2 | 4 | 8 + foreach (var si in array) { si.Dispose(); } } /// <summary> Version of the lsl protocol. @@ -97,369 +111,23 @@ public class liblsl /// The major version is <see cref="ProtocolVersion"/> / 100; The minor version is <see cref="ProtocolVersion"/> % 100; /// Clients with different minor versions are protocol-compatible with each other while clients with different major versions will refuse to work together.</summary> /// <returns> The protocole version. </returns> - public static int ProtocolVersion() { return dll.lsl_protocol_version(); } + public static int ProtocolVersion() { return Dll.lsl_protocol_version(); } /// <summary> Version of the liblsl library. /// /// The major version is <see cref="LibraryVersion"/> / 100; The minor version is <see cref="LibraryVersion"/> % 100;</summary> /// <returns> The library version. </returns> - public static int LibraryVersion() { return dll.lsl_library_version(); } + public static int LibraryVersion() { return Dll.lsl_library_version(); } /// <summary> Obtain a local system time stamp in seconds. /// /// The resolution is better than a millisecond. /// This reading can be used to assign time stamps 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 LocalClock() to obtain a better estimate of when a sample was actually captured. + /// it can be used as an offset to LocalClock() to obtain a better estimate of when a sample was actually captured. /// See <c>StreamOutlet::PushSample()</c> for a use case. </summary> /// <returns> The local system time stamp in seconds. </returns> - public static double LocalClock() { return dll.lsl_local_clock(); } - - - // ========================== - // === Stream Declaration === - // ========================== - - /// <summary> The stream_info object stores the declaration of a data stream. - /// - /// Represents the following information: - /// 1. stream data format (#channels, channel format) - /// 1. core information (stream name, content type, sampling rate) - /// 1. optional meta-data about the stream content (channel labels, measurement units, etc.) - /// - /// Whenever a program wants to provide a new stream on the lab network, - /// it will typically first create a stream_info to describe its properties - /// and then construct a stream_outlet with it to create the stream on the network. - /// Recipients who discover the outlet can query the stream_info; it is also written to disk when recording the stream - /// (playing a similar role as a file header).</summary> - public class StreamInfo - { - /// <summary> Initializes a new instance of the <see cref="StreamInfo"/> object. - /// - /// Core stream information is specified here. Any remaining meta-data can be added later. </summary> - /// <param name="name"> Name of the stream. Describes the device (or product series) that this stream makes available (for use by programs, experimenters or data analysts). - /// Cannot be empty. </param> - /// <param name="type"> Content type of the stream. - /// Please see [https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) (or web search for: meta-data) - /// for pre-defined content-type names, but you can also make up your own. - /// The content type is the preferred way to find streams (as opposed to searching by name). </param> - /// <param name="channelCount"> Number of channels per sample. This stays constant for the lifetime of the stream. </param> - /// <param name="sampling"> The sampling rate (in Hz) as advertised by the data source, if regular (otherwise set to IRREGULAR_RATE). </param> - /// <param name="channelFormat"> Format/type 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 cf_double64). </param> - /// <param name="sourceId"> 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. </param> - public StreamInfo(string name, string type, int channelCount = 1, double sampling = IRREGULAR_RATE, - channel_format_t channelFormat = channel_format_t.cf_float32, string sourceId = "") - { - obj = dll.lsl_create_streaminfo(name, type, channelCount, sampling, channelFormat, sourceId); - } - - /// <summary> Initializes a new instance of the <see cref="StreamInfo"/> class. </summary> - /// <param name="handle"> The handle. </param> - public StreamInfo(IntPtr handle) { obj = handle; } - - /// <summary> Finalizes an instance of the <see cref="StreamInfo"/> object. </summary> - ~StreamInfo() { dll.lsl_destroy_streaminfo(obj); } - - // ======================== - // === Core Information === - // ======================== - // (these fields are assigned at construction) - - /// <summary> Name of the stream. - /// - /// This 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). </summary> - /// <returns> The name of the stream. </returns> - public string Name() { return Marshal.PtrToStringAnsi(dll.lsl_get_name(obj)); } - - /// <summary> Content type of the stream. - /// - /// 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 [https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) (or web search for: XDF meta-data). </summary> - /// <returns> The content type of the stream (in <c>string</c>). </returns> - public string Type() { return Marshal.PtrToStringAnsi(dll.lsl_get_type(obj)); } - - /// <summary> Number of channels of the stream. A stream has at least one channel; the channel count stays constant for all samples. </summary> - /// <returns> The Number of channels of the stream. </returns> - public int ChannelCount() { return dll.lsl_get_channel_count(obj); } - - /// <summary> Sampling rate of the stream, according to the source (in Hz). - /// - /// If a stream is irregularly sampled, this should be set to IRREGULAR_RATE. - /// 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. </summary> - /// <returns> The Sampling rate of the stream (in <c>double</c>). </returns> - public double Sampling() { return dll.lsl_get_nominal_srate(obj); } - - /// <summary> Channel format of the stream. - /// - /// All channels in a stream have the same format. However, a device might offer multiple time-synched streams each with its own format. </summary> - /// <returns>The hannel format of the stream (in <see cref="channel_format_t"/> enum) </returns> - public channel_format_t ChannelFormat() { return dll.lsl_get_channel_format(obj); } - - /// <summary> Unique identifier of the stream's source, if available. - /// - /// 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. </summary> - /// <returns> The Identifier (in <c>string</c>). </returns> - public string SourceId() { return Marshal.PtrToStringAnsi(dll.lsl_get_source_id(obj)); } - - - // ====================================== - // === Additional Hosting Information === - // ====================================== - // (these fields are implicitly assigned once bound to an outlet/inlet) - - /// <summary> Protocol version used to deliver the stream. </summary> - /// <returns> The protocol version (in <c>int</c>). </returns> - public int Version() { return dll.lsl_get_version(obj); } - - /// <summary> Creation time stamp of the stream. - /// - /// This is the time stamp when the stream was first created (as determined via LocalClock() on the providing machine). </summary> - /// <returns> The Time Stamp (in <c>double</c>). </returns> - public double CreatedAt() { return dll.lsl_get_created_at(obj); } - - /// <summary> Unique ID of the stream outlet instance (once assigned). - /// - /// 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). </summary> - /// <returns> The Unique Identifier (in <c>string</c>). </returns> - public string Uid() { return Marshal.PtrToStringAnsi(dll.lsl_get_uid(obj)); } - - /// <summary> Session ID for the given stream. - /// - /// 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). </summary> - /// <returns> The Session Identifier (in <c>string</c>). </returns> - public string SessionId() { return Marshal.PtrToStringAnsi(dll.lsl_get_session_id(obj)); } - - /// <summary> Hostname of the providing machine. </summary> - /// <returns> The Hostname (in <c>string</c>). </returns> - public string Hostname() { return Marshal.PtrToStringAnsi(dll.lsl_get_hostname(obj)); } - - // ======================== - // === Data Description === - // ======================== - - /// <summary> Extended description of the stream. - /// - /// It is highly recommended that at least the channel labels are described here. - /// See code examples on the LSL wiki. Other information, such as amplifier settings, - /// measurement units if deviating from defaults, setup information, subject information, etc., - /// can be specified here, as well. Meta-data recommendations follow the XDF file format project - /// ([https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) or web search for: XDF meta-data). </summary> - /// <remarks>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. </remarks> - /// <returns> A <see cref="XMLElement "/> containing the description. </returns> - public XMLElement Desc() { return new XMLElement(dll.lsl_get_desc(obj)); } - - /// <summary> Retrieve the entire <see cref="StreamInfo"/> in XML format. - /// - /// This yields an XML document (in string form) whose top-level element is <info>. The info element contains - /// one element for each field of the stream_info class, including: - /// a) the core elements <c>Name</c>, <c>Type</c>, <c>ChannelCount</c>, <c>Sampling</c>, <c>ChannelFormat</c>, <c>SourceId</c> - /// b) the misc elements <c>Version</c>, <c>CreatedAt</c>, <c>Uid</c>, <c>SessionId</c>, <c>v4address</c>, <c>v4data_port</c>, <c>v4service_port</c>, <c>v6address</c>, <c>v6data_port</c>, <c>v6service_port</c> - /// c) the extended description element <c>desc</c> with user-defined sub-elements. </summary> - /// <returns> A <c>string</c> with the entire <see cref="StreamInfo"/>. </returns> - public string AsXML() - { - IntPtr pXml = dll.lsl_get_xml(obj); - string res = Marshal.PtrToStringAnsi(pXml); - dll.lsl_destroy_string(pXml); - return res; - } - - /// <summary> Get access to the underlying handle. </summary> - /// <returns> the Handle. </returns> - public IntPtr Handle() { return obj; } - - private readonly IntPtr obj; - } - - - // ======================= - // ==== Stream Outlet ==== - // ======================= - - /// <summary> A stream outlet. - /// - /// Outlets are used to make streaming data (and the meta-data) available on the lab network. </summary> - public class StreamOutlet - { - /// <summary> Initializes a new instance of <see cref="StreamOutlet"/>. This makes the stream discoverable. </summary> - /// <param name="info"> The stream information to use for creating this stream. Stays constant over the lifetime of the outlet. </param> - /// <param name="chunkSize"> Optionally the desired chunk granularity (in samples) for transmission. - /// If unspecified, each push operation yields one chunk. Inlets can override this setting. </param> - /// <param name="maxBuffered">Optionally the maximum amount of data to buffer (in seconds if there is a nominal sampling rate, otherwise x100 in samples). - /// The default is 6 minutes of data. </param> - public StreamOutlet(StreamInfo info, int chunkSize = 0, int maxBuffered = 360) { obj = dll.lsl_create_outlet(info.Handle(), chunkSize, maxBuffered); } - - /// <summary> Finalizes an instance of the <see cref="StreamOutlet"/>. - /// - /// The stream will no longer be discoverable after destruction and all paired inlets will stop delivering data. </summary> - ~StreamOutlet() { dll.lsl_destroy_outlet(obj); } - - - // ======================================== - // === Pushing a sample into the outlet === - // ======================================== - - /// <summary> Push an array of values as a sample into the outlet. </summary> - /// <param name="data"> An array of values to push (one for each channel). </param> - /// <param name="time"> Optionally the capture time of the sample, in agreement with <see cref="LocalClock"/>; if omitted, the current time is used. </param> - /// <param name="pushthrough"> Optionally whether to push the sample through to the receivers instead of buffering it with subsequent samples. - /// Note that the chunk_size, if specified at outlet construction, takes precedence over the pushthrough flag. </param> - public void PushSample(float[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_ftp(obj, data, time, pushthrough ? 1 : 0); } - - /// <inheritdoc cref="PushSample(float[],double,bool)"/> - /// <param name="data"> An array of values to push (one for each channel). </param> - public void PushSample(double[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_dtp(obj, data, time, pushthrough ? 1 : 0); } - - /// <inheritdoc cref="PushSample(float[],double,bool)"/> - /// <param name="data"> An array of values to push (one for each channel). </param> - public void PushSample(int[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_itp(obj, data, time, pushthrough ? 1 : 0); } - - /// <inheritdoc cref="PushSample(float[],double,bool)"/> - /// <param name="data"> An array of values to push (one for each channel). </param> - public void PushSample(short[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_stp(obj, data, time, pushthrough ? 1 : 0); } - - /// <inheritdoc cref="PushSample(float[],double,bool)"/> - /// <param name="data"> An array of values to push (one for each channel). </param> - public void PushSample(char[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_ctp(obj, data, time, pushthrough ? 1 : 0); } - - /// <inheritdoc cref="PushSample(float[],double,bool)"/> - /// <param name="data"> An array of values to push (one for each channel). </param> - public void PushSample(string[] data, double time = 0.0, bool pushthrough = true) { dll.lsl_push_sample_strtp(obj, data, time, pushthrough ? 1 : 0); } - - - // =================================================== - // === Pushing an chunk of samples into the outlet === - // =================================================== - - /// <summary> Push a chunk of samples into the outlet. Single time provided. </summary> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - /// <param name="time"> Optionally the capture time of the sample, in agreement with <see cref="LocalClock"/>; if omitted, the current time is used. </param> - /// <param name="pushthrough"> Optionally whether to push the sample through to the receivers instead of buffering it with subsequent samples. - /// Note that the chunkSize, if specified at outlet construction, takes precedence over the pushthrough flag. </param> - public void PushChunk(float[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_ftp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(double[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_dtp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(int[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_itp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(short[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_stp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(char[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_ctp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(string[,] data, double time = 0.0, bool pushthrough = true) - { - dll.lsl_push_chunk_strtp(obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); - } - - - /// <summary> Push a chunk of multiplexed samples into the outlet. One time per sample is provided. </summary> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(float[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_ftnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(double[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_dtnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(int[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_itnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(short[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_stnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(char[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_ctnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> - /// <param name="data"> A rectangular array of values for multiple samples. </param> - public void PushChunk(string[,] data, double[] times, bool pushthrough = true) - { - dll.lsl_push_chunk_strtnp(obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); - } - - - // =============================== - // === Miscellaneous Functions === - // =============================== - - /// <summary> Check whether consumers are currently registered. - /// - /// While it does not hurt, there is technically no reason to push samples if there is no consumer. </summary> - /// <returns> <c>true</c> or <c>false</c>. </returns> - public bool HaveConsumers() { return dll.lsl_have_consumers(obj) > 0; } - - /// <summary> Wait until some consumer shows up (without wasting resources). </summary> - /// <param name="timeout"> The timeout. </param> - /// <returns> True if the wait was successful, false if the timeout expired. </returns> - public bool WaitForConsumers(double timeout) { return dll.lsl_wait_for_consumers(obj) > 0; } - - /// <summary> Retrieve the stream info provided by this outlet. - /// - /// This is what was used to create the stream (and also has the Additional Network Information fields assigned). </summary> - /// <returns> A <see cref="StreamInfo"/> </returns> - public StreamInfo Info() { return new StreamInfo(dll.lsl_get_info(obj)); } - - private readonly IntPtr obj; - } - + public static double LocalClock() { return Dll.lsl_local_clock(); } // =========================== // ==== Resolve Functions ==== @@ -478,10 +146,10 @@ public class liblsl /// <remarks> If [waitTime] is too short (less than 0.5s) only a subset (or none) of the outlets that are present on the network may be returned.</remarks> public static StreamInfo[] ResolveStreams(double waitTime = 1.0) { - IntPtr[] buf = new IntPtr[1024]; - int num = dll.lsl_resolve_all(buf, (uint) buf.Length, waitTime); - StreamInfo[] res = new StreamInfo[num]; - for (int k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } + var buf = new IntPtr[1024]; + var num = Dll.lsl_resolve_all(buf, (uint) buf.Length, waitTime); + var res = new StreamInfo[num]; + for (var k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } return res; } @@ -496,10 +164,10 @@ public class liblsl /// <returns> An array of matching stream info objects (excluding their meta-data), any of which can subsequently be used to open an inlet. </returns> public static StreamInfo[] ResolveStream(string prop, string value, int minimum = 1, double timeout = FOREVER) { - IntPtr[] buf = new IntPtr[1024]; - int num = dll.lsl_resolve_byprop(buf, (uint) buf.Length, prop, value, minimum, timeout); - StreamInfo[] res = new StreamInfo[num]; - for (int k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } + var buf = new IntPtr[1024]; + var num = Dll.lsl_resolve_byprop(buf, (uint) buf.Length, prop, value, minimum, timeout); + var res = new StreamInfo[num]; + for (var k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } return res; } @@ -515,879 +183,1227 @@ public class liblsl /// <remarks> See Also : [Wikipedia XPath 1.0](http://en.wikipedia.org/w/index.php?title=XPath_1.0). </remarks> public static StreamInfo[] ResolveStream(string pred, int minimum = 1, double timeout = FOREVER) { - IntPtr[] buf = new IntPtr[1024]; - int num = dll.lsl_resolve_bypred(buf, (uint) buf.Length, pred, minimum, timeout); - StreamInfo[] res = new StreamInfo[num]; - for (int k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } + var buf = new IntPtr[1024]; + var num = Dll.lsl_resolve_bypred(buf, (uint) buf.Length, pred, minimum, timeout); + var res = new StreamInfo[num]; + for (var k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } return res; } - // ====================== - // ==== Stream Inlet ==== - // ====================== - - /// <summary> A stream inlet. - /// - /// Inlets are used to receive streaming data (and meta-data) from the lab network. </summary> - public class StreamInlet + /// <summary> Check an error condition and throw an exception if appropriate. </summary> + /// <param name="code"> The error code. </param> + /// <exception cref="TimeoutException"> The operation failed due to a timeout. </exception> + /// <exception cref="LostException"> The stream has been lost. </exception> + /// <exception cref="ArgumentException"> An argument was incorrectly specified (e.g., wrong format or wrong length). </exception> + /// <exception cref="InternalException"> An internal internal error has occurred. </exception> + /// <exception cref="Exception"> An unknown error has occurred. </exception> + public static void CheckError(int code) { - /// <summary> Initializes a new instance of <see cref="StreamInlet"/> from a resolved stream info. </summary> - /// <param name="info"> A resolved stream info object (as coming from one of the resolver functions). </param> - /// <param name="maxBuflen"> Optionally the maximum amount of 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. </param> - /// <param name="maxChunklen"> Optionally the maximum size, in samples, at which chunks are transmitted - /// (the default corresponds to the chunk sizes used by the sender). - /// 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. - /// If left unspecified (=0), the sender determines the chunk granularity. </param> - /// <param name="recover"> Try to silently recover lost streams that are recoverable (=those that that have a SourceId set). - /// In all other cases (recover is false or the stream is not recoverable) functions may throw a <see cref="LostException"/> - /// if the stream's source is lost (e.g., due to an app or computer crash). </param> - /// <remarks> The <see cref="StreamInlet"/> may also be constructed with a fully-specified <see cref="StreamInfo"/> - /// 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). </remarks> - public StreamInlet(StreamInfo info, int maxBuflen = 360, int maxChunklen = 0, bool recover = true) + if (code >= 0) { return; } + switch (code) { - obj = dll.lsl_create_inlet(info.Handle(), maxBuflen, maxChunklen, recover ? 1 : 0); + case -1: throw new System.TimeoutException("The operation failed due to a timeout."); + case -2: throw new LostException("The stream has been lost."); + case -3: throw new System.ArgumentException("An argument was incorrectly specified (e.g., wrong format or wrong length)."); + case -4: throw new InternalException("An internal internal error has occurred."); + default: throw new Exception("An unknown error has occurred."); } + } +} - /// <summary> Finalizes an instance of <see cref="StreamInlet"/>. - /// - /// The inlet will automatically disconnect if destroyed. </summary> - ~StreamInlet() { dll.lsl_destroy_inlet(obj); } - - /// <summary> Retrieve the complete information of the given stream, including the extended description. - /// - /// Can be invoked at any time of the stream's lifetime. </summary> - /// <param name="timeout"> Optional timeout of the operation (default: no timeout). </param> - /// <returns> <see cref="StreamInfo"/>. </returns> - /// <exception cref="TimeoutException"> If the timeout expires. </exception> - /// <exception cref="LostException"> If the stream source has been lost. </exception> - public StreamInfo Info(double timeout = FOREVER) - { - int ec = 0; - IntPtr res = dll.lsl_get_fullinfo(obj, timeout, ref ec); - CheckError(ec); - return new StreamInfo(res); - } +#region Stream - /// <summary> Subscribe to the data stream. - /// - /// All samples pushed in at the other end from this moment onwards will be queued and - /// eventually be delivered in response to <see cref="PullSample(float[],double)"/> or <see cref="PullChunk(float[,],double[],double)"/> calls. - /// Pulling a sample without some preceding OpenStream is permitted (the stream will then be opened implicitly). </summary> - /// <param name="timeout"> Optional timeout of the operation (default: no timeout). </param> - /// <exception cref="TimeoutException"> If the timeout expires. </exception> - /// <exception cref="LostException"> If the stream source has been lost. </exception> - public void OpenStream(double timeout = FOREVER) - { - int ec = 0; - dll.lsl_open_stream(obj, timeout, ref ec); - CheckError(ec); - } +public abstract class LSLObject : SafeHandle +{ + protected IntPtr Obj => handle; - /// <summary> Set post-processing flags to use. - /// - /// By default, the inlet performs NO post-processing and returns the ground-truth time stamps, - /// which can then be manually synchronized using TimeCorrection(), and then smoothed/dejittered if desired. - /// This function allows automating these two and possibly more operations. </summary> - /// <param name="flags"> An integer that is the result of bitwise OR'ing one or more options - /// from <see cref="processing_options_t"/> together (e.g., post_clocksync|post_dejitter); the default is to enable all options. </param> - /// <remarks> When you enable this, you will no longer receive or be able to recover the original time stamps. </remarks> - public void SetPostprocessing(processing_options_t flags = processing_options_t.post_ALL) { dll.lsl_set_postprocessing(obj, flags); } - - /// <summary> Drop the current data stream. - /// - /// 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. </summary> - public void CloseStream() { dll.lsl_close_stream(obj); } - - /// <summary> Retrieve an estimated time correction offset for the given stream. - /// - /// The first call to this function takes several miliseconds until a reliable first estimate is obtained. - /// Subsequent calls are instantaneous (and rely on periodic background updates). - /// The precision of these estimates should be below 1 ms (empirically within +/-0.2 ms). </summary> - /// <param name="timeout"> Optional timeout to acquire the first time-correction estimate (default: no timeout). </param> - /// <returns> The time correction estimate. This is the number that needs to be added to a time stamp - /// that was remotely generated via <c>lsl_local_clock()</c> to map it into the local clock domain of this machine. </returns> - /// <exception cref="TimeoutException"> If the timeout expires. </exception> - /// <exception cref="LostException"> If the stream source has been lost. </exception> - public double TimeCorrection(double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_time_correction(obj, timeout, ref ec); - CheckError(ec); - return res; - } - // ======================================= - // === Pulling a sample from the inlet === - // ======================================= - - /// <summary> Pull a sample from the inlet and read it into an array of values. Handles type checking and conversion. </summary> - /// <param name="sample"> An array to hold the resulting values. </param> - /// <param name="timeout"> Optional, the timeout for this operation, if any. Use 0.0 to make the function non-blocking. </param> - /// <returns>The capture time of the sample on the remote machine, or 0.0 if no new sample was available. - /// To remap this time stamp to the local clock, add the value returned by <see cref="TimeCorrection"/> to it. </returns> - /// <exception cref="LostException"> If the stream source has been lost. </exception> - public double PullSample(float[] sample, double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_pull_sample_f(obj, sample, sample.Length, timeout, ref ec); - CheckError(ec); - return res; - } + protected LSLObject(IntPtr obj) : base(IntPtr.Zero, true) + { +#if LSL_PRINT_OBJECT_LIFETIMES + System.Console.Out.WriteLine($"Created object {obj:X}"); +#endif + if (obj == IntPtr.Zero) { throw new InternalException("Error creating object"); } + SetHandle(obj); + } - /// <inheritdoc cref="PullSample(float[],double)"/> - /// <param name="sample"> An array to hold the resulting values. </param> - public double PullSample(double[] sample, double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_pull_sample_d(obj, sample, sample.Length, timeout, ref ec); - CheckError(ec); - return res; - } + public override bool IsInvalid => handle == IntPtr.Zero; - /// <inheritdoc cref="PullSample(float[],double)"/> - /// <param name="sample"> An array to hold the resulting values. </param> - public double PullSample(int[] sample, double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_pull_sample_i(obj, sample, sample.Length, timeout, ref ec); - CheckError(ec); - return res; - } + /// <summary> To be implemented in inheriting classes: the liblsl function to destroy the internal object </summary> + protected abstract void DestroyLSLObject(IntPtr obj); - /// <inheritdoc cref="PullSample(float[],double)"/> - /// <param name="sample"> An array to hold the resulting values. </param> - public double PullSample(short[] sample, double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_pull_sample_s(obj, sample, sample.Length, timeout, ref ec); - CheckError(ec); - return res; - } + protected override bool ReleaseHandle() + { +#if LSL_PRINT_OBJECT_LIFETIMES + System.Console.Out.WriteLine($"Destroying object {handle:X}"); +#endif + DestroyLSLObject(handle); + return true; + } +} - /// <inheritdoc cref="PullSample(float[],double)"/> - /// <param name="sample"> An array to hold the resulting values. </param> - public double PullSample(char[] sample, double timeout = FOREVER) - { - int ec = 0; - double res = dll.lsl_pull_sample_c(obj, sample, sample.Length, timeout, ref ec); - CheckError(ec); - return res; - } +// ========================== +// === Stream Declaration === +// ========================== + +/// <summary> The stream_info object stores the declaration of a data stream. +/// +/// Represents the following information: +/// 1. stream data format (#channels, channel format) +/// 1. core information (stream name, content type, sampling rate) +/// 1. optional meta-data about the stream content (channel labels, measurement units, etc.) +/// +/// Whenever a program wants to provide a new stream on the lab network, +/// it will typically first create a stream_info to describe its properties +/// and then construct a stream_outlet with it to create the stream on the network. +/// Recipients who discover the outlet can query the stream_info; it is also written to disk when recording the stream +/// (playing a similar role as a file header).</summary> +public class StreamInfo : LSLObject +{ + /// <summary> Initializes a new instance of the <see cref="StreamInfo"/> object. + /// + /// Core stream information is specified here. Any remaining meta-data can be added later. </summary> + /// <param name="name"> Name of the stream. Describes the device (or product series) that this stream makes available (for use by programs, experimenters or data analysts). + /// Cannot be empty. </param> + /// <param name="type"> Content type of the stream. + /// Please see [https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) (or web search for: meta-data) + /// for pre-defined content-type names, but you can also make up your own. + /// The content type is the preferred way to find streams (as opposed to searching by name). </param> + /// <param name="channelCount"> Number of channels per sample. This stays constant for the lifetime of the stream. </param> + /// <param name="sampling"> The sampling rate (in Hz) as advertised by the data source, if regular (otherwise set to IRREGULAR_RATE). </param> + /// <param name="channelFormat"> Format/type 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 Double64). </param> + /// <param name="sourceId"> 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. </param> + public StreamInfo(string name, string type, int channelCount = 1, double sampling = LSL.IRREGULAR_RATE, + ChannelFormat channelFormat = LSL4Unity.ChannelFormat.Float32, string sourceId = "") : + base(Dll.lsl_create_streaminfo(name, type, channelCount, sampling, channelFormat, sourceId)) + { + } - /// <inheritdoc cref="PullSample(float[],double)"/> - /// <param name="sample"> An array to hold the resulting values. </param> - public double PullSample(string[] sample, double timeout = FOREVER) - { - int ec = 0; - IntPtr[] tmp = new IntPtr[sample.Length]; - double res = dll.lsl_pull_sample_str(obj, tmp, tmp.Length, timeout, ref ec); - CheckError(ec); - try - { - for (int k = 0; k < tmp.Length; k++) { sample[k] = Marshal.PtrToStringAnsi(tmp[k]); } - } - finally - { - foreach (IntPtr t in tmp) { dll.lsl_destroy_string(t); } - } - return res; - } + /// <summary> Initializes a new instance of the <see cref="StreamInfo"/> class. </summary> + /// <param name="handle"> The handle. </param> + public StreamInfo(IntPtr handle) : base(handle) { } + protected override void DestroyLSLObject(IntPtr obj) { Dll.lsl_destroy_streaminfo(obj); } - // ================================================= - // === Pulling a chunk of samples from the inlet === - // ================================================= + // ======================== + // === Core Information === + // ======================== + // (these fields are assigned at construction) - /// <summary> Pull a chunk of data from the inlet. </summary> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - /// <param name="times"> A pre-allocated buffer where time stamps shall be stored. </param> - /// <param name="timeout"> Optionally the timeout for this operation, if any. - /// When the timeout expires, the function may return before the entire buffer is filled. - /// The default value of 0.0 will retrieve only data available for immediate pickup. </param> - /// <returns> Number of samples written to the data and timestamp buffers.</returns> - /// <exception cref="LostException"> If the stream source has been lost. </exception> - public int PullChunk(float[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - uint res = dll.lsl_pull_chunk_f(obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - return (int) res / buffer.GetLength(1); - } + /// <summary> Name of the stream. + /// + /// This 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). </summary> + /// <returns> The name of the stream. </returns> + public string Name() { return Marshal.PtrToStringAnsi(Dll.lsl_get_name(Obj)); } + + /// <summary> Content type of the stream. + /// + /// 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 [https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) (or web search for: XDF meta-data). </summary> + /// <returns> The content type of the stream (in <c>string</c>). </returns> + public string Type() { return Marshal.PtrToStringAnsi(Dll.lsl_get_type(Obj)); } + + /// <summary> Number of channels of the stream. A stream has at least one channel; the channel count stays constant for all samples. </summary> + /// <returns> The Number of channels of the stream. </returns> + public int ChannelCount() { return Dll.lsl_get_channel_count(Obj); } + + /// <summary> Sampling rate of the stream, according to the source (in Hz). + /// + /// If a stream is irregularly sampled, this should be set to IRREGULAR_RATE. + /// 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. </summary> + /// <returns> The Sampling rate of the stream (in <c>double</c>). </returns> + public double Sampling() { return Dll.lsl_get_nominal_srate(Obj); } + + /// <summary> Channel format of the stream. + /// + /// All channels in a stream have the same format. However, a device might offer multiple time-synched streams each with its own format. </summary> + /// <returns>The hannel format of the stream (in <see cref="LSL4Unity.ChannelFormat"/> enum) </returns> + public ChannelFormat ChannelFormat() { return Dll.lsl_get_channel_format(Obj); } - /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - public int PullChunk(double[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - uint res = dll.lsl_pull_chunk_d(obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - return (int) res / buffer.GetLength(1); - } + /// <summary> Unique identifier of the stream's source, if available. + /// + /// 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. </summary> + /// <returns> The Identifier (in <c>string</c>). </returns> + public string SourceId() { return Marshal.PtrToStringAnsi(Dll.lsl_get_source_id(Obj)); } - /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - public int PullChunk(int[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - uint res = dll.lsl_pull_chunk_i(obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - return (int) res / buffer.GetLength(1); - } - /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - public int PullChunk(short[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - uint res = dll.lsl_pull_chunk_s(obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - return (int) res / buffer.GetLength(1); - } + // ====================================== + // === Additional Hosting Information === + // ====================================== + // (these fields are implicitly assigned once bound to an outlet/inlet) - /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - public int PullChunk(char[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - uint res = dll.lsl_pull_chunk_c(obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - return (int) res / buffer.GetLength(1); - } + /// <summary> Protocol version used to deliver the stream. </summary> + /// <returns> The protocol version (in <c>int</c>). </returns> + public int Version() { return Dll.lsl_get_version(Obj); } - /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> - /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> - public int PullChunk(string[,] buffer, double[] times, double timeout = 0.0) - { - int ec = 0; - IntPtr[,] tmp = new IntPtr[buffer.GetLength(0), buffer.GetLength(1)]; - uint res = dll.lsl_pull_chunk_str(obj, tmp, times, (uint) tmp.Length, (uint) times.Length, timeout, ref ec); - CheckError(ec); - try - { - for (int s = 0; s < tmp.GetLength(0); s++) - { - for (int c = 0; c < tmp.GetLength(1); c++) { buffer[s, c] = Marshal.PtrToStringAnsi(tmp[s, c]); } - } - } - finally - { - for (int s = 0; s < tmp.GetLength(0); s++) - { - for (int c = 0; c < tmp.GetLength(1); c++) { dll.lsl_destroy_string(tmp[s, c]); } - } - } - return (int) res / buffer.GetLength(1); - } + /// <summary> Creation time stamp of the stream. + /// + /// This is the time stamp when the stream was first created (as determined via LocalClock() on the providing machine). </summary> + /// <returns> The Time Stamp (in <c>double</c>). </returns> + public double CreatedAt() { return Dll.lsl_get_created_at(Obj); } - /// <summary> Query whether samples are currently available for immediate pickup. </summary> - /// <returns> Number of samples available. </returns> - /// <remarks> that it is not a good idea to use <see cref="SamplesAvailable"/> to determine whether a <c>pull_*()</c> call would block: - /// to be sure, set the pull timeout to 0.0 or an acceptably low value. - /// If the underlying implementation supports it, the value will be the number of samples available (otherwise it will be 1 or 0). </remarks> - public int SamplesAvailable() { return (int) dll.lsl_samples_available(obj); } - - /// <summary> Query whether the clock was potentially reset since the last call to <see cref="WasClockReset"/>. - /// - /// This is a rarely-used function that is only useful to applications that combine multiple <see cref="TimeCorrection"/> values to estimate precise clock drift; - /// it allows to tolerate cases where the source machine was hot-swapped or restarted in between two measurements. </summary> - /// <returns> <c>true</c> if clock was reset... </returns> - public bool WasClockReset() { return (int) dll.lsl_was_clock_reset(obj) != 0; } - - private readonly IntPtr obj; - } + /// <summary> Unique ID of the stream outlet instance (once assigned). + /// + /// 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). </summary> + /// <returns> The Unique Identifier (in <c>string</c>). </returns> + public string Uid() { return Marshal.PtrToStringAnsi(Dll.lsl_get_uid(Obj)); } + /// <summary> Session ID for the given stream. + /// + /// 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). </summary> + /// <returns> The Session Identifier (in <c>string</c>). </returns> + public string SessionId() { return Marshal.PtrToStringAnsi(Dll.lsl_get_session_id(Obj)); } + + /// <summary> Hostname of the providing machine. </summary> + /// <returns> The Hostname (in <c>string</c>). </returns> + public string Hostname() { return Marshal.PtrToStringAnsi(Dll.lsl_get_hostname(Obj)); } - // ===================== - // ==== XML Element ==== - // ===================== + // ======================== + // === Data Description === + // ======================== - /// <summary> A lightweight XML element tree; models the <see cref="StreamInfo.Desc"/> field of <see cref="StreamInfo"/>. + /// <summary> Extended description of the stream. + /// + /// It is highly recommended that at least the channel labels are described here. + /// See code examples on the LSL wiki. Other information, such as amplifier settings, + /// measurement units if deviating from defaults, setup information, subject information, etc., + /// can be specified here, as well. Meta-data recommendations follow the XDF file format project + /// ([https://github.com/sccn/xdf/wiki/Meta-Data](https://github.com/sccn/xdf/wiki/Meta-Data) or web search for: XDF meta-data). </summary> + /// <remarks>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. </remarks> + /// <returns> A <see cref="XMLElement "/> containing the description. </returns> + public XMLElement Desc() { return new XMLElement(Dll.lsl_get_desc(Obj)); } + + /// <summary> Retrieve the entire <see cref="StreamInfo"/> in XML format. /// - /// Has a name and can have multiple named children or have text content as value; attributes are omitted. - /// Insider note: The interface is modeled after a subset of pugixml's node type and is compatible with it. </summary> - /// <remarks> See Also : [http://pugixml.googlecode.com/svn/tags/latest/docs/manual/access.html](http://pugixml.googlecode.com/svn/tags/latest/docs/manual/access.html). </remarks> - public struct XMLElement + /// This yields an XML document (in string form) whose top-level element is <info>. The info element contains + /// one element for each field of the stream_info class, including: + /// a) the core elements <c>Name</c>, <c>Type</c>, <c>ChannelCount</c>, <c>Sampling</c>, <c>ChannelFormat</c>, <c>SourceId</c> + /// b) the misc elements <c>Version</c>, <c>CreatedAt</c>, <c>Uid</c>, <c>SessionId</c>, <c>v4address</c>, <c>v4data_port</c>, <c>v4service_port</c>, <c>v6address</c>, <c>v6data_port</c>, <c>v6service_port</c> + /// c) the extended description element <c>desc</c> with user-defined sub-elements. </summary> + /// <returns> A <c>string</c> with the entire <see cref="StreamInfo"/>. </returns> + public string AsXML() { - /// <summary> Initializes a new instance of <see cref="XMLElement"/> struct. </summary> - /// <param name="handle"> The handle. </param> - public XMLElement(IntPtr handle) { obj = handle; } + var pXml = Dll.lsl_get_xml(Obj); + var res = Marshal.PtrToStringAnsi(pXml); + Dll.lsl_destroy_string(pXml); + return res; + } +} - // === Tree Navigation === - /// <summary> Get the first child of the element. </summary> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement FirstChild() { return new XMLElement(dll.lsl_first_child(obj)); } +// ======================= +// ==== Stream Outlet ==== +// ======================= - /// <summary> Get the last child of the element. </summary> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement LastChild() { return new XMLElement(dll.lsl_last_child(obj)); } +/// <summary> A stream outlet. +/// +/// Outlets are used to make streaming data (and the meta-data) available on the lab network. </summary> +public class StreamOutlet : LSLObject +{ + /// <summary> Initializes a new instance of <see cref="StreamOutlet"/>. This makes the stream discoverable. </summary> + /// <param name="info"> The stream information to use for creating this stream. Stays constant over the lifetime of the outlet. </param> + /// <param name="chunkSize"> Optionally the desired chunk granularity (in samples) for transmission. + /// If unspecified, each push operation yields one chunk. Inlets can override this setting. </param> + /// <param name="maxBuffered">Optionally the maximum amount of data to buffer (in seconds if there is a nominal sampling rate, otherwise x100 in samples). + /// The default is 6 minutes of data. </param> + public StreamOutlet(StreamInfo info, int chunkSize = 0, int maxBuffered = 360) : base(Dll.lsl_create_outlet(info.DangerousGetHandle(), chunkSize, + maxBuffered)) + { + } - /// <summary> Get the next sibling in the children list of the parent node. </summary> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement NextSibling() { return new XMLElement(dll.lsl_next_sibling(obj)); } + protected override void DestroyLSLObject(IntPtr obj) { Dll.lsl_destroy_outlet(obj); } + + // ======================================== + // === Pushing a sample into the outlet === + // ======================================== + + /// <summary> Push an array of values as a sample into the outlet. </summary> + /// <param name="data"> An array of values to push (one for each channel). </param> + /// <param name="time"> Optionally the capture time of the sample, in agreement with <see cref="LocalClock"/>; if omitted, the current time is used. </param> + /// <param name="pushthrough"> Optionally whether to push the sample through to the receivers instead of buffering it with subsequent samples. + /// Note that the chunk_size, if specified at outlet construction, takes precedence over the pushthrough flag. </param> + public void PushSample(float[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_ftp(Obj, data, time, pushthrough ? 1 : 0); } + + /// <inheritdoc cref="PushSample(float[],double,bool)"/> + /// <param name="data"> An array of values to push (one for each channel). </param> + public void PushSample(double[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_dtp(Obj, data, time, pushthrough ? 1 : 0); } + + /// <inheritdoc cref="PushSample(float[],double,bool)"/> + /// <param name="data"> An array of values to push (one for each channel). </param> + public void PushSample(int[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_itp(Obj, data, time, pushthrough ? 1 : 0); } + + /// <inheritdoc cref="PushSample(float[],double,bool)"/> + /// <param name="data"> An array of values to push (one for each channel). </param> + public void PushSample(short[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_stp(Obj, data, time, pushthrough ? 1 : 0); } + + /// <inheritdoc cref="PushSample(float[],double,bool)"/> + /// <param name="data"> An array of values to push (one for each channel). </param> + public void PushSample(char[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_ctp(Obj, data, time, pushthrough ? 1 : 0); } + + /// <inheritdoc cref="PushSample(float[],double,bool)"/> + /// <param name="data"> An array of values to push (one for each channel). </param> + public void PushSample(string[] data, double time = 0.0, bool pushthrough = true) { Dll.lsl_push_sample_strtp(Obj, data, time, pushthrough ? 1 : 0); } + + // =================================================== + // === Pushing an chunk of samples into the outlet === + // =================================================== + + /// <summary> Push a chunk of samples into the outlet. Single time provided. </summary> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + /// <param name="time"> Optionally the capture time of the sample, in agreement with <see cref="LocalClock"/>; if omitted, the current time is used. </param> + /// <param name="pushthrough"> Optionally whether to push the sample through to the receivers instead of buffering it with subsequent samples. + /// Note that the chunkSize, if specified at outlet construction, takes precedence over the pushthrough flag. </param> + public void PushChunk(float[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_ftp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } - /// <summary> Get the previous sibling in the children list of the parent node. </summary> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement PreviousSibling() { return new XMLElement(dll.lsl_previous_sibling(obj)); } + /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(double[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_dtp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } - /// <summary> Get the parent node. </summary> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement Parent() { return new XMLElement(dll.lsl_parent(obj)); } + /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(int[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_itp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } + /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(short[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_stp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } - // === Tree Navigation by Name === + /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(char[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_ctp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } - /// <summary> Get a child with a specified name. </summary> - /// <param name="name"> The child name. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement Child(string name) { return new XMLElement(dll.lsl_child(obj, name)); } + /// <inheritdoc cref="PushChunk(float[,],double,bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(string[,] data, double time = 0.0, bool pushthrough = true) + { + Dll.lsl_push_chunk_strtp(Obj, data, (uint) data.Length, time, pushthrough ? 1 : 0); + } - /// <summary> Get the next sibling with the specified name. </summary> - /// <param name="name"> The next sibling name. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement NextSibling(string name) { return new XMLElement(dll.lsl_next_sibling_n(obj, name)); } - /// <summary> Get the previous sibling with the specified name. </summary> - /// <param name="name"> The previous sibling name. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement PreviousSibling(string name) { return new XMLElement(dll.lsl_previous_sibling_n(obj, name)); } + /// <summary> Push a chunk of multiplexed samples into the outlet. One time per sample is provided. </summary> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(float[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_ftnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } + /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(double[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_dtnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } - // === Content Queries === + /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(int[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_itnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } - /// <summary> Whether this node is empty. </summary> - /// <returns> <c>true</c> or <c>false</c>. </returns> - public bool Empty() { return dll.lsl_empty(obj) != 0; } + /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(short[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_stnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } - /// <summary> Whether this is a text body (instead of an XML element). True both for plain char data and CData. </summary> - /// <returns> <c>true</c> or <c>false</c>. </returns> - public bool IsText() { return dll.lsl_is_text(obj) != 0; } + /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(char[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_ctnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } - /// <summary> Name of the element. </summary> - /// <returns> the name as <c>string</c>. </returns> - public string Name() { return Marshal.PtrToStringAnsi(dll.lsl_name(obj)); } + /// <inheritdoc cref="PushChunk(float[,],double[],bool)"/> + /// <param name="data"> A rectangular array of values for multiple samples. </param> + public void PushChunk(string[,] data, double[] times, bool pushthrough = true) + { + Dll.lsl_push_chunk_strtnp(Obj, data, (uint) data.Length, times, pushthrough ? 1 : 0); + } - /// <summary> Value of the element. </summary> - /// <returns> the value as <c>string</c>. </returns> - public string Value() { return Marshal.PtrToStringAnsi(dll.lsl_value(obj)); } - /// <summary> Get child value (value of the first child that is text). </summary> - /// <returns> the child value as <c>string</c>. </returns> - public string ChildValue() { return Marshal.PtrToStringAnsi(dll.lsl_child_value(obj)); } + // =============================== + // === Miscellaneous Functions === + // =============================== - /// <summary> Get child value of a child with a specified name. </summary> - /// <param name="name"> The child name. </param> - /// <returns> the child value as <c>string</c>. </returns> - public string ChildValue(string name) { return Marshal.PtrToStringAnsi(dll.lsl_child_value_n(obj, name)); } + /// <summary> Check whether consumers are currently registered. + /// + /// While it does not hurt, there is technically no reason to push samples if there is no consumer. </summary> + /// <returns> <c>true</c> or <c>false</c>. </returns> + public bool HaveConsumers() { return Dll.lsl_have_consumers(Obj) > 0; } + /// <summary> Wait until some consumer shows up (without wasting resources). </summary> + /// <param name="timeout"> The timeout. </param> + /// <returns> True if the wait was successful, false if the timeout expired. </returns> + public bool WaitForConsumers(double timeout = LSL.FOREVER) { return Dll.lsl_wait_for_consumers(Obj, timeout) > 0; } - // === Modification === + /// <summary> Retrieve the stream info provided by this outlet. + /// + /// This is what was used to create the stream (and also has the Additional Network Information fields assigned). </summary> + /// <returns> A <see cref="StreamInfo"/> </returns> + public StreamInfo Info() { return new StreamInfo(Dll.lsl_get_info(Obj)); } +} - /// <summary> Append a child node with a given name, which has a (nameless) plain-text child with the given text value. </summary> - /// <param name="name"> The child name. </param> - /// <param name="value"> The child value. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement AppendChildValue(string name, string value) { return new XMLElement(dll.lsl_append_child_value(obj, name, value)); } - /// <summary> Prepend a child node with a given name, which has a (nameless) plain-text child with the given text value. </summary> - /// <param name="name"> The child name. </param> - /// <param name="value"> The child value. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement PrependChildValue(string name, string value) { return new XMLElement(dll.lsl_prepend_child_value(obj, name, value)); } +// ====================== +// ==== Stream Inlet ==== +// ====================== - /// <summary> Set the text value of the (nameless) plain-text child of a named child node. </summary> - /// <param name="name"> The child name. </param> - /// <param name="value"> The child value. </param> - /// <returns> True if the wait was successful, false otherwise. </returns> - public bool SetChildValue(string name, string value) { return dll.lsl_set_child_value(obj, name, value) != 0; } +/// <summary> A stream inlet. +/// +/// Inlets are used to receive streaming data (and meta-data) from the lab network. </summary> +public class StreamInlet : LSLObject +{ + /// <summary> Initializes a new instance of <see cref="StreamInlet"/> from a resolved stream info. </summary> + /// <param name="info"> A resolved stream info object (as coming from one of the resolver functions). </param> + /// <param name="maxBuflen"> Optionally the maximum amount of 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. </param> + /// <param name="maxChunklen"> Optionally the maximum size, in samples, at which chunks are transmitted + /// (the default corresponds to the chunk sizes used by the sender). + /// 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. + /// If left unspecified (=0), the sender determines the chunk granularity. </param> + /// <param name="recover"> Try to silently recover lost streams that are recoverable (=those that that have a SourceId set). + /// In all other cases (recover is false or the stream is not recoverable) functions may throw a <see cref="LostException"/> + /// if the stream's source is lost (e.g., due to an app or computer crash). </param> + /// <remarks> The <see cref="StreamInlet"/> may also be constructed with a fully-specified <see cref="StreamInfo"/> + /// 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). </remarks> + public StreamInlet(StreamInfo info, int maxBuflen = 360, int maxChunklen = 0, bool recover = true, + ProcessingOptions postproc_flags = ProcessingOptions.None) : base(Dll.lsl_create_inlet(info.DangerousGetHandle(), maxBuflen, maxChunklen, + recover ? 1 : 0)) + { + Dll.lsl_set_postprocessing(Obj, postproc_flags); + } - /// <summary> Set the element's name. </summary> - /// <param name="name"> The new name. </param> - /// <returns> <c>false</c> if the node is empty. </returns> - public bool SetName(string name) { return dll.lsl_set_name(obj, name) != 0; } + protected override void DestroyLSLObject(IntPtr obj) { Dll.lsl_destroy_inlet(obj); } - /// <summary> Set the element's value. </summary> - /// <param name="value"> The new value. </param> - /// <returns> <c>false</c> if the node is empty. </returns> - public bool SetValue(string value) { return dll.lsl_set_value(obj, value) != 0; } + /// <summary> Retrieve the complete information of the given stream, including the extended description. + /// + /// Can be invoked at any time of the stream's lifetime. </summary> + /// <param name="timeout"> Optional timeout of the operation (default: no timeout). </param> + /// <returns> <see cref="StreamInfo"/>. </returns> + /// <exception cref="TimeoutException"> If the timeout expires. </exception> + /// <exception cref="LostException"> If the stream source has been lost. </exception> + public StreamInfo Info(double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_get_fullinfo(Obj, timeout, ref ec); + LSL.CheckError(ec); + return new StreamInfo(res); + } - /// <summary> Append a child element with the specified name. </summary> - /// <param name="name"> The child name. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement AppendChild(string name) { return new XMLElement(dll.lsl_append_child(obj, name)); } + /// <summary> Subscribe to the data stream. + /// + /// All samples pushed in at the other end from this moment onwards will be queued and + /// eventually be delivered in response to <see cref="PullSample(float[],double)"/> or <see cref="PullChunk(float[,],double[],double)"/> calls. + /// Pulling a sample without some preceding OpenStream is permitted (the stream will then be opened implicitly). </summary> + /// <param name="timeout"> Optional timeout of the operation (default: no timeout). </param> + /// <exception cref="TimeoutException"> If the timeout expires. </exception> + /// <exception cref="LostException"> If the stream source has been lost. </exception> + public void OpenStream(double timeout = LSL.FOREVER) + { + var ec = 0; + Dll.lsl_open_stream(Obj, timeout, ref ec); + LSL.CheckError(ec); + } - /// <summary> Prepend a child element with the specified name. </summary> - /// <param name="name"> The child name. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement PrependChild(string name) { return new XMLElement(dll.lsl_prepend_child(obj, name)); } + /// <summary> Drop the current data stream. + /// + /// 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. </summary> + public void CloseStream() { Dll.lsl_close_stream(Obj); } - /// <summary> Append a copy of the specified element as a child. </summary> - /// <param name="e"> The element to copy. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement AppendCopy(XMLElement e) { return new XMLElement(dll.lsl_append_copy(obj, e.obj)); } + /// <summary> Retrieve an estimated time correction offset for the given stream. + /// + /// The first call to this function takes several miliseconds until a reliable first estimate is obtained. + /// Subsequent calls are instantaneous (and rely on periodic background updates). + /// The precision of these estimates should be below 1 ms (empirically within +/-0.2 ms). </summary> + /// <param name="timeout"> Optional timeout to acquire the first time-correction estimate (default: no timeout). </param> + /// <returns> The time correction estimate. This is the number that needs to be added to a time stamp + /// that was remotely generated via <c>lsl_local_clock()</c> to map it into the local clock domain of this machine. </returns> + /// <exception cref="TimeoutException"> If the timeout expires. </exception> + /// <exception cref="LostException"> If the stream source has been lost. </exception> + public double TimeCorrection(double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_time_correction(Obj, timeout, ref ec); + LSL.CheckError(ec); + return res; + } - /// <summary> Prepend a child element with the specified name. </summary> - /// <param name="e"> The element to copy. </param> - /// <returns> New <see cref="XMLElement"/>. </returns> - public XMLElement PrependCopy(XMLElement e) { return new XMLElement(dll.lsl_prepend_copy(obj, e.obj)); } + // ======================================= + // === Pulling a sample from the inlet === + // ======================================= + + /// <summary> Pull a sample from the inlet and read it into an array of values. Handles type checking and conversion. </summary> + /// <param name="sample"> An array to hold the resulting values. </param> + /// <param name="timeout"> Optional, the timeout for this operation, if any. Use 0.0 to make the function non-blocking. </param> + /// <returns>The capture time of the sample on the remote machine, or 0.0 if no new sample was available. + /// To remap this time stamp to the local clock, add the value returned by <see cref="TimeCorrection"/> to it. </returns> + /// <exception cref="LostException"> If the stream source has been lost. </exception> + public double PullSample(float[] sample, double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_pull_sample_f(Obj, sample, sample.Length, timeout, ref ec); + LSL.CheckError(ec); + return res; + } - /// <summary> Remove a child element with the specified name. </summary> - /// <param name="name"> The child name. </param> - public void RemoveChild(string name) { dll.lsl_remove_child_n(obj, name); } + /// <inheritdoc cref="PullSample(float[],double)"/> + /// <param name="sample"> An array to hold the resulting values. </param> + public double PullSample(double[] sample, double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_pull_sample_d(Obj, sample, sample.Length, timeout, ref ec); + LSL.CheckError(ec); + return res; + } - /// <summary> Remove a specified child element. </summary> - /// <param name="e"> The element to remove. </param> - public void RemoveChild(XMLElement e) { dll.lsl_remove_child(obj, e.obj); } - - private readonly IntPtr obj; + /// <inheritdoc cref="PullSample(float[],double)"/> + /// <param name="sample"> An array to hold the resulting values. </param> + public double PullSample(int[] sample, double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_pull_sample_i(Obj, sample, sample.Length, timeout, ref ec); + LSL.CheckError(ec); + return res; } + /// <inheritdoc cref="PullSample(float[],double)"/> + /// <param name="sample"> An array to hold the resulting values. </param> + public double PullSample(short[] sample, double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_pull_sample_s(Obj, sample, sample.Length, timeout, ref ec); + LSL.CheckError(ec); + return res; + } - // =========================== - // === Continuous Resolver === - // =========================== + /// <inheritdoc cref="PullSample(float[],double)"/> + /// <param name="sample"> An array to hold the resulting values. </param> + public double PullSample(char[] sample, double timeout = LSL.FOREVER) + { + var ec = 0; + var res = Dll.lsl_pull_sample_c(Obj, sample, sample.Length, timeout, ref ec); + LSL.CheckError(ec); + return res; + } - /// <summary> A convenience class that resolves streams continuously in the background throughout - /// its lifetime and which can be queried at any time for the set of streams that are currently visible on the network. </summary> - public class ContinuousResolver + /// <inheritdoc cref="PullSample(float[],double)"/> + /// <param name="sample"> An array to hold the resulting values. </param> + public double PullSample(string[] sample, double timeout = LSL.FOREVER) { - /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams on the network. - /// - /// This is analogous to the functionality offered by the free function <see cref="ResolveStreams"/>.</summary> - /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), - /// this is the time in seconds after which it is no longer reported by the resolver. </param> - public ContinuousResolver(double forgetAfter = 5.0) { obj = dll.lsl_create_continuous_resolver(forgetAfter); } - - /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams with a specific value for a given property. - /// - /// This is analogous to the functionality provided by the free function <see cref="ResolveStream(string,string,int,double)"/>.</summary> - /// <param name="prop"> The <see cref="StreamInfo"/> property that should have a specific value (e.g., "name", "type", "SourceId", or "desc/manufaturer"). </param> - /// <param name="value"> The string value that the property should have (e.g., "EEG" as the type property). </param> - /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), - /// this is the time in seconds after which it is no longer reported by the resolver. </param> - public ContinuousResolver(string prop, string value, double forgetAfter = 5.0) + var ec = 0; + var tmp = new IntPtr[sample.Length]; + var res = Dll.lsl_pull_sample_str(Obj, tmp, tmp.Length, timeout, ref ec); + LSL.CheckError(ec); + try { - obj = dll.lsl_create_continuous_resolver_byprop(prop, value, forgetAfter); + for (var k = 0; k < tmp.Length; k++) { sample[k] = Marshal.PtrToStringAnsi(tmp[k]); } } + finally + { + foreach (var t in tmp) { Dll.lsl_destroy_string(t); } + } + return res; + } - /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams that match a given XPath 1.0 predicate. - /// - /// This is analogous to the functionality provided by the free function <see cref="ResolveStream(string,int,double)"/>. </summary> - /// <param name="pred"> The predicate string, e.g. "name='BioSemi'" or "type='EEG' and starts-with(name,'BioSemi') and count(info/desc/channel)=32" </param> - /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), - /// this is the time in seconds after which it is no longer reported by the resolver. </param> - public ContinuousResolver(string pred, double forgetAfter = 5.0) { obj = dll.lsl_create_continuous_resolver_bypred(pred, forgetAfter); } - /// <summary> Finalizes an instance of the <see cref="ContinuousResolver"/> class. </summary> - ~ContinuousResolver() { dll.lsl_destroy_continuous_resolver(obj); } + // ================================================= + // === Pulling a chunk of samples from the inlet === + // ================================================= - /// <summary> Obtain the set of currently present streams on the network (i.e. resolve result). </summary> - /// <returns> An array of matching stream info objects (excluding their meta-data), any of which can subsequently be used to open an inlet. </returns> - public StreamInfo[] Results() - { - IntPtr[] buf = new IntPtr[1024]; - int num = dll.lsl_resolver_results(obj, buf, (uint) buf.Length); - StreamInfo[] res = new StreamInfo[num]; - for (int k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } - return res; - } + /// <summary> Pull a chunk of data from the inlet. </summary> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + /// <param name="times"> A pre-allocated buffer where time stamps shall be stored. </param> + /// <param name="timeout"> Optionally the timeout for this operation, if any. + /// When the timeout expires, the function may return before the entire buffer is filled. + /// The default value of 0.0 will retrieve only data available for immediate pickup. </param> + /// <returns> Number of samples written to the data and timestamp buffers.</returns> + /// <exception cref="LostException"> If the stream source has been lost. </exception> + public int PullChunk(float[,] buffer, double[] times, double timeout = 0.0) + { + var ec = 0; + var res = Dll.lsl_pull_chunk_f(Obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + return (int) res / buffer.GetLength(1); + } - private readonly IntPtr obj; + /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + public int PullChunk(double[,] buffer, double[] times, double timeout = 0.0) + { + var ec = 0; + var res = Dll.lsl_pull_chunk_d(Obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + return (int) res / buffer.GetLength(1); } - // ======================= - // === Exception Types === - // ======================= + /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + public int PullChunk(int[,] buffer, double[] times, double timeout = 0.0) + { + var ec = 0; + var res = Dll.lsl_pull_chunk_i(Obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + return (int) res / buffer.GetLength(1); + } - /// <summary> Exception class that indicates that a stream inlet's source has been irrecoverably lost. </summary> - /// <seealso cref="System.Exception" /> - public class LostException : System.Exception + /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + public int PullChunk(short[,] buffer, double[] times, double timeout = 0.0) { - public LostException() { } - public LostException(string message) { } - public LostException(string message, System.Exception inner) { } - protected LostException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + var ec = 0; + var res = Dll.lsl_pull_chunk_s(Obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + return (int) res / buffer.GetLength(1); } - /// <summary> Exception class that indicates that an internal error has occurred inside liblsl. </summary> - /// <seealso cref="System.Exception" /> - public class InternalException : System.Exception + /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + public int PullChunk(char[,] buffer, double[] times, double timeout = 0.0) { - public InternalException() { } - public InternalException(string message) { } - public InternalException(string message, System.Exception inner) { } - protected InternalException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + var ec = 0; + var res = Dll.lsl_pull_chunk_c(Obj, buffer, times, (uint) buffer.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + return (int) res / buffer.GetLength(1); } - /// <summary> Check an error condition and throw an exception if appropriate. </summary> - /// <param name="code"> The error code. </param> - /// <exception cref="TimeoutException"> The operation failed due to a timeout. </exception> - /// <exception cref="LostException"> The stream has been lost. </exception> - /// <exception cref="ArgumentException"> An argument was incorrectly specified (e.g., wrong format or wrong length). </exception> - /// <exception cref="InternalException"> An internal internal error has occurred. </exception> - /// <exception cref="Exception"> An unknown error has occurred. </exception> - public static void CheckError(int code) + /// <inheritdoc cref="PullChunk(float[,],double[],double)"/> + /// <param name="buffer"> A pre-allocated buffer where the channel data shall be stored. </param> + public int PullChunk(string[,] buffer, double[] times, double timeout = 0.0) { - if (code < 0) + var ec = 0; + var tmp = new IntPtr[buffer.GetLength(0), buffer.GetLength(1)]; + var res = Dll.lsl_pull_chunk_str(Obj, tmp, times, (uint) tmp.Length, (uint) times.Length, timeout, ref ec); + LSL.CheckError(ec); + try { - switch (code) + for (var s = 0; s < tmp.GetLength(0); s++) { - case -1: throw new System.TimeoutException("The operation failed due to a timeout."); - case -2: throw new LostException("The stream has been lost."); - case -3: throw new System.ArgumentException("An argument was incorrectly specified (e.g., wrong format or wrong length)."); - case -4: throw new InternalException("An internal internal error has occurred."); - default: throw new System.Exception("An unknown error has occurred."); + for (var c = 0; c < tmp.GetLength(1); c++) { buffer[s, c] = Marshal.PtrToStringAnsi(tmp[s, c]); } } } + finally + { + for (var s = 0; s < tmp.GetLength(0); s++) + { + for (var c = 0; c < tmp.GetLength(1); c++) { Dll.lsl_destroy_string(tmp[s, c]); } + } + } + return (int) res / buffer.GetLength(1); } + /// <summary> Query whether samples are currently available for immediate pickup. </summary> + /// <returns> Number of samples available. </returns> + /// <remarks> that it is not a good idea to use <see cref="SamplesAvailable"/> to determine whether a <c>pull_*()</c> call would block: + /// to be sure, set the pull timeout to 0.0 or an acceptably low value. + /// If the underlying implementation supports it, the value will be the number of samples available (otherwise it will be 1 or 0). </remarks> + public int SamplesAvailable() { return (int) Dll.lsl_samples_available(Obj); } - // === Internal: C library function definitions. === + /// <summary> Query whether the clock was potentially reset since the last call to <see cref="WasClockReset"/>. + /// + /// This is a rarely-used function that is only useful to applications that combine multiple <see cref="TimeCorrection"/> values to estimate precise clock drift; + /// it allows to tolerate cases where the source machine was hot-swapped or restarted in between two measurements. </summary> + /// <returns> <c>true</c> if clock was reset... </returns> + public bool WasClockReset() { return (int) Dll.lsl_was_clock_reset(Obj) != 0; } +} + +// =========================== +// === Continuous Resolver === +// =========================== + + +/// <summary> A convenience class that resolves streams continuously in the background throughout +/// its lifetime and which can be queried at any time for the set of streams that are currently visible on the network. </summary> +public class ContinuousResolver : LSLObject +{ + /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams on the network. + /// + /// This is analogous to the functionality offered by the free function <see cref="ResolveStreams"/>.</summary> + /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), + /// this is the time in seconds after which it is no longer reported by the resolver. </param> + public ContinuousResolver(double forgetAfter = 5.0) : base(Dll.lsl_create_continuous_resolver(forgetAfter)) { } - private class dll + /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams with a specific value for a given property. + /// + /// This is analogous to the functionality provided by the free function <see cref="ResolveStream(string,string,int,double)"/>.</summary> + /// <param name="prop"> The <see cref="StreamInfo"/> property that should have a specific value (e.g., "name", "type", "SourceId", or "desc/manufaturer"). </param> + /// <param name="value"> The string value that the property should have (e.g., "EEG" as the type property). </param> + /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), + /// this is the time in seconds after which it is no longer reported by the resolver. </param> + public ContinuousResolver(string prop, string value, double forgetAfter = 5.0) : base(Dll.lsl_create_continuous_resolver_byprop(prop, value, forgetAfter)) { + } + + /// <summary> Initializes a new instance of the <see cref="ContinuousResolver"/> class that resolves all streams that match a given XPath 1.0 predicate. + /// + /// This is analogous to the functionality provided by the free function <see cref="ResolveStream(string,int,double)"/>. </summary> + /// <param name="pred"> The predicate string, e.g. "name='BioSemi'" or "type='EEG' and starts-with(name,'BioSemi') and count(info/desc/channel)=32" </param> + /// <param name="forgetAfter"> When a stream is no longer visible on the network (e.g., because it was shut down), + /// this is the time in seconds after which it is no longer reported by the resolver. </param> + public ContinuousResolver(string pred, double forgetAfter = 5.0) : base(Dll.lsl_create_continuous_resolver_bypred(pred, forgetAfter)) { } + + + protected override void DestroyLSLObject(IntPtr obj) { Dll.lsl_destroy_continuous_resolver(obj); } + + /// <summary> Obtain the set of currently present streams on the network (i.e. resolve result). </summary> + /// <returns> An array of matching stream info objects (excluding their meta-data), any of which can subsequently be used to open an inlet. </returns> + public StreamInfo[] Results() + { + var buf = new IntPtr[1024]; + var num = Dll.lsl_resolver_results(Obj, buf, (uint) buf.Length); + var res = new StreamInfo[num]; + for (var k = 0; k < num; k++) { res[k] = new StreamInfo(buf[k]); } + return res; + } +} + +#endregion + +// ===================== +// ==== XML Element ==== +// ===================== + +/// <summary> A lightweight XML element tree; models the <see cref="StreamInfo.Desc"/> field of <see cref="StreamInfo"/>. +/// +/// Has a name and can have multiple named children or have text content as value; attributes are omitted. +/// Insider note: The interface is modeled after a subset of pugixml's node type and is compatible with it. </summary> +/// <remarks> See Also : [http://pugixml.googlecode.com/svn/tags/latest/docs/manual/access.html](http://pugixml.googlecode.com/svn/tags/latest/docs/manual/access.html). </remarks> +public struct XMLElement +{ + /// <summary> Initializes a new instance of <see cref="XMLElement"/> struct. </summary> + /// <param name="handle"> The handle. </param> + public XMLElement(IntPtr handle) { _obj = handle; } + + // === Tree Navigation === + + /// <summary> Get the first child of the element. </summary> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement FirstChild() { return new XMLElement(Dll.lsl_first_child(_obj)); } + + /// <summary> Get the last child of the element. </summary> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement LastChild() { return new XMLElement(Dll.lsl_last_child(_obj)); } + + /// <summary> Get the next sibling in the children list of the parent node. </summary> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement NextSibling() { return new XMLElement(Dll.lsl_next_sibling(_obj)); } + + /// <summary> Get the previous sibling in the children list of the parent node. </summary> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement PreviousSibling() { return new XMLElement(Dll.lsl_previous_sibling(_obj)); } + + /// <summary> Get the parent node. </summary> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement Parent() { return new XMLElement(Dll.lsl_parent(_obj)); } + + + // === Tree Navigation by Name === + + /// <summary> Get a child with a specified name. </summary> + /// <param name="name"> The child name. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement Child(string name) { return new XMLElement(Dll.lsl_child(_obj, name)); } + + /// <summary> Get the next sibling with the specified name. </summary> + /// <param name="name"> The next sibling name. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement NextSibling(string name) { return new XMLElement(Dll.lsl_next_sibling_n(_obj, name)); } + + /// <summary> Get the previous sibling with the specified name. </summary> + /// <param name="name"> The previous sibling name. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement PreviousSibling(string name) { return new XMLElement(Dll.lsl_previous_sibling_n(_obj, name)); } + + + // === Content Queries === + + /// <summary> Whether this node is empty. </summary> + /// <returns> <c>true</c> or <c>false</c>. </returns> + public bool Empty() { return Dll.lsl_empty(_obj) != 0; } + + /// <summary> Whether this is a text body (instead of an XML element). True both for plain char data and CData. </summary> + /// <returns> <c>true</c> or <c>false</c>. </returns> + public bool IsText() { return Dll.lsl_is_text(_obj) != 0; } + + /// <summary> Name of the element. </summary> + /// <returns> the name as <c>string</c>. </returns> + public string Name() { return Marshal.PtrToStringAnsi(Dll.lsl_name(_obj)); } + + /// <summary> Value of the element. </summary> + /// <returns> the value as <c>string</c>. </returns> + public string Value() { return Marshal.PtrToStringAnsi(Dll.lsl_value(_obj)); } + + /// <summary> Get child value (value of the first child that is text). </summary> + /// <returns> the child value as <c>string</c>. </returns> + public string ChildValue() { return Marshal.PtrToStringAnsi(Dll.lsl_child_value(_obj)); } + + /// <summary> Get child value of a child with a specified name. </summary> + /// <param name="name"> The child name. </param> + /// <returns> the child value as <c>string</c>. </returns> + public string ChildValue(string name) { return Marshal.PtrToStringAnsi(Dll.lsl_child_value_n(_obj, name)); } + + + // === Modification === + + /// <summary> Append a child node with a given name, which has a (nameless) plain-text child with the given text value. </summary> + /// <param name="name"> The child name. </param> + /// <param name="value"> The child value. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement AppendChildValue(string name, string value) { return new XMLElement(Dll.lsl_append_child_value(_obj, name, value)); } + + /// <summary> Prepend a child node with a given name, which has a (nameless) plain-text child with the given text value. </summary> + /// <param name="name"> The child name. </param> + /// <param name="value"> The child value. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement PrependChildValue(string name, string value) { return new XMLElement(Dll.lsl_prepend_child_value(_obj, name, value)); } + + /// <summary> Set the text value of the (nameless) plain-text child of a named child node. </summary> + /// <param name="name"> The child name. </param> + /// <param name="value"> The child value. </param> + /// <returns> True if the wait was successful, false otherwise. </returns> + public bool SetChildValue(string name, string value) { return Dll.lsl_set_child_value(_obj, name, value) != 0; } + + /// <summary> Set the element's name. </summary> + /// <param name="name"> The new name. </param> + /// <returns> <c>false</c> if the node is empty. </returns> + public bool SetName(string name) { return Dll.lsl_set_name(_obj, name) != 0; } + + /// <summary> Set the element's value. </summary> + /// <param name="value"> The new value. </param> + /// <returns> <c>false</c> if the node is empty. </returns> + public bool SetValue(string value) { return Dll.lsl_set_value(_obj, value) != 0; } + + /// <summary> Append a child element with the specified name. </summary> + /// <param name="name"> The child name. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement AppendChild(string name) { return new XMLElement(Dll.lsl_append_child(_obj, name)); } + + /// <summary> Prepend a child element with the specified name. </summary> + /// <param name="name"> The child name. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement PrependChild(string name) { return new XMLElement(Dll.lsl_prepend_child(_obj, name)); } + + /// <summary> Append a copy of the specified element as a child. </summary> + /// <param name="e"> The element to copy. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement AppendCopy(XMLElement e) { return new XMLElement(Dll.lsl_append_copy(_obj, e._obj)); } + + /// <summary> Prepend a child element with the specified name. </summary> + /// <param name="e"> The element to copy. </param> + /// <returns> New <see cref="XMLElement"/>. </returns> + public XMLElement PrependCopy(XMLElement e) { return new XMLElement(Dll.lsl_prepend_copy(_obj, e._obj)); } + + /// <summary> Remove a child element with the specified name. </summary> + /// <param name="name"> The child name. </param> + public void RemoveChild(string name) { Dll.lsl_remove_child_n(_obj, name); } + + /// <summary> Remove a specified child element. </summary> + /// <param name="e"> The element to remove. </param> + public void RemoveChild(XMLElement e) { Dll.lsl_remove_child(_obj, e._obj); } + + private readonly IntPtr _obj; +} + +// ======================= +// === Exception Types === +// ======================= + +#region Exception Types + +/// <summary> Exception class that indicates that a stream inlet's source has been irrecoverably lost. </summary> +/// <seealso cref="System.Exception" /> +public class LostException : Exception +{ + public LostException(string message = "", Exception inner = null) { } + protected LostException(SerializationInfo info, StreamingContext context) { } +} + +/// <summary> Exception class that indicates that an internal error has occurred inside liblsl. </summary> +/// <seealso cref="System.Exception" /> +public class InternalException : Exception +{ + public InternalException(string message = "", Exception inner = null) { } + protected InternalException(SerializationInfo info, StreamingContext context) { } +} + +#endregion + +#region Internal: C library function definitions + +internal static class Dll +{ #if (UNITY_EDITOR_WIN && UNITY_EDITOR_64) - private const string LIBNAME = "liblsl64"; + private const string LIBNAME = "liblsl64"; #elif UNITY_EDITOR_WIN - const string LIBNAME = "liblsl32"; + private const string LIBNAME = "liblsl32"; #elif UNITY_STANDALONE_WIN - // a build hook will took care that the correct dll will be renamed after a successfull build - const string LIBNAME = "liblsl"; + // a build hook will took care that the correct dll will be renamed after a successfull build + private const string LIBNAME = "liblsl"; #elif (UNITY_EDITOR_LINUX && UNITY_EDITOR_64) || UNITY_STANDALONE_LINUX - const string LIBNAME = "liblsl64.so"; + private const string LIBNAME = "liblsl64.so"; #elif UNITY_EDITOR_LINUX - const string LIBNAME = "liblsl32.so"; + private const string LIBNAME = "liblsl32.so"; #elif UNITY_STANDALONE_LINUX - const string LIBNAME = "liblsl.so"; + private const string LIBNAME = "liblsl.so"; #elif Unity_EDITOR_OSX || UNITY_STANDALONE_OSX - //32-bit dylib no longer provided. - const string LIBNAME = "liblsl64"; + //32-bit dylib no longer provided. + private const string LIBNAME = "liblsl64"; #elif UNITY_STANDALONE_OSX - const string LIBNAME = "liblsl"; + private const string LIBNAME = "liblsl"; #elif UNITY_ANDROID - const string LIBNAME = "lslAndroid"; + private const string LIBNAME = "lslAndroid"; #endif - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_protocol_version(); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_protocol_version(); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_library_version(); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_library_version(); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_local_clock(); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_local_clock(); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_streaminfo(string name, string type, int channelCount, double sampling, channel_format_t channelFormat, - string sourceId); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_streaminfo(string name, string type, int channelCount, double sampling, ChannelFormat channelFormat, + string sourceId); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_destroy_streaminfo(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_destroy_streaminfo(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_name(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_name(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_type(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_type(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_get_channel_count(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_get_channel_count(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_get_nominal_srate(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_get_nominal_srate(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern channel_format_t lsl_get_channel_format(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern ChannelFormat lsl_get_channel_format(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_source_id(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_source_id(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_get_version(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_get_version(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_get_created_at(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_get_created_at(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_uid(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_uid(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_session_id(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_session_id(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_hostname(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_hostname(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_desc(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_desc(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_xml(IntPtr info); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_xml(IntPtr info); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_outlet(IntPtr info, int chunkSize, int maxBuffered); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_outlet(IntPtr info, int chunkSize, int maxBuffered); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_destroy_outlet(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_destroy_outlet(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_ftp(IntPtr obj, float[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_ftp(IntPtr obj, float[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_dtp(IntPtr obj, double[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_dtp(IntPtr obj, double[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_itp(IntPtr obj, int[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_itp(IntPtr obj, int[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_stp(IntPtr obj, short[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_stp(IntPtr obj, short[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_ctp(IntPtr obj, char[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_ctp(IntPtr obj, char[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_strtp(IntPtr obj, string[] data, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_sample_strtp(IntPtr obj, string[] data, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_sample_buftp(IntPtr obj, char[][] data, uint[] lengths, double timestamp, int pushthrough); + //[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + //public static extern int lsl_push_sample_buftp(IntPtr obj, char[][] data, uint[] lengths, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_ftp(IntPtr obj, float[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_ftp(IntPtr obj, float[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_ftnp(IntPtr obj, float[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_ftnp(IntPtr obj, float[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_dtp(IntPtr obj, double[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_dtp(IntPtr obj, double[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_dtnp(IntPtr obj, double[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_dtnp(IntPtr obj, double[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_itp(IntPtr obj, int[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_itp(IntPtr obj, int[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_itnp(IntPtr obj, int[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_itnp(IntPtr obj, int[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_stp(IntPtr obj, short[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_stp(IntPtr obj, short[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_stnp(IntPtr obj, short[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_stnp(IntPtr obj, short[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_ctp(IntPtr obj, char[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_ctp(IntPtr obj, char[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_ctnp(IntPtr obj, char[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_ctnp(IntPtr obj, char[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_strtp(IntPtr obj, string[,] data, uint dataElements, double timestamp, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_strtp(IntPtr obj, string[,] data, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_strtnp(IntPtr obj, string[,] data, uint dataElements, double[] timestamps, int pushthrough); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_push_chunk_strtnp(IntPtr obj, string[,] data, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_buftp(IntPtr obj, char[][] data, uint[] lengths, uint dataElements, double timestamp, int pushthrough); + //[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + //public static extern int lsl_push_chunk_buftp(IntPtr obj, char[][] data, uint[] lengths, uint dataElements, double timestamp, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_push_chunk_buftnp(IntPtr obj, char[][] data, uint[] lengths, uint dataElements, double[] timestamps, int pushthrough); + //[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + //public static extern int lsl_push_chunk_buftnp(IntPtr obj, char[][] data, uint[] lengths, uint dataElements, double[] timestamps, int pushthrough); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_have_consumers(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_have_consumers(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_wait_for_consumers(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_wait_for_consumers(IntPtr obj, double timeout); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_info(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_info(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_resolve_all(IntPtr[] buffer, uint bufferElements, double waitTime); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_resolve_all(IntPtr[] buffer, uint bufferElements, double waitTime); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_resolve_byprop(IntPtr[] buffer, uint bufferElements, string prop, string value, int minimum, double waitTime); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_resolve_byprop(IntPtr[] buffer, uint bufferElements, string prop, string value, int minimum, double waitTime); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_resolve_bypred(IntPtr[] buffer, uint bufferElements, string pred, int minimum, double waitTime); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_resolve_bypred(IntPtr[] buffer, uint bufferElements, string pred, int minimum, double waitTime); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_inlet(IntPtr info, int maxBuflen, int maxChunklen, int recover); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_inlet(IntPtr info, int maxBuflen, int maxChunklen, int recover); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_destroy_inlet(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_destroy_inlet(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_get_fullinfo(IntPtr obj, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_get_fullinfo(IntPtr obj, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_open_stream(IntPtr obj, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_open_stream(IntPtr obj, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_set_postprocessing(IntPtr obj, processing_options_t processingFlags); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_set_postprocessing(IntPtr obj, ProcessingOptions processingFlags); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_close_stream(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_close_stream(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_time_correction(IntPtr obj, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_time_correction(IntPtr obj, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_f(IntPtr obj, float[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_f(IntPtr obj, float[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_d(IntPtr obj, double[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_d(IntPtr obj, double[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_i(IntPtr obj, int[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_i(IntPtr obj, int[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_s(IntPtr obj, short[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_s(IntPtr obj, short[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_c(IntPtr obj, char[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_c(IntPtr obj, char[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_str(IntPtr obj, IntPtr[] buffer, int bufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern double lsl_pull_sample_str(IntPtr obj, IntPtr[] buffer, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern double lsl_pull_sample_buf(IntPtr obj, char[][] buffer, uint[] bufferLengths, int bufferElements, double timeout, ref int ec); + //[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + //public static extern double lsl_pull_sample_buf(IntPtr obj, char[][] buffer, uint[] bufferLengths, int bufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_destroy_string(IntPtr str); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_destroy_string(IntPtr str); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_f(IntPtr obj, float[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_f(IntPtr obj, float[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, uint timestampBufferElements, + double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_d(IntPtr obj, double[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_d(IntPtr obj, double[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, + uint timestampBufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_i(IntPtr obj, int[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_i(IntPtr obj, int[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, uint timestampBufferElements, + double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_s(IntPtr obj, short[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_s(IntPtr obj, short[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, uint timestampBufferElements, + double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_c(IntPtr obj, char[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_c(IntPtr obj, char[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, uint timestampBufferElements, + double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_str(IntPtr obj, IntPtr[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_pull_chunk_str(IntPtr obj, IntPtr[,] dataBuffer, double[] timestampBuffer, uint dataBufferElements, + uint timestampBufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_pull_chunk_buf(IntPtr obj, char[][,] dataBuffer, uint[,] lengthsBuffer, double[] timestampBuffer, uint dataBufferElements, - uint timestampBufferElements, double timeout, ref int ec); + //[DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + //public static extern uint lsl_pull_chunk_buf(IntPtr obj, char[][,] dataBuffer, uint[,] lengthsBuffer, double[] timestampBuffer, uint dataBufferElements, uint timestampBufferElements, double timeout, ref int ec); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_samples_available(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_samples_available(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern uint lsl_was_clock_reset(IntPtr obj); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern uint lsl_was_clock_reset(IntPtr obj); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_first_child(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_first_child(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_last_child(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_last_child(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_next_sibling(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_next_sibling(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_previous_sibling(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_previous_sibling(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_parent(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_parent(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_child(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_child(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_next_sibling_n(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_next_sibling_n(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_previous_sibling_n(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_previous_sibling_n(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_empty(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_empty(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_is_text(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_is_text(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_name(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_name(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_value(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_value(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_child_value(IntPtr e); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_child_value(IntPtr e); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_child_value_n(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_child_value_n(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_append_child_value(IntPtr e, string name, string value); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_append_child_value(IntPtr e, string name, string value); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_prepend_child_value(IntPtr e, string name, string value); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_prepend_child_value(IntPtr e, string name, string value); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_set_child_value(IntPtr e, string name, string value); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_set_child_value(IntPtr e, string name, string value); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_set_name(IntPtr e, string rhs); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_set_name(IntPtr e, string rhs); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_set_value(IntPtr e, string rhs); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_set_value(IntPtr e, string rhs); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_append_child(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_append_child(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_prepend_child(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_prepend_child(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_append_copy(IntPtr e, IntPtr e2); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_append_copy(IntPtr e, IntPtr e2); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_prepend_copy(IntPtr e, IntPtr e2); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_prepend_copy(IntPtr e, IntPtr e2); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_remove_child_n(IntPtr e, string name); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_remove_child_n(IntPtr e, string name); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_remove_child(IntPtr e, IntPtr e2); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_remove_child(IntPtr e, IntPtr e2); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_continuous_resolver(double forgetAfter); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_continuous_resolver(double forgetAfter); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_continuous_resolver_byprop(string prop, string value, double forgetAfter); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_continuous_resolver_byprop(string prop, string value, double forgetAfter); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern IntPtr lsl_create_continuous_resolver_bypred(string pred, double forgetAfter); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern IntPtr lsl_create_continuous_resolver_bypred(string pred, double forgetAfter); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern int lsl_resolver_results(IntPtr obj, IntPtr[] buffer, uint bufferElements); + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern int lsl_resolver_results(IntPtr obj, IntPtr[] buffer, uint bufferElements); - [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] - public static extern void lsl_destroy_continuous_resolver(IntPtr obj); - } + [DllImport(LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] + public static extern void lsl_destroy_continuous_resolver(IntPtr obj); } + +#endregion } diff --git a/OV/OVInlets.cs b/OV/OVInlets.cs index 4a7aea8b1efbc19878f47c46be54eaab93f9e0b2..75fef6fe33bc26aa8dd5ee077def447b542c53ea 100644 --- a/OV/OVInlets.cs +++ b/OV/OVInlets.cs @@ -17,8 +17,8 @@ public abstract class OVInlet<T> : MonoBehaviour public string StreamName => streamName; public int ChannelCount => expectedChannels; - protected liblsl.StreamInlet inlet; - private liblsl.ContinuousResolver resolver; + protected StreamInlet inlet; + private ContinuousResolver resolver; private bool readyToResolve = true; protected int expectedChannels = 0; @@ -38,7 +38,7 @@ public abstract class OVInlet<T> : MonoBehaviour } Debug.Log("Creating LSL resolver for stream " + streamName); - resolver = new liblsl.ContinuousResolver("name", streamName); + resolver = new ContinuousResolver("name", streamName); ResolveStream(); } @@ -69,10 +69,10 @@ public abstract class OVInlet<T> : MonoBehaviour public void PopSamples() { samples = null; } - private void CreateInlet(liblsl.StreamInfo result) + private void CreateInlet(StreamInfo result) { - Debug.Log($"Resolving Stream : Name = {streamName}, Steam Info Name = {result.Name()}, Stream Info Type = ({result.Type()}"); - inlet = new liblsl.StreamInlet(result); + //Debug.Log($"Resolving Stream : Name = {streamName}, Steam Info Name = {result.Name()}, Stream Info Type = ({result.Type()}"); + inlet = new StreamInlet(result); expectedChannels = inlet.Info().ChannelCount(); } @@ -84,7 +84,7 @@ public abstract class OVInlet<T> : MonoBehaviour yield return new WaitUntil(() => readyToResolve); // False mutex to wait Found Stream before search an other readyToResolve = false; // Avoïd double resolver - liblsl.StreamInfo[] results = resolver.Results(); + StreamInfo[] results = resolver.Results(); if (waitStream) { yield return new WaitUntil(() => results.Length > 0); } if (results.Length > 0) { CreateInlet(results[0]); } readyToResolve = true; diff --git a/OV/OVOutlets.cs b/OV/OVOutlets.cs index 63216b7a5a4d95775ceae576844e653359193275..fba454c10fc62d90db496deadb0f35fdc7fe3526 100644 --- a/OV/OVOutlets.cs +++ b/OV/OVOutlets.cs @@ -21,8 +21,8 @@ public abstract class OVOutlet<T> : MonoBehaviour public string StreamName => streamName; public int ChannelCount => channelCount; - protected liblsl.StreamOutlet outlet; - protected liblsl.StreamInfo info; + protected StreamOutlet outlet; + protected StreamInfo info; protected T[] samples; protected StreamTypes streamType = StreamTypes.Int; @@ -40,13 +40,13 @@ public abstract class OVOutlet<T> : MonoBehaviour } } - private liblsl.channel_format_t GetFormat() + private ChannelFormat GetFormat() { switch (streamType) { - case StreamTypes.Double: return liblsl.channel_format_t.cf_double64; - case StreamTypes.Float: return liblsl.channel_format_t.cf_float32; - case StreamTypes.Int: return liblsl.channel_format_t.cf_int32; + case StreamTypes.Double: return ChannelFormat.Double64; + case StreamTypes.Float: return ChannelFormat.Float32; + case StreamTypes.Int: return ChannelFormat.Int32; default: throw new ArgumentOutOfRangeException(); } } @@ -56,8 +56,8 @@ public abstract class OVOutlet<T> : MonoBehaviour private void Start() { samples = new T[channelCount]; - info = new liblsl.StreamInfo(streamName, GetType(), channelCount, liblsl.IRREGULAR_RATE, GetFormat()); - outlet = new liblsl.StreamOutlet(info); + info = new StreamInfo(streamName, GetType(), channelCount, LSL.IRREGULAR_RATE, GetFormat()); + outlet = new StreamOutlet(info); Debug.Log($"Creating Stream : Name = {info.Name()}, Type = {info.Type()}, Channel Count = {info.ChannelCount()}, Format = {info.ChannelFormat()}"); } diff --git a/Scripts/LSLMarkerStream.cs b/Scripts/LSLMarkerStream.cs index 77c71fa69c5580f40f3cdfaeece3100d4783e631..46a11d99a9c4f878dd0e17f5cb563a21840da2d0 100644 --- a/Scripts/LSLMarkerStream.cs +++ b/Scripts/LSLMarkerStream.cs @@ -11,12 +11,12 @@ public class LSLMarkerStream : MonoBehaviour private const string UNIQUE_SOURCE_ID = "D3F83BB699EB49AB94A9FA44B88882AB"; private const int LSL_CHANNEL_COUNT = 1; - private const double NOMINAL_SRATE = liblsl.IRREGULAR_RATE; + private const double NOMINAL_SRATE = LSL.IRREGULAR_RATE; - private const liblsl.channel_format_t LSL_CHANNEL_FORMAT = liblsl.channel_format_t.cf_string; + private const ChannelFormat LSL_CHANNEL_FORMAT = ChannelFormat.Str; - private liblsl.StreamInfo info; - private liblsl.StreamOutlet outlet; + private StreamInfo info; + private StreamOutlet outlet; //Assuming that markers are never send in regular intervalls private string[] sample; @@ -24,8 +24,8 @@ public class LSLMarkerStream : MonoBehaviour private void Awake() { sample = new string[LSL_CHANNEL_COUNT]; - info = new liblsl.StreamInfo(streamName, streamType, LSL_CHANNEL_COUNT, NOMINAL_SRATE, LSL_CHANNEL_FORMAT, UNIQUE_SOURCE_ID); - outlet = new liblsl.StreamOutlet(info); + info = new StreamInfo(streamName, streamType, LSL_CHANNEL_COUNT, NOMINAL_SRATE, LSL_CHANNEL_FORMAT, UNIQUE_SOURCE_ID); + outlet = new StreamOutlet(info); } public void Write(string marker, double timeStamp = 0) diff --git a/Scripts/LSLOutlet.cs b/Scripts/LSLOutlet.cs index f66c6425dc7e364cb894eb8e45d2098aafd3c19f..392a9720b88fb4319a829b89bf608f5cc7351062 100644 --- a/Scripts/LSLOutlet.cs +++ b/Scripts/LSLOutlet.cs @@ -7,8 +7,8 @@ public enum MomentForSampling { Update, FixedUpdate, LateUpdate } public class LSLOutlet : MonoBehaviour { - private liblsl.StreamOutlet outlet; - private liblsl.StreamInfo info; + private StreamOutlet outlet; + private StreamInfo info; private float[] sample; public string streamName = "Unity.ExampleStream"; @@ -24,8 +24,8 @@ public class LSLOutlet : MonoBehaviour watch.Start(); sample = new float[channelCount]; - info = new liblsl.StreamInfo(streamName, streamType, channelCount, Time.fixedDeltaTime * 1000); - outlet = new liblsl.StreamOutlet(info); + info = new StreamInfo(streamName, streamType, channelCount, Time.fixedDeltaTime * 1000); + outlet = new StreamOutlet(info); } public void FixedUpdate() diff --git a/Scripts/LSLTimeSync.cs b/Scripts/LSLTimeSync.cs index 5a6fce452c015deee6403d6f4ee58b32de9e6353..7966866c50335a5e99dba60a516bab82b40e06ba 100644 --- a/Scripts/LSLTimeSync.cs +++ b/Scripts/LSLTimeSync.cs @@ -16,8 +16,8 @@ namespace LSL4Unity public double LateUpdateTimeStamp { get; private set; } private void Awake() { Instance = this; } - private void FixedUpdate() { FixedUpdateTimeStamp = liblsl.LocalClock(); } - private void Update() { UpdateTimeStamp = liblsl.LocalClock(); } - private void LateUpdate() { LateUpdateTimeStamp = liblsl.LocalClock(); } + private void FixedUpdate() { FixedUpdateTimeStamp = LSL.LocalClock(); } + private void Update() { UpdateTimeStamp = LSL.LocalClock(); } + private void LateUpdate() { LateUpdateTimeStamp = LSL.LocalClock(); } } } diff --git a/Scripts/LSLTransformOutlet.cs b/Scripts/LSLTransformOutlet.cs index 0542aa4702e038113f20278831ba6b522ca32517..73ab5576e94c54d9bd67b8d09b640903e3068ca9 100644 --- a/Scripts/LSLTransformOutlet.cs +++ b/Scripts/LSLTransformOutlet.cs @@ -11,8 +11,8 @@ public class LSLTransformOutlet : MonoBehaviour private string uniqueSourceId; private int channelCount = 0; - private liblsl.StreamOutlet outlet; - private liblsl.StreamInfo streamInfo; + private StreamOutlet outlet; + private StreamInfo streamInfo; /// <summary> Use a array to reduce allocation costs and reuse it for each sampling call. </summary> private float[] sample; @@ -26,7 +26,7 @@ public class LSLTransformOutlet : MonoBehaviour public Transform sampleSource; /// <summary> Due to an instable framerate we assume a irregular data rate. </summary> - private const double DATA_RATE = liblsl.IRREGULAR_RATE; + private const double DATA_RATE = LSL.IRREGULAR_RATE; private void Awake() { @@ -41,7 +41,7 @@ public class LSLTransformOutlet : MonoBehaviour // initialize the array once channelCount = channelDefinitions.Count; sample = new float[channelCount]; - streamInfo = new liblsl.StreamInfo(streamName, streamType, channelCount, DATA_RATE, liblsl.channel_format_t.cf_float32, uniqueSourceId); + streamInfo = new StreamInfo(streamName, streamType, channelCount, DATA_RATE, ChannelFormat.Float32, uniqueSourceId); // it's not possible to create a XMLElement before and append it. var chns = streamInfo.Desc().AppendChild("channels"); @@ -51,7 +51,7 @@ public class LSLTransformOutlet : MonoBehaviour chns.AppendChild("channel").AppendChildValue("label", def.label).AppendChildValue("unit", def.unit).AppendChildValue("type", def.type); } - outlet = new liblsl.StreamOutlet(streamInfo); + outlet = new StreamOutlet(streamInfo); } /// <summary> @@ -93,7 +93,7 @@ public class LSLTransformOutlet : MonoBehaviour sample[++offset] = pos.z; } - outlet.PushSample(sample, liblsl.LocalClock()); + outlet.PushSample(sample, LSL.LocalClock()); } diff --git a/Scripts/Resolver.cs b/Scripts/Resolver.cs index 52c1173905c5319a3a171a2b3dfd923edca3f5f1..956b85e6ac55d1fe4539f9ed87708fa6e074f98f 100644 --- a/Scripts/Resolver.cs +++ b/Scripts/Resolver.cs @@ -16,12 +16,12 @@ public class Resolver : MonoBehaviour, IEventSystemHandler public float forgetStreamAfter = 1.0f; public List<LSLStreamInfoWrapper> streams; - private liblsl.ContinuousResolver resolver; + private ContinuousResolver resolver; /// <summary> Use this for initialization. </summary> private void Start() { - resolver = new liblsl.ContinuousResolver(forgetStreamAfter); + resolver = new ContinuousResolver(forgetStreamAfter); StartCoroutine(ResolveContinuously()); } @@ -79,7 +79,7 @@ public class Resolver : MonoBehaviour, IEventSystemHandler public string name; public string type; - public liblsl.StreamInfo Item { get; } + public StreamInfo Item { get; } public string StreamUid { get; } public int ChannelCount { get; } @@ -89,7 +89,7 @@ public class Resolver : MonoBehaviour, IEventSystemHandler public double DataRate { get; } public int StreamVersion { get; } - public LSLStreamInfoWrapper(liblsl.StreamInfo item) + public LSLStreamInfoWrapper(StreamInfo item) { Item = item; name = item.Name();