Low Level LSL Framework - kirtonBCIlab/bci-essentials-unity GitHub Wiki
In addition to the provided paradigm controller behaviours, you can use the LSL framework to communicate directly with BCI Essentials python.
See the back end marker api.
Writing Markers
BCI Essentials is controlled using marker strings sent as LSL samples, which come in two broad types: Command Markers and Event Markers. The LSLMarkerWriter
component and related types are provided to streamline this process and improve clarity by explicitly defining the supported set of markers. To use it yourself, simply add the component to a unity scene and use any of the public Push..Marker
methods using a Marker class or an explicit helper overload:
using BCIEssentials.LSLFramework;
LSLMarkerWriter OutStream = GetComponent<LSLMarkerWriter>();
OutStream.PushTrialStartedMarker();
// or
OutStream.PushCommandMarker<TrialStartedMarker>();
// or
OutStream.PushMarker(new TrialStartedMarker());
OutStream.PushMITrainingMarker(1, 0, 2.5f);
//or
MIEventMarker marker = new(1, 0, 3.2f);
marker.EpochLength = 2.5f;
OutStream.PushMarker(marker);
OutStream.PushMIClassificationMarker(1, 1.5f);
Push..TrainingMarker
methods will not trigger predictions.
PushString
can also be used to send a raw marker directly, outside of the defined types.
Object/Class Indexing
Provided marker classes and write methods are 0-indexed in regards to objects/classes, and will "translate" to the 1-indexed markers and response python back-end expects. This "translation" has to be done manually if working with raw marker strings.
Reading Markers
Though the python back end will provide a number of meaningful responses, in most cases we only care about predictions from the classifier. Two levels of a stream reader class are provided at different levels of utility. In most cases, the LSLResponseProvider
should be the most helpful. Adding this component to a Unity scene will allow you to subscribe to response callbacks by type, dynamically opening and polling an inlet, providing parsed responses. Incoming responses will be provided to all subscribers but are not otherwise be saved. This means that any incoming responses not relevant to any active subscriber will be discarded upon reception.
using BCIEssentials.LSLFramework;
LSLResponseProvider InStream = GetComponent<LSLResponseProvider>();
void OnPredictionReceived(LSLPredictionResponse prediction)
{
Debug.Log($"Prediction received for object #{prediction.Value}");
}
InStream.SubscribePredictions(OnPredictionReceived);
// or
InStream.Subscribe<LSLPredictionResponse>(OnPredictionReceived);
// or
InStream.SubscribePredictions(prediction => {
Debug.Log($"Prediction received for object #{prediction.Value}");
}
// or
InStream.SubscribeAll(response => {
if (response is LSLPredictionResponse prediction) {
Debug.Log($"Prediction received for object #{prediction.Value}");
}
}
// or
InStream.Subscribe<SingleChannelLSLResponse>(response => {
if (response is LSLPredictionResponse prediction) {
Debug.Log($"Prediction received for object #{prediction.Value}");
}
}
// then
InStream.UsubscribePredictions(OnPredictionReceived);
// The unsubscribe method does not need to match the subscribe helper used.
// Invalidated callbacks (from destroyed components) will be automatically unsubscribed.
// This enabled the safe use of lambdas regardless of component lifetimes,
// but they cannot be unsubscribed manually unless a reference is stored somewhere.
The basic LSLStreamReader
can also be used to manually pull typed responses if desired.
Response Types
Type | Description |
---|---|
LSLResponse |
Base class for all responses, indicates parsing failure if not a more specific type |
EmptyLSLResponse |
LSL sample with no content |
SingleChannelLSLResponse |
Base class for all expected responses, indicates parsing failure if not a more specific type |
LSLPing |
"ping" |
LSLPredictionResponse |
Prediction from the classifier indicating the selection of a specific object |
LSLMarkerReceipt |
Confirmation of receipt for a command or event marker with inherited classes for each type of marker, indicates parsing failure if not a more specific type |