User Tutorial:StimulusPresentation: Difference between revisions

From BCI2000 Wiki
Jump to navigation Jump to search
Line 106: Line 106:
Initialize the Stimuli matrix. A good practice is to separate your stimuli into "banks" for easy evaluation later. Set n_stimuli to the total number of columns needed in the stimuli matrix (i.e., if there are 10 stimuli, but you have them separated into two banks (task 1-5, and rest 11-15), n_stimuli should be set to 15). In this example there are 103 unique stimuli, but they are separated into a total of 301 columns in the Stimuli matrix The column number of the stimulus in the stimuli matrix corresponds to that stimulus' Stimulus Code. The Stimulus Code is a BCI2000 state that reflects which stimuli were actually presented throughout the experiment, sampled at the same rate as the signal source. Details about each stimulus are stored in the rows. You must include the first 3 rows in the order shown, regardless of whether or not you will present e.g. a caption. Replace the 'audio' row label with 'av' to include *.mp4 files. These rows can be expanded to include additional information about each stimulus, such as an image's mean luminance (this can be useful for analyzing the data later). Each cell needs to be initialized as an empty string for BCI2000 to recognize the Stimuli parameter. The current row labels are described below:
Initialize the Stimuli matrix. A good practice is to separate your stimuli into "banks" for easy evaluation later. Set n_stimuli to the total number of columns needed in the stimuli matrix (i.e., if there are 10 stimuli, but you have them separated into two banks (task 1-5, and rest 11-15), n_stimuli should be set to 15). In this example there are 103 unique stimuli, but they are separated into a total of 301 columns in the Stimuli matrix The column number of the stimulus in the stimuli matrix corresponds to that stimulus' Stimulus Code. The Stimulus Code is a BCI2000 state that reflects which stimuli were actually presented throughout the experiment, sampled at the same rate as the signal source. Details about each stimulus are stored in the rows. You must include the first 3 rows in the order shown, regardless of whether or not you will present e.g. a caption. Replace the 'audio' row label with 'av' to include *.mp4 files. These rows can be expanded to include additional information about each stimulus, such as an image's mean luminance (this can be useful for analyzing the data later). Each cell needs to be initialized as an empty string for BCI2000 to recognize the Stimuli parameter. The current row labels are described below:
* caption - text written to the screen by BCI2000
* caption - text written to the screen by BCI2000
* icon - a *.bmp or *.png image file
* icon - the path to a *.bmp or *.png image file
* audio - a *.wav  audio file
* audio - the path to a *.wav  audio file
* StimulusDuration - the duration of each individual stimulus
* StimulusDuration - the duration of each individual stimulus
* AudioVolume - the volume of audio files. Enter a '0' if there are no audio stimuli, as this field must be filled with a value 0-100
* AudioVolume - the volume of audio files. Enter a '0' if there are no audio stimuli, as this field must be filled with a value 0-100
Line 149: Line 149:


<li>
<li>
Create image stimuli
Populate the Stimuli matrix with details about the task (image) stimuli in columns 1-50. Note that there needs to be at least an empty string for each row.
<pre>
<pre>
%% Study images 1-50
%% Study images 1-50
Line 166: Line 166:


<li>
<li>
Create variable duration fixation cross
Create fixation cross stimuli to act as the 'rest' data. In this example, we create a variable duration rest period by sampling from a distribution of stimulus durations ranging from 0.5-1.5 s. Because BCI2000 presents stimuli for entire processing blocks (block size, e.g., 50 ms) we ensure that the rest period duration options are divisible by BCI2000 block size. Note that the value entered for the 'StimulusDuration' row of each stimulus is a string with the letter 's' (seconds) appended to the end.
<pre>
<pre>
%% inter-stimulus interval (fixation cross) 101-150
%% inter-stimulus interval (fixation cross) 101-150
Line 192: Line 192:


<li>
<li>
Instructions
Populate the Stimuli matrix with the instructions. These instructions are captions specified in the "settings" section. Instuctions could alternately be an image of text. Note that captions do not wrap to the second line.
<pre>
<pre>
%% Instructions 201-202
%% Instructions 201-202
Line 212: Line 212:


<li>
<li>
Sync pulse
Include a Stimulus Code for a sync pulse. This could be used to e.g. trigger an output TTL to synchronize recording from a device not connected to BCI2000.
<pre>
<pre>
%% Sync pulse 301
%% Sync pulse 301

Revision as of 20:33, 6 July 2022

Synopsis

This is a tutorial for implementing a stimulus presentation experiment using the default BCI2000 stimulus presentation application module. The BCI2000 stimulus presentation application module is implemented directly in C++ and has ongoing development support, making it ideal for creating stimulus presentation experiments. In this tutorial we will use a MATLAB script to create a BCI2000 parameter fragment (text file) detailing the experiment. With this script you can define different types of stimuli (images, audio, video, or other outputs), the stimulus duration, interstimulus interval, early offset expressions (for advancing to the next stimulus), and the sequence in which to present the stimuli. You can also store any other BCI2000 parameters (such as the source sampling rate) in this parameter fragment using the same structure described in this MATLAB script. This parameter fragment can be loaded into BCI2000 while running the Stimulus Presentation application module to run your experiment.

Use this template MATLAB script as a starting point to implement your experiment!

A parallel script for implementing BCI2000 stimulus presentation experiments in Python is coming soon!

Demo Script Location

The Stimulus Presentation MATLAB demo script can be found in the root directory of your BCI2000 folder in /tools/matlab/StimulusPresentationScript_Demo.m

A parallel version of this script implemented in Python is coming soon!

Video

Limitations

Note that the BCI2000 Stimulus Presentation application module is limited to feedforward experiments where the subjects behavior does not affect the experimental workflow.

General Use Instructions

The MATLAB demo script for BCI2000 Stimulus Presentation is an m-file that can be run to create a BCI2000 parameter file. This parameter file can be loaded into an instance of BCI2000 using the stimulus presentation application module to run your experiment. The steps to do this are as follows:

  1. Open and run StimulusPresentationScript_Demo.m located in the /tools/matlab folder. This will produce a new parameter file /parms/demo_parms.prm.
  2. Start a batch file using the Stimulus Presentation application module, e.g., /batch/StimulusPresentation_SignalGenerator.bat.
  3. In the configuration window, load the parameter file demo_parms.prm.
  4. Set config and press start to run the experiment!

Tutorial

  1. This section identifies the path of the BCI2000 root directory, relative to the location of the StimulusPresentationScript_Demo.m file. Here we also add the 'tools' folder to the MATLAB path so we can later use the convert_bciprm function to write our experiment to a BCI2000 parameter file.
    %% Set the path of the BCI2000 main directory here
    BCI2000pathparts = regexp(pwd,filesep,'split');
    BCI2000path = '';
    for i = 1:length(BCI2000pathparts)-2
        BCI2000path = [BCI2000path BCI2000pathparts{i} filesep];
    end
    settings.BCI2000path = BCI2000path;
    clear BCI2000path BCI2000pathparts i
    
    % Add BCI2000 tools to path
    addpath(genpath(fullfile(settings.BCI2000path,'tools')))
    
  2. Set various parameters to be stored in the parameter file. Most of these are standard parameters for BCI2000 stimulus presentation. Here the save path settings.parm_filename stores the location where the parameter file is written.
    %% Settings
    settings.SamplingRate          = '256Hz'; % device sampling rate
    settings.SampleBlockSize       = '8';     % number of samples in a block
    
    settings.PreRunDuration        = '2s';
    settings.PostRunDuration       = '0.5s';
    settings.TaskDuration          = '2s';
    settings.InstructionDuration   = '30s';
    settings.SyncPulseDuration     = '1s';
    settings.BaselineMinDuration   = '0.5s';
    settings.BaselineMaxDuration   = '1.5s';
    settings.NumberOfSequences     = '1';
    settings.StimulusWidth         = '30';
    settings.WindowTop             = '0';
    settings.WindowLeft            = '0';
    settings.WindowWidth           = '640';
    settings.WindowHeight          = '480';
    settings.BackgroundColor       = '0x000000';
    settings.CaptionColor          = '0xFFFFFF';
    settings.CaptionSwitch         = '1';
    settings.WindowBackgroundColor = '0x000000';
    settings.ISIMinDuration        = '0s';
    settings.ISIMaxDuration        = '0s';
    settings.SubjectName           = 'BCI';
    settings.DataDirectory         = fullfile('..','data');
    settings.SubjectSession        = 'auto';
    settings.SubjectRun            = '01';
    settings.parm_filename         = fullfile(settings.BCI2000path,'parms','demo_parms.prm');
    settings.UserComment           = 'Enter user comment here';
    
    settings.InstructionsCaption   = {'Stimulus Presentation Task. Press space to continue'; 'End of task.'};
    
  3. Create a structure with details about the stimuli that will be presented. In this example, we are simply using all of the images in the /prog/images folder. Stimuli can also be *.wav files or *.mp4 files.
    %% Get task images
    task_images = dir(fullfile(settings.BCI2000path,'prog','images','*.bmp'));
    
  4. Initialize the Stimuli matrix. A good practice is to separate your stimuli into "banks" for easy evaluation later. Set n_stimuli to the total number of columns needed in the stimuli matrix (i.e., if there are 10 stimuli, but you have them separated into two banks (task 1-5, and rest 11-15), n_stimuli should be set to 15). In this example there are 103 unique stimuli, but they are separated into a total of 301 columns in the Stimuli matrix The column number of the stimulus in the stimuli matrix corresponds to that stimulus' Stimulus Code. The Stimulus Code is a BCI2000 state that reflects which stimuli were actually presented throughout the experiment, sampled at the same rate as the signal source. Details about each stimulus are stored in the rows. You must include the first 3 rows in the order shown, regardless of whether or not you will present e.g. a caption. Replace the 'audio' row label with 'av' to include *.mp4 files. These rows can be expanded to include additional information about each stimulus, such as an image's mean luminance (this can be useful for analyzing the data later). Each cell needs to be initialized as an empty string for BCI2000 to recognize the Stimuli parameter. The current row labels are described below:
    • caption - text written to the screen by BCI2000
    • icon - the path to a *.bmp or *.png image file
    • audio - the path to a *.wav audio file
    • StimulusDuration - the duration of each individual stimulus
    • AudioVolume - the volume of audio files. Enter a '0' if there are no audio stimuli, as this field must be filled with a value 0-100
    • Category - additional information about the category of a stimulus can be stored, such as whether a stimulus code corresponds to an instruction
    • EarlyOffsetExpression - an expression that will cause BCI2000 to end the current stimulus before StimulusDuration elapses, e.g. KeyDown == 32 (when the subject/experimenter presses the space key)
    %% Set up the different stimuli so they are represented by unique stimulus codes, separated into banks for easy evaluation later
    n_stimuli = 301; % Total events
    n_rows    = 7;
    
    % break down into blocks for easier analysis later
    % 1-50:    image stimuli
    % 101-150: inter-stimulus interval (variable duration)
    % 201:     instructions
    % 301:     sync pulse
    
    % Set up Stimuli
    param.Stimuli.Section         = 'Application';
    param.Stimuli.Type            = 'matrix';
    param.Stimuli.DefaultValue    = '';
    param.Stimuli.LowRange        = '';
    param.Stimuli.HighRange       = '';
    param.Stimuli.Comment         = 'captions and icons to be displayed, sounds to be played for different stimuli';
    param.Stimuli.Value           = cell(n_rows,n_stimuli);
    param.Stimuli.Value(:)        = {''};
    param.Stimuli.RowLabels       = cell(n_rows,1);
    param.Stimuli.RowLabels(:)    = {''};
    param.Stimuli.ColumnLabels    = cell(1,n_stimuli);
    param.Stimuli.ColumnLabels(:) = {''};
    
    param.Stimuli.RowLabels{1}  = 'caption';
    param.Stimuli.RowLabels{2}  = 'icon';
    param.Stimuli.RowLabels{3}  = 'audio';
    param.Stimuli.RowLabels{4}  = 'StimulusDuration';
    param.Stimuli.RowLabels{5}  = 'AudioVolume';
    param.Stimuli.RowLabels{6}  = 'Category';
    param.Stimuli.RowLabels{7}  = 'EarlyOffsetExpression';
    
  5. Populate the Stimuli matrix with details about the task (image) stimuli in columns 1-50. Note that there needs to be at least an empty string for each row.
    %% Study images 1-50
    for idx = 1:length(task_images)
        param.Stimuli.ColumnLabels{idx} = sprintf('%d',idx);
        param.Stimuli.Value{1,idx}      = '';
        param.Stimuli.Value{2,idx}      = sprintf('%s',fullfile('..','prog','images',task_images(idx).name));
        param.Stimuli.Value{3,idx}      = '';
        param.Stimuli.Value{4,idx}      = settings.TaskDuration;
        param.Stimuli.Value{5,idx}      = '0';      
        param.Stimuli.Value{6,idx}      = 'image'; 
        param.Stimuli.Value{7,idx}      = ''; 
    end 
    
  6. Create fixation cross stimuli to act as the 'rest' data. In this example, we create a variable duration rest period by sampling from a distribution of stimulus durations ranging from 0.5-1.5 s. Because BCI2000 presents stimuli for entire processing blocks (block size, e.g., 50 ms) we ensure that the rest period duration options are divisible by BCI2000 block size. Note that the value entered for the 'StimulusDuration' row of each stimulus is a string with the letter 's' (seconds) appended to the end.
    %% inter-stimulus interval (fixation cross) 101-150
    % variable duration from 0.5-1.5s
    SamplingRate = str2double(settings.SamplingRate(1:end-2));
    BlockSize    = str2double(settings.SampleBlockSize);
    MinDuration  = str2double(settings.BaselineMinDuration(1:end-1));
    MaxDuration  = str2double(settings.BaselineMaxDuration(1:end-1));
    for idx = 101:100+length(task_images)
        blockvals = MinDuration:BlockSize/SamplingRate:MaxDuration;
        randval   = randi(length(blockvals));
        duration  = blockvals(randval);
        
        param.Stimuli.ColumnLabels{idx} = sprintf('%d',idx);
        param.Stimuli.Value{1,idx}      = '+';
        param.Stimuli.Value{2,idx}      = '';
        param.Stimuli.Value{3,idx}      = '';
        param.Stimuli.Value{4,idx}      = strcat(num2str(duration,7),'s');
        param.Stimuli.Value{5,idx}      = '0';      
        param.Stimuli.Value{6,idx}      = 'fixation'; 
        param.Stimuli.Value{7,idx}      = ''; 
    end
    
  7. Populate the Stimuli matrix with the instructions. These instructions are captions specified in the "settings" section. Instuctions could alternately be an image of text. Note that captions do not wrap to the second line.
    %% Instructions 201-202
    idx_iter = 1;
    for idx = 201:200+length(settings.InstructionsCaption)
        param.Stimuli.ColumnLabels{idx} = sprintf('%d',idx);
        param.Stimuli.Value{1,idx}      = settings.InstructionsCaption{idx_iter};
        param.Stimuli.Value{2,idx}      = '';
        param.Stimuli.Value{3,idx}      = '';
        param.Stimuli.Value{4,idx}      = settings.InstructionDuration;
        param.Stimuli.Value{5,idx}      = '0';    
        param.Stimuli.Value{6,idx}      = 'instruction'; 
        param.Stimuli.Value{7,idx}      = 'KeyDown == 32'; % space key 
        
        idx_iter = idx_iter + 1;
    end 
    
  8. Include a Stimulus Code for a sync pulse. This could be used to e.g. trigger an output TTL to synchronize recording from a device not connected to BCI2000.
    %% Sync pulse 301
    idx = 301;
    param.Stimuli.ColumnLabels{idx} = sprintf('%d',idx);
    param.Stimuli.Value{1,idx}      = '';
    param.Stimuli.Value{2,idx}      = '';
    param.Stimuli.Value{3,idx}      = '';
    param.Stimuli.Value{4,idx}      = settings.SyncPulseDuration;
    param.Stimuli.Value{5,idx}      = '0';      
    param.Stimuli.Value{6,idx}      = 'sync'; 
    param.Stimuli.Value{7,idx}      = '';    
    
  9. Present images in a random sequence
    %% Sequence
    % 1-50:    image stimuli
    % 101-150: inter-stimulus interval (variable duration)
    % 201:     instructions
    % 301:     sync pulse
    
    randOrder = randperm(length(task_images));
    taskseq   = [];
    for i = 1:length(task_images)
        currentImage = randOrder(i);
        taskseq      = [taskseq (100+currentImage) currentImage];
    end
    
    seq = [301 201 taskseq 202 301]';
    
    param.Sequence.Section      = 'Application';
    param.Sequence.Type         = 'intlist';
    param.Sequence.DefaultValue = '1';
    param.Sequence.LowRange     = '1';
    param.Sequence.HighRange    = '';
    param.Sequence.Comment      = 'Sequence in which stimuli are presented (deterministic mode)/ Stimulus frequencies for each stimulus (random mode)';
    param.Sequence.Value        = cellfun(@num2str, num2cell(seq), 'un',0);
    param.Sequence.NumericValue = seq;
    
  10. Additional parameters can be stored
    %%
    param.SamplingRate.Section         = 'Source';
    param.SamplingRate.Type            = 'int';
    param.SamplingRate.DefaultValue    = '256Hz';
    param.SamplingRate.LowRange        = '1';
    param.SamplingRate.HighRange       = '';
    param.SamplingRate.Comment         = 'sample rate';
    param.SamplingRate.Value           = {settings.SamplingRate};
    
  11. Write parameters to parameter file
    %% write the param struct to a bci2000 parameter file
    parameter_lines = convert_bciprm( param );
    fid = fopen(settings.parm_filename, 'w');
    
    for i=1:length(parameter_lines)
        fprintf( fid, '%s', parameter_lines{i} );
        fprintf( fid, '\r\n' );
    end
    fclose(fid);
    

See Also