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);
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.
In a loop I want to use a counter to use different variables.
The problem is that both use the % symbol: %VAR%
Showing where I get the error %%, Simplified:
textGH1 = Peter
textGH2 = rocks.
i = 1
Loop, 2
{
SendInput {Enter}
Sleep 50
SendInput, %textGH%i%%
Sleep 50
SendInput {Enter}
i++
}
Error: Empty variable reference %%
What I tried:
I have looked up arrays for autohotkey, but I only found
pseudo-arrays using the %COUNTER% as a workaround, similiar to how I
did it. Second, I have assigned a variable in the loop to your current variable:
text := textGH%i%
However this doesn't call the variable, as expected (needed %textGH%i%% again)
I think I understand what you are trying? Swap variables when the counter changes. Easiest way to do this is with an Array.
Here's a solution for you play with:
Define Array. Array's Index values automatically, so Peter is at Index 1 Rocks is at Index 2.
MyArray := ["Peter", "Rocks"]
Loop amount of times as there are Values in your Array.
Loop, % MyArray.MaxIndex() {
SendInput {Enter}
Sleep 50
Send your Array at [Index] A_Index is a built in counter for Loops.
SendInput % MyArray[A_Index]
Sleep 50
SendInput {Enter}
}
Hope this helps.
This should work:
SendInput, %textGH%%i%
This is my code
IniRead, custommessage1, whisperconfig.ini, messages, Message1
IniRead, custommessage2, whisperconfig.ini, messages, Message2
^NumPad1::whispermessage(1)
whispermessage(var){
finalwhisper := "custommessage" + var ;this equals custommessage1
Msgbox, %finalwhisper%
BlockInput On
SendInput ^{Enter}%finalwhisper%{Enter} ;<-- problem
BlockInput Off
return
}
So in the first line i am importing the value of custommessage1 (it could be "hi im henrik"). This is what i wish to end up getting as a output.
Inside the function i want the var (which is 1 in this case) to be merged with a variable called custommessage ending with a result of custommessage1
i want the endresult to do a SendInput %custommessage1%.
this way i can have one function for up to 9 triggers including var numbers.
Can anyone help? i am sure this is fairly simple however i am new to this coding thing so bear with me.
Your function only knows about its own variables, the ones you pass in as parameters, and global variables.
You're trying to access custommessage1, which is outside the function and isn't global. You either need to make all your variables global, or pass them in to the function.
I suggest the latter, using an array. Here's an example, make sure you're running the latest version of AHK for this to work properly.
IniRead, custommessage1, whisperconfig.ini, messages, Message1
IniRead, custommessage2, whisperconfig.ini, messages, Message2
; Store all messages in an array
messageArray := [custommessage1, custommessage2]
; Pass the array and message you want to print
^NumPad1::whispermessage(messageArray, 1)
whispermessage(array, index){
; Store the message at the given index in 'finalwhisper'
finalwhisper := array[index]
Msgbox, %finalwhisper% ; Test msgbox
; Print the message
BlockInput On
SendInput ^{Enter}%finalwhisper%{Enter}
BlockInput Off
}
Alternatively, and this is outside the scope of your question, you could load the keys from the .ini file dynamically, meaning you wouldn't have to create new variables each time you added a key/value pair.
Here's how you could do that:
; Read all the key/value pairs for this section
IniRead, keys, whisperconfig.ini, messages
Sort, keys ; Sort the keys so that they are in order
messageArray := [] ; Init array
; Loop over the key/value pairs and store all the values
Loop, Parse, keys, `n
{
; Trim of the key part, and only store the value
messageArray.insert(RegExReplace(A_LoopField, "^.+="))
}
; Pass the array and message you want to print
^NumPad1::whispermessage(messageArray, 1)
whispermessage(array, index){
; Store the message at the given index in 'finalwhisper'
finalwhisper := array[index]
Msgbox, %finalwhisper% ; Test msgbox
; Print the message
BlockInput On
SendInput ^{Enter}%finalwhisper%{Enter}
BlockInput Off
}
So I wanted to check if certain application exists or not.
If it does and there was no input within 4.5 min, switch to that app and perform some task.
Basically an AFK cheater.
This is what I have so far:
#SingleInstance force
#Persistent
settimer, idleCheck, 1000; check every second
return
idleCheck:
if WinExist(App Name with Spaces); if app is running
{
if(A_TimeIdle >= 270000); and there was no input in 4.5 min
{
WinActivate; switch to that app
sendInput z; and perform an action
}
}
return
Now obviously that doesn't work since I'd not be posting here otherwise.
The question is very simple yet I couldn't find an answer.
Thanks in Advance.
WinExist is a function and function parameters are expressions...
In expressions you need to use double quotes " around strings and you don't need % around variables
you also need to have a space before the semicolon to use comments
#SingleInstance force
#Persistent
settimer, idleCheck, 1000 ; check every second
return
idleCheck:
if WinExist("App Name with or without Spaces") ; if app is running
{
if(A_TimeIdle >= 270000) ; and there was no input in 4.5 min
{
WinActivate ; switch to that app
sendInput z ; and perform an action
}
}
return
Hope it helps
In my program , suppose I fix no. of users N=6 .
Threshold value of SIR SIRo=6; Output generated is SIRmean=10 (suppose).
I want to make a loop so that
if SIRmean < SIRo
disp N=6
else
decrease the counter till SIRmean> SIRo
and display the value of N for which this condition holds true.
end
You can use simple for loop with downcounter and break on condition:
N=6;
for k=N:-1:0
SIRmean = calc_SIR_mean(N);
if SIRmean < SIRo
disp(['N=' k])
break;
end
end
Function calc_SIR_mean should return mean value based on number of users (it is not clear from your question how you receive this value).