liblsl 1.0.0 copy "liblsl: ^1.0.0" to clipboard
liblsl: ^1.0.0 copied to clipboard

A Dart (and Flutter) native library for working with Lab Streaming Layer (LSL / liblsl).

1.0.0 #

liblsl.dart now wraps every function exported by the liblsl C library, so it has reached API parity with liblsl and moves to 1.0.0.

New features #

  • lsl_time_correction_ex is now wrapped: LSLInlet.getTimeCorrectionEx() / getTimeCorrectionExSync() return an LSLTimeCorrection carrying the clock offset, the remoteTime it was measured against, and the uncertainty (the probe's full round-trip time; bound is half of the RTT, indicating the +/- uncertainty).
  • Inlet time-stamp post-processing: LSLInlet.setPostProcessing() / setPostProcessingSync() take a Set<LSLProcessingOptions> (none/clockSync/dejitter/monotonize/threadSafe), and setSmoothingHalftime() / setSmoothingHalftimeSync() tune the dejitter window. Note that clockSync rewrites time stamps into the local clock domain and is not compatible with layers that apply the correction themselves, such as liblsl_coordinator (or your own application, if it handles the time correction).
  • LSLInlet.wasClockReset() / wasClockResetSync() report whether the source machine's clock may have been reset, which invalidates an offset fitted over several estimates. Reading it clears the flag.
  • LSL.lastError() exposes liblsl's thread-local last-error message.
  • Every exported liblsl function is now reachable from the high-level API:
    • LSLOutlet.pushSample / pushSampleSync / pushSamplePointerSync take optional timestamp (back-date a sample to its capture time) and pushthrough arguments (lsl_push_sample_*t / *tp), for every format including strings.
    • pushChunk / pushChunkTyped (and *Sync) take pushthrough (lsl_push_chunk_*tp / *tnp).
    • String streams now support pushChunk / pushChunkSync (lsl_push_chunk_str*).
    • Binary strings (values that may contain NUL bytes): LSLOutlet.pushSampleBytes / pushChunkBytes and LSLInlet.pullSampleBytes / pullChunkBytes (plus *Sync), wrapping the lsl_*_buf family.
    • LSLOutlet.getInfo() / getInfoSync() return the outlet's served stream info (lsl_get_info).
    • LSLStreamInfo gains channelBytes, sampleBytes, createdAt, sessionId, protocolVersion, matchesQuery() and copy(); LSL.protocolVersion reports the library's protocol version.
    • LSLXmlNode gains prependChildElement, prependChildValue, appendCopy, prependCopy, childValueNamed, setChildValue, removeChild and removeChildNamed.

Improvements #

  • On macOS and Linux, loading liblsl raises the process's soft open-file limit (when below 65536) as far as the system allows, so many streams no longer fail with "Too many open files" (macOS allows 256 by default). It only warns if the system refuses; set LIBLSL_DART_NO_RLIMIT=1 to opt out.
  • Exceptions raised from a nonzero liblsl error code now name the code (timeout/lost/argument/internal) and append liblsl's own message when it has one, instead of reporting a bare integer. A timeout code now raises LSLTimeout rather than a plain LSLException.
  • The published package is much smaller (about 3 MB instead of about 58 MB): the JOSS paper and its analysis are no longer included (they stay in the repository and the Zenodo archive), and the unused experimental src/wasm-demo has been removed.

Fixes #

  • pullSample on int8 streams returned negative values as unsigned (-3 came back as 253); it now reads them as signed, matching pullChunk.

  • Fixed a leaked native continuous resolver for every LSLStreamResolverContinuousByPredicate / ...ByProperty. create() made an unfiltered native resolver in the base class and then overwrote its handle with the filtered one, so destroy() never freed the first. Each resolver kept sending waves thereby constatly increasing UDP socket count each time a resolver was created. Subclasses now override createNativeResolver(), and exactly one native resolver is created per object.

  • The predicate/property strings passed to the continuous resolver are now freed after creation instead of leaking.

  • create() on a continuous resolver now throws LSLException if liblsl cannot create it (e.g. an invalid predicate), instead of keeping a null handle.

0.14.1+0 #

  • Added identities to LSLInlet, LSLOutlet and LSLStreamInfo (hashCode and ==).

0.14.0+0 #

New features #

  • Sync/blocking transport support: LSL.createOutlet/LSL.createInlet (and the LSLOutlet/LSLInlet constructors) accept transportOptions: Set<LSLTransportOptions>, matching lsl_create_outlet_ex/lsl_create_inlet_ex. syncBlocking enables zero-copy blocking socket writes (not available for string streams — ArgumentError at creation); bufsizeInSamples/bufsizeInThousandths change the unit of maxBuffer.
  • Chunked transfer API on outlets and inlets, in both direct and isolate modes: pushChunk/pushChunkSync (List<List<T>>), pushChunkTyped/pushChunkTypedSync (flat TypedData fast path, single memmove), pullChunk/pullChunkSync, pullChunkTyped/ pullChunkTypedSync, and the zero-copy pullChunkPointerSync. Chunk results are returned as LSLChunk<T>/LSLChunkTyped/LSLChunkPointer with per-sample timestamps; per-sample timestamps can also be supplied on push. String streams support list-form pullChunk only.
  • Standalone benchmark suite (benchmark/) comparing direct/isolate/sync-blocking transports for sample and chunk operations, with p50/p95/p99 latency, throughput, loss, and RSS metrics; a new Benchmark GitHub workflow stores results per push/tag via github-action-benchmark on gh-pages and attaches them to releases.

Fixes #

  • Fixed native memory leaks on string streams: pushed samples leaked one UTF-8 copy per channel per push, and pulled samples never released the liblsl-allocated strings (lsl_destroy_string).
  • Fixed ec/buffer leaks on error paths in LSLPullSample.pullSample/ createSample.
  • Fixed a use-after-free crash when an inlet's lsl_open_stream failed at creation and the inlet was later destroyed.
  • destroy() is now safe and idempotent after partially failed creation and LSLReusableBuffer.free() is now idempotent.
  • Isolate managers Timer leak fixed. Response timeout is cancelled on completion and is now configurable.
  • LSL.createOutlet default chunkSize corrected from 1 to 0 (liblsl semantics: one chunk per push), matching the LSLOutlet default.

Performance #

  • Hot push paths no longer wrap data in IList; LSLPushSample.listToBuffer now takes Iterable<dynamic>.
  • lsl_local_clock, lsl_have_consumers, lsl_samples_available, and lsl_inlet_flush now use isLeaf FFI calls; buffer converters are inline-hinted; chunk TypedData copies compile to memmove.

0.13.3+0 #

  • Updated build hook to add the flag -Wl,--allow-shlib-undefined for Android builds, to prevent an error when building the shared library.

0.13.2+0 #

0.13.0+0 #

  • Updated native_toolchain_c to ^0.18.0.
  • Updated android_libcpp_shared to ^0.1.1.

0.12.0+0 #

  • Package no longer requires dev/beta version of Dart SDK, as code_assets is now stable and does not require any special flags for building.

0.11.0+0 #

  • Update liblsl fork, stream fullinfo bugfix (commit ID: 9f0b6122).
  • Version increment change due to liblsl version updating from 1.16.2 -> 1.17.5. See liblsl releases.

0.10.2+0 #

  • Updated liblsl fork, sync with upstream. (commit ID: 76b054da)

0.10.1+1 #

  • Updated liblsl fork to fix a double-free issue (commit ID: 846c4199)

0.10.0+2 #

  • Updated liblsl fork asio to match boost 1.89.0 (standalone asio)
  • Updated pugixml to v1.15

0.10.0+1 #

  • Update liblsl fork, use boost 1.89.0
  • Update hooks to ^0.20.5.
  • Update code_assets to ^0.19.10.
  • Update native_toolchain_c to ^0.17.2.

0.10.0+0 #

  • Expose LSLInlet<T>.inlet which returns the underlying lsl_inlet pointer.
  • Expose LSLOutlet<T>.inlet which returns the underlying lsl_outlet pointer.
  • Added methods for creating an inlet and outlet from the pointer directly: LSLInlet.createFromPointer and LSLOutlet.createFromPointer.
  • Performance test use the above to allow a single isolate for all outlets, and a single isolate for all inlets, reducing overhead and improving performance.
  • All non-performance tests made concurrency safe, which reduces test-suite run time. Performance tests still need to be run without concurrency to ensure accurate(ish) timing.
  • Exported some lower-level classes for advanced use cases: LSLPullSample, LSLPushSample, LSLMapper, LSLSamplePointer, LSLReusableBuffer, LSLReusableBufferInt8, LSLReusableBufferDouble and LSLReusableBufferFloat.
  • Added advanced methods LSLInlet.pullSamplePointerSync() which returns a LSLSamplePointer and LSLOutlet.dataToBufferPointer which returns a buuffer pointer for pushing samples.
  • Replace List with IList for sample management from fast_immutable_collections for better performance and immutability guarantees.
  • Added a check for created state in LSLInlet.destroy and LSLOutlet.destroy to avoid trying to destroy uncreated inlets/outlets.
  • Isolated inlet and outlet now pass sample pointers as opposed to sample objects with dart collections, preventing copying between isolates and improving performance.
  • Improved performance of precise interval scheduling functions by reusing a single stopwatch instance and removing bounds checks, also will refuse to sleep if interval is below 1000 microseconds to avoid oversleeping.

0.9.1 #

  • Updated hooks from ^0.20.0 to ^0.20.1.
  • Updated code_assets from ^0.19.0 to ^0.19.7.
  • Updated LSL Api config documentation
  • Updated readme
  • Improved tests and added a performance test
  • Added an async version of runPreciseInterval (runPreciseIntervalAsync) that works with async callbacks
  • Added continous stream resolvers by property and predicate (LSLStreamResolverContinuousByProperty and LSLStreamResolverContinuousByPredicate)
  • LSL.createInlet now returns the typed version of the inlet rather than dynamice (e.g. LSLInlet<double> rather than LSLInlet<dynamic>)
  • Added draft JOSS paper

0.9.0 #

This is a major update that includes breaking changes. It introduces a new LSLStreamInfoWithMetadata class that allows for operations aligned with the C/C++ API that support reading and manipulation of the metadata associated with a stream. By default, when resolving streams, the metadata is not included and can only be retrieved after creating an inlet with the new method LSLInlet.getFullInfo, or, during inlet creation, you may pass includeMetadata: true to the constructor to include the metadata in the inlet.

In addition, the stream resolver methods have been updated, and now there are LSL.resolveStreamsByProperty and LSL.resolveStreamsByPredicate methods to filter streams during the resolution process. The LSL.resolveStreams method will still continue to resolve all available streams. The continuous versions of the filtered resolvers have not yet been implemented, but will be in the next release.

Main changes in this release: #

  • 🚀 Introduced LSLStreamInfoWithMetadata class that allows for reading and manipulating stream metadata, aligning with the C/C++ API.
  • 🚀 Updated stream resolvers, and added LSL.resolveStreamsByProperty and LSL.resolveStreamsByPredicate methods to the main LSL class.
  • 🚀 Updated LSLInlet to include a new method getFullInfo that retrieves the full stream info with metadata.
  • 🚀 Updated LSLInlet constructor with the includeMetadata property.
  • 🚀 Added resetUid method to `LSLStreamInfo``
    • This method was also added to liblsl
  • Forked liblsl version updated to commit bea40e2c.
  • Added a bunch of XML classes to handle the metadata, which group children and can be used for creating nodes or traversing the XML tree.

0.8.1 #

Dependency updates. Added a new static method createContinuousStreamResolver to the LSL class for creating and managing your own continuous stream resolver, existing stream resolver method works the same, but now you have the option to keep resolving streams in the background while the API is being used.

  • Updated hooks from ^0.19.1 to ^0.20.0.
  • Updated native_toolchain_c from ^0.16.1 to ^0.17.1.

0.8.0 #

🚨🚨🚨 This is a major update that includes breaking changes 🚨🚨🚨

It brings the awesome new ability to choose if you want to use isolates or not, and if not, you get access to synchronous methods for pulling and pushing samples. This update also includes some minor API changes to improve consistency and usability.

  • 🚀 LSLIsolatedInlet and LSLIsolatedOutlet have been replaced with LSLInlet and LSLOutlet, respectively. The new classes both run by default in isolated mode, but can be configured to run without isolates by passing useIsolates: false to the constructor. This allows for more flexibility in how the LSL API is used, while still providing the benefits of isolates for performance and concurrency.
    • This also means that there are now *Sync methods for some common operations, such as pullSampleSync, pushSampleSync, etc. These methods allow for synchronous operations without isolates, which can be useful in some cases for more precise control over timing and performance.
  • 🚀 LSLInlet and LSLOutlet buffer length and chunk size parameters have been consistently renamed to maxBuffer and chunkSize, respectively, to better reflect their purpose and usage, and to have a consistent naming scheme across the API.
  • 🚀 The streamInfo parameter in LSLInlet and LSLOutlet constructors has been made a positional parameter, to make it less verbose to create inlets and outlets.
  • 🚀 The LSLStreamInfo.streamInfo property is no longer nullable, and will instead throw an exception if the stream info is not set. This avoids having to do null checks when using the stream info pointer.
  • 🚀 The LSL.createInlet and LSL.createOutlet convenience methods now have the additonal useIsolates parameter, which allows for creating inlets and outlets without isolates if set to false. This is useful for cases where isolates are not needed or desired, such as when using the API in a synchronous context.

0.7.1 #

This is a minor change that requires the dev/main version of the Dart SDK, as hooks and native_toolchain_c still require a version later than the last stable release. This is a temporary change until the next supported stable Dart SDK release.

0.7.0 #

This release is a major update that includes breaking changes. This update provides a large performance improvement by reusing a buffer for samples, reducing the number of allocations and copies required when sending samples.

There are also new packages that complement liblsl.dart, and are still work-in-progress, but will allow setting up an entire experiment workflow without requiring any programming. Currently these packages include:

  • liblsl_timing: This package provides an application for measuring LSL timing performance with your specific device and network configuration. The test app automatically coordinates between all connected devices on the network, and passes the test configuration via a coordinator - the coordinator is the first device that starts the application. There are three tests included:
    • Latency: This test measures the latency of sending and receiving samples over LSL at the specified frequency.
    • Sync: This test is intended to measure the clock synchronization and drift between devices.
    • Interactive: This test has a button on screen and when the button is pressed, an LSL sample is sent, all receiving devices will then flash a black square on the screen. This test is intended to measure the entire end-to-end latency, including Flutter rendering, input and display lag. To measure this effectively, it would be ideal to have a touch sensor of some kind (e.g. FSR) and a photodiode sensor to measure the moment of touch and the moment of display change, respectively. There is a companion app in C++ written for a Bela (Beaglebone Black) which takes digital inputs and logs the timestamps along with the LSL samples. You can see more at the bela-lsl-timing repository.
  • liblsl_analysis: This package allows for analysis of the data provided by the liblsl_timing package. It currently only allows loading a single TSV file from the timing test, but will be updated to allow loading files from all the reporting devices to create a comprehensive report of the timing performance across all devices. The report will include:
    • Latency statistics for each device
    • Clock synchronization and drift statistics
    • Interactive test results with timestamps and latency measurements (if available)

Main changes in this release: #

  • Use custom fork of liblsl which allows API configuration to be specified at runtime (once, before any other LSL functions are called). This means that anything in the LSL API configuration file can be set, including on mobile platforms that do not support environment variables or allow editing files in /etc or ./.
    • This is exposed via the C/C++ API as lsl_set_config_filename and lsl_set_config_content to set the configuration file name and content (directly as a std::string/char*), respectively. The Dart API now provides a LSLConfig class that can be used to set the configuration file name and content, which can be used in LSL.setConfigFilename and LSL.setConfigContent methods.
  • New LSLReusableBuffer class for allowing sample structures to avoid creating new instances for each sample. This reuse significantly enhances the performance by reducing the allocations during sample pulling and pushing.
  • Generic push sample functions have now been replaced with specific implementations for each type e.g. LSLPushSampleFloat.
  • A new helper function runPreciseInterval has been added to handle precision interval timing, using an adjustable busy-wait loop. The API is subject to change, but allows for a callback and a mutable dyanmic state to be passed in, which can be used to update the state of the callback. This is useful for implementing precise timing in LSL applications (such as requiring 1000Hz sample creation with high precision -> resulting in a mean of 1.0000, median of 1.0082 ms over 180,000 samples).
  • hooks package updated from 0.19.0 to 0.19.1.
  • native_toolchain_c updated from ^0.16.0 to ^0.16.1.
  • ffigen updated from 18.1.0 to 19.0.0.
  • test package updated from 1.25.15 to 1.26.0.

0.6.2-dev.0 #

  • Make SDK constraint ^3.9.0-0

0.6.1 #

  • Attempt to fix package issue on pub.dev
  • bump dart SDK constraint to ^3.9.0 due to hooks package
  • Added LSLStreamResolver mixin (empty for now).
  • Added dartdoc dev dependency, and added doc directory to .gitignore

0.6.0 #

  • New LSLIsolatedInlet.getTimeCorrection method to get the LSL reported sample time correction
  • Removed deprecated LSLStreamInlet and LSLStreamOutlet classes
  • Remove deprecated native_assets_cli dependency
  • Add hooks 0.19.0 dependency instead of native_assets_cli
  • Add code_assets 0.19.0
  • Update native_toolchain_c to ^0.16.0

0.5.1 #

  • Fix package name on Android
  • Update ffigen to 18.1.0
  • Update native_assets_cli to 0.14.0
  • Update native_toolchain_c to 0.11.0

0.5.0 #

  • Generated dylib is now without the lib prefix (if it is the prefix on the platform). i.e. libliblsl.so is now just liblsl.so
  • Tested working on Raspberry Pi 4 (64bit)
  • Tests are now less chatty

0.4.1 #

  • Update native_assets_cli to 0.13.0
  • Update native_toolchain_c to 0.10.0

0.4.0 #

  • Inlet and outlets now pass sample pointer addresses rather than sample objects, making them more efficient (note: this has no public facing changes to the API)
  • Updated the liblsl_test package (android NDK version, entitlements, manifests)
  • Updated readme documentation, describing Android and iOS specifics

0.3.0 #

  • Outlets and inlets now have to be destroyed by the user
  • Outlets and inlets are now contained in Isolates
  • Updated liblsl to 7e61a2e
  • Added a fully self-contained example

0.2.1 #

  • Fixed linting issues.

0.2.0 #

  • Restructured API, renamed liblsl to native_liblsl.

0.1.2 #

  • Updated native_assets_cli to 0.12.0
  • Updated native_toolchain_c to 0.9.0
  • Removed spurious api.dart file (might come back later)

0.1.1 #

  • Added missing meta dependency

0.1.0 #

  • Restructured everything to be a bit more modular
  • There's now an API for all the basic LSL functions 🥳🎈
  • Started adding docs
  • Test includes pushing and pulling a sample

0.0.2 #

  • Android support 🎉

0.0.1 #

  • Initial release
  • Native compilation confirmed working on Windows, OSX and iOS. Will be testing Linux and Android soon.
3
likes
160
points
146
downloads

Documentation

API reference

Publisher

verified publisherzeyus.com

Weekly Downloads

A Dart (and Flutter) native library for working with Lab Streaming Layer (LSL / liblsl).

Homepage
Repository (GitHub)
View/report issues
Contributing

License

MIT (license)

Dependencies

android_libcpp_shared, code_assets, fast_immutable_collections, ffi, hooks, logging, meta, native_toolchain_c

More

Packages that depend on liblsl