我正在使用PsychToolbox在fMRI扫描仪上运行实验。我想收集扫描仪发送的脉冲的时间点。如果您不熟悉此主题:扫描仪会发出信号 - 等同于定义的时间间隔内按键盘数字" 5"。第一个这样的信号启动了代码,以便通过扫描仪进行测量,并且代码的启动时间被同步。我的代码是顺序的,如下所示。我如何实现一个循环,即使我的主代码运行在" -loop?
"我的代码:
% here I wait for the scanner to input a "5", then my code will start
KbQueueCreate;
KbQueueStart;
Trigger = KbName('5%');
keyboard = -1;
AllowedKeys = zeros(256,1); %Disable all keys
AllowedKeys([LeftPress RightPress MiddlePress]) = 1; %Also allow escape always.
while 1
[pressed, firstPress] = KbQueueCheck();
if firstPress(Trigger)
startExperiment = GetSecs;
break
end
end
KbQueueStop;
KbQueueRelease;
% In the foolowing loop, the main experiment runs. here I show a screen,
% signal etc.
for i = 1:nrTrials
% here I would like to have code checking for scanner inputs
end
功能Kbqueuecheck应检查从最近拨打KBQueUestart开始的任何键盘输入。任何输入都非常欢迎。
我认为您要这一切都是错误的。捕获和处理事件的方法更简单,而根本不涉及循环昂贵。具体来说,MATLAB(和八度)通过图形句柄实例最有效地执行此操作(即使您不一定需要图形对象可以看到它)。
这是一个非常简单的示例(我在八度上创建了这个示例,但它也应该在MATLAB上使用)。运行它以查看其工作原理,然后进行相应研究和修改。
%% In file 'run_fMRI_experiment.m'
% Main experiment function
function fMRIData = run_fMRI_experiment
global fMRIData;
F = figure;
text(-1,0,{'Glorious Experiment, she is now runnink, yes?', 'Please to be pressink "5", I will collectink data.', '', 'Please to be pressink key of escapeness when finishedski, da?'}, 'fontsize', 14);
axis off; axis ([-1, 1, -1, 1]);
set (F, 'keypressfcn', @detectFirstKeyPress);
waitfor(F);
end
% subfunctions (i.e. in same file) acting as callback functions
function detectFirstKeyPress(CallerHandle, KeyPressEvent)
if strcmp(KeyPressEvent.Key, '5')
set (CallerHandle, 'keypressfcn', {@detectKeyPresses, tic()});
fprintf ('Experiment started at %sn', datestr(now()));
end
end
function detectKeyPresses (CallerHandle, KeyPressEvent, StartTime)
if strcmp (KeyPressEvent.Key, '5');
global fMRIData; fMRIData(end+1) = toc(StartTime);;
fprintf('"5" pressed at %d seconds.n', fMRIData(end)); return
elseif strcmp (KeyPressEvent.Key, 'escape');
disp ('Escape Pressed. Ending Experiment');
close (CallerHandle);
end
end
ps:请注意,要视为要链接到图的键盘事件,该图必须焦点。IE。在终端处于焦点时,请勿按" 5",这不会计算在内。只需启动函数并在出现窗口时开始按按钮。
ps2:顺便说一句,如果您必须必须对某些任务进行异步执行某些任务,则MATLAB为此提供了批处理功能。也值得一看。
我们使用扫描仪进行类似的操作,因此我提供了使用的功能。我通常只检测到第一个触发启动刺激,并且以后不会读取触发器。但是我确实在后台注册了所有触发器以及其他响应,因此我可以检查事件时有任何问题。
功能,kbqueue.m,几乎可以完成所有相关的响应集,包括背景密钥寄存器。请阅读如何使用不同功能的帮助。代码的某些部分,例如不同的键盘索引,对Mac和Linux的测试均未得到很好的测试。
function varargout = KbQueue(cmd, param)
% KbQueueXXX series functions have some good features:
% 1. detect short key event, like those from fOPR KbCheck may miss;
% 2. detect only interested keys;
% 3. buffer key event while code is running.
%
% But the syntax of KbQueueXXX is a little inconvenient due to the flexibility.
%
% This code tries to provide a convenient wrapper for practical purpose, by
% sacrificing some flexibility, like deviceIndex etc. By default, KbQueue
% detects events for the first device (this doesn't seem the case for Windows,
% where it detects all devices). If you have more than one keyboard, and like to
% detect a different one, you have to add following in your code:
%
% global KbQueueDevice; KbQueueDevice = 2;
%
% where 2 means the second device. You need to find this index for your target
% keyboard.
%
% KbQueue('start', keys);
% - Create a queue and start it. The second input specify the keys to be queued.
% The default are numbers 1~5, both keyboard and keypad numbers. The key names
% adopt those with KbName('UnifyKeyNames'). The 'start' command can be called
% more than once to update new keys. If it is not called, other subfunctions
% will call it with the default keys automatically.
%
% Example:
% KbQueue('start', {'LeftArrow' 'RightArrow'}); % start to queue 2 arrows
%
% The input keys can also be 256-element keyCode, such as that returned by
% KbCheck etc. It can also be the index in the keyCode, such as that returned by
% KbQueue('wait') etc.
%
% nEvents = KbQueue('nEvents' [, 'type']);
% - Return number of events in the queue. The default event type is 'press',
% which returns the number of keypress. It can be 'release' or 'all', which will
% return number of release events or all events.
%
% [pressTime, pressCode] = KbQueue('wait' [, secs or keys]);
% - Wait for a new event, and return key press time and keycode. During the
% wait, pressing Escape will abort code execution, unless 'Escape' is included
% in defined keys.
%
% If there is no second input, this will wait forever till a defined key by
% previous 'start' command is detected.
%
% If the second input is numeric, it will be interpreted as the seconds to wait,
% and wait for a key defined by previous 'start' command is pressed, or the
% seconds has elapsed, whichever is earlier. If there is no any event during the
% time window, both output will be empty. For example:
%
% [t, key] = KbQueue('wait', 1); % wait for 1 sec or a key is detected
%
% If the second input is a string or cellstr, it will be interpreted as the
% key(s) to detect. The provided keys here affects only this call, i.e. it has
% no effect on the queue keys defined by previous 'start' command. For example:
%
% t = KbQueue('wait', {'5%' '5'}); % wait till 5 is pressed
%
% [pressCodeTime, releaseCodeTime] = KbQueue('check' [, t0]);
% - Return first and last press keycode and times for each queued key, and
% optionally release keycode and times. The output will be empty if there is no
% buffered response. Both output are two row vector, with the first row for the
% keycode and second row for times. If the second input t0, default 0, is
% provided, the returned times will be relative to t0. For example:
%
% press = KbQueue('check'); % return 1st and last key press in the queue
% pressedKey = KbName(press(1, :); % convert keycode into key names
%
% KbQueue('flush');
% - Flush events in the current queue.
%
% t = KbQueue('until', whenSecs);
% - This is the same as WaitSecs('UntilTime', whenSecs), but allows to exit by
% pressing ESC. If whenSecs is not provided or is already passed, this still
% checks ESC, so allows use to quit. For example:
% KbQueue('until', GetSecs+5); % wait for 5 secs from now
% KbQueue('until'); % only check ESC exit
%
% [pressCodeTime, releaseCodeTime] = KbQueue('stop' [, t0]);
% - This is similar to 'check' command, but it returns all queued events since
% last 'flush', or since the queue was started. It also stops and releases the
% queue. This provides a way to check response in the end of a session. For
% example:
% KbQueue('start', {'5%' '5'}); % start to queue 5 at beginning of a session
% KbQueue('flush'); % optionally remove unwanted events at a time point
% t0 = GetSecs; % the start time of your experiment
% % run your experiment
% pressCodeTime = KbQueue('stop', t0); % get all keycode and time
% 10/2012 wrote it, xiangrui.li@gmail.com
% 12/2012 try to release queue from GetChar etc, add nEvents
% 11/2014 try to use response device for OSX and Linux
persistent kCode started evts;
global KbQueueDevice; % allow to change in user code
if isempty(started), started = false; end
if nargin<1 || isempty(cmd), cmd = 'start'; end
if any(cmd=='?'), subFuncHelp('KbQueue', cmd); return; end
if strcmpi(cmd, 'start')
if started, BufferEvents; end
if nargin<2
param = {'1' '2' '3' '4' '5' '1!' '2@' '3#' '4$' '5%'};
end
KbName('UnifyKeyNames');
if ischar(param) || iscellstr(param) % key names
kCode = zeros(256, 1);
kCode(KbName(param)) = 1;
elseif length(param)==256 % full keycode
kCode = param;
else
kCode = zeros(256, 1);
kCode(param) = 1;
end
if isempty(KbQueueDevice), KbQueueDevice = responseDevice; end
try KbQueueReserve(2, 1, KbQueueDevice); end %#ok
KbQueueCreate(KbQueueDevice, kCode);
KbQueueStart(KbQueueDevice);
started = true;
return;
end
if ~started, KbQueue('start'); end
if strcmpi(cmd, 'nEvents')
BufferEvents;
n = length(evts);
if n, nPress = sum([evts.Pressed] == 1);
else nPress = 0;
end
if nargin<2, param = 'press'; end
if strncmpi(param, 'press', 5)
varargout{1} = nPress;
elseif strncmpi(param, 'release', 7)
varargout{1} = n - nPress;
else
varargout{1} = n;
end
elseif strcmpi(cmd, 'check')
[down, p1, r1, p2, r2] = KbQueueCheck(KbQueueDevice);
if ~down
varargout = repmat({[]}, 1, nargout);
return;
end
if nargin<2, param = 0; end
i1 = find(p1); i2 = find(p2);
varargout{1} = [i1 i2; [p1(i1) p2(i2)]-param];
if nargout>1
i1 = find(r1); i2 = find(r2);
varargout{2} = [i1 i2; [r1(i1) r2(i2)]-param];
end
elseif strcmpi(cmd, 'wait')
endSecs = GetSecs;
secs = inf; % wait forever unless secs provided
newCode = kCode; % use old keys unless new keys provided
if nargin>1 % new keys or secs provided
if isempty(param), param = inf; end
if isnumeric(param) % input is secs
secs = param;
else % input is keys
newCode = zeros(256, 1);
newCode(KbName(param)) = 1;
end
end
esc = KbName('Escape');
escExit = ~newCode(esc);
newCode(esc) = 1;
changed = any(newCode~=kCode);
if changed % change it so we detect new keys
BufferEvents;
KbQueueCreate(KbQueueDevice, newCode);
KbQueueStart(KbQueueDevice); % Create and Start are twins here :)
else
KbQueueFlush(KbQueueDevice, 1); % flush KbQueueCheck buffer
end
endSecs = endSecs+secs;
while 1
[down, p1] = KbQueueCheck(KbQueueDevice);
if down || GetSecs>endSecs, break; end
WaitSecs('YieldSecs', 0.005);
end
if changed % restore original keys if it is changed
BufferEvents;
KbQueueCreate(KbQueueDevice, kCode);
KbQueueStart(KbQueueDevice);
end
if isempty(p1)
varargout = repmat({[]}, 1, nargout);
return;
end
ind = find(p1);
if escExit && any(ind==esc)
error('User pressed ESC. Exiting ...');
end
varargout = {p1(ind) ind};
elseif strcmpi(cmd, 'flush')
KbQueueFlush(KbQueueDevice, 3); % flush both buffers
evts = [];
elseif strcmpi(cmd, 'until')
if nargin<2 || isempty(param), param = 0; end
while 1
[down, t, kc] = KbCheck(-1);
if down && kc(KbName('Escape'))
error('User pressed ESC. Exiting ...');
end
if t>=param, break; end
WaitSecs('YieldSecs', 0.005);
end
if nargout, varargout = {t}; end
elseif strcmpi(cmd, 'stop')
KbQueueStop(KbQueueDevice);
started = false;
if nargout
BufferEvents;
if isempty(evts)
varargout = repmat({[]}, 1, nargout);
return;
end
isPress = [evts.Pressed] == 1;
if nargin<2, param = 0; end
varargout{1} = [[evts(isPress).Keycode]
[evts(isPress).Time]-param];
if nargout>1
varargout{2} = [[evts(~isPress).Keycode]
[evts(~isPress).Time]-param];
end
end
KbQueueRelease(KbQueueDevice);
else
error('Unknown command: %s.', cmd);
end
function BufferEvents % buffer events so we don't lose them
n = KbEventAvail(KbQueueDevice);
if n<1, return; end
for ic = 1:n
foo(ic) = KbEventGet(KbQueueDevice); %#ok
end
if isempty(evts), evts = foo;
else evts = [evts foo];
end
end
end
function idx = responseDevice
if IsWin, idx = []; return; end % all keyboards
clear PsychHID; % refresh
[ind, pName] = GetKeyboardIndices;
if IsOSX
idx = ind(1); % based on limited computers
else % Linux
for i = length(ind):-1:1
if ~isempty(strfind(pName{i}, 'HIDKeys')) || ...
~isempty(strfind(pName{i}, 'fORP')) % faked, need to update
idx = ind(i);
return;
end
idx = ind(end); % based on limited computers
end
end
end