Skip to content

Commit c6b988b

Browse files
authored
Merge pull request #2 from Remi-Gau/remi-change_keyboard_defaults
change to keyboards function and defaults
2 parents 1909cee + f91826c commit c6b988b

19 files changed

+582
-180
lines changed

.travis.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Travis CI (https://travis-ci.org/)
2+
3+
language: c
4+
dist: bionic
5+
cache:
6+
apt: true # only works with Pro version
7+
8+
env:
9+
global:
10+
- OCTFLAGS="--no-gui --no-window-system --silent"
11+
12+
before_install:
13+
- travis_retry sudo apt-get -y -qq update
14+
- travis_retry sudo apt-get -y install octave
15+
- travis_retry sudo apt-get -y install liboctave-dev
16+
17+
install:
18+
- octave $OCTFLAGS --eval "addpath (pwd); savepath ();"
19+
20+
before_script:
21+
# Change current directory
22+
- cd tests
23+
24+
script:
25+
- octave $OCTFLAGS --eval "results = runtests; assert(all(~[results.Failed]))"

README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,30 @@ You can then use the [matlab package manager](https://github.com/mobeets/mpm), t
7070
end
7171
```
7272

73+
## Setting up keyboards
74+
75+
To select a specific keyboard to be used by the experimenter or the participant, you need to know
76+
the value assigned by PTB to each keyboard device.
77+
78+
To know this copy-paste this on the command window:
79+
80+
``` matlab
81+
[keyboardNumbers, keyboardNames] = GetKeyboardIndices;
82+
83+
disp(keyboardNumbers);
84+
disp(keyboardNames);
85+
```
86+
87+
You can then assign a specific device number to the main keyboard or the response box in the `cfg` structure
88+
89+
- `cfg.keyboard.responseBox` would be the device number of the device used by the participant to give his/her
90+
response: like the button box in the scanner or a separate keyboard for a behavioral experiment
91+
- `cfg.keyboard.keyboard` would be the device number of the keyboard on which the experimenter will type or
92+
press the keys necessary to start or abort the experiment.
93+
94+
`cfg.keyboard.responseBox` and `cfg.keyboard.keyboard` can be different or the same.
95+
96+
Using empty vectors (ie `[]`) or a negative value for those means that you will let PTB find and use the default device.
7397

7498
## Structure and function details
7599

@@ -106,23 +130,27 @@ It is wrapper function to use `KbQueue` which is definitely what you should used
106130

107131
You can easily collect responses while running some other code at the same time.
108132

109-
It will only take responses from the `response box` which can simply be the "main keyboard" or
110-
another keyboard connected to the computer or the response box that the participant is using.
133+
It will only take responses from one device which can simply be the "main keyboard"
134+
(the default device that PTB will find) or another keyboard connected to the computer
135+
or the response box that the participant is using.
111136

112137
You can use it in a way so that it only takes responses from certain keys and ignore others (like
113138
the triggers from an MRI scanner).
114139

115140
If you want to know more on how to use it check its help section and the `CPP_getResponseDemo.m`.
116141

117-
To select a specific keyboard to be used by the experimenter or the participant, you need to know
118-
the value assigned by PTB to each keyboard device.
142+
In brief, there are several actions you can execute with this function.
119143

120-
To know this copy-paste this on the command window:
144+
- init: initialize the buffer for key presses on a given device (you can also specify the keys of interest that should be listened to).
145+
- start: start listening to the key presses (carefully insert into your script - where do you want to start buffering the responses).
146+
- check: till that point, it will check the buffer for all key presses.
147+
- It only reports presses on the keys of interest mentioned at initialization.
148+
- It **can** also check for presses on the escape key and abort if the escape key is part of the keys of interest.
149+
- flush: Empties the buffer of key presses in case you want to discard any previous key presses.
150+
- stop: Stops buffering key presses. You can still restart by calling "start" again.
151+
- release: Closes the buffer for good.
121152

122-
[keyboardNumbers, keyboardNames] = GetKeyboardIndices;
123153

124-
keyboardNumbers
125-
keyboardNames
126154

127155
### deg2Pix
128156

checkAbort.m

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
1-
function checkAbort(cfg)
1+
function checkAbort(cfg, deviceNumber)
22
% Check for experiment abortion from operator
3+
% When no deviceNumber is set then it will check the default device
4+
% When an abort key s detected this will set a global variable and throw a
5+
% specific error that can then be catched.
6+
%
7+
% Maint script
8+
% try
9+
% % Your awesome experiment
10+
% catch ME % when something goes wrong
11+
% switch ME.identifier
12+
% case 'checkAbort:abortRequested'
13+
% % stuff to do when an abort is requested (save data...)
14+
% otherwise
15+
% % stuff to do otherwise
16+
% rethrow(ME) % display the error
17+
% end
18+
% end
319

4-
[keyIsDown, ~, keyCode] = KbCheck(cfg.keyboard);
20+
if nargin < 1 || isempty(cfg)
21+
error('I need at least one input.')
22+
end
523

6-
if keyIsDown && keyCode(KbName(cfg.escapeKey))
7-
8-
global stopEverything
9-
stopEverything = true;
10-
11-
cleanUp();
24+
if nargin < 2 || isempty(deviceNumber)
25+
deviceNumber = -1;
26+
end
27+
28+
[keyIsDown, ~, keyCode] = KbCheck(deviceNumber);
29+
30+
if keyIsDown && keyCode(KbName(cfg.keyboard.escapeKey))
1231

13-
error('Escape key press detected: aborting experiment.')
32+
errorAbort();
1433

1534
end
1635

demos/CPP_checkAbortDemo.m

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
1-
cd ..
1+
% add parent directory to the path (to make sure we can access the CPP_PTB
2+
% functions)
3+
addpath(fullfile(pwd, '..'))
24

3-
cfg.keyboard = [];
4-
cfg.escapeKey = 'ESCAPE';
5+
% set up
6+
cfg.keyboard.escapeKey = 'ESCAPE';
7+
8+
% beginning of demo
59
KbName('UnifyKeyNames');
610

7-
% stay in the loop until the escape key is pressed
8-
while GetSecs < Inf
11+
12+
try
13+
14+
% stay in the loop until the escape key is pressed
15+
while GetSecs < Inf
16+
17+
checkAbort(cfg)
18+
19+
end
20+
21+
catch ME
922

10-
checkAbort(cfg)
23+
switch ME.identifier
24+
case 'checkAbort:abortRequested'
25+
warning('You pressed the escape key: will try to fail gracefully.')
26+
fprintf('\nWe did catch your abort signal.\n')
27+
otherwise
28+
rethrow(ME) % display other errors
29+
end
1130

1231
end

demos/CPP_getResponseDemo.m

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,100 @@
11
%% Demo showing how to use the getResponse function
22

33
% This small script shows how to use the getReponse function
4-
% (a wrapper around the KbQueue function from PTB)
54

6-
% start with a clean slate
7-
cd ..
8-
clear; clc;
5+
% add parent directory to matlab path (so we can access the CPP_PTB functions)
6+
addpath(fullfile(pwd, '..'))
7+
8+
9+
%% start with a clean slate
10+
clear;
11+
clc;
12+
913
if IsOctave
1014
more off % for a better display experience
1115
end
1216

13-
%% set parameters
17+
% use the default set up (use main keyboard and use the ESCAPE key to abort)
18+
cfg = setDefaultsPTB;
1419

15-
% cfg.responseBox would be the device number of the device used by the participant to give his/her
16-
% response: like the button box in the scanner or a separate keyboard for a behavioral experiment
17-
%
18-
% cfg.keyboard would be the device number of the keyboard on which the experimenter will type or
19-
% press the keys necessary to start or abort the experiment.
20+
% show the default option
21+
disp(cfg.keyboard)
2022

21-
% cfg.responseBox and cfg.keyboard can be different or the same.
23+
%% set parameters
2224

23-
% If you want to know the device number of all the keyboards and responses
24-
% boxes connected to the computer you can use the following code.
25-
% [cfg.keyboardNumbers, cfg.keyboardNames] = GetKeyboardIndices
25+
% Change the values set by defaults (for more info about the keyboard see the doc)
26+
% cfg.keyboard.keyboard = ??
27+
% cfg.keyboard.responseBox = ??
28+
% cfg.keyboard.escapeKey = ??
2629

27-
% Using empty vectors should work for linux when to select the "main"
28-
% keyboard. You might have to try some other values for Windows. To
29-
% assigne a specific keyboard input the kb assigned value (see README)
30-
cfg.keyboard = [];
31-
cfg.responseBox = [];
30+
% Decide which device you want to collect responses from
31+
deviceNumber = []; % default device (PTB will find it for you)
32+
% deviceNumber = cfg.keyboard.keyboard; % the one you may have chosen as the main keyboard
33+
% deviceNumber = cfg.keyboard.responseBox; % the one you may have chosen as the response box
3234

33-
cfg.escapeKey = 'ESCAPE';
35+
% if you want getResponse to ignore the key release
36+
getOnlyPress = 1;
3437

3538
% We set which keys are "valid", any keys other than those will be ignored
36-
expParameters.responseKey = {};
39+
cfg.keyboard.responseKey = {'a', 'b'};
3740

41+
% This would make sure that you listen to presses of the escape key
42+
cfg.keyboard.responseKey{end+1} = cfg.keyboard.escapeKey;
3843

39-
%% init
4044

41-
% Keyboard
42-
% Make sure keyboard mapping is the same on all supported operating systems
43-
% Apple MacOS/X, MS-Windows and GNU/Linux:
44-
KbName('UnifyKeyNames');
45-
46-
47-
% we ask PTB to tell us which keyboard devices are connected to the computer
48-
[cfg.keyboardNumbers, cfg.keyboardNames] = GetKeyboardIndices;
49-
50-
cfg.keyboardNumbers
51-
cfg.keyboardNames
45+
%% Final checks
5246

47+
% Make sure keyboard mapping is the same on all supported operating systems
48+
KbName('UnifyKeyNames');
5349

5450
% Test that the keyboards are correctly configured
5551
testKeyboards(cfg);
5652

57-
% Give the time to the test key to be released and not listened
53+
% Give the time to the test key to be released and not listened to
5854
WaitSecs(1);
5955

60-
6156
fprintf('\nDuring the next 5 seconds we will collect responses on the following keys: \n\n');
62-
if isempty(expParameters.responseKey)
57+
if isempty(cfg.keyboard.responseKey)
6358
fprintf('\nALL KEYS\n\n');
6459
else
65-
for iKey=1:numel(expParameters.responseKey)
66-
fprintf('\n%s', expParameters.responseKey{iKey});
60+
for iKey=1:numel(cfg.keyboard.responseKey)
61+
fprintf('\n%s', cfg.keyboard.responseKey{iKey});
6762
end
6863
fprintf('\n\n');
6964
end
7065

7166

7267
%% Run demo
7368

69+
try
70+
7471
% Create the keyboard queue to collect responses.
75-
getResponse('init', cfg, expParameters, 1);
72+
getResponse('init', deviceNumber, cfg);
7673

7774
% Start collecting responses for 5 seconds
7875
% Each new key press is added to the queue of events recorded by KbQueue
7976
startSecs = GetSecs();
80-
getResponse('start', cfg, expParameters, 1);
77+
getResponse('start', deviceNumber);
8178

8279
% Here we wait for 5 seconds but are still collecting responses.
8380
% So you could still be doing something else (presenting audio and visual stim) and
8481
% still collect responses.
8582
WaitSecs(5);
8683

8784
% Check what keys were pressed (all of them)
88-
responseEvents = getResponse('check', cfg, expParameters, 0);
89-
90-
% The following line would only return key presses and not releases
91-
% responseEvents = getResponse('check', cfg, expParameters, 1);
85+
% If the escapeKey was pressed at any time, it will only abort when you
86+
% getResponse('check')
87+
responseEvents = getResponse('check', deviceNumber, cfg, getOnlyPress);
9288

9389
% This can be used to flush the queue: empty all events that are still present in the queue
94-
getResponse('flush', cfg, expParameters, 1);
90+
getResponse('flush', deviceNumber);
91+
92+
% If you wan to stop listening to key presses. You could start listening again
93+
% later by calling: getResponse('start', deviceNumber)
94+
getResponse('stop', deviceNumber);
9595

96-
% If you wan to stop listening to key presses.
97-
getResponse('stop', cfg, expParameters, 1);
96+
% If you wan to destroyt the queue: you would have to initialize it again
97+
getResponse('release', deviceNumber);
9898

9999

100100
%% Now we look what keys were pressed and when
@@ -106,9 +106,29 @@
106106
eventType = 'released';
107107
end
108108

109-
fprintf('\n%s was %s at time %.3f seconds\n', ...
110-
responseEvents(iEvent).key_name, ...
109+
fprintf('\n %s was %s at time %.3f seconds\n', ...
110+
responseEvents(iEvent).keyName, ...
111111
eventType, ...
112112
responseEvents(iEvent).onset - startSecs);
113113

114114
end
115+
116+
117+
catch ME
118+
119+
getResponse('release', deviceNumber)
120+
121+
switch ME.identifier
122+
123+
case 'getResponse:abortRequested'
124+
warning('You pressed the escape key: will try to fail gracefully.')
125+
126+
fprintf('\nWe did catch your abort signal.\n')
127+
128+
otherwise
129+
rethrow(ME) % display other errors
130+
131+
end
132+
end
133+
134+

demos/CPP_pressSpace4meDemo.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
% add parent directory to the path (to make sure we can access the CPP_PTB
2+
% functions)
3+
addpath(fullfile(pwd, '..'))
4+
5+
% beginning of demo
6+
KbName('UnifyKeyNames');
7+
8+
% press the key "space" to "start" the experiment
9+
pressSpace4me

demos/CPP_wait4TriggerDemo.m

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
cd ..
2-
3-
cfg.device = 'Scanner';
4-
1+
% set up
2+
cfg.testingDevice = 'scanner';
53
cfg.numTriggers = 4;
4+
cfg.triggerKey = 't';
5+
6+
% this field is not required but can be mentioned
7+
cfg.MRI.repetitionTime = 3;
68

7-
cfg.triggerKey = 'space';
9+
% add parent directory to the path (to make sure we can access the CPP_PTB
10+
% functions)
11+
addpath(fullfile(pwd, '..'))
812

13+
% beginning of demo
914
KbName('UnifyKeyNames');
1015

16+
% press the key "t" to simulate triggers
1117
wait4Trigger(cfg)

errorAbort.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
function errorAbort
2+
errorStruct.message = 'Escape key press detected: aborting experiment.';
3+
errorStruct.identifier = 'checkAbort:abortRequested';
4+
5+
error(errorStruct)
6+
end

0 commit comments

Comments
 (0)