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 @ 29:b51bf546ca3f

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