Skip to content

Commit d299a76

Browse files
author
David Medine
committed
first commit to newly hosted repo
0 parents  commit d299a76

File tree

95 files changed

+4620
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+4620
-0
lines changed

env_add.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
% This file is here only so that you can put this directory into the dependencies folder of a BCILAB installation.

examples/HandleMetaData.m

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
%% instantiate the library
2+
lib = lsl_loadlib();
3+
4+
% create a new StreamInfo and declare some meta-data (in accordance with XDF format)
5+
info = lsl_streaminfo(lib,'MetaTester','EEG',8,100,'cf_float32','myuid56872');
6+
chns = info.desc().append_child('channels');
7+
for label = {'C3','C4','Cz','FPz','POz','CPz','O1','O2'}
8+
ch = chns.append_child('channel');
9+
ch.append_child_value('label',label{1});
10+
ch.append_child_value('unit','microvolts');
11+
ch.append_child_value('type','EEG');
12+
end
13+
info.desc().append_child_value('manufacturer','SCCN');
14+
cap = info.desc().append_child('cap');
15+
cap.append_child_value('name','EasyCap');
16+
cap.append_child_value('size','54');
17+
cap.append_child_value('labelscheme','10-20');
18+
19+
% create outlet for the stream
20+
outlet = lsl_outlet(info);
21+
22+
23+
% === the following could run on another computer ===
24+
25+
% resolve the stream and open an inlet
26+
lib = lsl_loadlib();
27+
result = {};
28+
while isempty(result)
29+
result = lsl_resolve_byprop(lib,'type','EEG'); end
30+
inlet = lsl_inlet(result{1});
31+
% get the full stream info (including custom meta-data) and dissect it
32+
inf = inlet.info();
33+
fprintf('The stream''s XML meta-data is: \n');
34+
fprintf([inf.as_xml() '\n']);
35+
fprintf(['The manufacturer is: ' inf.desc().child_value('manufacturer') '\n']);
36+
fprintf(['The cap circumference is: ' inf.desc().child('cap').child_value('size') '\n']);
37+
fprintf('The channel labels are as follows:\n');
38+
ch = inf.desc().child('channels').child('channel');
39+
for k = 1:inf.channel_count()
40+
fprintf([' ' ch.child_value('label') '\n']);
41+
ch = ch.next_sibling();
42+
end

examples/ReceiveData.m

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
%% instantiate the library
2+
disp('Loading the library...');
3+
lib = lsl_loadlib();
4+
5+
% resolve a stream...
6+
disp('Resolving an EEG stream...');
7+
result = {};
8+
while isempty(result)
9+
result = lsl_resolve_byprop(lib,'type','EEG'); end
10+
11+
% create a new inlet
12+
disp('Opening an inlet...');
13+
inlet = lsl_inlet(result{1});
14+
15+
disp('Now receiving data...');
16+
while true
17+
% get data from the inlet
18+
[vec,ts] = inlet.pull_sample();
19+
% and display it
20+
fprintf('%.2f\t',vec);
21+
fprintf('%.5f\n',ts);
22+
end

examples/ReceiveDataInChunks.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
% instantiate the library
2+
disp('Loading the library...');
3+
lib = lsl_loadlib();
4+
5+
% resolve a stream...
6+
disp('Resolving an EEG stream...');
7+
result = {};
8+
while isempty(result)
9+
result = lsl_resolve_byprop(lib,'type','EEG'); end
10+
11+
% create a new inlet
12+
disp('Opening an inlet...');
13+
inlet = lsl_inlet(result{1});
14+
15+
disp('Now receiving chunked data...');
16+
while true
17+
% get chunk from the inlet
18+
[chunk,stamps] = inlet.pull_chunk();
19+
for s=1:length(stamps)
20+
% and display it
21+
fprintf('%.2f\t',chunk(:,s));
22+
fprintf('%.5f\n',stamps(s));
23+
end
24+
pause(0.05);
25+
end

examples/ReceiveStringMarkers.m

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
% instantiate the library
2+
disp('Loading the library...');
3+
lib = lsl_loadlib();
4+
5+
% resolve a stream...
6+
disp('Resolving a Markers stream...');
7+
result = {};
8+
while isempty(result)
9+
result = lsl_resolve_byprop(lib,'type','Markers'); end
10+
11+
% create a new inlet
12+
disp('Opening an inlet...');
13+
inlet = lsl_inlet(result{1});
14+
15+
disp('Now receiving data...');
16+
while true
17+
% get data from the inlet
18+
[mrks,ts] = inlet.pull_sample();
19+
% and display it
20+
fprintf('got %s at time %.5f\n',mrks{1},ts);
21+
end

examples/SendData.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
%% instantiate the library
2+
disp('Loading library...');
3+
lib = lsl_loadlib();
4+
5+
% make a new stream outlet
6+
disp('Creating a new streaminfo...');
7+
info = lsl_streaminfo(lib,'BioSemi','EEG',8,100,'cf_float32','sdfwerr32432');
8+
9+
disp('Opening an outlet...');
10+
outlet = lsl_outlet(info);
11+
12+
% send data into the outlet, sample by sample
13+
disp('Now transmitting data...');
14+
while true
15+
outlet.push_sample(randn(8,1));
16+
pause(0.01);
17+
end

examples/SendDataInChunks.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
% instantiate the library
2+
disp('Loading library...');
3+
lib = lsl_loadlib();
4+
5+
% make a new stream outlet
6+
disp('Creating a new streaminfo...');
7+
info = lsl_streaminfo(lib,'BioSemi','EEG',8,100,'cf_float32','sdfwerr32432');
8+
9+
disp('Opening an outlet...');
10+
outlet = lsl_outlet(info);
11+
12+
% send data into the outlet
13+
disp('Now transmitting chunked data...');
14+
while true
15+
outlet.push_chunk(randn(8,50));
16+
pause(0.5);
17+
end

examples/SendStringMarkers.m

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
% instantiate the library
2+
disp('Loading library...');
3+
lib = lsl_loadlib();
4+
5+
% make a new stream outlet
6+
% the name (here MyMarkerStream) is visible to the experimenter and should be chosen so that
7+
% it is clearly recognizable as your MATLAB software's marker stream
8+
% The content-type should be Markers by convention, and the next three arguments indicate the
9+
% data format (1 channel, irregular rate, string-formatted).
10+
% The so-called source id is an optional string that allows for uniquely identifying your
11+
% marker stream across re-starts (or crashes) of your script (i.e., after a crash of your script
12+
% other programs could continue to record from the stream with only a minor interruption).
13+
disp('Creating a new marker stream info...');
14+
info = lsl_streaminfo(lib,'MyMarkerStream','Markers',1,0,'cf_string','myuniquesourceid23443');
15+
16+
disp('Opening an outlet...');
17+
outlet = lsl_outlet(info);
18+
19+
% send markers into the outlet
20+
disp('Now transmitting data...');
21+
markers = {'Test', 'Blah', 'Marker', 'XXX', 'Testtest', 'Test-1-2-3'};
22+
while true
23+
pause(rand()*3);
24+
mrk = markers{min(length(markers), 1+floor(rand()*(length(markers))))};
25+
disp(['now sending ' mrk]);
26+
outlet.push_sample({mrk}); % note that the string is wrapped into a cell-array
27+
end

examples/readme.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
To run these examples, you must have added the folder include/lsl_matlab to your MATLAB path (recursively).
2+
You can do this either via the File/Path... menu or via the command addpath(genpath('your_path_to/liblsl-1.xx/include/lsl_matlab'))
3+
Also, if you have rebuilt the library, make sure that liblsl-1.xx/include/lsl_matlab/bin contains a copy of the library files in the liblsl-1.xx/bin.

lsl_inlet.m

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
classdef lsl_inlet < handle
2+
% A stream inlet.
3+
% Inlets are used to receive streaming data (and meta-data) from the lab network.
4+
%
5+
% Note:
6+
% Most functions of the inlet can throw an error of type 'lsl:lost_error' if the stream
7+
% read by this inlet has been lost irretrievably. Those functions that take a timeout can in
8+
% addition throw an error of type 'lsl:timeout_error'.
9+
10+
properties (Hidden)
11+
LibHandle = 0; % this is a handle to the liblsl library (to call its functions)
12+
InletHandle = 0; % this is a handle to an lsl_inlet object within the library.
13+
ChannelCount = 0; % copy of the inlet's channel count
14+
IsString = 0; % whether this is a string-formatted inlet
15+
end
16+
17+
methods
18+
19+
function self = lsl_inlet(info, maxbuffered, chunksize, recover)
20+
% Inlet = lsl_inlet(Streaminfo, MaxBuffered, ChunkSize, Recover)
21+
% Construct a new stream inlet from a resolved stream info.
22+
%
23+
% In:
24+
% Streaminfo : A resolved stream info object (as coming from one of the lsl_resolve_* functions).
25+
%
26+
% MaxBuffered : Optionally the maximum amount of data to buffer (in seconds if there is a nominal
27+
% sampling rate, otherwise x100 in samples). Recording applications want to use a fairly
28+
% large buffer size here, while real-time applications would only buffer as much as
29+
% they need to perform their next calculation. (default: 360)
30+
%
31+
% ChunkSize : Optionally the maximum size, in samples, at which chunks are transmitted
32+
% (0 corresponds to the chunk sizes used by the sender).
33+
% Recording applications can use a generous size here (leaving it to the network how
34+
% to pack things), while real-time applications may want a finer (perhaps 1-sample) granularity.
35+
% (default 0)
36+
%
37+
% Recover : Try to silently recover lost streams that are recoverable (=those that that have a source_id set).
38+
% In all other cases (recover is false or the stream is not recoverable) functions may throw a
39+
% lost_error if the stream's source is lost (e.g., due to an app or computer crash).
40+
% (default: 1)
41+
42+
if ~exist('maxbuffered','var') || isempty(maxbuffered) maxbuffered = 360; end
43+
if ~exist('chunksize','var') || isempty(chunksize) chunksize = 0; end
44+
if ~exist('recover','var') || isempty(recover) recover = 1; end
45+
self.LibHandle = info.LibHandle;
46+
self.ChannelCount = info.channel_count();
47+
self.IsString = strcmp(info.channel_format(),'cf_string');
48+
self.InletHandle = lsl_create_inlet(info.LibHandle,info.InfoHandle,maxbuffered,chunksize,recover);
49+
end
50+
51+
52+
function delete(self)
53+
% Destroy the inlet when it is deleted.
54+
% The inlet will automatically disconnect if destroyed.
55+
56+
lsl_destroy_inlet(self.LibHandle,self.InletHandle);
57+
end
58+
59+
60+
function result = info(self, timeout)
61+
% Retrieve the complete information of the given stream, including the extended description.
62+
% Fullinfo = info(Timeout)
63+
%
64+
% Can be invoked at any point of the stream's lifetime.
65+
%
66+
% In:
67+
% Timeout : Timeout of the operation, in seconds (default: 60).
68+
%
69+
% Out:
70+
% Fullinfo : the full information, including description field, of the stream.
71+
72+
if ~exist('timeout','var') || isempty(timeout) timeout = 60; end
73+
result = lsl_streaminfo(self.LibHandle,lsl_get_fullinfo(self.LibHandle, self.InletHandle, timeout));
74+
end
75+
76+
77+
function open_stream(self, timeout)
78+
% Subscribe to the data stream from this moment onwards.
79+
% open_stream(Timeout)
80+
%
81+
% All samples pushed in at the other end from this moment onwards will be queued and
82+
% eventually be delivered in response to pull_sample() or pull_chunk() calls.
83+
% Pulling a sample without some preceding open_stream is permitted (the stream will then
84+
% be opened implicitly).
85+
%
86+
% In:
87+
% Timeout : Timeout of the operation (default: 60).
88+
%
89+
% Out:
90+
% Fullinfo : the full information, including description field, of the stream.
91+
92+
if ~exist('timeout','var') || isempty(timeout) timeout = 60; end
93+
lsl_open_stream(self.LibHandle, self.InletHandle, timeout);
94+
end
95+
96+
97+
function close_stream(self)
98+
% Drop the current data stream.
99+
% close_stream()
100+
%
101+
% All samples that are still buffered or in flight will be dropped and the source will halt its buffering of data for this inlet.
102+
% If an application stops being interested in data from a source (temporarily or not) but keeps the outlet alive, it should
103+
% call close_streams() to not pressure the source outlet to buffer unnecessarily large amounts of data.
104+
105+
lsl_close_stream(self.LibHandle, self.InletHandle);
106+
end
107+
108+
109+
function result = time_correction(self,timeout)
110+
% Retrieve an estimated time correction offset for the given stream.
111+
%
112+
% The first call to this function takes several miliseconds until a reliable first estimate is obtained.
113+
% Subsequent calls are instantaneous (and rely on periodic background updates).
114+
% The precision of these estimates should be below 1 ms (empirically within +/-0.2 ms).
115+
%
116+
% In:
117+
% Timeout : Timeout to acquire the first time-correction estimate (default: 60).
118+
%
119+
% Out:
120+
% Offset : The time correction estimate. If the first estimate cannot within the alloted time,
121+
% the result is NaN.
122+
123+
if ~exist('timeout','var') || isempty(timeout) timeout = 60; end
124+
result = lsl_time_correction(self.LibHandle, self.InletHandle,timeout);
125+
end
126+
127+
128+
function [data,timestamp] = pull_sample(self,timeout,binary_blobs)
129+
% Pull a sample from the inlet and read it into an array of values.
130+
% [SampleData,Timestamp] = pull_sample(Timeout)
131+
%
132+
% In:
133+
% Timeout : The timeout for this operation, if any. (default: 60)
134+
% Use 0 to make the function non-blocking.
135+
%
136+
% Out:
137+
% SampleData : The sample's contents. This is either a numeric vector (type: double) if
138+
% the stream holds numeric contents, or a cell array of strings if the stream
139+
% is string-formatted, or a cell array of uint8 vectors if BinaryBlobs is set
140+
% to true.
141+
%
142+
% Note: this is empty if the operation times out.
143+
%
144+
% Timeout : Timeout for the operation. (default: 60)
145+
%
146+
% BinaryBlobs : If true, strings will be received as uint8
147+
% arrays that may contain the \0 character.
148+
% Otherwise strings will be received as char,
149+
% which cannot contain \0's. (default: false)
150+
%
151+
% Notes:
152+
% It is not a good idea to set the timeout to a very large value since MATLAB would
153+
% otherwise become unresponsive - better call pull_sample in a loop until it succeeds.
154+
155+
if ~exist('timeout','var') || isempty(timeout) timeout = 60; end
156+
if self.IsString
157+
if ~exist('binary_blobs','var') || isempty(binary_blobs) binary_blobs = false; end
158+
if binary_blobs
159+
[data,timestamp] = lsl_pull_sample_buf(self.LibHandle,self.InletHandle,self.ChannelCount,timeout);
160+
else
161+
[data,timestamp] = lsl_pull_sample_str(self.LibHandle,self.InletHandle,self.ChannelCount,timeout);
162+
end
163+
else
164+
[data,timestamp] = lsl_pull_sample_d(self.LibHandle,self.InletHandle,self.ChannelCount,timeout);
165+
end
166+
end
167+
168+
169+
function [chunk,timestamps] = pull_chunk(self)
170+
% Pull a chunk of numeric samples and their timestamps from the inlet.
171+
% [ChunkData,Timestamps] = pull_chunk()
172+
%
173+
% This function obtains a chunk of data from the inlet; the chunk contains all samples
174+
% that have become available since the last chunk/sample was requested. Note that the
175+
% result may be empty. For each returned sample there is also a timestamp being
176+
% returned.
177+
%
178+
% Out:
179+
% ChunkData : The chunk contents; this is a MxN matrix with one column per returned
180+
% sample (and as many rows as the stream has channels).
181+
%
182+
% Timestamps : A vector of timestamps for the returned samples.
183+
184+
[chunk,timestamps] = lsl_pull_chunk_d(self.LibHandle,self.InletHandle,self.ChannelCount);
185+
end
186+
187+
function h = get_libhandle(self)
188+
% get the library handle (e.g., to query the clock)
189+
h = self.LibHandle;
190+
end
191+
end
192+
end

0 commit comments

Comments
 (0)