PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PLabStreamingLayer.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/utility/PLabStreamingLayer.cpp
4// Purpose: PEBL wrapper for Lab Streaming Layer (LSL)
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2026 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9// This file is part of the PEBL project.
10//
11// PEBL is free software; you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation; either version 2 of the License, or
14// (at your option) any later version.
15//
16// PEBL is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with PEBL; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
26#ifdef PEBL_USE_LSL
27
28#include "PLabStreamingLayer.h"
29#include <iostream>
30
31// Include LSL C API header from LabRecorder's bundled liblsl
32extern "C" {
33 #include <lsl_c.h>
34}
35
36PLSL::PLSL(const std::string& name,
37 const std::string& type,
38 const std::string& source_id)
39 : mInfo(NULL),
40 mOutlet(NULL)
41{
42 // Create stream info using C API
43 // lsl_create_streaminfo(name, type, channel_count, nominal_srate, channel_format, source_id)
44 mInfo = lsl_create_streaminfo(
45 name.c_str(),
46 type.c_str(),
47 1, // Channel count (1 for single marker channel)
48 0.0, // LSL_IRREGULAR_RATE - irregular/event-driven
49 cft_string, // Channel format - string markers
50 source_id.c_str() // empty string is valid; NULL may crash some versions
51 );
52
53 if (!mInfo) {
54 const char* err = lsl_last_error();
55 std::cerr << "ERROR: Failed to create LSL stream info"
56 << (err ? std::string(": ") + err : "") << "\n";
57 std::cerr << " name=[" << name << "] type=[" << type
58 << "] source_id=[" << source_id << "]\n";
59 return;
60 }
61
62 // Create the LSL outlet
63 mOutlet = lsl_create_outlet(mInfo, 0, 360); // chunk_size=0 (auto), max_buffered=360s
64
65 if (!mOutlet) {
66 std::cerr << "ERROR: Failed to create LSL outlet\n";
67 lsl_destroy_streaminfo(mInfo);
68 mInfo = NULL;
69 return;
70 }
71
72 std::cerr << "LSL outlet created: [" << name << "] type=[" << type << "] source=["
73 << (source_id.empty() ? "auto" : source_id) << "]\n";
74}
75
77{
78 if (mOutlet) {
79 lsl_destroy_outlet(mOutlet);
80 mOutlet = NULL;
81 std::cerr << "LSL outlet closed\n";
82 }
83 if (mInfo) {
84 lsl_destroy_streaminfo(mInfo);
85 mInfo = NULL;
86 }
87}
88
89void PLSL::SendMarker(const std::string& marker)
90{
91 if (mOutlet) {
92 // Push string sample (LSL automatically timestamps it)
93 const char* data = marker.c_str();
94 lsl_push_sample_str(mOutlet, &data);
95 }
96}
97
98void PLSL::SendMarker(int marker)
99{
100 if (mOutlet) {
101 // Convert integer to string and send
102 std::string str_marker = std::to_string(marker);
103 const char* data = str_marker.c_str();
104 lsl_push_sample_str(mOutlet, &data);
105 }
106}
107
108void PLSL::SendMarkerTimed(const std::string& marker, double timestamp)
109{
110 if (mOutlet) {
111 // Push sample with explicit timestamp
112 // pushthrough=1 means push immediately, 0 means use buffering
113 const char* data = marker.c_str();
114 lsl_push_sample_strtp(mOutlet, &data, timestamp, 1);
115 }
116}
117
119{
120 return mOutlet ? (lsl_have_consumers(mOutlet) != 0) : false;
121}
122
123bool PLSL::WaitForConsumers(double timeout)
124{
125 return mOutlet ? (lsl_wait_for_consumers(mOutlet, timeout) != 0) : false;
126}
127
128#else // !PEBL_USE_LSL - Stub implementations when LSL is not available
129
130#include "PLabStreamingLayer.h"
131
132// No-op constructor
133PLSL::PLSL(const std::string& name,
134 const std::string& type,
135 const std::string& source_id)
136 : mInfo(NULL),
137 mOutlet(NULL)
138{
139 // LSL not available - no-op
140}
141
142// No-op destructor
144{
145 // LSL not available - no-op
146}
147
148// No-op marker functions
149void PLSL::SendMarker(const std::string& marker)
150{
151 // LSL not available - no-op
152}
153
154void PLSL::SendMarker(int marker)
155{
156 // LSL not available - no-op
157}
158
159void PLSL::SendMarkerTimed(const std::string& marker, double timestamp)
160{
161 // LSL not available - no-op
162}
163
164// Always return false when LSL not available
166{
167 return false;
168}
169
170bool PLSL::WaitForConsumers(double timeout)
171{
172 return false;
173}
174
175#endif // PEBL_USE_LSL
#define NULL
Definition BinReloc.cpp:317
bool HaveConsumers()
bool WaitForConsumers(double timeout)
void SendMarker(const std::string &marker)
void SendMarkerTimed(const std::string &marker, double timestamp)
PLSL(const std::string &name, const std::string &type, const std::string &source_id)