File lsl_cpp.h
File List > include > lsl_cpp.h
Go to the documentation of this file
#ifndef LSL_CPP_H
#define LSL_CPP_H
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
extern "C" {
#include "lsl_c.h"
}
namespace lsl {
int32_t check_error(int32_t ec);
const double IRREGULAR_RATE = 0.0;
const double DEDUCED_TIMESTAMP = -1.0;
const double FOREVER = 32000000.0;
enum channel_format_t {
cf_float32 = 1,
cf_double64 = 2,
cf_string = 3,
cf_int32 = 4,
cf_int16 = 5,
cf_int8 = 6,
cf_int64 = 7,
cf_undefined = 0
};
enum processing_options_t {
post_none = 0,
post_clocksync = 1,
post_dejitter = 2,
post_monotonize = 4,
post_threadsafe = 8,
post_ALL = 1 | 2 | 4 | 8
};
inline int32_t protocol_version() { return lsl_protocol_version(); }
inline int32_t library_version() { return lsl_library_version(); }
inline const char *library_info() { return lsl_library_info(); }
inline const char *base_version() { return lsl_base_version(); }
inline const char *security_version() { return lsl_security_version(); }
inline const char *full_version() { return lsl_full_version(); }
inline bool is_secure_build() { return lsl_is_secure_build() != 0; }
inline bool local_security_enabled() { return lsl_local_security_enabled() != 0; }
inline bool security_is_locked() { return lsl_security_is_locked() != 0; }
inline bool security_unlock(const char *passphrase) { return lsl_security_unlock(passphrase) != 0; }
inline const char *local_security_fingerprint() { return lsl_local_security_fingerprint(); }
inline double local_clock() { return lsl_local_clock(); }
class xml_element;
class stream_info {
public:
stream_info(const std::string &name, const std::string &type, int32_t channel_count = 1,
double nominal_srate = IRREGULAR_RATE, channel_format_t channel_format = cf_float32,
const std::string &source_id = std::string())
: obj(lsl_create_streaminfo((name.c_str()), (type.c_str()), channel_count, nominal_srate,
(lsl_channel_format_t)channel_format, (source_id.c_str())), &lsl_destroy_streaminfo) {
if (obj == nullptr) throw std::invalid_argument(lsl_last_error());
}
stream_info(): stream_info("untitled", "", 0, 0, cf_undefined, ""){}
stream_info(const stream_info &) noexcept = default;
stream_info(lsl_streaminfo handle) : obj(handle, &lsl_destroy_streaminfo) {}
stream_info clone() { return stream_info(lsl_copy_streaminfo(obj.get())); }
// ========================
// === Core Information ===
// ========================
// (these fields are assigned at construction)
std::string name() const { return lsl_get_name(obj.get()); }
std::string type() const { return lsl_get_type(obj.get()); }
int32_t channel_count() const { return lsl_get_channel_count(obj.get()); }
double nominal_srate() const { return lsl_get_nominal_srate(obj.get()); }
channel_format_t channel_format() const {
return static_cast<channel_format_t>(lsl_get_channel_format(obj.get()));
}
std::string source_id() const { return lsl_get_source_id(obj.get()); }
// ======================================
// === Additional Hosting Information ===
// ======================================
// (these fields are implicitly assigned once bound to an outlet/inlet)
int32_t version() const { return lsl_get_version(obj.get()); }
double created_at() const { return lsl_get_created_at(obj.get()); }
std::string uid() const { return lsl_get_uid(obj.get()); }
std::string session_id() const { return lsl_get_session_id(obj.get()); }
std::string hostname() const { return lsl_get_hostname(obj.get()); }
// ============================
// === Security Information ===
// ============================
bool security_enabled() const { return lsl_get_security_enabled(obj.get()) == 1; }
std::string security_fingerprint() const { return lsl_get_security_fingerprint(obj.get()); }
// ========================
// === Data Description ===
// ========================
xml_element desc();
bool matches_query(const char *query) const {
return lsl_stream_info_matches_query(obj.get(), query);
}
// ===============================
// === Miscellaneous Functions ===
// ===============================
std::string as_xml() const {
char *tmp = lsl_get_xml(obj.get());
std::string result(tmp);
lsl_destroy_string(tmp);
return result;
}
int32_t channel_bytes() const { return lsl_get_channel_bytes(obj.get()); }
int32_t sample_bytes() const { return lsl_get_sample_bytes(obj.get()); }
std::shared_ptr<lsl_streaminfo_struct_> handle() const { return obj; }
stream_info &operator=(const stream_info &rhs) {
if (this != &rhs) obj = stream_info(rhs).handle();
return *this;
}
stream_info(stream_info &&rhs) noexcept = default;
stream_info &operator=(stream_info &&rhs) noexcept = default;
static stream_info from_xml(const std::string &xml) {
return stream_info(lsl_streaminfo_from_xml(xml.c_str()));
}
private:
std::shared_ptr<lsl_streaminfo_struct_> obj;
};
// =======================
// ==== Stream Outlet ====
// =======================
class stream_outlet {
public:
stream_outlet(const stream_info &info, int32_t chunk_size = 0, int32_t max_buffered = 360,
lsl_transport_options_t flags = transp_default)
: channel_count(info.channel_count()), sample_rate(info.nominal_srate()),
obj(lsl_create_outlet_ex(info.handle().get(), chunk_size, max_buffered, flags),
&lsl_destroy_outlet) {}
// ========================================
// === Pushing a sample into the outlet ===
// ========================================
template <class T, int32_t N>
void push_sample(const T data[N], double timestamp = 0.0, bool pushthrough = true) {
check_numchan(N);
push_sample(&data[0], timestamp, pushthrough);
}
template<typename T>
void push_sample(
const std::vector<T> &data, double timestamp = 0.0, bool pushthrough = true) {
check_numchan(data.size());
push_sample(data.data(), timestamp, pushthrough);
}
void push_sample(const float *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_ftp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const double *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_dtp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const int64_t *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_ltp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const int32_t *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_itp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const int16_t *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_stp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const char *data, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_ctp(obj.get(), (data), timestamp, pushthrough);
}
void push_sample(const std::string *data, double timestamp = 0.0, bool pushthrough = true) {
std::vector<uint32_t> lengths(channel_count);
std::vector<const char *> pointers(channel_count);
for (int32_t k = 0; k < channel_count; k++) {
pointers[k] = data[k].c_str();
lengths[k] = (uint32_t)data[k].size();
}
lsl_push_sample_buftp(obj.get(), pointers.data(), lengths.data(), timestamp, pushthrough);
}
template <class T>
void push_numeric_struct(const T &sample, double timestamp = 0.0, bool pushthrough = true) {
if (info().sample_bytes() != sizeof(T))
throw std::runtime_error(
"Provided object size does not match the stream's sample size.");
push_numeric_raw((void *)&sample, timestamp, pushthrough);
}
void push_numeric_raw(const void *sample, double timestamp = 0.0, bool pushthrough = true) {
lsl_push_sample_vtp(obj.get(), (sample), timestamp, pushthrough);
}
// ===================================================
// === Pushing an chunk of samples into the outlet ===
// ===================================================
template <class T>
void push_chunk(
const std::vector<T> &samples, double timestamp = 0.0, bool pushthrough = true) {
if (!samples.empty()) {
if (timestamp == 0.0) timestamp = local_clock();
if (sample_rate != IRREGULAR_RATE)
timestamp = timestamp - (samples.size() - 1) / sample_rate;
push_sample(samples[0], timestamp, pushthrough && samples.size() == 1);
for (std::size_t k = 1; k < samples.size(); k++)
push_sample(samples[k], DEDUCED_TIMESTAMP, pushthrough && k == samples.size() - 1);
}
}
template <class T>
void push_chunk(const std::vector<T> &samples, const std::vector<double> ×tamps,
bool pushthrough = true) {
for (unsigned k = 0; k < samples.size() - 1; k++)
push_sample(samples[k], timestamps[k], false);
if (!samples.empty()) push_sample(samples.back(), timestamps.back(), pushthrough);
}
template <class T>
void push_chunk_numeric_structs(
const std::vector<T> &samples, double timestamp = 0.0, bool pushthrough = true) {
if (!samples.empty()) {
if (timestamp == 0.0) timestamp = local_clock();
if (sample_rate != IRREGULAR_RATE)
timestamp = timestamp - (samples.size() - 1) / sample_rate;
push_numeric_struct(samples[0], timestamp, pushthrough && samples.size() == 1);
for (std::size_t k = 1; k < samples.size(); k++)
push_numeric_struct(
samples[k], DEDUCED_TIMESTAMP, pushthrough && k == samples.size() - 1);
}
}
template <class T>
void push_chunk_numeric_structs(const std::vector<T> &samples,
const std::vector<double> ×tamps, bool pushthrough = true) {
for (unsigned k = 0; k < samples.size() - 1; k++)
push_numeric_struct(samples[k], timestamps[k], false);
if (!samples.empty()) push_numeric_struct(samples.back(), timestamps.back(), pushthrough);
}
template<typename T>
void push_chunk_multiplexed(
const std::vector<T> &buffer, double timestamp = 0.0, bool pushthrough = true) {
if (!buffer.empty())
push_chunk_multiplexed(
buffer.data(), static_cast<unsigned long>(buffer.size()), timestamp, pushthrough);
}
template<typename T>
void push_chunk_multiplexed(const std::vector<T> &buffer,
const std::vector<double> ×tamps, bool pushthrough = true) {
if (!buffer.empty() && !timestamps.empty())
push_chunk_multiplexed(
buffer.data(), static_cast<unsigned long>(buffer.size()), timestamps.data(), pushthrough);
}
void push_chunk_multiplexed(const float *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_ftp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const double *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_dtp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const int64_t *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_ltp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const int32_t *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_itp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const int16_t *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_stp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const char *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
lsl_push_chunk_ctp(obj.get(), buffer, static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
void push_chunk_multiplexed(const std::string *buffer, std::size_t buffer_elements,
double timestamp = 0.0, bool pushthrough = true) {
if (buffer_elements) {
std::vector<uint32_t> lengths(buffer_elements);
std::vector<const char *> pointers(buffer_elements);
for (std::size_t k = 0; k < buffer_elements; k++) {
pointers[k] = buffer[k].c_str();
lengths[k] = (uint32_t)buffer[k].size();
}
lsl_push_chunk_buftp(obj.get(), pointers.data(), lengths.data(),
static_cast<unsigned long>(buffer_elements), timestamp, pushthrough);
}
}
void push_chunk_multiplexed(const float *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_ftnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const double *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_dtnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const int64_t *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_ltnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const int32_t *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_itnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const int16_t *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_stnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const char *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
lsl_push_chunk_ctnp(obj.get(), data_buffer, static_cast<unsigned long>(data_buffer_elements),
(timestamp_buffer), pushthrough);
}
void push_chunk_multiplexed(const std::string *data_buffer, const double *timestamp_buffer,
std::size_t data_buffer_elements, bool pushthrough = true) {
if (data_buffer_elements) {
std::vector<uint32_t> lengths(data_buffer_elements);
std::vector<const char *> pointers(data_buffer_elements);
for (std::size_t k = 0; k < data_buffer_elements; k++) {
pointers[k] = data_buffer[k].c_str();
lengths[k] = (uint32_t)data_buffer[k].size();
}
lsl_push_chunk_buftnp(obj.get(), pointers.data(), lengths.data(),
static_cast<unsigned long>(data_buffer_elements), timestamp_buffer, pushthrough);
}
}
// ===============================
// === Miscellaneous Functions ===
// ===============================
bool have_consumers() { return lsl_have_consumers(obj.get()) != 0; }
bool wait_for_consumers(double timeout) { return lsl_wait_for_consumers(obj.get(), timeout) != 0; }
stream_info info() const { return stream_info(lsl_get_info(obj.get())); }
std::shared_ptr<lsl_outlet_struct_> handle() { return obj; }
~stream_outlet() = default;
stream_outlet(stream_outlet &&res) noexcept = default;
stream_outlet &operator=(stream_outlet &&rhs) noexcept = default;
private:
// The outlet is a non-copyable object.
stream_outlet(const stream_outlet &rhs);
stream_outlet &operator=(const stream_outlet &rhs);
void check_numchan(std::size_t N) const {
if (N != static_cast<std::size_t>(channel_count))
throw std::runtime_error("Provided element count (" + std::to_string(N) +
") does not match the stream's channel count (" +
std::to_string(channel_count) + '.');
}
int32_t channel_count;
double sample_rate;
std::shared_ptr<lsl_outlet_struct_> obj;
};
// ===========================
// ==== Resolve Functions ====
// ===========================
inline std::vector<stream_info> resolve_streams(double wait_time = 1.0) {
lsl_streaminfo buffer[1024];
int nres = check_error(lsl_resolve_all(buffer, sizeof(buffer), wait_time));
return std::vector<stream_info>(&buffer[0], &buffer[nres]);
}
inline std::vector<stream_info> resolve_stream(const std::string &prop, const std::string &value,
int32_t minimum = 1, double timeout = FOREVER) {
lsl_streaminfo buffer[1024];
int nres = check_error(
lsl_resolve_byprop(buffer, sizeof(buffer), prop.c_str(), value.c_str(), minimum, timeout));
return std::vector<stream_info>(&buffer[0], &buffer[nres]);
}
inline std::vector<stream_info> resolve_stream(
const std::string &pred, int32_t minimum = 1, double timeout = FOREVER) {
lsl_streaminfo buffer[1024];
int nres =
check_error(lsl_resolve_bypred(buffer, sizeof(buffer), pred.c_str(), minimum, timeout));
return std::vector<stream_info>(&buffer[0], &buffer[nres]);
}
// ======================
// ==== Stream Inlet ====
// ======================
class stream_inlet {
public:
stream_inlet(const stream_info &info, int32_t max_buflen = 360, int32_t max_chunklen = 0,
bool recover = true, lsl_transport_options_t flags = transp_default)
: channel_count(info.channel_count()),
obj(lsl_create_inlet_ex(info.handle().get(), max_buflen, max_chunklen, recover, flags),
&lsl_destroy_inlet) {}
std::shared_ptr<lsl_inlet_struct_> handle() { return obj; }
stream_inlet(stream_inlet &&rhs) noexcept = default;
stream_inlet &operator=(stream_inlet &&rhs) noexcept= default;
stream_info info(double timeout = FOREVER) {
int32_t ec = 0;
lsl_streaminfo res = lsl_get_fullinfo(obj.get(), timeout, &ec);
check_error(ec);
return stream_info(res);
}
void open_stream(double timeout = FOREVER) {
int32_t ec = 0;
lsl_open_stream(obj.get(), timeout, &ec);
check_error(ec);
}
void close_stream() { lsl_close_stream(obj.get()); }
double time_correction(double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_time_correction(obj.get(), timeout, &ec);
check_error(ec);
return res;
}
double time_correction(double *remote_time, double *uncertainty, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_time_correction_ex(obj.get(), remote_time, uncertainty, timeout, &ec);
check_error(ec);
return res;
}
void set_postprocessing(uint32_t flags = post_ALL) {
check_error(lsl_set_postprocessing(obj.get(), flags));
}
// =======================================
// === Pulling a sample from the inlet ===
// =======================================
template <class T, int N> double pull_sample(T sample[N], double timeout = FOREVER) {
return pull_sample(&sample[0], N, timeout);
}
double pull_sample(std::vector<float> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<double> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<int64_t> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<int32_t> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<int16_t> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<char> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(std::vector<std::string> &sample, double timeout = FOREVER) {
sample.resize(channel_count);
return pull_sample(&sample[0], (int32_t)sample.size(), timeout);
}
double pull_sample(float *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_f(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(double *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_d(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(int64_t *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_l(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(int32_t *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_i(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(int16_t *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_s(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(char *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_c(obj.get(), buffer, buffer_elements, timeout, &ec);
check_error(ec);
return res;
}
double pull_sample(std::string *buffer, int32_t buffer_elements, double timeout = FOREVER) {
int32_t ec = 0;
if (buffer_elements) {
std::vector<char *> result_strings(buffer_elements);
std::vector<uint32_t> result_lengths(buffer_elements);
double res = lsl_pull_sample_buf(
obj.get(), result_strings.data(), result_lengths.data(), buffer_elements, timeout, &ec);
check_error(ec);
for (int32_t k = 0; k < buffer_elements; k++) {
buffer[k].assign(result_strings[k], result_lengths[k]);
lsl_destroy_string(result_strings[k]);
}
return res;
} else
throw std::runtime_error(
"Provided element count does not match the stream's channel count.");
}
template <class T> double pull_numeric_struct(T &sample, double timeout = FOREVER) {
return pull_numeric_raw((void *)&sample, sizeof(T), timeout);
}
double pull_numeric_raw(void *sample, int32_t buffer_bytes, double timeout = FOREVER) {
int32_t ec = 0;
double res = lsl_pull_sample_v(obj.get(), sample, buffer_bytes, timeout, &ec);
check_error(ec);
return res;
}
// =================================================
// === Pulling a chunk of samples from the inlet ===
// =================================================
template <class T>
bool pull_chunk(std::vector<std::vector<T>> &chunk, std::vector<double> ×tamps) {
std::vector<T> sample;
chunk.clear();
timestamps.clear();
while (double ts = pull_sample(sample, 0.0)) {
chunk.push_back(sample);
timestamps.push_back(ts);
}
return !chunk.empty();
}
template <class T> double pull_chunk(std::vector<std::vector<T>> &chunk) {
double timestamp = 0.0;
std::vector<T> sample;
chunk.clear();
while (double ts = pull_sample(sample, 0.0)) {
chunk.push_back(sample);
timestamp = ts;
}
return timestamp;
}
template <class T> std::vector<std::vector<T>> pull_chunk() {
std::vector<std::vector<T>> result;
std::vector<T> sample;
while (pull_sample(sample, 0.0)) result.push_back(sample);
return result;
}
std::size_t pull_chunk_multiplexed(float *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_f(obj.get(), data_buffer, timestamp_buffer,
(unsigned long)data_buffer_elements, (unsigned long)timestamp_buffer_elements, timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(double *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_d(obj.get(), data_buffer, timestamp_buffer,
(unsigned long)data_buffer_elements, (unsigned long)timestamp_buffer_elements, timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(int64_t *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_l(obj.get(), data_buffer, timestamp_buffer,
(unsigned long)data_buffer_elements, (unsigned long)timestamp_buffer_elements, timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(int32_t *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_i(obj.get(), data_buffer, timestamp_buffer,
(unsigned long)data_buffer_elements, (unsigned long)timestamp_buffer_elements, timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(int16_t *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_s(obj.get(), data_buffer, timestamp_buffer,
(unsigned long)data_buffer_elements, (unsigned long)timestamp_buffer_elements, timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(char *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
std::size_t res = lsl_pull_chunk_c(obj.get(), data_buffer, timestamp_buffer,
static_cast<unsigned long>(data_buffer_elements), static_cast<unsigned long>(timestamp_buffer_elements), timeout,
&ec);
check_error(ec);
return res;
}
std::size_t pull_chunk_multiplexed(std::string *data_buffer, double *timestamp_buffer,
std::size_t data_buffer_elements, std::size_t timestamp_buffer_elements,
double timeout = 0.0) {
int32_t ec = 0;
if (data_buffer_elements) {
std::vector<char *> result_strings(data_buffer_elements);
std::vector<uint32_t> result_lengths(data_buffer_elements);
std::size_t num = lsl_pull_chunk_buf(obj.get(), result_strings.data(), result_lengths.data(),
timestamp_buffer, static_cast<unsigned long>(data_buffer_elements),
static_cast<unsigned long>(timestamp_buffer_elements), timeout, &ec);
check_error(ec);
for (std::size_t k = 0; k < num; k++) {
data_buffer[k].assign(result_strings[k], result_lengths[k]);
lsl_destroy_string(result_strings[k]);
}
return num;
};
return 0;
}
template <typename T>
bool pull_chunk_multiplexed(std::vector<T> &chunk, std::vector<double> *timestamps = nullptr,
double timeout = 0.0, bool append = false) {
if (!append) {
chunk.clear();
if (timestamps) timestamps->clear();
}
std::vector<T> sample;
double ts;
if ((ts = pull_sample(sample, timeout)) == 0.0) return false;
chunk.insert(chunk.end(), sample.begin(), sample.end());
if (timestamps) timestamps->push_back(ts);
const auto target = samples_available();
chunk.reserve(chunk.size() + target * this->channel_count);
if (timestamps) timestamps->reserve(timestamps->size() + target);
while ((ts = pull_sample(sample, 0.0)) != 0.0) {
#if LSL_CPP11
chunk.insert(chunk.end(), std::make_move_iterator(sample.begin()),
std::make_move_iterator(sample.end()));
#else
chunk.insert(chunk.end(), sample.begin(), sample.end());
#endif
if (timestamps) timestamps->push_back(ts);
}
return true;
}
template <class T>
bool pull_chunk_numeric_structs(std::vector<T> &chunk, std::vector<double> ×tamps) {
T sample;
chunk.clear();
timestamps.clear();
while (double ts = pull_numeric_struct(sample, 0.0)) {
chunk.push_back(sample);
timestamps.push_back(ts);
}
return !chunk.empty();
}
template <class T> double pull_chunk_numeric_structs(std::vector<T> &chunk) {
double timestamp = 0.0;
T sample;
chunk.clear();
while (double ts = pull_numeric_struct(sample, 0.0)) {
chunk.push_back(sample);
timestamp = ts;
}
return timestamp;
}
template <class T> std::vector<T> pull_chunk_numeric_structs() {
std::vector<T> result;
T sample;
while (pull_numeric_struct(sample, 0.0)) result.push_back(sample);
return result;
}
std::size_t samples_available() { return lsl_samples_available(obj.get()); }
uint32_t flush() noexcept { return lsl_inlet_flush(obj.get()); }
bool was_clock_reset() { return lsl_was_clock_reset(obj.get()) != 0; }
void smoothing_halftime(float value) { check_error(lsl_smoothing_halftime(obj.get(), value)); }
int get_channel_count() const { return channel_count; }
private:
// The inlet is a non-copyable object.
stream_inlet(const stream_inlet &rhs);
stream_inlet &operator=(const stream_inlet &rhs);
int32_t channel_count;
std::shared_ptr<lsl_inlet_struct_> obj;
};
// =====================
// ==== XML Element ====
// =====================
class xml_element {
public:
xml_element(lsl_xml_ptr obj = 0) : obj(obj) {}
// === Tree Navigation ===
xml_element first_child() const { return lsl_first_child(obj); }
xml_element last_child() const { return lsl_last_child(obj); }
xml_element next_sibling() const { return lsl_next_sibling(obj); }
xml_element previous_sibling() const { return lsl_previous_sibling(obj); }
xml_element parent() const { return lsl_parent(obj); }
// === Tree Navigation by Name ===
xml_element child(const std::string &name) const { return lsl_child(obj, (name.c_str())); }
xml_element next_sibling(const std::string &name) const {
return lsl_next_sibling_n(obj, (name.c_str()));
}
xml_element previous_sibling(const std::string &name) const {
return lsl_previous_sibling_n(obj, (name.c_str()));
}
// === Content Queries ===
bool empty() const { return lsl_empty(obj) != 0; }
bool is_text() const { return lsl_is_text(obj) != 0; }
const char *name() const { return lsl_name(obj); }
const char *value() const { return lsl_value(obj); }
const char *child_value() const { return lsl_child_value(obj); }
const char *child_value(const std::string &name) const {
return lsl_child_value_n(obj, (name.c_str()));
}
// === Modification ===
xml_element append_child_value(const std::string &name, const std::string &value) {
return lsl_append_child_value(obj, (name.c_str()), (value.c_str()));
}
xml_element prepend_child_value(const std::string &name, const std::string &value) {
return lsl_prepend_child_value(obj, (name.c_str()), (value.c_str()));
}
bool set_child_value(const std::string &name, const std::string &value) {
return lsl_set_child_value(obj, (name.c_str()), (value.c_str())) != 0;
}
bool set_name(const std::string &rhs) { return lsl_set_name(obj, rhs.c_str()) != 0; }
bool set_value(const std::string &rhs) { return lsl_set_value(obj, rhs.c_str()) != 0; }
xml_element append_child(const std::string &name) {
return lsl_append_child(obj, name.c_str());
}
xml_element prepend_child(const std::string &name) {
return lsl_prepend_child(obj, (name.c_str()));
}
xml_element append_copy(const xml_element &e) { return lsl_append_copy(obj, e.obj); }
xml_element prepend_copy(const xml_element &e) { return lsl_prepend_copy(obj, e.obj); }
void remove_child(const std::string &name) { lsl_remove_child_n(obj, (name.c_str())); }
void remove_child(const xml_element &e) { lsl_remove_child(obj, e.obj); }
private:
lsl_xml_ptr obj;
};
inline xml_element stream_info::desc() { return lsl_get_desc(obj.get()); }
// =============================
// ==== Continuous Resolver ====
// =============================
class continuous_resolver {
public:
continuous_resolver(double forget_after = 5.0)
: obj(lsl_create_continuous_resolver(forget_after), &lsl_destroy_continuous_resolver) {}
continuous_resolver(
const std::string &prop, const std::string &value, double forget_after = 5.0)
: obj(lsl_create_continuous_resolver_byprop((prop.c_str()), (value.c_str()), forget_after),
&lsl_destroy_continuous_resolver) {}
continuous_resolver(const std::string &pred, double forget_after = 5.0)
: obj(lsl_create_continuous_resolver_bypred((pred.c_str()), forget_after), &lsl_destroy_continuous_resolver) {}
std::vector<stream_info> results() {
lsl_streaminfo buffer[1024];
return std::vector<stream_info>(
buffer, buffer + check_error(lsl_resolver_results(obj.get(), buffer, sizeof(buffer))));
}
continuous_resolver(continuous_resolver &&rhs) noexcept = default;
continuous_resolver &operator=(continuous_resolver &&rhs) noexcept = default;
private:
std::unique_ptr<lsl_continuous_resolver_, void(*)(lsl_continuous_resolver_*)> obj;
};
// ===============================
// ==== Exception Definitions ====
// ===============================
class lost_error : public std::runtime_error {
public:
explicit lost_error(const std::string &msg) : std::runtime_error(msg) {}
};
class timeout_error : public std::runtime_error {
public:
explicit timeout_error(const std::string &msg) : std::runtime_error(msg) {}
};
inline int32_t check_error(int32_t ec) {
if (ec < 0) {
switch (ec) {
case lsl_timeout_error: throw timeout_error("The operation has timed out.");
case lsl_lost_error:
throw timeout_error(
"The stream has been lost; to continue reading, you need to re-resolve it.");
case lsl_argument_error:
throw std::invalid_argument("An argument was incorrectly specified.");
case lsl_internal_error: throw std::runtime_error("An internal error has occurred.");
default: throw std::runtime_error("An unknown error has occurred.");
}
}
return ec;
}
} // namespace lsl
#endif // LSL_CPP_H