use lsl_sys::*;
use std::convert::{From, TryFrom};
use std::ffi;
use std::fmt;
use std::rc;
use std::vec;
pub const IRREGULAR_RATE: f64 = 0.0;
pub const DEDUCED_TIMESTAMP: f64 = -1.0;
pub const FOREVER: f64 = 32000000.0;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Error {
BadArgument,
Timeout,
StreamLost,
ResourceCreation,
Internal,
Unknown,
}
type Result<T> = std::result::Result<T, Error>;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum ChannelFormat {
Float32 = 1,
Double64 = 2,
String = 3,
Int32 = 4,
Int16 = 5,
Int8 = 6,
Int64 = 7,
Undefined = 0,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum ProcessingOption {
None = 0,
ClockSync = 1,
Dejitter = 2,
Monotonize = 4,
Threadsafe = 8,
ALL = 1 | 2 | 4 | 8,
}
pub fn protocol_version() -> i32 {
unsafe { lsl_protocol_version() }
}
pub fn library_version() -> i32 {
unsafe { lsl_library_version() }
}
pub fn library_info() -> String {
unsafe { make_string(lsl_library_info()) }
}
pub fn local_clock() -> f64 {
unsafe { lsl_local_clock() }
}
#[derive(Debug)]
pub struct StreamInfo {
handle: rc::Rc<StreamInfoHandle>,
}
impl StreamInfo {
pub fn new(
stream_name: &str,
stream_type: &str,
channel_count: u32,
nominal_srate: f64,
channel_format: ChannelFormat,
source_id: &str,
) -> Result<StreamInfo> {
if stream_name.is_empty() || nominal_srate < 0.0 || channel_count >= 0x80000000 {
return Err(Error::BadArgument);
}
let stream_name = ffi::CString::new(stream_name)?;
let stream_type = ffi::CString::new(stream_type)?;
let source_id = ffi::CString::new(source_id)?;
unsafe {
let handle = lsl_create_streaminfo(
stream_name.as_ptr(),
stream_type.as_ptr(),
channel_count as i32,
nominal_srate,
channel_format.to_native(),
source_id.as_ptr(),
);
match handle.is_null() {
false => Ok(StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) }),
true => Err(Error::ResourceCreation),
}
}
}
pub fn stream_name(&self) -> String {
unsafe { make_string(lsl_get_name(self.handle.handle )) }
}
pub fn stream_type(&self) -> String {
unsafe { make_string(lsl_get_type(self.handle.handle )) }
}
pub fn channel_count(&self) -> i32 {
unsafe { lsl_get_channel_count(self.handle.handle ) }
}
pub fn nominal_srate(&self) -> f64 {
unsafe { lsl_get_nominal_srate(self.handle.handle) }
}
pub fn channel_format(&self) -> ChannelFormat {
unsafe { ChannelFormat::from_native(lsl_get_channel_format(self.handle.handle)) }
}
pub fn source_id(&self) -> String {
unsafe { make_string(lsl_get_source_id(self.handle.handle)) }
}
pub fn version(&self) -> i32 {
unsafe { lsl_get_version(self.handle.handle) }
}
pub fn created_at(&self) -> f64 {
unsafe { lsl_get_created_at(self.handle.handle) }
}
pub fn uid(&self) -> String {
unsafe { make_string(lsl_get_uid(self.handle.handle)) }
}
pub fn session_id(&self) -> String {
unsafe { make_string(lsl_get_session_id(self.handle.handle)) }
}
pub fn hostname(&self) -> String {
unsafe { make_string(lsl_get_hostname(self.handle.handle)) }
}
pub fn desc(&mut self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_get_desc(self.handle.handle),
doc: self.handle.clone()
}
}
}
pub fn matches_query(&self, query: &str) -> bool {
if let Ok(query) = ffi::CString::new(query) {
unsafe { lsl_stream_info_matches_query(self.handle.handle, query.as_ptr()) != 0 }
} else {
false
}
}
pub fn to_xml(&self) -> Result<String> {
unsafe {
let tmpstr = lsl_get_xml(self.handle.handle);
if tmpstr.is_null() {
return Err(Error::Internal);
}
let result = ffi::CStr::from_ptr(tmpstr).to_string_lossy().into_owned();
lsl_destroy_string(tmpstr);
Ok(result)
}
}
pub fn channel_bytes(&self) -> i32 {
unsafe { lsl_get_channel_bytes(self.handle.handle) }
}
pub fn sample_bytes(&self) -> i32 {
unsafe { lsl_get_sample_bytes(self.handle.handle) }
}
pub fn from_blank() -> Result<StreamInfo> {
StreamInfo::new("untitled", "", 0, 0.0, ChannelFormat::Undefined, "")
}
pub fn from_xml(xml: &str) -> Result<StreamInfo> {
let xml = ffi::CString::new(xml)?;
unsafe {
let handle = lsl_streaminfo_from_xml(xml.as_ptr());
match handle.is_null() {
false => Ok(StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) }),
true => Err(Error::ResourceCreation),
}
}
}
fn from_handle(handle: lsl_streaminfo) -> StreamInfo {
assert!(
!handle.is_null(),
"Attempted to create a StreamInfo from a NULL handle."
);
StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle } ) }
}
fn native_handle(&self) -> lsl_streaminfo {
self.handle.handle
}
}
impl Clone for StreamInfo {
fn clone(&self) -> StreamInfo {
unsafe {
let handle = lsl_copy_streaminfo(self.handle.handle);
assert!(
!handle.is_null(),
"Failed to clone native lsl_streaminfo object."
);
StreamInfo { handle: rc::Rc::new(StreamInfoHandle { handle }) }
}
}
}
impl fmt::Display for StreamInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(name={}, type={}, fmt={}, srate={})",
self.stream_name(),
self.stream_type(),
self.channel_format(),
self.nominal_srate()
)
}
}
#[derive(Debug)]
pub struct StreamOutlet {
handle: lsl_outlet,
channel_count: usize,
nominal_rate: f64,
}
impl StreamOutlet {
pub fn new(info: &StreamInfo, chunk_size: i32, max_buffered: i32) -> Result<StreamOutlet> {
let channel_count = info.channel_count() as usize;
let nominal_rate = info.nominal_srate();
if chunk_size < 0 || max_buffered < 0 || channel_count >= 0x80000000 || nominal_rate < 0.0 {
return Err(Error::BadArgument);
}
unsafe {
let handle =
lsl_create_outlet(info.native_handle(), chunk_size as i32, max_buffered as i32);
match handle.is_null() {
false => Ok(StreamOutlet {
handle,
channel_count,
nominal_rate,
}),
true => Err(Error::ResourceCreation),
}
}
}
pub fn have_consumers(&self) -> bool {
unsafe { lsl_have_consumers(self.handle) != 0 }
}
pub fn wait_for_consumers(&self, timeout: f64) -> bool {
unsafe { lsl_wait_for_consumers(self.handle, timeout) != 0 }
}
pub fn info(&self) -> Result<StreamInfo> {
unsafe {
let info_handle = lsl_get_info(self.handle);
match info_handle.is_null() {
false => Ok(StreamInfo::from_handle(info_handle)),
true => Err(Error::ResourceCreation),
}
}
}
fn assert_len(&self, len: usize) {
assert_eq!(
len, self.channel_count,
"StreamOutlet received data whose length {} does not \
match the outlet's channel count {}",
len, self.channel_count
);
}
fn safe_push_numeric<T>(
&self,
func: NativePushFunction<T>,
data: &vec::Vec<T>,
timestamp: f64,
pushthrough: bool,
) -> Result<()> {
self.assert_len(data.len());
unsafe {
errcode_to_result(func(self.handle, data.as_ptr(), timestamp, pushthrough as i32))?;
}
Ok(())
}
fn safe_push_blob<T: AsRef<[u8]>>(
&self,
data: &vec::Vec<T>,
timestamp: f64,
pushthrough: bool,
) -> Result<()> {
self.assert_len(data.len());
let ptrs: Vec<_> = data.iter().map(|x| x.as_ref().as_ptr()).collect();
let lens: Vec<_> = data
.iter()
.map(|x| u32::try_from(x.as_ref().len()).unwrap())
.collect();
unsafe {
errcode_to_result(lsl_push_sample_buftp(
self.handle,
ptrs.as_ptr() as *mut *const std::os::raw::c_char,
lens.as_ptr(),
timestamp,
pushthrough as i32,
))?;
}
Ok(())
}
}
pub trait Pushable<T> {
fn push_sample(&self, data: &T) -> Result<()>;
fn push_chunk(&self, data: &vec::Vec<T>) -> Result<()>;
fn push_chunk_stamped(&self, samples: &vec::Vec<T>, stamps: &vec::Vec<f64>) -> Result<()>;
}
impl<T, U: ExPushable<T>> Pushable<T> for U {
fn push_sample(&self, data: &T) -> Result<()> {
self.push_sample_ex(data, 0.0, true)
}
fn push_chunk(&self, data: &vec::Vec<T>) -> Result<()> {
self.push_chunk_ex(data, 0.0, true)
}
fn push_chunk_stamped(&self, samples: &vec::Vec<T>, stamps: &vec::Vec<f64>) -> Result<()> {
self.push_chunk_stamped_ex(samples, stamps, true)
}
}
pub trait ExPushable<T>: HasNominalRate {
fn push_sample_ex(&self, data: &T, timestamp: f64, pushthrough: bool) -> Result<()>;
fn push_chunk_ex(
&self,
samples: &vec::Vec<T>,
timestamp: f64,
pushthrough: bool,
) -> Result<()> {
if !samples.is_empty() {
let mut timestamp = if timestamp == 0.0 {
local_clock()
} else {
timestamp
};
let srate = self.nominal_srate();
let max_k = samples.len() - 1;
if srate != IRREGULAR_RATE {
timestamp -= (max_k as f64) / srate;
}
self.push_sample_ex(&samples[0], timestamp, pushthrough && (samples.len() == 1))?;
for k in 1..=max_k {
self.push_sample_ex(&samples[k], DEDUCED_TIMESTAMP, pushthrough && (k == max_k))?;
}
}
Ok(())
}
fn push_chunk_stamped_ex(
&self,
samples: &vec::Vec<T>,
timestamps: &vec::Vec<f64>,
pushthrough: bool,
) -> Result<()> {
assert_eq!(samples.len(), timestamps.len());
let max_k = samples.len() - 1;
for k in 0..max_k {
self.push_sample_ex(&samples[k], timestamps[k], false)?;
}
if !samples.is_empty() {
self.push_sample_ex(&samples[max_k], timestamps[max_k], pushthrough)?;
}
Ok(())
}
}
impl ExPushable<vec::Vec<f32>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<f32>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_ftp, data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<f64>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<f64>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_dtp, data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<i8>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<i8>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_ctp, data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<i16>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<i16>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_stp, data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<i32>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<i32>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_itp, data, timestamp, pushthrough)
}
}
#[cfg(not(windows))] impl ExPushable<vec::Vec<i64>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<i64>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_numeric(lsl_push_sample_ltp, data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<String>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<String>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_blob(data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<&str>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<&str>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_blob(data, timestamp, pushthrough)
}
}
impl ExPushable<vec::Vec<&[u8]>> for StreamOutlet {
fn push_sample_ex(&self, data: &vec::Vec<&[u8]>, timestamp: f64, pushthrough: bool) -> Result<()> {
self.safe_push_blob(data, timestamp, pushthrough)
}
}
impl Drop for StreamOutlet {
fn drop(&mut self) {
unsafe {
lsl_destroy_outlet(self.handle);
}
}
}
#[doc(hidden)]
pub trait HasNominalRate {
fn nominal_srate(&self) -> f64;
}
impl HasNominalRate for StreamOutlet {
fn nominal_srate(&self) -> f64 {
self.nominal_rate
}
}
pub fn resolve_streams(wait_time: f64) -> Result<vec::Vec<StreamInfo>> {
let mut buffer = [0 as lsl_streaminfo; 1024];
unsafe {
let num_resolved = errcode_to_result(lsl_resolve_all(
buffer.as_mut_ptr(),
buffer.len() as u32,
wait_time,
))? as usize;
let results: Vec<_> = buffer[0..num_resolved]
.iter()
.map(|x| StreamInfo::from_handle(*x))
.collect();
Ok(results)
}
}
pub fn resolve_byprop(
prop: &str,
value: &str,
minimum: i32,
wait_time: f64,
) -> Result<vec::Vec<StreamInfo>> {
let mut buffer = [0 as lsl_streaminfo; 1024];
let prop = ffi::CString::new(prop)?;
let value = ffi::CString::new(value)?;
unsafe {
let num_resolved = errcode_to_result(lsl_resolve_byprop(
buffer.as_mut_ptr(),
buffer.len() as u32,
prop.as_ptr(),
value.as_ptr(),
minimum,
wait_time,
))? as usize;
let results: Vec<_> = buffer[0..num_resolved]
.iter()
.map(|x| StreamInfo::from_handle(*x))
.collect();
Ok(results)
}
}
pub fn resolve_bypred(pred: &str, minimum: i32, wait_time: f64) -> Result<vec::Vec<StreamInfo>> {
let mut buffer = [0 as lsl_streaminfo; 1024];
let pred = ffi::CString::new(pred)?;
unsafe {
let num_resolved = errcode_to_result(lsl_resolve_bypred(
buffer.as_mut_ptr(),
buffer.len() as u32,
pred.as_ptr(),
minimum,
wait_time,
))? as usize;
let results: Vec<_> = buffer[0..num_resolved]
.iter()
.map(|x| StreamInfo::from_handle(*x))
.collect();
Ok(results)
}
}
#[derive(Debug)]
pub struct StreamInlet {
handle: lsl_inlet,
channel_count: usize,
}
impl StreamInlet {
pub fn new(
info: &StreamInfo,
max_buflen: i32,
max_chunklen: i32,
recover: bool,
) -> Result<StreamInlet> {
let channel_count = info.channel_count() as usize;
if max_buflen < 0 || max_chunklen < 0 || channel_count >= 0x80000000 {
return Err(Error::BadArgument);
}
unsafe {
let handle = lsl_create_inlet(
info.native_handle(),
max_buflen,
max_chunklen,
recover as i32,
);
match handle.is_null() {
false => Ok(StreamInlet {
handle,
channel_count,
}),
true => Err(Error::ResourceCreation),
}
}
}
pub fn info(&self, timeout: f64) -> Result<StreamInfo> {
let mut ec = [0 as i32];
unsafe {
let handle = lsl_get_fullinfo(self.handle, timeout, ec.as_mut_ptr());
errcode_to_result(ec[0])?;
match handle.is_null() {
false => Ok(StreamInfo::from_handle(handle)),
true => Err(Error::Unknown),
}
}
}
pub fn open_stream(&self, timeout: f64) -> Result<()> {
let mut ec = [0 as i32];
unsafe {
lsl_open_stream(self.handle, timeout, ec.as_mut_ptr());
errcode_to_result(ec[0])?;
}
Ok(())
}
pub fn close_stream(&self) {
unsafe {
lsl_close_stream(self.handle);
}
}
pub fn time_correction(&self, timeout: f64) -> Result<f64> {
let mut ec = [0 as i32];
unsafe {
let result = lsl_time_correction(self.handle, timeout, ec.as_mut_ptr());
errcode_to_result(ec[0])?;
Ok(result)
}
}
pub fn time_correction_ex(&self, timeout: f64) -> Result<(f64, f64, f64)> {
let mut ec = [0 as i32];
let mut retvals = [0.0, 0.0];
unsafe {
let result = lsl_time_correction_ex(
self.handle,
retvals[0..].as_mut_ptr(),
retvals[1..].as_mut_ptr(),
timeout,
ec.as_mut_ptr(),
);
errcode_to_result(ec[0])?;
Ok((result, retvals[0], retvals[1]))
}
}
pub fn set_postprocessing(&self, options: &[ProcessingOption]) -> Result<()> {
let mut flags: u32 = 0;
for &opt in options {
flags |= opt as u32;
}
unsafe {
let ec = lsl_set_postprocessing(self.handle, flags as u32);
errcode_to_result(ec)?;
Ok(())
}
}
pub fn samples_available(&self) -> u32 {
unsafe { lsl_samples_available(self.handle) as u32 }
}
pub fn was_clock_reset(&self) -> bool {
unsafe { lsl_was_clock_reset(self.handle) != 0 }
}
pub fn smoothing_halftime(&self, value: f32) {
unsafe {
lsl_smoothing_halftime(self.handle, value as f32);
}
}
fn safe_pull_numeric_buf<T: Clone + From<i8>>(
&self,
func: NativePullFunction<T>,
buf: &mut vec::Vec<T>,
timeout: f64,
) -> Result<f64> {
let mut ec = [0 as i32];
if buf.len() != self.channel_count {
buf.resize(self.channel_count, T::from(0));
}
unsafe {
let ts = func(
self.handle,
buf.as_mut_ptr(),
buf.len() as i32,
timeout,
ec.as_mut_ptr(),
);
errcode_to_result(ec[0])?;
Ok(ts)
}
}
fn safe_pull_numeric<T: Clone + From<i8>>(
&self,
func: NativePullFunction<T>,
timeout: f64,
) -> Result<(vec::Vec<T>, f64)> {
let mut result = vec![T::from(0); self.channel_count];
let ts = self.safe_pull_numeric_buf(func, &mut result, timeout)?;
if ts == 0.0 {
result.clear();
}
Ok((result, ts))
}
fn safe_pull_blob_buf<T: Clone>(
&self,
mapper: fn(&[u8]) -> T,
buf: &mut vec::Vec<T>,
timeout: f64,
) -> Result<f64> {
let mut ec = [0 as i32];
let mut ptrs = vec![0 as *mut ::std::os::raw::c_char; self.channel_count];
let mut lens = vec![0 as u32; self.channel_count];
unsafe {
let ts = lsl_pull_sample_buf(
self.handle,
ptrs.as_mut_ptr(),
lens.as_mut_ptr(),
ptrs.len() as i32,
timeout,
ec.as_mut_ptr(),
);
errcode_to_result(ec[0])?;
if buf.len() != self.channel_count {
buf.resize(self.channel_count, mapper(&[0 as u8; 0]));
}
if ts != 0.0 {
for k in 0..ptrs.len() {
let slice = std::slice::from_raw_parts(ptrs[k] as *const u8, lens[k] as usize);
buf[k] = mapper(slice);
lsl_destroy_string(ptrs[k]);
}
}
Ok(ts)
}
}
fn safe_pull_blob<T: Clone>(
&self,
mapper: fn(&[u8]) -> T,
timeout: f64,
) -> Result<(vec::Vec<T>, f64)> {
let mut ec = [0 as i32];
let mut ptrs = vec![0 as *mut ::std::os::raw::c_char; self.channel_count];
let mut lens = vec![0 as u32; self.channel_count];
unsafe {
let ts = lsl_pull_sample_buf(
self.handle,
ptrs.as_mut_ptr(),
lens.as_mut_ptr(),
ptrs.len() as i32,
timeout,
ec.as_mut_ptr(),
);
errcode_to_result(ec[0])?;
let mut sample = vec::Vec::<T>::new();
if ts != 0.0 {
for k in 0..ptrs.len() {
let slice = std::slice::from_raw_parts(ptrs[k] as *const u8, lens[k] as usize);
sample.push(mapper(slice));
lsl_destroy_string(ptrs[k]);
}
}
Ok((sample, ts))
}
}
}
impl Drop for StreamInlet {
fn drop(&mut self) {
unsafe {
lsl_destroy_inlet(self.handle);
}
}
}
pub trait Pullable<T> {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<T>, f64)>;
fn pull_sample_buf(&self, buf: &mut vec::Vec<T>, timeout: f64) -> Result<f64>;
fn pull_chunk(&self) -> Result<(vec::Vec<vec::Vec<T>>, vec::Vec<f64>)> {
let mut samples: vec::Vec<vec::Vec<T>> = vec![];
let mut stamps: vec::Vec<f64> = vec![];
loop {
let (sample, stamp) = self.pull_sample(0.0)?;
if stamp != 0.0 {
samples.push(sample);
stamps.push(stamp);
} else {
break; }
}
Ok((samples, stamps))
}
}
impl Pullable<f32> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<f32>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_f, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<f32>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_f, buf, timeout)
}
}
impl Pullable<f64> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<f64>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_d, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<f64>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_d, buf, timeout)
}
}
#[cfg(not(windows))] impl Pullable<i64> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<i64>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_l, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<i64>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_l, buf, timeout)
}
}
impl Pullable<i32> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<i32>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_i, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<i32>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_i, buf, timeout)
}
}
impl Pullable<i16> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<i16>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_s, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<i16>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_s, buf, timeout)
}
}
impl Pullable<i8> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<i8>, f64)> {
self.safe_pull_numeric(lsl_pull_sample_c, timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<i8>, timeout: f64) -> Result<f64> {
self.safe_pull_numeric_buf(lsl_pull_sample_c, buf, timeout)
}
}
impl Pullable<String> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<String>, f64)> {
self.safe_pull_blob(|x| String::from_utf8_lossy(x).into_owned(), timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<String>, timeout: f64) -> Result<f64> {
self.safe_pull_blob_buf(|x| String::from_utf8_lossy(x).into_owned(), buf, timeout)
}
}
impl Pullable<vec::Vec<u8>> for StreamInlet {
fn pull_sample(&self, timeout: f64) -> Result<(vec::Vec<vec::Vec<u8>>, f64)> {
self.safe_pull_blob(|x| x.to_vec(), timeout)
}
fn pull_sample_buf(&self, buf: &mut vec::Vec<vec::Vec<u8>>, timeout: f64) -> Result<f64> {
self.safe_pull_blob_buf(|x| x.to_vec(), buf, timeout)
}
}
#[derive(Clone, Debug)]
pub struct XMLElement {
cursor: lsl_xml_ptr,
doc: rc::Rc<StreamInfoHandle>,
}
impl XMLElement {
pub fn first_child(&self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_first_child(self.cursor),
doc: self.doc.clone(),
}
}
}
pub fn last_child(&self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_last_child(self.cursor),
doc: self.doc.clone(),
}
}
}
pub fn next_sibling(&self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_next_sibling(self.cursor),
doc: self.doc.clone(),
}
}
}
pub fn previous_sibling(&self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_previous_sibling(self.cursor),
doc: self.doc.clone(),
}
}
}
pub fn parent(&self) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_parent(self.cursor),
doc: self.doc.clone(),
}
}
}
pub fn child(&self, name: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
XMLElement {
cursor: lsl_child(self.cursor, name.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn next_sibling_named(&self, name: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
XMLElement {
cursor: lsl_next_sibling_n(self.cursor, name.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn previous_sibling_named(&self, name: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
XMLElement {
cursor: lsl_previous_sibling_n(self.cursor, name.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn empty(&self) -> bool {
unsafe { lsl_empty(self.cursor) != 0 }
}
pub fn is_text(&self) -> bool {
unsafe { lsl_is_text(self.cursor) != 0 }
}
pub fn name(&self) -> String {
unsafe { make_string(lsl_name(self.cursor)) }
}
pub fn value(&self) -> String {
unsafe { make_string(lsl_value(self.cursor)) }
}
pub fn child_value(&self) -> String {
unsafe { make_string(lsl_child_value(self.cursor)) }
}
pub fn child_value_named(&self, name: &str) -> String {
unsafe {
let name = make_cstring(name);
make_string(lsl_child_value_n(self.cursor, name.as_ptr()))
}
}
pub fn append_child_value(&mut self, name: &str, value: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
let value = make_cstring(value);
XMLElement {
cursor: lsl_append_child_value(self.cursor, name.as_ptr(), value.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn prepend_child_value(&mut self, name: &str, value: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
let value = make_cstring(value);
XMLElement {
cursor: lsl_prepend_child_value(self.cursor, name.as_ptr(), value.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn set_child_value(&mut self, name: &str, value: &str) -> bool {
unsafe {
let name = make_cstring(name);
let value = make_cstring(value);
lsl_set_child_value(self.cursor, name.as_ptr(), value.as_ptr()) != 0
}
}
pub fn set_name(&mut self, rhs: &str) -> bool {
unsafe {
let rhs = make_cstring(rhs);
lsl_set_name(self.cursor, rhs.as_ptr()) != 0
}
}
pub fn set_value(&mut self, rhs: &str) -> bool {
unsafe {
let rhs = make_cstring(rhs);
lsl_set_value(self.cursor, rhs.as_ptr()) != 0
}
}
pub fn append_child(&mut self, name: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
XMLElement {
cursor: lsl_append_child(self.cursor, name.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn prepend_child(&mut self, name: &str) -> XMLElement {
unsafe {
let name = make_cstring(name);
XMLElement {
cursor: lsl_prepend_child(self.cursor, name.as_ptr()),
doc: self.doc.clone(),
}
}
}
pub fn append_copy(&mut self, e: XMLElement) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_append_copy(self.cursor, e.cursor),
doc: self.doc.clone(),
}
}
}
pub fn prepend_copy(&mut self, e: XMLElement) -> XMLElement {
unsafe {
XMLElement {
cursor: lsl_prepend_copy(self.cursor, e.cursor),
doc: self.doc.clone(),
}
}
}
pub fn remove_child(&mut self, e: XMLElement) {
unsafe {
lsl_remove_child(self.cursor, e.cursor);
}
}
pub fn remove_child_named(&mut self, name: &str) {
unsafe {
let name = make_cstring(name);
lsl_remove_child_n(self.cursor, name.as_ptr());
}
}
pub fn is_valid(&self) -> bool {
!self.cursor.is_null()
}
}
impl fmt::Display for XMLElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_valid() {
write!(
f,
"(name={}, value={}, parent name={})",
self.name(),
self.value(),
self.parent().name()
)
} else {
write!(f, "(not valid)")
}
}
}
#[derive(Debug)]
pub struct ContinuousResolver {
handle: lsl_continuous_resolver,
}
impl ContinuousResolver {
pub fn new(forget_after: f64) -> Result<ContinuousResolver> {
if forget_after <= 0.0 {
return Err(Error::BadArgument);
}
unsafe {
let handle = lsl_create_continuous_resolver(forget_after);
match handle.is_null() {
false => Ok(ContinuousResolver { handle }),
true => Err(Error::ResourceCreation),
}
}
}
pub fn new_with_prop(prop: &str, value: &str, forget_after: f64) -> Result<ContinuousResolver> {
if forget_after <= 0.0 {
return Err(Error::BadArgument);
}
let prop = ffi::CString::new(prop)?;
let value = ffi::CString::new(value)?;
unsafe {
let handle =
lsl_create_continuous_resolver_byprop(prop.as_ptr(), value.as_ptr(), forget_after);
match handle.is_null() {
false => Ok(ContinuousResolver { handle }),
true => Err(Error::ResourceCreation),
}
}
}
pub fn new_with_pred(pred: &str, forget_after: f64) -> Result<ContinuousResolver> {
if forget_after <= 0.0 {
return Err(Error::BadArgument);
}
let pred = ffi::CString::new(pred)?;
unsafe {
let handle = lsl_create_continuous_resolver_bypred(pred.as_ptr(), forget_after);
match handle.is_null() {
false => Ok(ContinuousResolver { handle }),
true => Err(Error::ResourceCreation),
}
}
}
pub fn results(&self) -> Result<vec::Vec<StreamInfo>> {
let mut buffer = [0 as lsl_streaminfo; 1024];
unsafe {
let num_resolved = errcode_to_result(lsl_resolver_results(
self.handle,
buffer.as_mut_ptr(),
buffer.len() as u32,
))? as usize;
let results: Vec<_> = buffer[0..num_resolved]
.iter()
.map(|x| StreamInfo::from_handle(*x))
.collect();
Ok(results)
}
}
}
impl Drop for ContinuousResolver {
fn drop(&mut self) {
unsafe {
lsl_destroy_continuous_resolver(self.handle);
}
}
}
#[derive(Debug)]
struct StreamInfoHandle { handle: lsl_streaminfo }
impl Drop for StreamInfoHandle {
fn drop(&mut self) {
unsafe {
lsl_destroy_streaminfo(self.handle);
}
}
}
type NativePushFunction<T> = unsafe extern "C" fn(lsl_outlet, *const T, f64, i32) -> i32;
type NativePullFunction<T> = unsafe extern "C" fn(lsl_inlet, *mut T, i32, f64, *mut i32) -> f64;
impl ChannelFormat {
pub fn to_native(&self) -> lsl_channel_format_t {
match self {
ChannelFormat::Float32 => lsl_channel_format_t_cft_float32,
ChannelFormat::Double64 => lsl_channel_format_t_cft_double64,
ChannelFormat::String => lsl_channel_format_t_cft_string,
ChannelFormat::Int32 => lsl_channel_format_t_cft_int32,
ChannelFormat::Int16 => lsl_channel_format_t_cft_int16,
ChannelFormat::Int8 => lsl_channel_format_t_cft_int8,
ChannelFormat::Int64 => lsl_channel_format_t_cft_int64,
ChannelFormat::Undefined => lsl_channel_format_t_cft_undefined,
}
}
pub fn from_native(fmt: lsl_channel_format_t) -> ChannelFormat {
#[allow(non_upper_case_globals)]
match fmt {
lsl_channel_format_t_cft_float32 => ChannelFormat::Float32,
lsl_channel_format_t_cft_double64 => ChannelFormat::Double64,
lsl_channel_format_t_cft_string => ChannelFormat::String,
lsl_channel_format_t_cft_int32 => ChannelFormat::Int32,
lsl_channel_format_t_cft_int16 => ChannelFormat::Int16,
lsl_channel_format_t_cft_int8 => ChannelFormat::Int8,
lsl_channel_format_t_cft_int64 => ChannelFormat::Int64,
_ => ChannelFormat::Undefined,
}
}
}
impl fmt::Display for ChannelFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ChannelFormat::Float32 => "float32",
ChannelFormat::Double64 => "double64",
ChannelFormat::String => "string",
ChannelFormat::Int32 => "int32",
ChannelFormat::Int16 => "int16",
ChannelFormat::Int8 => "int8",
ChannelFormat::Int64 => "int64",
ChannelFormat::Undefined => "undefined",
};
write!(f, "{}", s)
}
}
impl From<ffi::NulError> for Error {
fn from(_: ffi::NulError) -> Error {
Error::BadArgument
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = match self {
Error::Timeout => "operation timed out",
Error::StreamLost => "stream has been lost",
Error::BadArgument => "incorrectly specified argument.",
Error::ResourceCreation => "resource creation failed.",
Error::Internal => "internal error in native library",
Error::Unknown => "unknown error",
};
write!(f, "{}", msg)
}
}
impl std::error::Error for Error {}
fn make_cstring(s: &str) -> ffi::CString {
ffi::CString::new(s).expect(
"Embedded zero bytes are invalid in strings passed to liblsl.",
)
}
unsafe fn make_string(s: *const ::std::os::raw::c_char) -> String {
assert!(
!s.is_null(),
"Attemt to create a string from a NULL pointer."
);
ffi::CStr::from_ptr(s).to_string_lossy().into_owned()
}
fn errcode_to_result(ec: i32) -> Result<i32> {
if ec < 0 {
#[allow(non_upper_case_globals)]
match ec {
lsl_error_code_t_lsl_timeout_error => Err(Error::Timeout),
lsl_error_code_t_lsl_argument_error => Err(Error::BadArgument),
lsl_error_code_t_lsl_lost_error => Err(Error::StreamLost),
lsl_error_code_t_lsl_internal_error => Err(Error::Internal),
_ => Err(Error::Unknown),
}
} else {
Ok(ec)
}
}