使用kbqueuecheck连续检查设备输入



我正在使用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

相关内容

  • 没有找到相关文章

最新更新