0

I have an experiment that collects keypresses ('x' or 'n') and reaction time in response to a stimulus display. If the participant presses either button once per trial the experiment runs fine. However, if they press the keyboard repeatedly or hold down a key, it will often crash. (I work with children and even if we ask them not to, this often will still happen).

The error that comes up when it crashes is:

Function is not defined for 'cell' inputs.

Error in Experiment (line 682)

fprintf(dataFile, formatString, SJNB, Date, Age, block, trial, trialFaceLabel, trialLoadLabel, Target, keyStroke, tStart, keyTime, Correct, RT, FaceGen);

Although it says 'Function is not defined for 'cell' inputs' (which is related to this post), this function does appear to work properly at all other times, so I can't see that it is simply not defined properly. It is only when too many keypresses have been pressed in a row (e.g. if the key has been held down), that this error occurs.

What changes can I make to ensure the experiment is robust and doesn't crash, even if there are multiple keypresses per trial? Any help would be appreciated.

I have included the code below.

Here's some additional info, in case this is helpful:

  • Prior to the experimental trial there is a practice loop which is set up in very much the same way but with two main differences, 1) the stimuli displayed to the screen are different and 2) the keypresses are not recorded. This loop doesn't seem to ever crash.

  • After crashing, the keypress responses are printed to the command line.

  • I have had a look at other similar posts, e.g. this post, but as far as I understand it, the keypress loop that I have written should also exit as soon as a key has been pressed - so I'm not sure why mine is not working in the same way.

The code below is all included in an experimental loop. What it's doing is: 1) searching for a keypress 2) calculating response time based on the keypress 3) beeping if the response was incorrect 4) printing to file (this is just before the end of the trial) (this is the line that the error references)

            %searching for keypress
            timedout = false;
            pressed = 0;
            %while pressed < 1;

            while ~timedout && ~pressed

                [keyIsDown, keyTime, keyCode] = KbCheck;

                if keyIsDown && ((keyTime-tStart) < max_stimulus_shown)
                    keyStroke = KbName(keyCode);
                    if any(strcmpi(keyStroke,leftKey)) || any(strcmpi(keyStroke,rightKey)) %|| any(strcmpi(keyStroke,exitKey))
                        pressed = 1;
                        WaitSecs(remainer-(keyTime-stimulus_shown));
                        break;
                    elseif any(strcmpi(keyStroke,exitKey))
                    disp('*** Experiment terminated ***');
                        break;
                        sca;
                    end
                elseif ((keyTime-tStart) > max_stimulus_shown)
                keyStroke = 'None';
                timedout = true;
                RT = 0;
                Correct = 0;
                pressed = 2; % 2 = not pressed
                KbQueueStop(); 
                end
            end 

            %calculate response times
            if pressed == 1 && (~timedout)
            RT = round((keyTime-tStart)*1000); % RT in ms
            if any(strcmpi(keyStroke,leftKey)) % pressed left (X)
                if any(strcmpi(Target, 'X')) % target was an X
                    Correct = 1;
                else % target was X, but pressed right (N)
                    Correct = 0;
                end
            elseif any(strcmpi(keyStroke,rightKey)) % they pressed right 
                if any(strcmpi(Target, 'N')) % target was an N
                    Correct = 1;
                else % target was N, but pressed right
                    Correct = 0;
                end
            elseif any(strcmpi(keyStroke,exitKey))
                disp('ESC');
                break;
            end
            end

            Screen('TextSize',Screen_wid, star_size);
            DrawFormattedText(Screen_wid, '.', 'center', 'center');
            WaitSecs(feedback);
            Screen('Flip', Screen_wid);

            %say when to beep                
            if Correct == 0 && ~timedout
            PsychPortAudio('Start', pahandle, repetitions, startCue, waitForDeviceStart);
            WaitSecs(remainer-beepLengthSecs);
            elseif Correct == 0 && timedout
            PsychPortAudio('Start', pahandle, repetitions, startCue, waitForDeviceStart);
            Screen('TextSize',Screen_wid, text_size);
            DrawFormattedText(Screen_wid, 'missed trial', 'center', 'center');
            Screen('Flip', Screen_wid);
            WaitSecs(beepLengthSecs+feedback);
            elseif Correct == 1
            WaitSecs(remainer+beepLengthSecs);
            end

            %WaitSecs(stimulus_shown); %stimulus shown for 0.2 seconds
            Screen('Flip', Screen_wid);

            dataFile = fopen(dataFileName, 'a');                               
            fprintf(dataFile, formatString, SJNB, Date, Age, block, trial, trialFaceLabel, trialLoadLabel, Target, keyStroke, tStart, keyTime, Correct, RT, FaceGen);
            fclose(dataFile);   
C_River
  • 11
  • 3
  • Most likely one of your inputs to the said `fprintf` method is a cell array, and YOU have to find out which one, as this is only possible in the whole context. Maybe one of the variables collecting the keypresses becomes a cell array when the key is constantly pressed, but is a single value, when the key is pressed for a short time!? – HansHirse Mar 13 '19 at 09:50

1 Answers1

0

The function KbName returns the name string of key(s) input, via either a vector of key codes ( key_name = KbName([21 22]) ), or a logical vector, as returned by KbCheck.

If only only key is provided / is True, it is returned as a string. But, if more than one key is pressed at the same time, it is returned as a cell array of strings.

For example, look at the output of:

% for key name consistency across operating systems
KbName('UnifyKeyNames')

% one key
KbName(21)

ans =

r

% more than one key
KbName([21 22])

ans =

  1×2 cell array

    'r'    's'

Your issue is that when more than one key is pressed at the same time (with respect to the key checking loop), keyStroke is a cell array containing the keys that were pressed. You seem to be processing the cell array appropriately within most of your code, with the exception of the call to fprintf, which expects a single string, not a cell array of strings.

If you want to save a record of all keys pressed, you can convert the cell array of strings to a single string with delimiter between elements, for example by adding the following line prior to the call to fprintf:

if iscell(keyStroke)
    keyStroke = strjoin(keyStroke, '_');
end

But, also keep in mind that you might want to change the logic of your experiment to reject trials in which two keys were pressed, or otherwise treat them differently than single key responses. You currently have the 'left' key taking precedence, in other words if the participant has the left key pressed and the target is an 'X', the trial will be marked correct, even if the right key is also pressed. Conversely if the participant has the left key pressed and the target is a 'N', the trial will be marked as incorrect, even if the right key is also pressed.

DMR
  • 1,479
  • 1
  • 8
  • 11
  • Thank you, this is very helpful! I added the lines that you suggested. I also added a new output variable indicating when keyStroke is a cell (i.e. more than one key has been pressed simultaneously), so that I can then discard those trials from the analysis. – C_River Mar 14 '19 at 15:20
  • Incidentally, this has got me thinking – it would actually be even better if I could identify when participants were pressing buttons randomly in general, as this would show they were not really responding to the stimulus at all, and so should be discarded. Based on your suggestions I’ll be able to exclude trials where they press two keys at once, but if they are alternately pressing the buttons, this will look like a valid response in the results. – C_River Mar 14 '19 at 15:21
  • Do you know if there is a way of doing this? It seems to be me that this might be possible if there is a way of recording all keypresses over the course of a trial. But, I’m not sure if this is possible based on the Kb functions. I am still thinking about the change in logic that you mention - but the above changes seem to have solved the crashing! :) – C_River Mar 14 '19 at 15:21
  • You're correct that in your current code, if a participant presses two buttons in succession within trial, the first button press would make the 'pressed' variable true, and further keypresses within the trial wouldn't be collected. Often, random responding is taken care of at the participant level, essentially if overall performance is at some threshold near chance, all of the data from that participant is excluded But, you can also record multiple keypresses within a trial (I'll describe in a subsequent comment). – DMR Mar 15 '19 at 01:51
  • Essentially what you would want to do is, don't stop polling after a keypress has been registered as you are now, instead add each keypress to a vector or cell array of presses for that trial. To facilitate this, also add a call to KbReleaseWait() after each keypress is detected, this will prevent multiple keypresses being erroneously detected from a single press, for example if the participant is holding a key down. – DMR Mar 15 '19 at 01:54