BrainStreamImportantConcepts
Important concepts: Markers, Events and Global Variables
Introduction
BrainStream essentially is an event handler, which events are different in the sense that execution of corresponding actions can optionally be distributed over multiple timepoints, i.e., discrete moments in time. The execution of these timepoints again can be distributed over different computers, which implies processed events can both be distributed in space (parallel) and in time. BrainStream incorporates special features to automatically guarantee a correct parallel processing of information by letting users exactly specify what information is required for each event.
Markers
All processing steps and pipelines need to be executed at a specific time and in a specific order. This is handled by markers, which are received in synchrony with the data stream. Markers in themselves do not represent any kind of processing. Meaning is given to them by the actions (processing steps or pipelines) associated with the markers. Actions can be freely defined in order to accomplish some particular task, for example presenting stimuli to participants, numerical data processing, or controlling a data acquisition system. Incoming markers can initiate an unlimited sequence of actions at an unlimited number of timepoints. Users can control the [.DocsSectionsMarkerHandling insertion of markers] and [.DocsSectionsBuildingExperiments#SecActions specify the actions] associated with them.
Outside BrainStream, markers are represented as numbers. However, as numbers are easy to confuse, the BrainStream platform works with marker names everywhere. Names are strings, can contain characters (a-z), numbers (0-9), and must start with a character. Users can specify any name for their markers, except the set of [.DocsSectionsReservedNames#ResMarkers reserved marker names] that are already used by BrainStream.
There are different marker types, for example stimulus, response and device control markers. Marker name, number, and type combinations are stored in [.DocsSectionsBuildingExperiments#SecDict Dictionary tables]. The set of dictionaries must be unique, i.e., for a certain marker type one cannot specify the same number multiple times, and overall marker names must be unique.
When a marker is received by the platform it instatiates a new event.
Events
Every marker that is received by BrainStream instantiates a new event. During an event, one or more actions can be executed at one or more different timepoints. Possible actions are modifying variables, executing functions or collecting data. An event ends when all actions that are specified for this event have been executed. A timeline with 3 events is show below:
File:DocsSectionsImportantConcepts.Event1.png
Figure 1: Three events
Event 1 begins when marker mrk1 arrives. During Event 1, only one action is executed. The event ends when this execution is complete. Markers mrk2 and mrk3 start Event 2 and Event 3, respectively. For Events 2 and 3, multiple actions and timepoints are specified. These events end when all actions associated with them have been executed.
File:DocsSectionsImportantConcepts.Event2.png
Figure 2: Events end when all associated actions have been executed
Thus, the scope of events can range from a single processing step to very complicated processing pipelines. Especially in more complex cases, events may contain a lot of information, for example a list of which actions must be executed at which timepoints, variables associated with the actions, and data that has been collected during the event. All this information is organized in a Matlab structure] variable with the name event. This structure contains a number of fixed fields and may contain additional fields depending on the experiment. For more detailed information see [[.DocsSectionsEventFields Fields of the Event Structure].
Within an event, information can be transferred from one action to another via the event structure. For example, take a look at Event 2 in figure 1. Let's assume that action 1 adds a variable Var1 to the event structure, in the field event.Var1: event.Var1 = 3. During action 2, this variable is modified, so that event.Var1 = 4. If Var1 is used for performing action 3, the change that was made by action 2 will be incorporated. However, the event structure has a limited lifetime: it only exists for the duration of the event. When action 3 is completed, the event structure associated with Event 2 disappears. Var1 and the change that action 2 made to it will not be transferred to later events, for example Event 3. Thus, we cannot transfer information between events via the event structure. For this, we must use global variables.
Global variables (user state)
Within events, information between different processing steps is preserved. However, different events can only exchange information via a set of globally maintained variables, also called the user state.
File:DocsSectionsImportantConcepts.GlobalVars1.png
Figure 3: Information exchange between events via the global variables
During events, variables can be retrieved from the global variables. Their content is copied into fields of the event structure with the name of the variable, for example the content of Var1 will be copied into event.Var1. The variables are then available in the event structure for the duration of the event. When the event ends, the (modified) content of the variables can be updated from the event structure to the global variables. Later events can retrieve the modified content of the variables and place it in their own event structure. Thus, information can be transferred between events via the global variables.
Retrieving and updating content of global variables
The statements 'get' and 'put' in the [.DocsSectionsBuildingExperiments#SecActions Actions table] control the exchange of variable content between the global variables and the current event. These statements can be used for exchange of information between different events within the same block.
get: copy content of a variable from the global variables into the event structure. The content will be copied into a new field of the event structure with the name of the variable. For example, a get statement for variable Var1 will create the field event.Var1.
put: copy the (modified) content of a variable from the event structure into the global variables.
The statements 'load' and 'save' in the Actions table control the exchange of variable content between a location on disk and the current event. These statements can be used for exchange of information between different blocks.
load: load variable content from the [.DocsSectionsPathsFolders#OutputFolder session folder] into a new field of the event structure with the name of the variable. For example, a variable stored in the session folder with the name Var1.mat will create the field event.Var1. It may often be useful to execute all load actions at the BS_INIT marker, as this will make sure that the variables are loaded at the very beginning of the block.
save: save the content of a variable to the session folder and runfolder. If 'save' is executed multiple times for a specific variable during the same block, backup copies will be saved in the runfolder while the session folder only stores the last updated value. It may often be useful to execute all save actions at the BS_EXIT marker.
As all these actions take time, it is advisable to perform them only when needed (for example, do not copy variables into the event structure that are not used during the current event).
Modifying variables
Variables’ content can be modified by changing them during execution of functions, or by specifying expressions in the Actions table. Possible expressions are listed below.
Specifying only a number or string means the variable will be set to that value:
-1, 0, [], {} ‘a string’
Change the current value of a variable, where $self can be used as an abbreviation for a variable's own name. :
ToneCount+1 $self+1
Expressions can use other global variables too, for instance in a Boolean expression using two of your global variables
Answer == [[CorrectAnswer|Correct Answer]] [[NumTrials|Num Trials]] - [[NumCorrect|Num Correct]]
Furthermore, functions can be incorporated in these expressions as long as the entire expression results in a single output. This output will then become the new content of the variable.
InitInstructionFigure()
This example function initializes properties for a figure. The return value of this function is the handle to this figure. The figure handle may be retrieved later by means of a get operation.
Note: variables can also be of type structure. In this case a function is required to initialize the fields of the structure.