function [Resp, RT] = collectResponse(varargin) % function [Resp, RT] = collectResponse(varargin) % % Author: Cendri Hutcherson % Last modified: 05-27-18 % % DESCRIPTION: Waits a specified amount of time for subject to respond, % records response and RT. Note: the RT that is returned is technically the % computer timestamp for when the response occurred, not the relative RT. % Thus, outside this function, to properly calculate the RT, you will want % to collect a timestamp for when a display began, then compare the RT to % that timestamp to get the duration of the response. % % USAGE: collectResponse([waitTime],[moveOn],[allowedKeys],[KbDevice],[PTBParams]) % % EXPLANATION: defaults to infinite waitTime, moving on as soon as any % button is pressed [i.e. moveOn = 1], with any key press triggering the % move. If you wish the program to wait out the remainder of the waitTime % even if a key has been pressed, use moveOn = 0. If you wish the program % to accept as input only certain keys, enter the allowed keys as a cell % (e.g. {'1' '2' '3' '4' '5' or {'RightKey','LeftKey'}. KbDevice specifies % which keyboard to use (default = any keyboard [-1]). % % If you have counterbalancing of key order (e.g., some subjects or trials % response right-left, others left-right, you have the option to recode the % raw response into its interpreted meaning using the KeyOrder field in the % PTBParams structure. For example, PTBParams.KeyOrder = {4 3 2 1} would % remap 1 to 4, 2 to 3, 3 to 2, and 4 to 1. % % The function can also be run in test mode, which allows you to run % through an experiment quickly without having to sit there entering % responses yourself. If this behavior is desired, the PTBParams structure % supplied to the function should have a field called "testMode" which % should be set to true % \ Set defaults \ % i.e., wait forever, move on as soon as an acceptable key has been % pressed, with any key allowed ListenTime = Inf; moveOn = 1; allowedKeys = []; KbDevice = -1; testMode = false; if length(varargin) >= 1 ListenTime = varargin{1}; end if isempty(ListenTime); ListenTime = Inf; end if length(varargin) >= 2 moveOn = varargin{2}; end if isempty(moveOn); moveOn = 1; end if length(varargin) >= 3 allowedKeys = varargin{3}; end if length(varargin) >= 4 KbDevice = varargin{4}; end if length(varargin) >= 5 PTBParams = varargin{5}; if isfield(PTBParams,'testMode') testMode = PTBParams.testMode; end end if ListenTime == Inf && moveOn ~= 1 error(['Infinite loop: You asked me to wait forever, even AFTER the' ... 'subject has responded!']) end % \ Main body \ % The first part of this tests to make sure that the function has not been % called in test mode. If it has not, it will first check to make sure no % key if ~testMode % Start the clock, with null responses as the default StartWaiting = GetSecs(); Resp = 'NULL'; RT = NaN; % Prevent keys that were already pressed before the function started % from triggering a response. This means that the person has to % actually press a NEW key to trigger the end of the cycle. while KbCheck(KbDevice) end % Check to see whether any key will work, or if we are waiting for a % specific key to be pressed. if isempty(allowedKeys) % Initiate a loop to listen for any key press, for as long as is % specified by the variable WaitTime (can be infinite). Once a % key has been pressed, log the key and the RT, and finish. while (GetSecs() - StartWaiting) < ListenTime [keyDown, secs, key] = KbCheck(KbDevice); if keyDown == 1 Resp = KbName(find(key==1)); RT = secs; break; end end else % Initiate a loop to listen for a specific key press, for as long as is % specified by the variable WaitTime (can be infinite). Once a % key has been pressed, log the key and the RT, and finish. while (GetSecs() - StartWaiting) < ListenTime [keyDown, secs, key] = KbCheck(KbDevice); GetMouse(); % needed to keep focus on PTB screen if keyDown == 1 if ~iscell(KbName(find(key==1))) Resp = KbName(find(key==1)); if any(strcmp(allowedKeys,Resp)) RT = secs; break; else Resp = 'NULL'; end end end end end % If no action should be taken immediately after the key press % occurred (i.e., moveOn is false), then wait out the remainder of the % specified wait time before exiting the function if moveOn ~= 1 while (GetSecs() - StartWaiting) < ListenTime end end else % In test mode, the following code will generate random responses, % restricted tothose responses that are possible (or plausible in the % case of any allowed key). The function will also generate % approximately 5% non-responses to mimic behavior of a % semi-inattentive subject. Allows for faster, easier testing. % select a random response if isempty(allowedKeys) allowedKeys = KbName('KeyNames'); allowedKeys = allowedKeys(4:40); % sensible keys end randomKey = allowedKeys{randi(length(allowedKeys),1)}; Resp = randomKey; % select a random RT if isinf(ListenTime) ListenTime = 10; RT = ListenTime*rand(1); else RT = 1.05*ListenTime*rand(1); % generates a small number of missed responses if RT > ListenTime Resp = 'NULL'; RT = NaN; end end RT = GetSecs + RT; WaitSecs(.05); end