To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at git://github.com/rmeddis/MAP.git .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Revision:

root / multithreshold 1.46 / subjGUI_MT.m @ 33:161913b595ae

History | View | Annotate | Download (63.5 KB)

1 0:f233164f4c86 rmeddis
function varargout = subjGUI_MT(varargin)
2
3
% Begin initialization code - DO NOT EDIT
4
gui_Singleton = 1;
5
gui_State = struct('gui_Name',       mfilename, ...
6
    'gui_Singleton',  gui_Singleton, ...
7
    'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ...
8
    'gui_OutputFcn',  @subjGUI_MT_OutputFcn, ...
9
    'gui_LayoutFcn',  [] , ...
10
    'gui_Callback',   []);
11
if nargin && isstr(varargin{1})
12
    gui_State.gui_Callback = str2func(varargin{1});
13
end
14
15
if nargout
16
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
17
else
18
    gui_mainfcn(gui_State, varargin{:});
19
end
20
% End initialization code - DO NOT EDIT
21
22
% --- Executes just before subjGUI_MT is made visible.
23
function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin)
24
25
% Choose default command line output for subjGUI_MT
26
handles.output = hObject;
27
initializeGUI(handles)
28
guidata(hObject, handles);
29
30
function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles)
31
% Get default command line output from handles structure
32
varargout{1} = handles.output;
33
34
% -----------------------------------------------------initializeGUI
35
function initializeGUI(handles)
36
global experiment
37
global subjectGUIHandles expGUIhandles
38 28:02aa9826efe0 rmeddis
addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ...
39 0:f233164f4c86 rmeddis
    ['..' filesep 'parameterStore'],  ['..' filesep 'wavFileStore'],...
40
    ['..' filesep 'testPrograms'])
41
42
dbstop if error
43
44
% subjectGUI size and location         % [left bottom width height]
45
scrnsize=get(0,'screensize');
46
set(0, 'units','pixels')
47
switch experiment.ear
48
    % use default size unless...
49
    case {'MAPmodel',  'MAPmodelMultich', 'MAPmodelSingleCh', ...
50
            'statsModelLogistic','statsModelRareEvent'}
51
        % 	subjectGUI not needed for modelling so minimize subject GUI
52
        set(gcf, 'units','pixels')
53
        y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)];
54
        set(gcf,'position',y, 'color',[.871 .961 .996])
55
56
    case 'MAPmodelListen',
57
        % 	subjectGUI is needed for display purposes. Make it large
58
        set(gcf, 'units','pixels')
59
        y=[.665*scrnsize(3) 0.02*scrnsize(4) ...
60
            0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside
61
        set(gcf,'position',y, 'color',[.871 .961 .996])
62
end
63
64
switch experiment.ear
65
    case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
66
        % Look to see if the button box exists and, if so, initialise it
67
        buttonBoxIntitialize		% harmless if no button box attached
68
end
69
70
% clear display of previous mean values. This is a new measurement series
71 28:02aa9826efe0 rmeddis
axes(expGUIhandles.axes4), cla
72 0:f233164f4c86 rmeddis
reset (expGUIhandles.axes4)
73
74
% handles needed in non-callback routines below
75
subjectGUIHandles=handles;
76
77
% start immediately
78
startNewExperiment(handles, expGUIhandles)
79
% This is the end of the experiment. Exit here and return to ExpGUI.
80
81
% ----------------------------------------------------- startNewExperiment
82
function startNewExperiment(handles, expGUIhandles)
83
% An experiment consists of a series of 'runs'.
84
% Resets all relevant variables at the beginning of a new experiment.
85
global experiment stimulusParameters betweenRuns
86
87
% 'start new experiment' button is the only valid action now
88
experiment.status='waitingForStart';
89
90
switch experiment.threshEstMethod
91
    % add appropriate labels to subject GUI buttons
92
    case {'2I2AFC++', '2I2AFC+++'}
93
        set(handles.pushbutton3,'string','')
94
        set(handles.pushbutton2,'string','2')
95
        set(handles.pushbutton1,'string','1')
96
        set(handles.pushbutton0,'string','0')
97
    case {'MaxLikelihood', 'oneIntervalUpDown'}
98
        if stimulusParameters.includeCue
99
            set(handles.pushbutton3,'string','')
100
            set(handles.pushbutton2,'string','2')
101
            set(handles.pushbutton1,'string','1')
102
            set(handles.pushbutton0,'string','0')
103
        else
104
            set(handles.pushbutton3,'string','')
105
            set(handles.pushbutton2,'string','YES')
106
            set(handles.pushbutton1,'string','NO')
107
            set(handles.pushbutton0,'string','')
108
        end
109
end
110
111
switch experiment.paradigm
112
    case 'discomfort'
113
        set(handles.pushbutton3,'string','')
114
        set(handles.pushbutton2,'string','uncomfortable')
115
        set(handles.pushbutton1,'string','loud')
116
        set(handles.pushbutton0,'string','comfortable')
117 28:02aa9826efe0 rmeddis
        experiment.allowCatchTrials=0;
118 0:f233164f4c86 rmeddis
end
119
120
% experiment.subjGUIfontSize is set on expGUI
121
set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize)
122
set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize)
123
set(handles.pushbutton1,'FontSize',experiment.subjGUIfontSize)
124
set(handles.pushbutton0,'FontSize',experiment.subjGUIfontSize)
125
set(handles.pushbuttoNotSure,'FontSize',experiment.subjGUIfontSize)
126
set(handles.pushbuttonGO,'FontSize',experiment.subjGUIfontSize)
127
set(handles.textMSG,'FontSize',experiment.subjGUIfontSize)
128
129
set(handles.pushbutton19,'visible','off') % unused button
130
131
% start command window summary of progress
132
fprintf(' \n ----------- NEW MEASUREMENTS\n')
133
disp(['paradigm:   ' experiment.paradigm])
134
cla(expGUIhandles.axes1)
135
cla(expGUIhandles.axes2)
136
cla(expGUIhandles.axes4)
137
cla(expGUIhandles.axes5)
138
139
experiment.stop=0;              % status of 'stop' button
140
experiment.pleaseRepeat=0;      % status of 'repeat' button
141
experiment.buttonBoxStatus='not busy';
142
143
% date and time and replace ':' with '_'
144 28:02aa9826efe0 rmeddis
date=datestr(now);idx=findstr(':',date);date(idx)='_';
145 0:f233164f4c86 rmeddis
experiment.date=date;
146
timeNow=clock; betweenRuns.timeNow= timeNow;
147
experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))];
148 28:02aa9826efe0 rmeddis
experiment.minElapsed=0;
149 0:f233164f4c86 rmeddis
150 28:02aa9826efe0 rmeddis
% unpack catch trial rates. The rate declines from the start rate
151 0:f233164f4c86 rmeddis
%  to the base rate using a time constant.
152
stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1);
153
stimulusParameters.catchTrialBaseRate=...
154
    stimulusParameters.catchTrialRates(2);
155
stimulusParameters.catchTrialTimeConstant=...
156
    stimulusParameters.catchTrialRates(3);
157
if stimulusParameters.catchTrialBaseRate==0
158
    stimulusParameters.catchTrialRate=0;
159
end
160
161
% for human measurements only, identify the start catch trial rate
162
switch experiment.ear
163
    case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
164
        fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
165
            stimulusParameters.catchTrialRate)
166
end
167
168
% Reset betweenRuns parameters. this occurs only at experiment start
169
% withinRuns values are reset in 'startNewRun'
170
% this approach creates a more readable structure summary printout.
171
betweenRuns.thresholds=[];
172
betweenRuns.thresholds_mean=[];
173
betweenRuns.thresholds_median=[];
174
betweenRuns.forceThresholds=[];
175
betweenRuns.observationCount=[];
176
betweenRuns.catchTrials=[];
177
betweenRuns.timesOfFirstReversals=[];
178
betweenRuns.bestThresholdTracks=[];
179
betweenRuns.levelTracks=[];
180
betweenRuns.responseTracks=[];
181
betweenRuns.slopeKTracks=[];
182
betweenRuns.gainTracks=[];
183
betweenRuns.VminTracks=[];
184
betweenRuns.bestGain=[];
185
betweenRuns.bestVMin=[];
186
betweenRuns.bestPaMin=[];
187
betweenRuns.bestLogisticM=[];
188
betweenRuns.bestLogisticK=[];
189
betweenRuns.resets=0;
190
betweenRuns.runNumber=0;
191
192
% Up to two parameters can be changed between runs
193
% Find the variable parameters and randomize them
194
% e.g. 'variableList1 = stimulusParameters.targetFrequency;'
195
eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']);
196
eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']);
197
nVar1=length(variableList1);
198
nVar2=length(variableList2);
199
200 28:02aa9826efe0 rmeddis
% Create two sequence vectors to represent the sequence of var1 and var2
201 0:f233164f4c86 rmeddis
% values. 'var1' changes most rapidly.
202
switch betweenRuns.randomizeSequence
203 30:1a502830d462 rmeddis
    % {'randomize within blocks', 'fixed sequence',...
204
    %  'randomize across blocks'}
205 0:f233164f4c86 rmeddis
    case 'fixed sequence'
206
        var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2);
207
        var2Sequence=reshape(repmat(betweenRuns.variableList2, ...
208
            nVar1,1),1,nVar1*nVar2);
209
    case 'randomize within blocks'
210
        % the blocks are not randomized
211
        var1Sequence=betweenRuns.variableList1;
212
        ranNums=rand(1, length(var1Sequence)); [x idx]=sort(ranNums);
213
        var1Sequence=var1Sequence(idx);
214
        betweenRuns.variableList1=variableList1(idx);
215
        var1Sequence=repmat(var1Sequence, 1,nVar2);
216
        var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1)...
217
            ,1,nVar1*nVar2);
218
    case 'randomize across blocks'
219
        var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2);
220
        var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1),...
221
            1,nVar1*nVar2);
222
        ranNums=rand(1, nVar1*nVar2);
223
        [x idx]=sort(ranNums);
224
        var1Sequence=var1Sequence(idx);
225
        var2Sequence=var2Sequence(idx);
226 28:02aa9826efe0 rmeddis
        % there should be one start value for every combination
227
        %  of var1/ var2. In principle this allows these values to be
228 0:f233164f4c86 rmeddis
        % programmed. Not currently in use.
229
        stimulusParameters.WRVstartValues=...
230
            stimulusParameters.WRVstartValues(idx);
231
end
232
betweenRuns.var1Sequence=var1Sequence;
233
betweenRuns.var2Sequence=var2Sequence;
234
235
% caught out vector needs to be linked to the length of the whole sequence
236
betweenRuns.caughtOut=zeros(1,length(var1Sequence));
237
238
disp('planned sequence:')
239
if min(var1Sequence)>1
240
    % use decidaml places only if necessary
241 28:02aa9826efe0 rmeddis
    disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f')  ])
242 0:f233164f4c86 rmeddis
else
243 28:02aa9826efe0 rmeddis
    disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f')  ])
244 0:f233164f4c86 rmeddis
end
245
if min(var1Sequence)>1
246 28:02aa9826efe0 rmeddis
    disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ])
247 0:f233164f4c86 rmeddis
else
248 28:02aa9826efe0 rmeddis
    disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ])
249 0:f233164f4c86 rmeddis
end
250
251
fprintf('\nvariable1 \t  variable2\t  \n')
252
fprintf('%s \t  %s\t  Threshold  \n',betweenRuns.variableName1,...
253
    betweenRuns.variableName2)
254
255
% Light up 'GO' on subjGUI and advise.
256
set(handles.editdigitInput,'visible','off')
257
switch experiment.ear
258
    case {'statsModelLogistic', 'statsModelRareEvent',...
259
            'MAPmodel',  'MAPmodelMultiCh','MAPmodelSingleCh'}
260
        % no changes required if model used
261
    otherwise
262
        set(handles.pushbuttonGO,'backgroundcolor','y')
263
        set(handles.pushbuttonGO,'visible','on')
264
        set(handles.frame1,'visible','off')
265
        set(handles.textMSG,'backgroundcolor', 'w')
266
        msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}];
267
        set(handles.textMSG,'string', msg)
268 28:02aa9826efe0 rmeddis
269 0:f233164f4c86 rmeddis
        set(handles.pushbuttoNotSure,'visible','off')
270
        set(handles.pushbuttonWrongButton,'visible','off')
271
        set(handles.pushbutton3,'visible','off')
272
        set(handles.pushbutton2,'visible','off')
273
        set(handles.pushbutton1,'visible','off')
274
        set(handles.pushbutton0,'visible','off')
275
        pause(.1) % to allow display to be drawn
276
end
277
278
% Selecting the 'GO' button is the only valid operation action now
279
experiment.status='waitingForGO'; 	% i.e. waiting for new run
280
281
% control is now either manual, model (MAP) or randomization
282
switch experiment.ear
283
    case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'}                                     % MAP model is now the subject
284
        stimulusParameters.calibrationdB=0;             % Pascals required!
285
        MAPmodelRunsGUI(handles)
286
        % model is now the subject
287 28:02aa9826efe0 rmeddis
    case  {'statsModelLogistic', 'statsModelRareEvent'}
288 0:f233164f4c86 rmeddis
        % no catch trials for the statistical model
289 28:02aa9826efe0 rmeddis
        stimulusParameters.catchTrialBaseRate=0;
290 0:f233164f4c86 rmeddis
        stimulusParameters.catchTrialRate=0;
291
        statsModelRunsGUI(handles)
292
    otherwise
293
        %manual operation; wait for user to click on 'GO'
294
end
295
296
% Experiment complete (after MAP or randomization)
297
% return to  'initializeGUI' and then back to expGUI
298
% Manual control finds its own way home. Program control assumed when
299
% the user hits the GO button
300
301
% -----------------------------------------------------------------   startNewRun
302
function startNewRun(handles)
303
% There are many ways to arrive here.
304
%  Under manual control this is achieved by hitting the GO button
305
%   either via the button box or a mouse click
306
%  MAP and randomization methods call this too
307
308
global experiment stimulusParameters betweenRuns withinRuns expGUIhandles
309 28:02aa9826efe0 rmeddis
global LevittControl rareEvent errormsg
310 0:f233164f4c86 rmeddis
311
figure(handles.figure1) % guarantee subject GUI visibility
312
313
% ignore call if program is not ready
314
if ~strcmp(experiment.status,'waitingForGO'), return, end
315
316
set(handles.pushbuttonGO,'visible','off')
317
318
% append message to expGUI message box to alert experimenter that the user
319
% is active
320
addToMsg('Starting new trial',0)
321
322
cla(expGUIhandles.axes1),  title(''); % stimulus
323
cla(expGUIhandles.axes2),  title(''); % WRV track
324
drawnow
325
326
betweenRuns.runNumber=betweenRuns.runNumber + 1;
327
328
withinRuns.trialNumber=1;
329
withinRuns.variableValue=...
330
    stimulusParameters.WRVstartValues(betweenRuns.runNumber);
331
% add random jitter to start level
332
if ~experiment.singleShot
333
    % SS or single shot allows the user to precisely set the WRV
334
    withinRuns.variableValue=withinRuns.variableValue +...
335
        (rand-0.5)*stimulusParameters.jitterStartdB;
336
end
337
338
withinRuns.peaks=[];
339
withinRuns.troughs=[];
340
withinRuns.levelList=[];
341
withinRuns.meanEstTrack=[];
342
withinRuns.bestSlopeK=[];
343
withinRuns.bestGain=[];
344
withinRuns.bestVMin=[];
345
withinRuns.forceThreshold=NaN;
346
withinRuns.responseList=[];
347
withinRuns.caughtOut=0;
348
withinRuns.wrongButton=0;
349
withinRuns.catchTrialCount=0;
350
withinRuns.thresholdEstimateTrack=[];
351
352
withinRuns.beginningOfPhase2=0;
353
withinRuns.nowInPhase2=0;
354
withinRuns.thisIsRepeatTrial=0;
355
356
rareEvent.Euclid=NaN;
357
rareEvent.bestGain=NaN;
358
rareEvent.bestVMin=NaN;
359
rareEvent.thresholddB=0;
360
rareEvent.bestPaMindB=NaN;
361
rareEvent.predictionLevels=[];
362
rareEvent.predictionsRE=[];
363
364
LevittControl.sequence=[];
365
366
% on-screen count of number of runs still to complete
367
trialsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber;
368
set(handles.toGoCounter,'string', trialsToGo);
369
370
switch experiment.threshEstMethod
371
    case {'2I2AFC++', '2I2AFC+++'}
372
        % For 2I2AFC the buttons need to be on the screen ab initio
373
        Levitt2      % inititalize Levitt2 procedure
374
end
375
376
switch experiment.ear
377
    case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
378
        % allow subject time to recover from 'go' press
379
        pause(experiment.clickToStimulusPause)
380
end
381
382
errormsg=nextStimulus(handles);				% get the show on the road
383
384
% terminate if there is any kind of problem
385
if ~isempty(errormsg)
386
    % e.g. limits exceeded, clipping
387
    disp(errormsg)
388
    runCompleted(handles)
389
    return
390
end
391
% return route is variable (see intro to this function)
392
393
% -----------------------------------------------------buttonBox_callback
394
function buttonBox_callback(obj, info)
395 28:02aa9826efe0 rmeddis
% deals with a button press on the button box.
396
397 0:f233164f4c86 rmeddis
global experiment
398
global serobj
399
global subjectGUIHandles
400
401
% do not accept callback if one is already in process
402
if strcmp(experiment.buttonBoxStatus,'busy')
403
    disp(' ignored button press')
404 28:02aa9826efe0 rmeddis
    return % to quiescent state
405 0:f233164f4c86 rmeddis
end
406
experiment.buttonBoxStatus='busy';
407
408
% identify the code of the button pressed
409
buttonPressedNo = fscanf(serobj,'%c',1);
410
411
% This is the map from the button to the Cedrus codes
412
switch experiment.buttonBoxType
413
    case 'horizontal'
414 28:02aa9826efe0 rmeddis
        pbGo='7'; 		pb0='1';
415
        pb1='2';		pb2='3';
416 0:f233164f4c86 rmeddis
        pbRepeat='4';	pbWrong='6';	pbBlank='5';
417
    case 'square'
418 28:02aa9826efe0 rmeddis
        pbGo='7';		pb0='1';
419
        pb1='3';		pb2='4';
420 0:f233164f4c86 rmeddis
        pbRepeat='8';	pbWrong='6';	pbBlank='5';
421
end
422
423
% decide what to do
424
switch experiment.status
425
    case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ...
426
            'endOfExperiment'}
427
        disp(' ignored button press')
428 28:02aa9826efe0 rmeddis
429 0:f233164f4c86 rmeddis
    case 'waitingForGO'
430
        % i.e. waiting for new run
431
        if strcmp(buttonPressedNo,pbGo)			% only GO button  accepted
432
            startNewRun(subjectGUIHandles)
433
        else
434
            disp(' ignored button press')
435
        end
436 28:02aa9826efe0 rmeddis
437 0:f233164f4c86 rmeddis
    case 'waitingForResponse'
438
        % response to stimuli
439
        switch buttonPressedNo
440
            case pb0						% button 0 (top left)
441
                switch experiment.threshEstMethod
442
                    case {'2I2AFC++', '2I2AFC+++'}
443
                        disp(' ignored button press')
444
                    otherwise
445
                        set(subjectGUIHandles.pushbutton0,...
446
                            'backgroundcolor','r')
447
                        pause(.1)
448
                        set(subjectGUIHandles.pushbutton0,...
449
                            'backgroundcolor',get(0,...
450
                            'defaultUicontrolBackgroundColor'))
451
                        userSelects0or1(subjectGUIHandles)
452
                end
453 28:02aa9826efe0 rmeddis
454 0:f233164f4c86 rmeddis
            case pb1						% button 1 (bottom left)
455
                switch experiment.threshEstMethod
456
                    case {'2I2AFC++', '2I2AFC+++'}
457 28:02aa9826efe0 rmeddis
                        userSelects0or1(subjectGUIHandles)
458 0:f233164f4c86 rmeddis
                    otherwise
459
                        set(subjectGUIHandles.pushbutton1,...
460
                            'backgroundcolor','r')
461
                        pause(.1)
462
                        set(subjectGUIHandles.pushbutton1,...
463
                            'backgroundcolor',get(0,...
464
                            'defaultUicontrolBackgroundColor'))
465
                        userSelects0or1(subjectGUIHandles)
466
                end
467 28:02aa9826efe0 rmeddis
468 0:f233164f4c86 rmeddis
            case pb2						% button 2 (bottom right)
469
                switch experiment.threshEstMethod
470
                    case {'2I2AFC++', '2I2AFC+++'}
471
                        userSelects2 (subjectGUIHandles)
472
                    otherwise
473
                        set(subjectGUIHandles.pushbutton2,...
474
                            'backgroundcolor','r')
475
                        pause(.1)
476
                        set(subjectGUIHandles.pushbutton2,...
477
                            'backgroundcolor',get(0,...
478
                            'defaultUicontrolBackgroundColor'))
479
                        userSelects2 (subjectGUIHandles)
480
                end
481 28:02aa9826efe0 rmeddis
482 0:f233164f4c86 rmeddis
            case pbRepeat                   % extreme right button
483
                switch experiment.threshEstMethod
484
                    case {'2I2AFC++', '2I2AFC+++'}
485
                        disp(' ignored button press')
486
                    otherwise
487 28:02aa9826efe0 rmeddis
488 0:f233164f4c86 rmeddis
                        set(subjectGUIHandles.pushbuttoNotSure,...
489
                            'backgroundcolor','r')
490
                        pause(.1)
491
                        set(subjectGUIHandles.pushbuttoNotSure,...
492
                            'backgroundcolor',get(0,...
493
                            'defaultUicontrolBackgroundColor'))
494
                        userSelectsPleaseRepeat (subjectGUIHandles)
495
                end
496 28:02aa9826efe0 rmeddis
497 0:f233164f4c86 rmeddis
            case {pbWrong, pbBlank}
498
                disp(' ignored button press')
499 28:02aa9826efe0 rmeddis
500 0:f233164f4c86 rmeddis
            otherwise						% unrecognised button
501
                disp('ignored button press')
502
        end									% end (button press number)
503
    otherwise
504
        disp('ignored button press')
505
end											% experiment status
506
507
% button box remains 'busy' until after the stimulus has been presented
508
experiment.buttonBoxStatus='not busy';
509
510
% -------------------------------------------------- pushbuttonGO_Callback
511
function pushbuttonGO_Callback(hObject, eventdata, handles)
512
% This is a mouse click path
513
% GO function is also called directly from button box
514
%  and from MAP model and stats model
515
516
set(handles.pushbuttonGO,'visible','off')
517
startNewRun(handles)
518
519
% ---------------------------------------------------pushbutton0_Callback
520
function pushbutton0_Callback(hObject, eventdata, handles)
521
global experiment
522
% This is a mouse click path
523
524
% ignore 0 button if 2I2AFC used
525 28:02aa9826efe0 rmeddis
if findstr(experiment.threshEstMethod,'2I2AFC')
526
    return  % to quiescent state
527
end
528 0:f233164f4c86 rmeddis
529
% userDoesNotHearTarget(handles)		% only possible interpretation
530
userDecides(handles, false)
531
532
% -------------------------------------------------- pushbutton1_Callback
533
function pushbutton1_Callback(hObject, eventdata, handles)
534
userSelects0or1(handles)				% also called from buttonBox
535
536
% ---------------------------------------------------pushbutton2_Callback
537
function pushbutton2_Callback(hObject, eventdata, handles)
538
userSelects2(handles)					% also called from buttonBox
539
540
% --------------------------------------------- pushbuttoNotSure_Callback
541
function pushbuttoNotSure_Callback(hObject, eventdata, handles)
542
userSelectsPleaseRepeat(handles)		% also called from buttonBox
543
544
% -------------------------------------------------- pushbutton3_Callback
545
function pushbutton3_Callback(hObject, eventdata, handles)
546
547
% ------------------------------------------------- pushbutton19_Callback
548
function pushbutton19_Callback(hObject, eventdata, handles)
549
% should be invisible (ignore)
550
551
% --------------------------------------- pushbuttonWrongButton_Callback
552
function pushbuttonWrongButton_Callback(hObject, eventdata, handles)
553
userSelectsWrongButton(handles)
554
555
% --------------------------------------- editdigitInput_Callback
556
function editdigitInput_Callback(hObject, eventdata, handles)
557
userSelects0or1(handles)				% after digit string input
558
559
560
561
% ----------------------------------------------------- userSelects0or1
562
function userSelects0or1(handles)
563
global experiment withinRuns
564
565 28:02aa9826efe0 rmeddis
switch experiment.threshEstMethod
566 0:f233164f4c86 rmeddis
    case {'2I2AFC++', '2I2AFC+++'}
567
        switch withinRuns.stimulusOrder
568
            case 'targetFirst';
569
                %                 userHearsTarget(handles)
570
                userDecides(handles, true)
571
            otherwise
572
                %                 userDoesNotHearTarget(handles)
573
                userDecides(handles, false)
574
        end
575
    otherwise
576
        % single interval
577
        % 0 or 1 are treated as equivalent (i.e. target is not heard)
578
        userDecides(handles, false)
579
end
580
% return to pushButton1 callback
581
582
% ----------------------------------------------------- userSelects2
583
function userSelects2(handles)
584
global experiment withinRuns
585 28:02aa9826efe0 rmeddis
switch experiment.threshEstMethod
586 0:f233164f4c86 rmeddis
    case {'2I2AFC++', '2I2AFC+++'}
587
        switch withinRuns.stimulusOrder
588
            case 'targetSecond';
589
                %                 userDoesNotHearTarget(handles)
590
                userDecides(handles, true)
591
            otherwise
592
                %                 userHearsTarget(handles)
593
                userDecides(handles, false)
594
        end
595
    otherwise
596
        % single interval (2 targets heard)
597
        userDecides(handles, true)
598
end
599
% return to pushButton2 callback
600
601
% ----------------------------------------------------- ---- userDecides
602
function userDecides(handles, saidYes)
603
global experiment stimulusParameters betweenRuns withinRuns
604 28:02aa9826efe0 rmeddis
global rareEvent logistic psy levelsBinVector errormsg
605 0:f233164f4c86 rmeddis
606
if experiment.singleShot
607 28:02aa9826efe0 rmeddis
    return  % not clear why this should be here
608 0:f233164f4c86 rmeddis
end
609
610
% ignore click if not 'waitingForResponse'
611
if ~strcmp(experiment.status,'waitingForResponse')
612
    disp('ignored click')
613 28:02aa9826efe0 rmeddis
    return % to userSelects
614 0:f233164f4c86 rmeddis
end
615
616
% speech reception threshold
617
if strcmp(stimulusParameters.targetType,'digitStrings')
618 28:02aa9826efe0 rmeddis
    % read triple digits from userGUI
619 0:f233164f4c86 rmeddis
    digitsInput=get(handles.editdigitInput,'string');
620
    % must be three digits
621
    if ~(length(digitsInput)==3)
622
        addToMsg(['error message: Wrong no of digits'], 0, 1)
623
        set(handles.textMSG,'string', 'Wrong no of digits', ...
624
            'BackgroundColor','r', 'ForegroundColor', 'w')
625
        set(handles.editdigitInput,'string','')
626
        return
627
    end
628
    % obtain correct answer from file name
629
    x=stimulusParameters.digitString;
630
    idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero
631
    disp([x '   ' digitsInput])
632
    if x==digitsInput
633 28:02aa9826efe0 rmeddis
        saidYes=1;  % i.e. correct response
634 0:f233164f4c86 rmeddis
    else
635 28:02aa9826efe0 rmeddis
        saidYes=0;  % i.e  wrong response
636 0:f233164f4c86 rmeddis
    end
637 28:02aa9826efe0 rmeddis
    set(handles.editdigitInput,'string','')
638
    set(handles.editdigitInput,'visible','off')
639
    pause(0.1)
640 0:f233164f4c86 rmeddis
end
641
642
% no button presses accepted while processing
643
experiment.status='processingResponse';
644
645
% catch trials. Restart trial if caught
646
if withinRuns.catchTrial
647
    if saidYes
648
        disp('catch trial - caught out')
649
        withinRuns.caughtOut=withinRuns.caughtOut+1;
650 28:02aa9826efe0 rmeddis
651
        % special: estimate caught out rate by allowing the trial
652 0:f233164f4c86 rmeddis
        %  to continue after catch
653
        if stimulusParameters.catchTrialBaseRate==0.5
654 28:02aa9826efe0 rmeddis
            %  To use this facility, set the catchTrialRate and the
655 0:f233164f4c86 rmeddis
            %   catchTrialBaseRate both to 0.5
656
            %    update false positive rate
657
            betweenRuns.caughtOut(betweenRuns.runNumber)=...
658 28:02aa9826efe0 rmeddis
                withinRuns.caughtOut;
659 0:f233164f4c86 rmeddis
            plotProgressThisTrial(handles)
660
            nextStimulus(handles);
661
            return
662
        end
663 28:02aa9826efe0 rmeddis
664
        % Punishment: caught out restarts the trial
665 0:f233164f4c86 rmeddis
        set(handles.frame1,'backgroundColor','r')
666
        set(handles.pushbuttonGO, ...
667
            'visible','on', 'backgroundcolor','y') % and go again
668
        msg=[{'Start again: catch trial error'}, {' '},...
669
            {'Please,click on the GO button'}];
670
        set(handles.textMSG,'string',msg)
671
        [y,fs]=wavread('ding.wav');
672
        wavplay(y/100,fs)
673 28:02aa9826efe0 rmeddis
674
        % raise catch trial rate temporarily.
675 0:f233164f4c86 rmeddis
        %  this is normally reduced on each new trial (see GO)
676
        stimulusParameters.catchTrialRate=...
677
            stimulusParameters.catchTrialRate+0.1;
678 28:02aa9826efe0 rmeddis
        if stimulusParameters.catchTrialRate>0.5
679
            stimulusParameters.catchTrialRate=0.5;
680 0:f233164f4c86 rmeddis
        end
681
        fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
682
            stimulusParameters.catchTrialRate)
683 28:02aa9826efe0 rmeddis
684 0:f233164f4c86 rmeddis
        betweenRuns.caughtOut(betweenRuns.runNumber)=...
685
            1+betweenRuns.caughtOut(betweenRuns.runNumber);
686
        betweenRuns.runNumber=betweenRuns.runNumber-1;
687
        experiment.status='waitingForGO';
688
        return % unwind and wait for button press
689
    else % (said No)
690 28:02aa9826efe0 rmeddis
        % user claims not to have heard target.
691
        % This is good as it was not present.
692
        % So, repeat the stimulus (possibly with target)
693 0:f233164f4c86 rmeddis
        %  and behave as if the last trial did not occur
694
        errormsg=nextStimulus(handles);
695 28:02aa9826efe0 rmeddis
696 0:f233164f4c86 rmeddis
        % terminate if there is any kind of problem
697
        if ~isempty(errormsg)
698
            % e.g. limits exceeded, clipping
699
            disp(['Error nextStimulus: ' errormsg])
700
            runCompleted(handles)
701
            return
702
        end
703
        return      % no further action - next trial
704
    end
705 28:02aa9826efe0 rmeddis
end     % of catch trial
706 0:f233164f4c86 rmeddis
707 28:02aa9826efe0 rmeddis
% Real target: analyse the response, make tracks and define next stim.
708 0:f233164f4c86 rmeddis
709
% Define response and update response list
710
if saidYes
711
    % target was heard, so response=1;
712
    withinRuns.responseList=[withinRuns.responseList 1];	% 'heard it!'
713
else
714
    % target was not hear heard, so response=0;
715
    withinRuns.responseList=[withinRuns.responseList 0];
716
end
717
withinRuns.levelList=[withinRuns.levelList withinRuns.variableValue];
718
trialNumber=length(withinRuns.responseList);
719
720
% keep track of peaks and troughs;
721
% identify direction of change during initial period
722
if saidYes
723
    % default step size before first reversal
724 28:02aa9826efe0 rmeddis
    WRVinitialStep=-stimulusParameters.WRVinitialStep;
725 0:f233164f4c86 rmeddis
    WRVsmallStep=-stimulusParameters.WRVsmallStep;
726
    % if the previous direction was 'less difficult', this must be a peak
727
    if strcmp(withinRuns.direction,'less difficult') ...
728
            && length(withinRuns.levelList)>1
729
        withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue];
730
    end
731
    withinRuns.direction='more difficult';
732
else
733
    % said 'no'
734
    % default step size before first reversal
735 28:02aa9826efe0 rmeddis
    WRVinitialStep=stimulusParameters.WRVinitialStep;
736 0:f233164f4c86 rmeddis
    WRVsmallStep=stimulusParameters.WRVsmallStep;
737 28:02aa9826efe0 rmeddis
738 0:f233164f4c86 rmeddis
    % if the previous direction was 'up', this must be a peak
739
    if strcmp(withinRuns.direction,'more difficult') ...
740
            && length(withinRuns.levelList)>1
741
        withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue];
742
    end
743
    withinRuns.direction='less difficult';
744
end
745
746 28:02aa9826efe0 rmeddis
% phase 2 is all the levels after and incuding the first reversal
747 0:f233164f4c86 rmeddis
%  plus the level before that
748 28:02aa9826efe0 rmeddis
% Look for the end of phase 1
749 0:f233164f4c86 rmeddis
if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ...
750
        length(withinRuns.troughs)>0
751
    % define phase 2
752
    withinRuns.beginningOfPhase2=trialNumber-1;
753
    withinRuns.nowInPhase2=1;
754
    WRVsmallStep=WRVinitialStep/2;
755
end
756
757
if withinRuns.nowInPhase2
758
    % keep a record of all levels and responses in phase 2 only
759
    withinRuns.levelsPhaseTwo=...
760
        withinRuns.levelList(withinRuns.beginningOfPhase2:end);
761
    withinRuns.responsesPhaseTwo=...
762
        withinRuns.responseList(withinRuns.beginningOfPhase2:end);
763
else
764
    withinRuns.levelsPhaseTwo=[];
765
end
766
767
% get (or substitute) threshold estimate
768
switch experiment.threshEstMethod
769
    case {'2I2AFC++', '2I2AFC+++'}
770
        % for plotting psychometric function only
771
        if withinRuns.beginningOfPhase2>0
772
            [psy, levelsBinVector, logistic, rareEvent]= ...
773
                bestFitPsychometicFunctions...
774
                (withinRuns.levelsPhaseTwo,  withinRuns.responsesPhaseTwo);
775
        end
776 28:02aa9826efe0 rmeddis
777 0:f233164f4c86 rmeddis
        if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs)
778
            thresholdEstimate= ...
779
                mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]);
780
        else
781
            thresholdEstimate=NaN;
782
        end
783 28:02aa9826efe0 rmeddis
784 0:f233164f4c86 rmeddis
    otherwise
785
        % single interval methods
786
        try
787
            % using the s trial after the first reversal
788
            [psy, levelsBinVector, logistic, rareEvent]= ...
789
                bestFitPsychometicFunctions(withinRuns.levelsPhaseTwo,...
790
                withinRuns.responsesPhaseTwo);
791
        catch
792
            logistic.bestThreshold=NaN;
793
        end
794
end
795
796
if withinRuns.nowInPhase2
797
    % save tracks of threshold estimates for plotting andprinting
798
    switch experiment.functionEstMethod
799
        case {'logisticLS', 'logisticML'}
800
            if withinRuns.nowInPhase2
801
                withinRuns.meanEstTrack=...
802
                    [withinRuns.meanEstTrack ...
803
                    mean(withinRuns.levelsPhaseTwo)];
804
                withinRuns.thresholdEstimateTrack=...
805
                    [withinRuns.thresholdEstimateTrack ...
806
                    logistic.bestThreshold];
807
            end
808
        case 'rareEvent'
809
            withinRuns.meanEstTrack=...
810
                [withinRuns.meanEstTrack rareEvent.thresholddB];
811
            withinRuns.thresholdEstimateTrack=...
812
                [withinRuns.thresholdEstimateTrack logistic.bestThreshold];
813
        case 'peaksAndTroughs'
814
            withinRuns.meanEstTrack=...
815
                [withinRuns.meanEstTrack thresholdEstimate];
816
            withinRuns.thresholdEstimateTrack=...
817
                [withinRuns.thresholdEstimateTrack thresholdEstimate];
818
    end
819
end
820
821
% special discomfort condition
822
% run is completed when subject hits '2' button
823
switch experiment.paradigm
824
    case 'discomfort'
825
        if saidYes
826
            runCompleted(handles)
827
            return
828
        end
829
end
830
831
% choose the next level for the stimulus
832
switch experiment.threshEstMethod
833
    case {'2I2AFC++', '2I2AFC+++'}
834
        if saidYes
835
            [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue);
836
        else
837
            [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue);
838
        end
839 28:02aa9826efe0 rmeddis
840 0:f233164f4c86 rmeddis
        % empty message means continue as normal
841 28:02aa9826efe0 rmeddis
        if ~isempty(msg)
842 0:f233164f4c86 rmeddis
            runCompleted(handles)
843
            return
844
        end
845
        newWRVvalue=withinRuns.variableValue-WRVinitialStep;
846 28:02aa9826efe0 rmeddis
847 0:f233164f4c86 rmeddis
    case {'MaxLikelihood', 'oneIntervalUpDown'}
848 28:02aa9826efe0 rmeddis
        % run completed by virtue of number of trials
849 0:f233164f4c86 rmeddis
        % or restart because listener is in trouble
850
        if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials
851
            % Use bonomial test to decide if there is an imbalance in the
852
            % number of 'yes'es and 'no's
853
            yesCount=sum(withinRuns.responseList);
854
            noCount=length(withinRuns.responseList)-yesCount;
855
            z=abs(yesCount-noCount)/(yesCount+noCount)^0.5;
856
            if z>1.96
857
                betweenRuns.resets=betweenRuns.resets+1;
858
                disp([ 'reset / z= ' num2str( z)  ...
859
                    '   Nresets= ' num2str( betweenRuns.resets) ] )
860
                withinRuns.peaks=[];
861
                withinRuns.troughs=[];
862
                withinRuns.levelList=withinRuns.levelList(end);
863
                withinRuns.meanEstTrack=withinRuns.meanEstTrack(end);
864
                withinRuns.forceThreshold=NaN;
865
                withinRuns.responseList=withinRuns.responseList(end);
866
                withinRuns.beginningOfPhase2=0;
867
                withinRuns.nowInPhase2=0;
868
                withinRuns.thresholdEstimateTrack=...
869
                    withinRuns.thresholdEstimateTrack(end);
870
            else
871
                runCompleted(handles)
872
                return
873
            end
874
        end
875 28:02aa9826efe0 rmeddis
876 0:f233164f4c86 rmeddis
        % set new value for WRV
877
        if withinRuns.nowInPhase2
878
            % phase 2
879
            currentMeanEst=withinRuns.thresholdEstimateTrack(end);
880
            switch experiment.threshEstMethod
881
                case 'MaxLikelihood'
882 28:02aa9826efe0 rmeddis
                    newWRVvalue=currentMeanEst;
883 0:f233164f4c86 rmeddis
                case {'oneIntervalUpDown'}
884
                    newWRVvalue=withinRuns.variableValue+WRVsmallStep;
885
            end
886
        else
887
            % phase 1
888
            if withinRuns.variableValue+2*WRVinitialStep>...
889
                    stimulusParameters.WRVlimits(2)
890
                % use smaller steps when close to maximum
891
                WRVinitialStep=WRVinitialStep/2;
892
            end
893
            newWRVvalue=withinRuns.variableValue+WRVinitialStep;
894
        end
895
    otherwise
896
        error(  'assessment method not recognised')
897
end
898
899
switch experiment.paradigm
900
    % prevent unrealistic gap durations 'gapDetection' tasks.
901
    % Note that the gap begins when the ramp ends not when stimulus ends
902
    case 'gapDetection'
903
        if newWRVvalue<-2*stimulusParameters.rampDuration
904
            newWRVvalue=-2*stimulusParameters.rampDuration;
905
            addToMsg('gap duration fixed at - 2 * ramp!',1, 1)
906
        end
907
end
908
909
withinRuns.variableValue=newWRVvalue;
910
withinRuns.trialNumber=withinRuns.trialNumber+1;
911
912
% Trial continues
913
plotProgressThisTrial(handles)
914
915
% next stimulus and so the cycle continues
916
errormsg=nextStimulus(handles);
917
% after the stimulus is presented, control returns here and the system
918
% waits for user action.
919
920
% terminate if there is any kind of problem
921
if ~isempty(errormsg)
922
    % e.g. limits exceeded, clipping
923
    disp(['Error nextStimulus: ' errormsg])
924
    runCompleted(handles)
925
    return
926
end
927
928
% ------------------------------------------------ userSelectsPleaseRepeat
929
function userSelectsPleaseRepeat(handles)
930
global experiment withinRuns
931
% ignore click if not 'waitingForResponse'
932
if ~strcmp(experiment.status,'waitingForResponse')
933
    disp('ignored click')
934
    return
935
end
936 28:02aa9826efe0 rmeddis
% Take no action other than to make a
937 0:f233164f4c86 rmeddis
%  tally of repeat requests
938
experiment.pleaseRepeat=experiment.pleaseRepeat+1;
939
withinRuns.thisIsRepeatTrial=1;
940
nextStimulus(handles);
941
942
% ------------------------------------------------ userSelectsWrongButton
943
function userSelectsWrongButton(handles)
944
global withinRuns experiment
945
% restart is the simplest solution for a 'wrong button' request
946
withinRuns.wrongButton=withinRuns.wrongButton+1;
947 28:02aa9826efe0 rmeddis
set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y')
948 0:f233164f4c86 rmeddis
msg=[{'Start again: wrong button pressed'}, {' '},...
949
    {'Please,click on the GO button'}];
950
set(handles.textMSG,'string',msg)
951
experiment.status='waitingForGO';
952
953
% ------------------------------------------------- plotProgressThisTrial
954
function plotProgressThisTrial(handles)
955 28:02aa9826efe0 rmeddis
% updates GUI: used for all responses
956 0:f233164f4c86 rmeddis
957
global experiment stimulusParameters betweenRuns withinRuns expGUIhandles
958
global  psy levelsBinVector binFrequencies rareEvent logistic statsModel
959
960
% plot the levelTrack and the threshold track
961
962
% Panel 2
963
% plot the levelList
964
axes(expGUIhandles.axes2); cla
965
plot( withinRuns.levelList,'o','markerFaceColor','k'), hold on
966
% plot the best threshold estimate tracks
967
if length(withinRuns.meanEstTrack)>=1
968
    % The length of the levelList is 2 greater than number of thresholds
969
    ptr=withinRuns.beginningOfPhase2+1;
970
    plot(ptr: ptr+length(withinRuns.meanEstTrack)-1, ...
971
        withinRuns.meanEstTrack, 'r')
972
    plot( ptr: ptr+length(withinRuns.thresholdEstimateTrack)-1, ...
973
        withinRuns.thresholdEstimateTrack, 'g')
974
    hold off
975
    estThresh=withinRuns.thresholdEstimateTrack(end);
976
    switch experiment.threshEstMethod
977
        % add appropriate labels to subject GUI buttons
978
        case {'2I2AFC++', '2I2AFC+++'}
979
            title([stimulusParameters.WRVname ' = ' ...
980
                num2str(withinRuns.variableValue, '%5.1f')])
981
        otherwise
982
            title([stimulusParameters.WRVname ' = ' ...
983
                num2str(withinRuns.variableValue, '%5.1f') ...
984
                ';    TH= ' num2str(estThresh, '%5.1f')])
985
    end
986
end
987
xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]);
988
ylim(stimulusParameters.WRVlimits)
989
grid on
990
991
% Panel 4: Summary of threshold estimates (not used here)
992 28:02aa9826efe0 rmeddis
% Estimates from previous runs are set in 'runCompleted'
993
% It is only necessary to change title showing runs/trials remaining
994 0:f233164f4c86 rmeddis
axes(expGUIhandles.axes4)
995
runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber;
996
if withinRuns.beginningOfPhase2>0
997
    trialsToGo= experiment.singleIntervalMaxTrials(1) ...
998
        + withinRuns.beginningOfPhase2- withinRuns.trialNumber;
999
    title(['trials remaining = ' num2str(trialsToGo) ...
1000
        ':    runs to go= ' num2str(runsToGo)])
1001
end
1002
1003
% plot psychometric function   - panel 5
1004
axes(expGUIhandles.axes5), cla
1005
plot(withinRuns.levelList, withinRuns.responseList,'b.'), hold on
1006
ylim([0 1])
1007
title('')
1008
1009
switch experiment.threshEstMethod
1010
    case {'MaxLikelihood', 'oneIntervalUpDown'}
1011
        if withinRuns.beginningOfPhase2>0
1012
            % display only when in phase 2.
1013
            withinRuns.levelsPhaseTwo=...
1014
                withinRuns.levelList(withinRuns.beginningOfPhase2:end);
1015
            withinRuns.responsesPhaseTwo=...
1016
                withinRuns.responseList(withinRuns.beginningOfPhase2:end);
1017 28:02aa9826efe0 rmeddis
1018 0:f233164f4c86 rmeddis
            % organise data as psychometric function
1019
            [psy, levelsBinVector, binFrequencies]= ...
1020
                psychometricFunction(withinRuns.levelsPhaseTwo,...
1021
                withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
1022 28:02aa9826efe0 rmeddis
1023 0:f233164f4c86 rmeddis
            % Plot the function
1024
            %   point by point with circles of appropiate weighted size
1025
            hold on,
1026
            for i=1:length(psy)
1027
                plot(levelsBinVector(i), psy(i), 'ro', ...
1028
                    'markersize', 50*binFrequencies(i)/sum(binFrequencies))
1029
            end
1030
            % save info for later
1031
            betweenRuns.psychometicFunction{betweenRuns.runNumber}=...
1032
                [levelsBinVector; psy];
1033 28:02aa9826efe0 rmeddis
1034 0:f233164f4c86 rmeddis
            % fitPsychometric functions is  computed in 'userDecides'
1035
            % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k')
1036
            plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
1037
            plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
1038
            if ~isnan(logistic.bestThreshold )
1039
                xlim([ 0 100 ])
1040 28:02aa9826efe0 rmeddis
                title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '...
1041
                    num2str(rareEvent.bestGain,'%6.3f') '  A=' ...
1042
                    num2str(rareEvent.bestVMin,'%8.1f')])
1043 0:f233164f4c86 rmeddis
            else
1044
                title(' ')
1045
            end
1046 28:02aa9826efe0 rmeddis
1047 0:f233164f4c86 rmeddis
            switch experiment.ear
1048
                %plot green line for statsModel a priori model
1049
                case 'statsModelLogistic'
1050
                    % plot proTem logistic (green) used by stats model
1051
                    p= 1./(1+exp(-statsModel.logisticSlope...
1052
                        *(levelsBinVector-logistic.bestThreshold)));
1053
                    if experiment.psyFunSlope<0, p=1-p;end
1054
                    titleText=[ ',  statsModel: logistic'];
1055
                    hold on,    plot(levelsBinVector, p,'g')
1056
                case  'statsModelRareEvent'
1057
                    pressure=28*10.^(levelsBinVector/20);
1058
                    p=1-exp(-stimulusParameters.targetDuration...
1059
                        *(statsModel.rareEvenGain...
1060
                        * pressure-statsModel.rareEventVmin));
1061
                    p(p<0)=0;
1062
                    if experiment.psyFunSlope<0, p=1-p;end
1063
                    hold on,    plot(levelsBinVector, p,'g')
1064
            end %(estMethod)
1065
        end
1066 28:02aa9826efe0 rmeddis
1067 0:f233164f4c86 rmeddis
    otherwise           % 2A2IFC
1068
        message3= ...
1069
            ([ 'peaks='  num2str(withinRuns.peaks) ...
1070
            'troughs='  num2str(withinRuns.troughs)]);
1071
        ylimRM([-0.1 1.1])	% 0=no / 1=yes
1072
        set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'})
1073
        ylabel('psychometric function'), xlabel('target level')
1074
        if length(levelsBinVector)>1
1075
            xlim([ min(levelsBinVector) max(levelsBinVector)])
1076
            xlim([ 0 100])
1077
        end
1078
end
1079
1080
% command window summary
1081
% Accumulate things to say in the message window
1082
message1= (['responses:      ' num2str(withinRuns.responseList,'%9.0f')]);
1083
switch experiment.paradigm
1084
    % more decimal places needed on GUI
1085
    case { 'gapDetection', 'frequencyDiscrimination', 'forwardMaskingD'}
1086
        message2= ([stimulusParameters.WRVname  ...
1087
            ':       ' num2str(withinRuns.levelList,'%7.3f')]);
1088
        message3= (['Thresh (logistic mean):   ' ...
1089
            num2str(withinRuns.thresholdEstimateTrack,'%7.3f')]);
1090
    otherwise
1091
        message2= ([stimulusParameters.WRVname ':      ' ...
1092
            num2str(withinRuns.levelList,'%7.1f')]);
1093
        message3= (['Thresh (logistic mean):   ' ...
1094
            num2str(withinRuns.thresholdEstimateTrack,'%7.1f')]);
1095
end
1096
1097
addToMsg(str2mat(message1, message2, message3), 0)
1098
1099
% -----------------------------------------------------runCompleted
1100
function runCompleted(handles)
1101
% Used at the end of each run
1102
global experiment stimulusParameters betweenRuns withinRuns
1103
global rareEvent expGUIhandles
1104
% disp('run completed')
1105
1106
experiment.status='runCompleted';
1107 28:02aa9826efe0 rmeddis
% quick update after final trial just to make sure
1108 0:f233164f4c86 rmeddis
plotProgressThisTrial(handles)
1109
1110
switch experiment.ear
1111
    case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
1112
            'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
1113
        % no changes required if model used
1114
    otherwise
1115
        set(handles.frame1,'visible','off')
1116
        set(handles.pushbuttoNotSure,'visible','off')
1117
        set(handles.pushbuttonWrongButton,'visible','off')
1118
        set(handles.pushbutton3,'visible','off')
1119
        set(handles.pushbutton2,'visible','off')
1120
        set(handles.pushbutton1,'visible','off')
1121
        set(handles.pushbutton0,'visible','off')
1122
        set(handles.pushbuttonGO,'visible','off')
1123
end
1124
1125
if isnan(withinRuns.forceThreshold)
1126
    % the experiment has been aborted for some reason
1127
    threshold=withinRuns.forceThreshold;
1128
    stdev=NaN;
1129
    logistic.bestK=NaN;
1130
    logistic.bestThreshold=NaN;
1131
    medianThreshold=NaN;
1132
    meanThreshold=NaN;
1133
else
1134
    % use only phase 2 levels and responses for calculating thresholds
1135
    withinRuns.levelsPhaseTwo=...
1136
        withinRuns.levelList(withinRuns.beginningOfPhase2:end);
1137
    withinRuns.responsesPhaseTwo=...
1138
        withinRuns.responseList(withinRuns.beginningOfPhase2:end);
1139
    [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ...
1140
        bestFitPsychometicFunctions...
1141
        (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo);
1142 28:02aa9826efe0 rmeddis
1143 0:f233164f4c86 rmeddis
    % plot final psychometric function
1144
    axes(expGUIhandles.axes5),cla
1145
    hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
1146
    hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
1147
    % organise data as psychometric function
1148
    [psy, levelsBinVector, binFrequencies]= ...
1149
        psychometricFunction(withinRuns.levelsPhaseTwo,...
1150
        withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
1151
    %   point by point with circles of appropiate weighted size
1152
    hold on,
1153
    for i=1:length(psy)
1154
        plot(levelsBinVector(i), psy(i), 'ro', ...
1155
            'markersize', 50*binFrequencies(i)/sum(binFrequencies))
1156
    end
1157
1158
    % experimental
1159
    medianThreshold=median(withinRuns.levelsPhaseTwo);
1160
    warning off
1161
    meanThreshold=mean(withinRuns.levelsPhaseTwo);
1162 28:02aa9826efe0 rmeddis
1163 0:f233164f4c86 rmeddis
    % identify the current threshold estimate
1164
    switch experiment.paradigm
1165
        case 'discomfort'
1166
            % most recent value (not truely a mean value)
1167
            threshold=withinRuns.levelList(end);
1168 28:02aa9826efe0 rmeddis
            stdev=NaN;
1169 0:f233164f4c86 rmeddis
        otherwise
1170
            switch experiment.threshEstMethod
1171
                case {'MaxLikelihood', 'oneIntervalUpDown'}
1172
                    % last value in the list
1173 28:02aa9826efe0 rmeddis
                    %                     threshold=withinRuns.meanEstTrack(end);
1174 0:f233164f4c86 rmeddis
                    threshold=withinRuns.thresholdEstimateTrack(end);
1175
                    stdev=NaN;
1176 28:02aa9826efe0 rmeddis
1177 0:f233164f4c86 rmeddis
                case {'2I2AFC++', '2I2AFC+++'}
1178
                    % use peaks and troughs
1179
                    try		% there may not be enough values to use
1180
                        peaksUsed=experiment.peaksUsed;
1181
                        threshold=...
1182
                            mean(...
1183
                            [withinRuns.peaks(end-peaksUsed+1:end) ...
1184
                            withinRuns.troughs(end-peaksUsed+1:end)]);
1185
                        stdev=...
1186
                            std([withinRuns.peaks(end-peaksUsed +1:end) ...
1187
                            withinRuns.troughs(end-peaksUsed:end)]);
1188
                    catch
1189
                        threshold=NaN;
1190
                        stdev=NaN;
1191
                    end
1192
            end
1193
    end
1194
end
1195
1196
% Store thresholds
1197
betweenRuns.thresholds=[betweenRuns.thresholds threshold];
1198
betweenRuns.thresholds_mean=[betweenRuns.thresholds_mean meanThreshold];
1199
betweenRuns.thresholds_median=...
1200
    [betweenRuns.thresholds_median medianThreshold];
1201
betweenRuns.forceThresholds=...
1202
    [betweenRuns.forceThresholds withinRuns.forceThreshold];
1203
1204
% count observations after the startup phase for record keeping
1205
betweenRuns.observationCount=...
1206
    [betweenRuns.observationCount length(withinRuns.levelList)];
1207
betweenRuns.timesOfFirstReversals=...
1208
    [betweenRuns.timesOfFirstReversals withinRuns.beginningOfPhase2];
1209
betweenRuns.catchTrials=...
1210
    [betweenRuns.catchTrials withinRuns.catchTrialCount];
1211
1212
% add variable length tracks to cell arrays
1213
if withinRuns.beginningOfPhase2>0
1214
    betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=...
1215 28:02aa9826efe0 rmeddis
        withinRuns.thresholdEstimateTrack;
1216 0:f233164f4c86 rmeddis
    betweenRuns.levelTracks{length(betweenRuns.thresholds)}=...
1217
        withinRuns.levelList(withinRuns.beginningOfPhase2:end);
1218
    betweenRuns.responseTracks{length(betweenRuns.thresholds)}=...
1219
        withinRuns.responseList(withinRuns.beginningOfPhase2:end);
1220
else
1221
    betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=[];
1222
    betweenRuns.levelTracks{length(betweenRuns.thresholds)}=[];
1223
    betweenRuns.responseTracks{length(betweenRuns.thresholds)}=[];
1224
end
1225
1226
betweenRuns.bestGain=[betweenRuns.bestGain rareEvent.bestGain];
1227
betweenRuns.bestVMin=[betweenRuns.bestVMin rareEvent.bestVMin];
1228
betweenRuns.bestPaMin=[betweenRuns.bestPaMin rareEvent.bestPaMindB];
1229
betweenRuns.bestLogisticM=...
1230
    [betweenRuns.bestLogisticM logistic.bestThreshold];
1231
betweenRuns.bestLogisticK=[betweenRuns.bestLogisticK logistic.bestK];
1232
1233
resultsSoFar=[betweenRuns.var1Sequence(betweenRuns.runNumber)'...
1234
    betweenRuns.var2Sequence(betweenRuns.runNumber)'...
1235
    betweenRuns.thresholds(betweenRuns.runNumber)'
1236
    ];
1237
1238
fprintf('%10.3f \t%10.3f \t%10.1f  \n', resultsSoFar')
1239
1240
switch experiment.ear
1241
    case {'left', 'right', 'diotic', 'dichoticLeft','dichoticRight'}
1242
        disp(['caught out= ' num2str(betweenRuns.caughtOut)])
1243
end
1244
1245
% plot history of thresholds in panel 4
1246
axes(expGUIhandles.axes4), cla
1247
plotColors='rgbmckywrgbmckyw';
1248
for i=1:length(betweenRuns.thresholds)
1249
    faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1);
1250
    switch betweenRuns.variableName1
1251
        case {'targetFrequency', 'maskerRelativeFrequency'}
1252
            if min(betweenRuns.var1Sequence)>0
1253
                semilogx(betweenRuns.var1Sequence(i), ...
1254
                    betweenRuns.thresholds(i),  'o', ...
1255
                    'markerSize', 5,'markerFaceColor',faceColor)
1256
            else
1257
                plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber),  ...
1258
                    betweenRuns.thresholds,  'o', ...
1259
                    'markerSize', 5,'markerFaceColor',faceColor)
1260
                plot(betweenRuns.var1Sequence(i),  ...
1261
                    betweenRuns.thresholds(i),  'o', ...
1262
                    'markerSize', 5,'markerFaceColor',faceColor)
1263
            end
1264
        otherwise
1265
            plot(betweenRuns.var1Sequence(i),  ...
1266
                betweenRuns.thresholds(i),  'o', 'markerSize', 5,...
1267
                'markerFaceColor',faceColor)
1268
    end
1269
    hold on
1270
end
1271
xlimRM([ min(betweenRuns.variableList1) max(betweenRuns.variableList1) ])
1272
ylim(stimulusParameters.WRVlimits)
1273
ylabel('thresholds')
1274
xlabel(betweenRuns.variableName1)
1275
set(gca,'ytick', [0 20 40 60 80 100])
1276
try
1277
    % problems if only one x value
1278
    set(gca,'XTick', sort(betweenRuns.variableList1))
1279
catch
1280
end
1281
grid on, set(gca,'XMinorGrid', 'off')
1282
1283 28:02aa9826efe0 rmeddis
% final run?
1284 0:f233164f4c86 rmeddis
if betweenRuns.runNumber==length(betweenRuns.var1Sequence)
1285
    % yes, end of experiment
1286
    fileName=['savedData/' experiment.name experiment.date ...
1287
        experiment.paradigm];
1288
    % 	save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl')
1289
    disp('Experiment completed')
1290 28:02aa9826efe0 rmeddis
1291 0:f233164f4c86 rmeddis
    % update subject GUI to acknowledge end of run
1292
    subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}];
1293
    set(handles.textMSG,'string', subjGUImsg   )
1294
    % play 'Tada'
1295
    [y,fs,nbits]=wavread('TADA.wav');
1296
    musicGain=10^(stimulusParameters.musicLeveldB/20);
1297
    y=y*musicGain;
1298
    wavplay(y/100,fs, 'async')
1299 28:02aa9826efe0 rmeddis
1300 0:f233164f4c86 rmeddis
    % update experimenter GUI
1301
    addToMsg('Experiment completed.',1)
1302 28:02aa9826efe0 rmeddis
1303 0:f233164f4c86 rmeddis
    printReport
1304 28:02aa9826efe0 rmeddis
    experiment.status='endOfExperiment';
1305 0:f233164f4c86 rmeddis
    return
1306
else
1307
    % No, hang on.
1308
    switch experiment.ear
1309
        case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
1310
                'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
1311
            % no changes required if model used
1312
        otherwise
1313
            % decrement catchTrialRate towards baseRate
1314
            stimulusParameters.catchTrialRate=...
1315
                stimulusParameters.catchTrialBaseRate + ...
1316
                (stimulusParameters.catchTrialRate...
1317
                -stimulusParameters.catchTrialBaseRate)...
1318
                *(1-exp(-stimulusParameters.catchTrialTimeConstant));
1319
            fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
1320
                stimulusParameters.catchTrialRate)
1321 28:02aa9826efe0 rmeddis
1322 0:f233164f4c86 rmeddis
            % and go again
1323 28:02aa9826efe0 rmeddis
            set(handles.pushbuttonGO,'backgroundcolor','y')
1324 0:f233164f4c86 rmeddis
            set(handles.frame1,'visible','off')
1325
            set(handles.pushbuttonGO,'visible','on')
1326
            msg=[{'Ready to start new trial'}, {' '},...
1327
                {'Please,click on the GO button'}];
1328
            set(handles.textMSG,'string',msg)
1329
    end
1330
    experiment.status='waitingForGO';
1331
    %     fprintf('\n')
1332 28:02aa9826efe0 rmeddis
1333 0:f233164f4c86 rmeddis
    [y,fs,nbits]=wavread('CHIMES.wav');
1334
    musicGain=10^(stimulusParameters.musicLeveldB/20);
1335
    y=y*musicGain;
1336
    wavplay(y/100,fs,'async')
1337
end
1338
1339
% -----------------------------------------------------MAPmodelRunsGUI
1340
% The computer presses the buttons
1341
function 	MAPmodelRunsGUI(handles)
1342
global experiment stimulusParameters method expGUIhandles
1343
global AN_IHCsynapseParams
1344
method=[];
1345
1346 28:02aa9826efe0 rmeddis
while strcmp(experiment.status,'waitingForGO')
1347 0:f233164f4c86 rmeddis
    % no catch trials for MAP model
1348
    experiment.allowCatchTrials=0;
1349 28:02aa9826efe0 rmeddis
1350
    % initiates run and plays first stimulus and it returns
1351 0:f233164f4c86 rmeddis
    %  without waiting for button press
1352 28:02aa9826efe0 rmeddis
    startNewRun(handles)
1353 0:f233164f4c86 rmeddis
1354 29:b51bf546ca3f rmeddis
    % show sample Rate on GUI; it must be set in MAPparams ##??
1355 0:f233164f4c86 rmeddis
    set(expGUIhandles.textsampleRate,'string',...
1356
        num2str(stimulusParameters.sampleRate))
1357 28:02aa9826efe0 rmeddis
1358 29:b51bf546ca3f rmeddis
    if experiment.singleShot % ##??
1359 0:f233164f4c86 rmeddis
        AN_IHCsynapseParams.plotSynapseContents=1;
1360
    else
1361
        AN_IHCsynapseParams.plotSynapseContents=0;
1362
    end
1363 28:02aa9826efe0 rmeddis
1364 0:f233164f4c86 rmeddis
    % continuous loop until the program stops itself
1365
    while strcmp(experiment.status,'waitingForResponse')
1366
        %  NB at this point the stimulus has been played
1367
        pause(0.1)  % to allow interrupt with CTRL/C
1368 28:02aa9826efe0 rmeddis
1369 0:f233164f4c86 rmeddis
        switch experiment.ear
1370
            case { 'MAPmodelListen'}
1371 28:02aa9826efe0 rmeddis
                % flash the buttons to show model response
1372
                set(handles.pushbutton1,'backgroundcolor','y','visible','on')
1373
                set(handles.pushbutton2,'backgroundcolor','y','visible','on')
1374 0:f233164f4c86 rmeddis
        end
1375
1376 28:02aa9826efe0 rmeddis
        % Analayse the current stimulus using MAP
1377
        [modelResponse earObject]= MAPmodel;
1378 0:f233164f4c86 rmeddis
1379
        if experiment.stop || experiment.singleShot
1380
            % trap for single trial or user interrupt using 'stop' button.
1381
            experiment.status= 'waitingForStart';
1382 33:161913b595ae rmeddis
%             experiment.stop=0;
1383 28:02aa9826efe0 rmeddis
            errormsg='manually stopped';
1384
            addToMsg(errormsg,1)
1385 0:f233164f4c86 rmeddis
            return
1386
        end
1387 28:02aa9826efe0 rmeddis
1388 0:f233164f4c86 rmeddis
        switch modelResponse
1389
            case 1
1390
                %     userDoesNotHearTarget(handles)
1391
                switch experiment.ear
1392
                    case {'MAPmodelListen'}
1393
                        % illuminate appropriate button
1394
                        set(handles.pushbutton1,...
1395
                            'backgroundcolor','r','visible','on')
1396
                        set(handles.pushbutton2,'backgroundcolor','y')
1397
                end
1398
                userDecides(handles, false)
1399
                if experiment.singleShot, return, end
1400
1401
            case 2
1402
                %   userHearsTarget(handles)
1403
                switch experiment.ear
1404
                    case {'MAPmodelListen'}
1405
                        % illuminate appropriate button (DEMO only)
1406
                        set(handles.pushbutton2,'backgroundcolor',...
1407
                            'r','visible','on')
1408
                        set(handles.pushbutton1,'backgroundcolor','y')
1409
                end
1410 28:02aa9826efe0 rmeddis
1411 0:f233164f4c86 rmeddis
                switch experiment.paradigm
1412
                    case 'discomfort'
1413
                        % always treat discomfort as 'not heard'
1414
                        userDecides(handles, false)
1415
                    otherwise
1416
                        userDecides(handles, true)
1417
                end
1418
            otherwise
1419
                % probably an abort
1420
                return
1421
        end
1422
    end
1423
end
1424
1425 28:02aa9826efe0 rmeddis
% -------------------------------------------------------MAPmodel
1426
function [modelResponse, MacGregorResponse]=MAPmodel
1427 9:ecad0ea62b43 rmeddis
1428
global experiment stimulusParameters audio withinRuns
1429 29:b51bf546ca3f rmeddis
% global outerMiddleEarParams DRNLParams AN_IHCsynapseParams
1430
global ICoutput ANdt dt savedBFlist ANprobRateOutput expGUIhandles
1431
global paramChanges
1432 9:ecad0ea62b43 rmeddis
1433
savePath=path;
1434 24:a5e4a43c1673 rmeddis
addpath(['..' filesep 'MAP'], ['..' filesep 'utilities'])
1435 9:ecad0ea62b43 rmeddis
modelResponse=[];
1436
MacGregorResponse=[];
1437
1438
% mono only (column vector)
1439
audio=audio(:,1)';
1440
1441
% if stop button pressed earlier
1442 28:02aa9826efe0 rmeddis
if experiment.stop
1443
    errormsg='manually stopped';
1444
    addToMsg(errormsg,1)
1445
    return
1446
end
1447 9:ecad0ea62b43 rmeddis
1448 28:02aa9826efe0 rmeddis
% ---------------------------------------------- run Model
1449 9:ecad0ea62b43 rmeddis
MAPparamsName=experiment.name;
1450
showPlotsAndDetails=experiment.MAPplot;
1451 29:b51bf546ca3f rmeddis
1452
% important buried constant ##??
1453 9:ecad0ea62b43 rmeddis
AN_spikesOrProbability='spikes';
1454 32:82fb37eb430e rmeddis
% AN_spikesOrProbability='probability';
1455 9:ecad0ea62b43 rmeddis
1456
% [response, method]=MAPsequenceSeg(audio, method, 1:8);
1457 28:02aa9826efe0 rmeddis
1458
if sum(strcmp(experiment.ear,{'MAPmodelMultiCh', 'MAPmodelListen'}))
1459
    % use BFlist specified in MAPparams file
1460
    BFlist= -1;
1461
else
1462
    BFlist=stimulusParameters.targetFrequency;
1463
end
1464
paramChanges=get(expGUIhandles.editparamChanges,'string');
1465 29:b51bf546ca3f rmeddis
% convert from string to a cell array
1466
eval(paramChanges);
1467 28:02aa9826efe0 rmeddis
1468
MAP1_14(audio, stimulusParameters.sampleRate, BFlist,...
1469
    MAPparamsName, AN_spikesOrProbability, paramChanges);
1470
1471 9:ecad0ea62b43 rmeddis
if showPlotsAndDetails
1472 23:6cce421531e2 rmeddis
    options.printModelParameters=0;
1473 9:ecad0ea62b43 rmeddis
    options.showModelOutput=1;
1474
    options.printFiringRates=1;
1475
    options.showACF=0;
1476
    options.showEfferent=1;
1477 23:6cce421531e2 rmeddis
    options.surfProbability=0;
1478 25:d2c4c07df02c rmeddis
    showMapOptions.surfSpikes=0;
1479 23:6cce421531e2 rmeddis
    UTIL_showMAP(options)
1480 9:ecad0ea62b43 rmeddis
end
1481
1482
% No response,  probably caused by hitting 'stop' button
1483 28:02aa9826efe0 rmeddis
if strcmp(AN_spikesOrProbability,'spikes') && isempty(ICoutput)
1484
    return
1485
end
1486 9:ecad0ea62b43 rmeddis
% ---------------------------------------------------------- end model run
1487
1488 28:02aa9826efe0 rmeddis
if strcmp(AN_spikesOrProbability,'spikes')
1489
    MacGregorResponse= sum(ICoutput,1);                 % use IC
1490
    dt=ANdt;
1491
    time=dt:dt:dt*length(MacGregorResponse);
1492
else
1493
    % for one channel, ANprobResponse=ANprobRateOutput
1494
    % for multi channel take strongest in any epoch
1495
    nChannels=length(savedBFlist);
1496
    % use only HSR fibers
1497
    ANprobRateOutput=ANprobRateOutput(end-nChannels+1:end,:);
1498
    time=dt:dt:dt*length(ANprobRateOutput);
1499
end
1500 9:ecad0ea62b43 rmeddis
1501 28:02aa9826efe0 rmeddis
% group delay on unit response - these values are iffy
1502
windowOnsetDelay= 0.004;
1503
windowOffsetDelay= 0.022; % long ringing time
1504 9:ecad0ea62b43 rmeddis
1505
% now find the response of the MacGregor model during the target presentation + group delay
1506
switch experiment.threshEstMethod
1507
    case {'2I2AFC++', '2I2AFC+++'}
1508 28:02aa9826efe0 rmeddis
        idx= time>stimulusParameters.testTargetBegins+windowOnsetDelay ...
1509
            & time<stimulusParameters.testTargetEnds+windowOffsetDelay;
1510 9:ecad0ea62b43 rmeddis
        nSpikesTrueWindow=sum(MacGregorResponse(:,idx));
1511 28:02aa9826efe0 rmeddis
        idx=find(time>stimulusParameters.testNonTargetBegins+windowOnsetDelay ...
1512
            & time<stimulusParameters.testNonTargetEnds+windowOffsetDelay);
1513
        if strcmp(AN_spikesOrProbability,'spikes')
1514
            nSpikesFalseWindow=sum(MacGregorResponse(:,idx));
1515
        else
1516
            nSpikesDuringTarget=mean(ANprobRateOutput(end,idx));
1517
            % compare with spontaneous rate
1518
            if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
1519
                nSpikesDuringTarget=1;  % i.e. at leastone MacG spike
1520
            else
1521
                nSpikesDuringTarget=0;
1522
            end
1523
        end
1524 9:ecad0ea62b43 rmeddis
        % nSpikesDuringTarget is +ve when more spikes are found
1525
        %   in the target window
1526
        difference= nSpikesTrueWindow-nSpikesFalseWindow;
1527
1528
        if difference>0
1529
            % hit
1530
            nSpikesDuringTarget=experiment.MacGThreshold+1;
1531
        elseif    difference<0
1532
            % miss (wrong choice)
1533
            nSpikesDuringTarget=experiment.MacGThreshold-1;
1534
        else
1535
            if rand>0.5
1536
                % hit (random choice)
1537
                nSpikesDuringTarget=experiment.MacGThreshold+1;
1538
            else
1539
                % miss (random choice)
1540
                nSpikesDuringTarget=experiment.MacGThreshold-1;
1541
            end
1542
        end
1543
        disp(['level target dummy decision: ' ...
1544
            num2str([withinRuns.variableValue nSpikesTrueWindow ...
1545
            nSpikesFalseWindow  nSpikesDuringTarget], '%4.0f') ] )
1546 28:02aa9826efe0 rmeddis
    otherwise
1547
        % single interval
1548
        idx=find(time>stimulusParameters.testTargetBegins +windowOnsetDelay...
1549
            & time<stimulusParameters.testTargetEnds+windowOffsetDelay);
1550
        if strcmp(AN_spikesOrProbability,'spikes')
1551
            nSpikesDuringTarget=sum(MacGregorResponse(:,idx));
1552
            timeX=time(idx);
1553
        else
1554
            % probability, use channel with the highest average rate
1555
            nSpikesDuringTarget=max(mean(ANprobRateOutput(:,idx),2));
1556
            if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
1557
                nSpikesDuringTarget=1;
1558
            else
1559
                nSpikesDuringTarget=0;
1560
            end
1561 9:ecad0ea62b43 rmeddis
1562 28:02aa9826efe0 rmeddis
        end
1563 9:ecad0ea62b43 rmeddis
end
1564
1565
if experiment.MAPplot
1566
    % add vertical lines to indicate target region
1567
    figure(99), subplot(6,1,6)
1568
    hold on
1569
    yL=get(gca,'YLim');
1570 28:02aa9826efe0 rmeddis
    plot([stimulusParameters.testTargetBegins + windowOnsetDelay ...
1571
        stimulusParameters.testTargetBegins   + windowOnsetDelay],yL,'r')
1572
    plot([stimulusParameters.testTargetEnds   + windowOffsetDelay ...
1573
        stimulusParameters.testTargetEnds     + windowOffsetDelay],yL,'r')
1574 9:ecad0ea62b43 rmeddis
end
1575
1576
% specify unambiguous response
1577 28:02aa9826efe0 rmeddis
if nSpikesDuringTarget>experiment.MacGThreshold
1578
    modelResponse=2;    % stimulus detected
1579
else
1580
    modelResponse=1;    % nothing heard (default)
1581 9:ecad0ea62b43 rmeddis
end
1582
1583
path(savePath)
1584 0:f233164f4c86 rmeddis
1585
% -----------------------------------------------------statsModelRunsGUI
1586
% The computer presses the buttons
1587
function 	statsModelRunsGUI(handles)
1588
% Decision are made at random using a prescribe statistical function
1589
% to set probabilities as a function of signal level.
1590
global experiment
1591
1592
experiment.allowCatchTrials=0;
1593
1594
while strcmp(experiment.status,'waitingForGO')
1595
    % i.e. waiting for new run
1596
    if experiment.stop
1597
        % user has requested an abort
1598
        experiment.status= 'waitingForStart';
1599
        addToMsg('manually stopped',1)
1600
        return
1601
    end
1602 28:02aa9826efe0 rmeddis
1603 0:f233164f4c86 rmeddis
    % initiates run and plays first stimulus and it returns
1604
    %  without waiting for button press
1605
    % NB stimulus is not actually generated (for speed)
1606
    startNewRun(handles)
1607 28:02aa9826efe0 rmeddis
1608 0:f233164f4c86 rmeddis
    while strcmp(experiment.status,'waitingForResponse')
1609
        % create artificial response here
1610
        modelResponse=statsModelGetResponse;
1611
        switch modelResponse
1612
            case 1
1613
                %                 userDoesNotHearTarget(handles)
1614
                userDecides(handles, false)
1615
            case 2
1616
                %                 userHearsTarget(handles)
1617
                userDecides(handles, true)
1618
        end
1619
    end
1620
end
1621
1622
% -----------------------------------------------------statsModelGetResponse
1623
function modelResponse=statsModelGetResponse(handles)
1624
global experiment  withinRuns  statsModel stimulusParameters
1625
% use the generating function to decide if a detection occurs or not
1626
1627
% pause(0.1)  % to allow stopping with CTRL/C but slows things down
1628
1629
% first compute the probability that a detection occurs
1630
switch experiment.ear
1631
    case {'statsModelLogistic'}
1632
        prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean)));
1633
        %         if experiment.psyFunSlope<0,
1634
        %             prob=1-prob;
1635
        %         end
1636 28:02aa9826efe0 rmeddis
1637 0:f233164f4c86 rmeddis
    case 'statsModelRareEvent'
1638
        if experiment.psyFunSlope<0
1639
            addToMsg('statsModelRareEvent cannot be used with negative slope',0)
1640
            error('statsModelRareEvent cannot be used with negative slope')
1641
        end
1642 28:02aa9826efe0 rmeddis
1643 0:f233164f4c86 rmeddis
        % standard formula is prob = 1 ? exp(-d (g P ? A))
1644
        % here A->A;  To find Pmin use A/gain
1645
        pressure=28*10^(withinRuns.variableValue/20);
1646
        gain=statsModel.rareEvenGain;
1647
        A=statsModel.rareEventVmin;
1648
        d=stimulusParameters.targetDuration;
1649
        gP_Vmin=gain*pressure-A;
1650
        if gP_Vmin>0
1651
            prob=1-exp(-d*(gP_Vmin));
1652
        else
1653
            prob=0;
1654
        end
1655
end
1656
1657
% Use the probability to choose whether or not a detection has occurred
1658
switch experiment.threshEstMethod
1659
    case {'MaxLikelihood', 'oneIntervalUpDown'}
1660
        if rand<prob
1661
            modelResponse=2; %bingo
1662
        else
1663
            modelResponse=1; %nothing heard
1664
        end
1665 28:02aa9826efe0 rmeddis
1666 0:f233164f4c86 rmeddis
    case {'2I2AFC++', '2I2AFC+++'}
1667
        if rand<prob
1668
            modelResponse=2; %bingo
1669
        else %if the stimulus is not audible, take a 50:50 chance of getting it right
1670
            if rand<0.5
1671
                modelResponse=2; %bingo
1672
            else
1673
                modelResponse=1; %nothing heard
1674
            end
1675
        end
1676
end
1677
1678
1679
% ------------------------------------------------------- printTabTable
1680
function printTabTable(M, headers)
1681
% printTabTable prints a matrix as a table with tabs
1682
%headers are optional
1683
%headers=strvcat('firstname', 'secondname')
1684
%  printTabTable([1 2; 3 4],strvcat('a1','a2'));
1685
1686
if nargin>1
1687
    [r c]=size(headers);
1688
    for no=1:r
1689
        fprintf('%s\t',headers(no,:))
1690
    end
1691
    fprintf('\n')
1692
end
1693
1694
[r c]=size(M);
1695
1696
for row=1:r
1697
    for col=1:c
1698
        if row==1 && col==1 && M(1,1)==-1000
1699
            %   Print nothing (tab follows below)
1700
        else
1701
            fprintf('%s',num2str(M(row,col)))
1702
        end
1703
        if col<c
1704
            fprintf('\t')
1705
        end
1706
    end
1707
    fprintf('\n')
1708
end
1709
1710
% ------------------------------------------------------- xlimRM
1711
function xlimRM(x)
1712
try
1713
    xlim([x(1) x(2)])
1714
catch
1715
end
1716
1717
% ------------------------------------------------------- ylimRM
1718
function ylimRM(x)
1719
try
1720
    ylim([x(1) x(2)])
1721
catch
1722
end
1723
1724
1725
function editdigitInput_CreateFcn(hObject, eventdata, handles)
1726
1727
% Hint: edit controls usually have a white background on Windows.
1728
%       See ISPC and COMPUTER.
1729
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
1730
    set(hObject,'BackgroundColor','white');
1731
end
1732
1733
1734
% -----------------------------------------------------buttonBoxIntitialize
1735
function buttonBoxIntitialize
1736
% initialize button box
1737
global  serobj
1738
try
1739
    fclose(serobj);
1740
catch
1741
end
1742
1743
try
1744
    serobj = serial('COM4') ;           	% Creating serial port object now its connected to COM4 !!! button boxes in booths are connected to COM2
1745
    serobj.Baudrate = 9600;           		% Set the baud rate at the specific value
1746
    set(serobj, 'Parity', 'none') ;     	% Set parity as none
1747
    set(serobj, 'Databits', 8) ;          	% set the number of data bits
1748
    set(serobj, 'StopBits', 1) ;         	% set number of stop bits as 1
1749
    set(serobj, 'Terminator', 'CR') ; 		% set the terminator value to carriage return
1750
    set(serobj, 'InputBufferSize', 512) ;  	% Buffer for read operation, default it is 512
1751
    set(serobj,'timeout',10);           	% 10 sec timeout on button press
1752
    set(serobj, 'ReadAsyncMode', 'continuous')
1753
    set(serobj, 'BytesAvailableFcn', @buttonBox_callback)
1754
    set(serobj, 'BytesAvailableFcnCount', 1)
1755
    set(serobj, 'BytesAvailableFcnMode', 'byte')
1756
    % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback')
1757 28:02aa9826efe0 rmeddis
1758 0:f233164f4c86 rmeddis
    fopen(serobj);
1759
    buttonBoxStatus=get(serobj,'status');
1760
catch
1761
    disp('** no button box found - use mouse **')
1762
end