PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEBLLSL.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/libs/PEBLLSL.cpp
4// Purpose: LSL (Lab Streaming Layer) built-in functions for PEBL
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// IMPORTANT: Include PLabStreamingLayer.h FIRST to avoid namespace pollution
29// from lsl_cpp.h (which includes lsl_c.h inside namespace lsl)
30#include "../utility/PLabStreamingLayer.h"
31#include "PEBLLSL.h"
32#include "../utility/PError.h"
33#include "../base/Variant.h"
34#include "../base/PComplexData.h"
35#include "../base/PList.h"
36
37// Forward declare LSL C API function
38extern "C" {
39 double lsl_local_clock();
40}
41
42// Global LSL outlet (singleton - one per PEBL instance)
43static PLSL* gLSLOutlet = nullptr;
44
45//============================================================================//
46// Phase 1: Outlet Functions (Send Markers)
47//============================================================================//
48
61{
62 PList* plist = v.GetComplexData()->GetList();
63
64 if (plist->Length() < 1) {
65 PError::SignalWarning("CreateLSLOutlet requires at least 1 argument (stream name)");
66 return Variant(0);
67 }
68
69 std::string name = plist->First().GetString();
70 std::string type = plist->Length() > 1 ? plist->Nth(2).GetString() : "Markers";
71 std::string source_id = plist->Length() > 2 ? plist->Nth(3).GetString() : "";
72
73 try {
74 // Close previous outlet if exists
75 if (gLSLOutlet) {
76 delete gLSLOutlet;
77 gLSLOutlet = nullptr;
78 }
79
80 // Create new outlet
81 gLSLOutlet = new PLSL(name, type, source_id);
82
83 if (!gLSLOutlet->IsValid()) {
84 fprintf(stderr, "LSL outlet object created but outlet is invalid (lsl_create_outlet failed)\n");
85 delete gLSLOutlet;
86 gLSLOutlet = nullptr;
87 return Variant(0);
88 }
89
90 return Variant(1); // Success
91 }
92 catch (std::exception& e) {
93 PError::SignalWarning("LSL outlet creation failed: " + std::string(e.what()));
94 return Variant(0); // Failure
95 }
96}
97
98
109{
110 if (!gLSLOutlet) {
111 // Silently skip if not initialized (this is intentional - no error)
112 return Variant(0);
113 }
114
115 PList* plist = v.GetComplexData()->GetList();
116
117 if (plist->Length() < 1) {
118 return Variant(0);
119 }
120
121 Variant marker = plist->First();
122
123 try {
124 if (marker.IsString()) {
125 gLSLOutlet->SendMarker(marker.GetString());
126 }
127 else if (marker.IsInteger()) {
128 gLSLOutlet->SendMarker(marker.GetInteger());
129 }
130 else if (marker.IsFloat()) {
131 // Convert float to string
132 gLSLOutlet->SendMarker(std::to_string(marker.GetFloat()));
133 }
134 else {
135 // Convert to string as fallback
136 gLSLOutlet->SendMarker(marker.GetString());
137 }
138
139 return Variant(1);
140 }
141 catch (std::exception& e) {
142 PError::SignalWarning("LSL marker send failed: " + std::string(e.what()));
143 return Variant(0);
144 }
145}
146
147
155{
156 if (gLSLOutlet) {
157 delete gLSLOutlet;
158 gLSLOutlet = nullptr;
159 }
160 return Variant(1);
161}
162
163
171{
172 if (!gLSLOutlet) {
173 return Variant(0);
174 }
175
176 return Variant(gLSLOutlet->HaveConsumers() ? 1 : 0);
177}
178
179
187{
188 return Variant(lsl_local_clock());
189}
190
191#else // !PEBL_USE_LSL - Stub implementations when LSL is disabled
192
193#include "PEBLLSL.h"
194#include "../base/Variant.h"
195
196// All LSL functions become no-ops that return success/0 when LSL is disabled
197
199{
200 // No-op: Return success (LSL functions silently disabled)
201 return Variant(1);
202}
203
205{
206 // No-op: Return success (LSL functions silently disabled)
207 return Variant(1);
208}
209
211{
212 // No-op: Return success
213 return Variant(1);
214}
215
217{
218 // No-op: No consumers when LSL disabled
219 return Variant(0);
220}
221
223{
224 // No-op: Return 0.0 when LSL disabled
225 return Variant(0.0);
226}
227
228#endif // PEBL_USE_LSL
Variant SendLSLMarker(Variant v)
Definition PEBLLSL.cpp:204
Variant CloseLSLOutlet(Variant v)
Definition PEBLLSL.cpp:210
Variant LSLHasConsumers(Variant v)
Definition PEBLLSL.cpp:216
Variant CreateLSLOutlet(Variant v)
Definition PEBLLSL.cpp:198
Variant LSLLocalClock(Variant v)
Definition PEBLLSL.cpp:222
PList * GetList() const
bool IsValid() const
bool HaveConsumers()
void SendMarker(const std::string &marker)
Definition PList.h:45
Variant Nth(unsigned int n)
Definition PList.cpp:181
unsigned long Length() const
Definition PList.h:89
Variant First()
Definition PList.cpp:169
pInt GetInteger() const
Definition Variant.cpp:997
bool IsString() const
Definition Variant.cpp:948
bool IsFloat() const
Definition Variant.cpp:937
std::string GetString() const
Definition Variant.cpp:1056
bool IsInteger() const
Definition Variant.cpp:942
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
pDouble GetFloat() const
Definition Variant.cpp:1025
void SignalWarning(const std::string &message)
Definition PError.cpp:119