Skip to content

Commit 169c53a

Browse files
authored
Merge pull request #30 from Remi-Gau/remi-REF_initPTB
refactor initPTB
2 parents 6069a96 + f016984 commit 169c53a

File tree

3 files changed

+218
-80
lines changed

3 files changed

+218
-80
lines changed

initPTB.m

Lines changed: 162 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,99 @@
11
function [cfg] = initPTB(cfg)
22
% This will initialize PsychToolBox
33
% - screen
4-
% - the windon opened takes the whole screen by default
5-
% - set in debug mode with window transparency if necessary
6-
% - can skip synch test if you ask for it (nicely)
7-
% - gets the flip interval
8-
% - computes the pixel per degree of visual angle
9-
% - set font details
4+
% - the windon opened takes the whole screen unless
5+
% cfg.testingSmallScreen is set to true
6+
% - debug mode : skips synch test and warnings
7+
% - window transparency enabled by cfg.testingTranspScreen set to true
8+
% - gets the flip interval
9+
% - computes the pixel per degree of visual angle:
10+
% the computation for ppd assumes the windows takes the whole screenDistance
11+
% - set font details
1012
% - keyboard
13+
% - hides cursor
1114
% - sound
12-
15+
%
16+
% OUTPUT:
17+
% cfg.keyboard = [];
18+
% cfg.responseBox = [];
19+
%
20+
% cfg.debug = true;
21+
% cfg.testingTranspScreen = true;
22+
% cfg.testingSmallScreen = true;
23+
%
24+
% cfg.screen : screen numbers where drawing the stimulation (external screen if available)
25+
% cfg.win : window opened by PTB
26+
% cfg.winRect : window rectangule positiona and dimensions in pixel coordinates
27+
% cfg.winWidth : window width in pixels
28+
% cfg.winHeight : window height in pixels
29+
% cfg.center : coordinate of the window center
30+
% cfg.ppd : pixels per degree assuming the window fills the whole screen
31+
% cfg.ifi : estimate of the monitor flip interval
32+
% cfg.monRefresh : monitor refresh rate
33+
% cfg.vbl : (I don't think this output is useful)
34+
% cfg.textFont = 'Courier New';
35+
% cfg.textSize = 18;
36+
% cfg.textStyle = 1;
37+
% cfg.backgroundColor = [0 0 0];
38+
% cfg.monitorWidth = 42;
39+
% cfg.screenDistance = 134;
1340

1441
% TO DO
1542
% - We might want to add a couple of IF in case the experiment does not use audio for example.
16-
% - the computation for ppd assumes the windows takes the whole screenDistance
17-
% - refactor the window opening section (pass the window size as argument)
1843

19-
checkDependencies()
2044

45+
checkDependencies()
2146

2247
% For octave: to avoid displaying messenging one screen at a time
2348
more off
2449

2550
% check for OpenGL compatibility, abort otherwise:
2651
AssertOpenGL;
2752

53+
cfg = setDefaultsPTB(cfg);
2854

29-
%% Keyboard
30-
% Make sure keyboard mapping is the same on all supported operating systems
31-
% Apple MacOS/X, MS-Windows and GNU/Linux:
32-
KbName('UnifyKeyNames');
33-
34-
35-
36-
37-
% ---------- FIX LATER ---------- %
38-
% might be over agressive to test this at every PTB init maybe make it
39-
% dependent on a debug "flag"
40-
41-
testKeyboards(cfg)
55+
initDebug(cfg);
4256

43-
% ---------- FIX LATER ---------- %
4457

45-
46-
47-
48-
% Don't echo keypresses to Matlab window
49-
ListenChar(-1);
58+
%% Keyboard
59+
initKeyboard
5060

5161

5262
%% Mouse
53-
% Hide the mouse cursor:
5463
HideCursor;
5564

5665

57-
%% Audio
58-
% Intialize PsychPortAudio
59-
InitializePsychSound(1);
66+
%% Audio
67+
cfg = initAudio(cfg);
68+
6069

6170

6271
%% Visual
6372

6473
% Get the screen numbers and draw to the external screen if avaliable
6574
cfg.screen = max(Screen('Screens'));
6675

67-
% init PTB with different options in concordance to the Debug Parameters
68-
if cfg.debug
69-
70-
% set to one because we don not care about time
71-
Screen('Preference', 'SkipSyncTests', 2);
72-
Screen('Preference', 'Verbosity', 0);
73-
Screen('Preferences', 'SuppressAllWarnings', 2);
74-
75-
if cfg.testingSmallScreen
76-
[cfg.win, cfg.winRect] = Screen('OpenWindow', cfg.screen, cfg.backgroundColor, [0,0, 480, 270]);
77-
else
78-
if cfg.testingTranspScreen
79-
PsychDebugWindowConfiguration
80-
end
81-
[cfg.win, cfg.winRect] = Screen('OpenWindow', cfg.screen, cfg.backgroundColor);
82-
end
83-
84-
else
85-
Screen('Preference','SkipSyncTests', 0);
86-
[cfg.win, cfg.winRect] = Screen('OpenWindow', cfg.screen, cfg.backgroundColor);
87-
88-
end
89-
76+
cfg = openWindow(cfg);
9077

9178
% window size info
9279
[cfg.winWidth, cfg.winHeight] = WindowSize(cfg.win);
9380

94-
95-
96-
97-
% ---------- FIX LATER ---------- %
98-
% I don't think we want to hard code the 2/3 here. We might just add it to
99-
% the Cfg structure
10081
if strcmpi(cfg.stimPosition,'scanner')
10182
cfg.winRect(1,4) = cfg.winRect(1,4)*2/3;
10283
end
103-
% ---------- FIX LATER ---------- %
104-
105-
106-
10784

10885
% Get the Center of the Screen
10986
cfg.center = [cfg.winRect(3), cfg.winRect(4)]/2;
11087

11188
% Computes the number of pixels per degree given the distance to screen and
11289
% monitor width
113-
11490
% This assumes that the window fills the whole screen
115-
V = 2*(180*(atan(cfg.monitorWidth/(2*cfg.screenDistance))/pi));
116-
cfg.ppd = cfg.winRect(3)/V;
91+
FOV = computeFOV(cfg);
92+
cfg.ppd = cfg.winRect(3)/FOV;
11793

11894

119-
% Enable alpha-blending, set it to a blend equation useable for linear
120-
% superposition with alpha-weighted source.
121-
Screen('BlendFunction', cfg.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
122-
123-
124-
%% Text and Font
125-
% Select specific text font, style and size:
126-
Screen('TextFont',cfg.win, cfg.textFont );
127-
Screen('TextSize',cfg.win, cfg.textSize);
128-
Screen('TextStyle', cfg.win, cfg.textStyle);
95+
%% Select specific text font, style and size
96+
initText(cfg)
12997

13098

13199
%% Timing
@@ -152,3 +120,118 @@
152120

153121

154122
end
123+
124+
125+
function initDebug(cfg)
126+
127+
% init PTB with different options in concordance to the debug Parameters
128+
Screen('Preference','SkipSyncTests', 0);
129+
if cfg.debug
130+
131+
Screen('Preference', 'SkipSyncTests', 2);
132+
Screen('Preference', 'Verbosity', 0);
133+
Screen('Preferences', 'SuppressAllWarnings', 2);
134+
135+
fprintf('\n\n\n\n')
136+
fprintf('########################################\n')
137+
fprintf('## DEBUG MODE. TIMING WILL BE OFF. ##\n')
138+
fprintf('########################################')
139+
fprintf('\n\n\n\n')
140+
141+
testKeyboards(cfg)
142+
143+
end
144+
145+
if cfg.testingTranspScreen
146+
PsychDebugWindowConfiguration
147+
end
148+
149+
end
150+
151+
function initKeyboard
152+
153+
% Make sure keyboard mapping is the same on all supported operating systems
154+
% Apple MacOS/X, MS-Windows and GNU/Linux:
155+
KbName('UnifyKeyNames');
156+
157+
% Don't echo keypresses to Matlab window
158+
ListenChar(-1);
159+
160+
end
161+
162+
function cfg = initAudio(cfg)
163+
164+
if cfg.initAudio
165+
166+
InitializePsychSound(1);
167+
168+
cfg.audio.devIdx= [];
169+
cfg.audio.playbackMode = 1;
170+
171+
if isfield(cfg.audio, 'useDevice')
172+
173+
% get audio device list
174+
audioDev = PsychPortAudio('GetDevices');
175+
176+
% find output device to use
177+
idx = find(...
178+
audioDev.NrInputChannels == cfg.audio.inputChannels && ...
179+
audioDev.NrOutputChannels == cfg.audio.channels && ...
180+
~cellfun(@isempty, regexp({audioDev.HostAudioAPIName}, cfg.audio.deviceName)));
181+
182+
% save device ID
183+
cfg.audio.devIdx = audioDev(idx).DeviceIndex;
184+
185+
% get device's sampling rate
186+
cfg.audio.fs = audioDev(idx).DefaultSampleRate;
187+
188+
end
189+
190+
cfg.audio.pahandle = PsychPortAudio('Open', ...
191+
cfg.audio.devIdx, ...
192+
cfg.audio.playbackMode, ...
193+
cfg.audio.requestedLatency, ...
194+
cfg.audio.fs, ...
195+
cfg.audio.channels);
196+
197+
% set initial PTB volume for safety (participants can adjust this manually
198+
% at the begining of the experiment)
199+
PsychPortAudio('Volume', cfg.audio.pahandle, cfg.audio.initVolume);
200+
201+
cfg.audio.pushSize = cfg.audio.fs * 0.010; %! push N ms only
202+
203+
cfg.audio.requestOffsetTime = 1; % offset 1 sec
204+
cfg.audio.reqsSampleOffset = cfg.audio.requestOffsetTime * cfg.audio.fs;
205+
206+
end
207+
end
208+
209+
function cfg = openWindow(cfg)
210+
211+
if cfg.testingSmallScreen
212+
[cfg.win, cfg.winRect] = Screen('OpenWindow', cfg.screen, cfg.backgroundColor, [0,0, 480, 270]);
213+
else
214+
[cfg.win, cfg.winRect] = Screen('OpenWindow', cfg.screen, cfg.backgroundColor);
215+
end
216+
217+
% Enable alpha-blending, set it to a blend equation useable for linear
218+
% superposition with alpha-weighted source.
219+
Screen('BlendFunction', cfg.win, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
220+
221+
end
222+
223+
function FOV = computeFOV(cfg)
224+
225+
% computes the number of degrees of visual angle in the whole field of view
226+
FOV = 2 *( 180 * ( atan( cfg.monitorWidth / (2*cfg.screenDistance) ) / pi));
227+
228+
end
229+
230+
231+
function initText(cfg)
232+
233+
Screen('TextFont', cfg.win, cfg.textFont);
234+
Screen('TextSize', cfg.win, cfg.textSize);
235+
Screen('TextStyle', cfg.win, cfg.textStyle);
236+
237+
end

setDefaultsPTB.m

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
function cfg = setDefaultsPTB(cfg)
2+
3+
% list the default values
4+
fieldsToSet.keyboard = [];
5+
fieldsToSet.responseBox = [];
6+
7+
fieldsToSet.debug = true;
8+
fieldsToSet.testingTranspScreen = true;
9+
fieldsToSet.testingSmallScreen = true;
10+
11+
fieldsToSet.backgroundColor = [0 0 0];
12+
13+
fieldsToSet.textFont = 'Courier New';
14+
fieldsToSet.textSize = 18;
15+
fieldsToSet.textStyle = 1;
16+
17+
fieldsToSet.monitorWidth = 42;
18+
fieldsToSet.screenDistance = 134;
19+
20+
if isfield(cfg, 'initAudio') && cfg.initAudio
21+
22+
fieldsToSet.audio.fs = 44800;
23+
fieldsToSet.audio.channels = 2;
24+
fieldsToSet.audio.initVolume = 1;
25+
fieldsToSet.audio.requestedLatency = 3;
26+
27+
% playing parameters
28+
% sound repetition
29+
fieldsToSet.audio.repeat = 1;
30+
31+
% Start immediately (0 = immediately)
32+
fieldsToSet.audio.startCue = 0;
33+
34+
% Should we wait for the device to really start?
35+
fieldsToSet.audio.waitForDevice = 1;
36+
37+
end
38+
39+
% loop through the defaults and set them in cfg if they don't exist
40+
names = fieldnames(fieldsToSet);
41+
42+
for i = 1:numel(names)
43+
cfg = setFieldToIfNotPresent(...
44+
cfg, ...
45+
names{i}, ...
46+
getfield(fieldsToSet, names{i})); %#ok<GFLD>
47+
end
48+
49+
50+
end
51+
52+
function struct = setFieldToIfNotPresent(struct, fieldName, value)
53+
if ~isfield(struct, fieldName)
54+
struct = setfield(struct, fieldName, value); %#ok<SFLD>
55+
end
56+
end

testKeyboards.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ function testKeyboards(cfg)
55
timeOut = 5;
66

77

8-
98
% Main keyboard used by the experimenter to quit the experiment if it is necessary
109
% cfg.keyboard
1110
fprintf('\n This is a test: press any key on the experimenter keyboard\n');

0 commit comments

Comments
 (0)