Creative Commons License R.Muralikrishnan, MPI for Empirical Aesthetics. This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Licence.
##############################################################################
##                                                                          ##
##  Categories Discrimination : Auditory Study with Discrimination Task     ##
##             Experiment Script for use with Presentation                  ##
##                       Experimenter : Pauline Larrouy-Maestri             ##
##                       Author: R. Muralikrishnan                          ##
##                                                                          ##
##############################################################################

##############################################################################
# Scenario Description Language (SDL) Header Part
##############################################################################

# These button codes have to correspond to those set in 'Settings -> Response'
active_buttons = 3;      # There are 3 active buttons defined in 'Settings -> Response'
button_codes = 1,2,3;    # These are the codes of those buttons

response_matching = simple_matching;  # Enables features not in legacy_matching

# Only those button presses that one expects are logged; not all
response_logging = log_active;

# Needed for EEG Trigger files
# write_codes = true;
# pulse_width = 5;
# default_output_port = 1;  # This is most probably going to be the same for everyone.
# This is the LPT Parallel port that you will define in the Port settings tab of
# Presentation.  This will be used as the output port for sending codes to EEG.
# Defining this here saves getting a port number in the PCL part from the output_manager
# and calling send_code etc.  Stimulus events themselves will send these for us, provided we
# define the correct port_codes in each Stimulus event.

# Default Settings for Font, FG and BG Colours etc.
default_background_color = "0, 0, 0"; # RGB codes in decimal; 000 => Black
default_font = "Verdana";
default_font_size = 28;
default_text_color = "000, 102, 153"; # #006699 A bluish colour
default_text_align = align_center;

default_deltat = 0;
default_picture_duration = next_picture; /* This implies that all pictures ...
/...are shown until the next picture is shown, unless otherwise specified */

#default_all_responses = false;

##############################################################################
# Scenario Description Language (SDL) Part
##############################################################################

begin;

# ============================
# SDL Variable Declarations: -
# ============================

#
# We don't use any variables.  All the timer parameters appear as numbers.

# ==============================
# Picture Stimuli Definitions: - Everything that is shown, including text!
# ==============================

# Screen definition for the trial that starts an Experimental Session.
# Used as the first screen that the participant sees.  Operator Controlled.
picture
{
   text { caption = "Welcome! The Experiment will begin in a moment."; };
    x = 0;
    y = 0;
} P_Start_Exp;

# Screen definition for the Pause trial
# Operator Controlled!
picture
{
   text { caption = "Let us continue after a break."; };
    x = 0;
    y = 0;
} P_Pause;

# Screen definition for the Continue trial
# Participant Controlled!
picture
{
    text { caption = "Press a button to continue."; };
    x = 0;
    y = 0;
} P_Continue;

# Screen definition for the Interruption trial
# Operator Controlled!
picture
{
   text { caption = "Sorry for the interruption.  Please wait."; };
    x = 0;
    y = 0;
} P_Interruption;

# Screen definition for showing the focus star
picture
{
  text { caption = "*"; } Txt_Focus_Star;
    x = 0;
    y = 0;
} P_Focus_Star;

# Screen definition for the Discrimination Task
picture
{
    text {
         caption = "Same or Different?"; };
    x = 0;
    y = 0;
} P_Discrimination_Task;

# Screen definition for the End_Thanks trial
picture
{
    text {caption = "Thank you! We're done!";};
    x = 0;
    y = 0;
} P_End_Thanks;

#####

####  Specific for presenting Auditory Stimuli   -------------------------------
# ============================
# Sound Stimuli Definitions: -
# ============================

sound
{
    wavefile
    {
        filename = "";        # PCL Program below fills the file name
        preload = false;
    } Wav_Auditory_Reference;
} S_Auditory_Reference;

####

sound
{
    wavefile
    {
        filename = "";        # PCL Program below fills the file name
        preload = false;
    } Wav_Auditory_Stimulus;
} S_Auditory_Stimulus;

####


#=====================
# Trial Definitions: -
#=====================

/****
/  Note that the name 'Trial' could be misleading.  Unless otherwise specified,
/  what we mean by a 'Trial' here is actually a sub-trial or sub-task that is
/  part - and thereby defines the structure - of an actual experimental trial.
****/

/****
/ Template for defining an SDL Trial.
/ trial
/ {
/      Trial-related parameters such as trial_duration, trial_type etc.
/     Trial-related parameters
/
/     Stimulus event 1 such as picture, sound, video, nothing or force-FB;
/     Stimulus event parameters such as time, event code etc.
/     If the parameters aren't specified, the default values are used!!!
/
/     Stimulus event 2;
/     its parameters...and so on
/ }
****/
trial
{
    trial_duration = 1500;
    all_responses = false;

    stimulus_event
    {
        picture {};     # This is the default picture, which is nothing!
        #code = "CLS";   # Comment this out to avoid seeing this in the logfile.
    };
} T_Blank_Screen;

# Definition of the Trial to start an Experimental Session.
# Operator Controlled!
trial
{
    trial_duration = forever;       # Keep running the trial...
    trial_type = specific_response; # ...until the following specific response.
    terminator_button = 3;          # Operator PC - ENTER key.
    stimulus_event
    {
        picture P_Start_Exp;        # Show P_Start_Exp.
        code = "The Experiment starts now!";
    };
} T_Start_Exp;

# Definition of the Trial to continue further.
# Participant Controlled!
trial
{
    trial_duration = forever;        # Keep running the trial...
    trial_type = specific_response;  # ...until the following specific response.
    terminator_button = 1,2;         # Participant Joystick - L or R button.

    stimulus_event
    {
        picture P_Continue;          # Show P_Continue.
        code = "Trials follow!";
    };
} T_Continue;

# Definition of the Trial to Launch an Experimental Trial
trial
{
    trial_duration = 500;       # Run the trial for 500 ms...
    all_responses = false;      # ..without recognising any key presses.

    stimulus_event
    {
        picture P_Focus_Star;   # Show the focus star for fixation.
        code = "";              # Code set below in the PCL program.
        # port_code = ;         # Port code set by the PCL program.
    } E_Launch_New_Trial;
} T_Launch_New_Trial;

# Definition of the Trial to keep showing the Focus Star for fixation.
trial
{
     trial_duration = 1000;    # Show star until the next clear-screen...
   all_responses = false;      # ..without recognising any key presses.
    stimulus_event
    {
        picture P_Focus_Star;   # Show the focus star for fixation.
        #code = "*"; # Comment this out later to avoid seeing this in the log file.
    };
} T_Keep_Showing_Star;

# Definition of the Trial to show a question for the Discrimination Task
trial
{
   trial_duration = forever;
   trial_type = specific_response;
   terminator_button = 1,2;  # The exact response can be either of these;

    stimulus_event
    {
        picture P_Discrimination_Task;
        code = "Discrimination-Task";     # PCL Program sets this.
        #port_code = 191;                 # Send '191' to EEG
        target_button = 2;                # 2 is good (=> Different), 1 is bad (=> Reference = Stimulus).
    } E_Discrimination_Task;              # This is necessary to test for stimulus_miss later!
} T_Discrimination_Task;

####  Specific for presenting Auditory Stimuli   -------------------------------

# Definition of the Trial to play the Reference Tone sequence
trial
{
    trial_duration = stimuli_length;
    all_responses = false;

    stimulus_event
    {
        sound S_Auditory_Reference;
        code = "";                        # PCL Program sets Event code
        # port_code = ;                   # PCL Program sets port code
    } E_Auditory_Reference;
} T_Auditory_Reference;

####

# Definition of the Trial to play the Stimulus Tone sequence
trial
{
    trial_duration = stimuli_length;
    all_responses = false;

    stimulus_event
    {
        sound S_Auditory_Stimulus;
        code = "";                        # PCL Program sets Event code
        # port_code = ;                   # PCL Program sets port code
    } E_Auditory_Stimulus;
} T_Auditory_Stimulus;

####

# Definition of the Trial to send a code indicating that a Timeout has occurred.
/* When a button-pressed response occurs, 1 or 2 or 3 or 4 is sent to EEG depending upon the button pressed.
   When a timeout occurs, nothing gets sent.  Just in order to have equal number of lines in the
   trigger files later, it's better to send a code indicating timeout.
   (Yes, IN ADDITION to the usual code 199 that will follow.)
*/
/*
trial
{
    trial_duration = 10;
    all_responses = false;

    stimulus_event
    {
        nothing {};                   # This is the default picture, which is nothing!
        code = "Timeout";
        port_code = 5;
    } E_Report_Timeout_to_EEG;
} T_Report_Timeout_to_EEG;
*/

# Definition of the Trial to log the response
trial
{
    trial_duration = 10;
    all_responses = false;

    stimulus_event
    {
        nothing {};
        time = 5;
        # Start this event after 5 ms from the start the trial.
        # This is just to make sure the order in the log-file is correct (due to the nothing task jumping the queue).
        # Basically, nothing is done!
        # code = "Send Code";      # Comment this out to avoid seeing this in the logfile.
        # port_code = 196, 197 or 199, PCL Program fills this!!!
    } E_Log_Response;
} T_Log_Response;

# Definition of the Trial to show a Pause
# Operator Controlled
trial
{
trial_duration = forever;
trial_type = specific_response;
terminator_button = 3;

    stimulus_event
    {
        picture P_Pause;
        code = "Pause";
    };
} T_Pause;

# Definition of the Trial to Interrupt in the middle of a session
# Operator Controlled
trial
{
trial_duration = forever;
trial_type = specific_response;
terminator_button = 3;

    stimulus_event
    {
        picture P_Interruption;
        code = "Interrupted!!!";
    };
} T_Interruption;

# Definition of the Trial to end the session
# Operator Controlled
trial
{
    trial_duration = forever;
    all_responses = false;
    trial_type = first_response;

    stimulus_event
    {
        picture P_End_Thanks;
        code = "End of Session!";
    };
    picture P_End_Thanks;
} T_End_Thanks;

##############################################################################
# Presentation Control Language (PCL) Program Part
##############################################################################

begin_pcl;

#int N_of_Trials = 20;                               # Number of Experimental Trials per Session.
int N_of_Blocks = 5;                                 # Number of Blocks per Session.
#int N_of_Trials_per_Block = N_of_Trials/N_of_Blocks; # Number of Experimental Trials per Block.

bool V_Finish_Block = false;

preset string V_Version; # Prompt the version at the beginning!!!

input_file F_Session_List = new input_file;
F_Session_List.open("Session_List_" + V_Version + ".txt");

# This is the file containing the list of stimuli for one session.
# Each line in this file must correspond to one experimental trial.

####

# Columns of strings in the order found in the input Session List file.
string V_Melody;
int V_Reference_Level;
int V_Stimulus_Level;

int V_Max_Level = 20; # This is the maximum that the stimulus level can go.  Once this is reached, we stop.
int V_Min_Level;      # Since we want the stimulus level to be always higher or equal to the reference level,
                      # we want the minimum level the stimulus can reach to be equal to the reference level in a given block.
                      # Say if the Ref_Level is 10, we don't want combinations such as 10+9 10+8 etc.
                      # Assigned after reading the Ref_Level from the session list below.


int V_Decrement_Value_Before_First_Increment = 2; # Until the participant chooses 'Same' for the first ever time (and therefore leads to an increment),
                                              # we need to DECREMENT the tones...we do this by this step value.
                                              # But after the first ever increment, this value is always 1.

int V_Changes_Needed_To_End_Staircase = 3; # This is the number of change of directions after which the trial will end!


string V_Auditory_Reference;
string V_Auditory_Stimulus;

string V_Previous_Response;
string V_Current_Response;

string V_Previous_Change_of_Direction_Trigger;

int V_Change_of_Direction_Counter = 0;


# output_port O_Port_to_EEG = output_port_manager.get_port(1);
stimulus_data D_Stimulus_Data = stimulus_manager.last_stimulus_data();
int D_Last_Response = response_manager.last_response();

#========================
# Main Experiment Loop: -
#========================
loop     /*** Begin Main Loop 1 for Blocks ***/
    int V_Current_Block = 1

until
    V_Current_Block > N_of_Blocks

begin
    if V_Current_Block == 1 then
        T_Start_Exp.present();
    else
        T_Pause.present();
    end;

    T_Continue.present();
    T_Blank_Screen.present();

    V_Finish_Block = false;
    V_Previous_Response = " ";
    V_Current_Response = " ";

    # In the Discrimination experiment, each line in the session list corresponds to one full block!
    # Because, starting with the reference and stimulus sequences, we will try many combinations
    # entirely based on participants'responses.  These will then be equivalent to the usual trials.

    V_Melody = F_Session_List.get_string(); # Eg: Mel1-2nd-end_P
    V_Reference_Level = F_Session_List.get_int(); # Eg: 0
    V_Stimulus_Level = F_Session_List.get_int(); # Eg: 17


    V_Min_Level = V_Reference_Level;

    V_Auditory_Reference = V_Melody + string(V_Reference_Level);
    V_Auditory_Stimulus = V_Melody + string(V_Stimulus_Level);

        # Do filename and event-code assignments valid during this exp-trial;
        # Load wavefile and bitmap stimuli that are needed for this trial;
        /* We do this before launching the experimental trial so as to keep
            as minimal intereference as possible in trial timings.  You see,
            these things consume processor time, however small they are!
        */
        ####  Specific for presenting Auditory Stimuli   -------------------------------
        Wav_Auditory_Reference.set_filename(V_Auditory_Reference + ".wav");
        Wav_Auditory_Reference.load();

        Wav_Auditory_Stimulus.set_filename(V_Auditory_Stimulus + ".wav");  # This will change based on participant response...see below.
        Wav_Auditory_Stimulus.load();

        E_Auditory_Reference.set_event_code(V_Auditory_Reference + ".wav");
        E_Auditory_Stimulus.set_event_code(V_Auditory_Stimulus + ".wav");    # This will change based on participant response...see below.

        ####

    #==========
    # Loop 2: -
    #==========
    loop    /*** Begin Loop 2 for Trials ***/
        int V_Current_Trial = 1

    until
        V_Finish_Block == true

    begin

        E_Launch_New_Trial.set_event_code("B" + string(V_Current_Block) + " T" + string(V_Current_Trial));

        # Execute the Experimental Trial!!!

        T_Launch_New_Trial.present();                          # Show Star for 500 ms

        /************ Auditory Stimulus Presentation Begins *****************/

        T_Auditory_Reference.present();

        #Txt_Focus_Star.set_caption(string(V_Stimulus_Level));
        #Txt_Focus_Star.redraw();
        T_Keep_Showing_Star.present();

        T_Auditory_Stimulus.present();
        T_Keep_Showing_Star.present();

        #T_Blank_Screen.set_duration(500);
        #T_Blank_Screen.present();
        #T_Blank_Screen.set_duration(1500);


        /************ Auditory Stimulus Presentation Ends *******************/

        /*****************  Task Begins  **********************/

        T_Discrimination_Task.present();

        # Monitor the Response and send appropriate port codes to EEG!!!
        D_Stimulus_Data = stimulus_manager.last_stimulus_data();

        if (D_Stimulus_Data.type() == stimulus_hit) then
            E_Log_Response.set_event_code("Different");
            T_Log_Response.present();

            V_Previous_Response = V_Current_Response;
            V_Current_Response = "Different";

        elseif (D_Stimulus_Data.type() == stimulus_incorrect) then
            E_Log_Response.set_event_code("Similar");
            T_Log_Response.present();

            V_Previous_Response = V_Current_Response;
            V_Current_Response = "Same";

        elseif (D_Stimulus_Data.type() == stimulus_miss) then
            E_Log_Response.set_event_code("Timeout");
            T_Log_Response.present();
        end;



        /*****************  Task Ends  **********************/

        T_Blank_Screen.present();

        if (V_Current_Response == "Different" && V_Previous_Response != "Different") then
			# Do the same trial again
			# No change of Direction

			# Do nothing...That is, present the same combination again without changes;



		  elseif (V_Current_Response == "Different" && V_Previous_Response == "Different") then
				# We've played the same set of reference+stimulus two times, and the participant answered that they are different both times.
				# Now we need to DECREMENT the stimulus sequence once and play the new combination.
				# That is, change of direction ... down.

				# Increment the change of direction counter only if the previous change of direction was caused by a different response.
				# The idea is, we only increment the counter for changes of directions (i.e., caused by different responses).
				# Changes in the same direction don't count...in that case, don't do anything with the counter; (but not also reset it....untlike the longstaircase version).
				if (V_Previous_Change_of_Direction_Trigger != V_Current_Response) then
					V_Change_of_Direction_Counter = V_Change_of_Direction_Counter + 1;
				# Long-Staircase version
				#else
				#	V_Change_of_Direction_Counter = 1;
				end;

				V_Previous_Change_of_Direction_Trigger = "Different";



				if (V_Stimulus_Level <= V_Min_Level) then
				  # We have reached the lowest extreme...we cannot go down any further!
				  # So we set the flag to end the block.
					 V_Finish_Block = true;

				else
					V_Stimulus_Level = V_Stimulus_Level - V_Decrement_Value_Before_First_Increment;  # Will be reset to 1 the first time participant presses 'Same'.
					V_Auditory_Stimulus = V_Melody + string(V_Stimulus_Level);

					Wav_Auditory_Stimulus.set_filename(V_Auditory_Stimulus + ".wav");
					Wav_Auditory_Stimulus.load();

					E_Auditory_Stimulus.set_event_code(V_Auditory_Stimulus + ".wav");
				end;



				V_Current_Response = " "; # Reset this, because, when Different is also pressed the next time,
				V_Previous_Response = " "; # we should not land here, for every time different is pressed, the same tone has to be played twice!




		   elseif (V_Current_Response == "Same") then
				# We don't need to play the same combination again.
				# Instead, we need to INCREMENT the stimulus sequence once and play the new combination.
				# That is, change of direction ... up.

				# If 'Same' is pressed, then we are going to increment the tone.
				# And as soon as the first increment happens, from the next time,
				# the decrement should always be 1.  So reset V_Decrement_Value_Before_First_Increment = 1;
				if (V_Decrement_Value_Before_First_Increment > 1) then
					V_Decrement_Value_Before_First_Increment = 1;
				end;



				# Increment the change of direction counter only if the previous change of direction was caused by a different response.
				# The idea is, we only increment the counter for Consecutive changes of directions (i.e., caused by different responses).
				# Changes in the same direction don't count..., don't do anything with the counter; (but not also reset it....untlike the longstaircase version).
				if (V_Previous_Change_of_Direction_Trigger != V_Current_Response) then
					V_Change_of_Direction_Counter = V_Change_of_Direction_Counter + 1;
				# Long-Staircase version
				#else
				#	V_Change_of_Direction_Counter = 1;
				end;


				V_Previous_Change_of_Direction_Trigger = "Same";


				if (V_Stimulus_Level == V_Max_Level) then
				  # We have reached the highest extreme...we cannot go up any further!
				  # So we set the flag to end the block.
					 V_Finish_Block = true;

				else
					V_Stimulus_Level = V_Stimulus_Level + 1;
					V_Auditory_Stimulus = V_Melody + string(V_Stimulus_Level);

					Wav_Auditory_Stimulus.set_filename(V_Auditory_Stimulus + ".wav");
					Wav_Auditory_Stimulus.load();

					E_Auditory_Stimulus.set_event_code(V_Auditory_Stimulus + ".wav");
				end;


		   end;

       if V_Change_of_Direction_Counter > V_Changes_Needed_To_End_Staircase then
			V_Finish_Block = true;
		 end;

        # Go to the Next Trial
        V_Current_Trial = V_Current_Trial + 1;



    #===========
    # End Loop 2
    #===========
    end; /*** End Loop 2 for Trials ***/


        ####  Specific for presenting Auditory Stimuli   -------------------------------
        # Unload all memory occupiers such as wavefile stimuli.
        Wav_Auditory_Reference.unload();
        Wav_Auditory_Stimulus.unload();
        ####

# Go to the Next Block
V_Current_Block = V_Current_Block + 1;

#================
# End Main Loop 1
#================
end;    /*** End Main Loop 1 for Blocks ***/

# Close all the open files
F_Session_List.close();

# Finish off the session!!!
T_End_Thanks.present();