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 / Copy_of_multithreshold 1.46 / nextStimulus.m @ 28:02aa9826efe0

History | View | Annotate | Download (35.7 KB)

1
function errormsg=nextStimulus(handles)
2
% Handles everything concerned with the stimulus presentation
3
%  called from startNewRun in subjGUI
4

    
5
global experiment stimulusParameters withinRuns betweenRuns
6
experiment.status='presentingStimulus';
7
errormsg='';
8

    
9
% interrupt by 'stop' button
10
if experiment.stop
11
    disp('******** experiment manually stopped  *****************')
12
    experiment.status= 'waitingForStart';
13
    addToMsg('manually stopped',1)
14
    return
15
end
16

    
17
% -----------------------------------------choose catch trials at random
18
% catch trials are for subject threshold measurements only
19
% this is the only place where withinRuns.catchTrial is set
20
if experiment.allowCatchTrials
21
    if withinRuns.trialNumber==1;
22
        % first trial is never a catch trial
23
        withinRuns.catchTrial=0;
24
        withinRuns.catchTrialCount=0;   % reset count on first trial
25
    elseif withinRuns.trialNumber==2 ...
26
            && withinRuns.catchTrialCount==0
27
        % second trial is always a catch trial
28
        withinRuns.catchTrial=1;
29
        withinRuns.catchTrialCount=1;   % this must be the first
30
    elseif withinRuns.thisIsRepeatTrial
31
        % for requested repeats do not change catch trial status
32
        withinRuns.thisIsRepeatTrial=0; % reset toggle
33
    else
34
        % choose whether or not to have a catch trial
35
        R=rand;
36
        if R<stimulusParameters.catchTrialRate
37
            % catch trial
38
            withinRuns.catchTrial=1;
39
            addToMsg('Catch Trial',1)
40
            withinRuns.catchTrialCount=withinRuns.catchTrialCount+1;
41
        else
42
            % not a catch trial
43
            withinRuns.catchTrial=0;
44
        end
45
    end
46
else
47
    % no catch trials for statistical evaluations or 2AIFC or (poss) MAP
48
    withinRuns.catchTrial=0;
49
end
50

    
51
%------------ during stimulus presentation show appropriate button images
52
switch experiment.ear
53
    case {'statsModelLogistic', 'statsModelRareEvent',...
54
            'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh'}
55
        % no buttons shown
56
    otherwise
57
        switch experiment.threshEstMethod
58
            case {'2I2AFC++', '2I2AFC+++'}
59
                %Except for 2I2AFC
60
                % For 2I2AFC the buttons on the screen ab initio
61
                set(handles.frame1,'visible','off')
62
                set(handles.pushbuttonGO,'visible','off')
63
                set(handles.pushbuttoNotSure,'visible','off')
64
                set(handles.pushbuttonWrongButton,'visible','off')
65
                set(handles.pushbutton3,'visible','off')
66
                set(handles.pushbutton0,'visible','off')
67
                set(handles.pushbutton1,'visible','on')
68
                set(handles.pushbutton2,'visible','on')
69
                drawnow
70
            otherwise
71
                % i.e. single interval/ maxLikelihood
72
                set(handles.frame1,'backgroundColor','w')
73
                set(handles.frame1,'visible','off')
74
                set(handles.pushbuttoNotSure,'visible','off')
75
                set(handles.pushbuttonWrongButton,'visible','off')
76
                set(handles.pushbutton3,'visible','off')
77
                set(handles.pushbutton2,'visible','off')
78
                set(handles.pushbutton1,'visible','off')
79
                set(handles.pushbutton0,'visible','off')
80
                pause(.1)
81
        end
82
end
83

    
84
set(handles.textMSG,'BackgroundColor','w', 'ForegroundColor', 'b')
85

    
86
% Now the serious business of crafting and presenting the stimulus
87
errormsg= stimulusMakeAndPlay (handles);
88

    
89
if ~isempty(errormsg)
90
    % e.g. clipping. subjGUI will service the error
91
    return
92
end
93

    
94
% after playing the stimulus, reset the subjectGUI
95
switch experiment.ear
96
    case {'statsModelLogistic', 'statsModelRareEvent',...
97
            'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh'}
98
        % no changes required if model used
99
        % NB these changes do occur is 'MAPmodelListen' is selected
100
    otherwise
101
        switch experiment.threshEstMethod
102
            case {'2I2AFC++', '2I2AFC+++'}
103
                % buttons already visible
104
            otherwise
105
                % single interval now make buttons visible
106
                set(handles.frame1,'visible','on')
107
                set(handles.pushbuttoNotSure,'visible','on')
108
                % set(handles.pushbuttonWrongButton,'visible','on')
109
                set(handles.pushbutton0,'visible','on')
110
                set(handles.pushbutton1,'visible','on')
111
                set(handles.pushbutton2,'visible','on')
112
                set(handles.pushbutton3,'visible','on')
113
        end
114
end
115

    
116
switch experiment.paradigm
117
    case 'SRT'
118
        set(handles.frame1,'backgroundColor','w')
119
        set(handles.frame1,'visible','off')
120
        set(handles.pushbuttoNotSure,'visible','off')
121
        set(handles.pushbuttonWrongButton,'visible','off')
122
        set(handles.pushbutton3,'visible','off')
123
        set(handles.pushbutton2,'visible','off')
124
        set(handles.pushbutton1,'visible','off')
125
        set(handles.pushbutton0,'visible','off')
126
        set(handles.editdigitInput,'visible','on')
127
        set(handles.editdigitInput,'string',[])
128
        pause(.2)
129
        uicontrol(handles.editdigitInput)
130
        
131
    otherwise
132
        set(handles.editdigitInput,'visible','off')
133
end
134

    
135

    
136
experiment.status='waitingForResponse';
137
% home again
138

    
139
% ------------------------------------------------------------------------------------------stimulusMakeAndPlay
140
function errormsg=stimulusMakeAndPlay (handles)
141
global experiment stimulusParameters betweenRuns withinRuns expGUIhandles  audio
142
% creates the stimulus and plays it; there are two stimuli; cue and test
143
%  called from nextStimulus
144

    
145
errormsg='';
146

    
147
% first post the subjects instructions on subjGUI
148
set(handles.textMSG,'string', stimulusParameters.subjectText)
149

    
150
% select the new levels of the between runs variables
151
num=betweenRuns.runNumber;
152
cmd=(['stimulusParameters.' betweenRuns.variableName1 '= ' ...
153
    num2str(betweenRuns.var1Sequence(num)) ';']);
154
% e.g.  stimulusParameters.targetFrequency= 1000;
155
eval(cmd);
156

    
157
cmd=(['stimulusParameters.' betweenRuns.variableName2 '= ' ...
158
    num2str(betweenRuns.var2Sequence(num)) ';']);
159
% e.g.  stimulusParameters.targetDuration= 0.1;
160
eval(cmd);
161

    
162
switch experiment.paradigm
163
    % target level may vary between runs
164
    case {'trainingIFMC', 'TMC','TMC_16ms', 'TMC - ELP', 'IFMC','IFMC_8ms','IFMC_16ms'}
165
        idx=floor(num/length(betweenRuns.variableList1)-0.01)+1;
166
        cmd=(['stimulusParameters.targetLevel = ' ...
167
            num2str(stimulusParameters.targetLevels(idx)) ';']);
168
        eval(cmd);
169
        if withinRuns.trialNumber==1
170
            disp(['targetLevel=' num2str(stimulusParameters.targetLevel)])
171
        end
172
end
173

    
174

    
175
% for more readable code use shorter variable names;
176
% NB these may change below; these are only the starting values
177

    
178
targetType=        stimulusParameters.targetType;
179
targetDuration=    stimulusParameters.targetDuration;
180
targetLevel=       stimulusParameters.targetLevel;
181
targetFrequency=   stimulusParameters.targetFrequency;
182

    
183
maskerType=        stimulusParameters.maskerType;
184
maskerDuration=    stimulusParameters.maskerDuration;
185
maskerLevel=       stimulusParameters.maskerLevel;
186
maskerRelativeFrequency=  stimulusParameters.maskerRelativeFrequency;
187
maskerFrequency=   maskerRelativeFrequency*targetFrequency;
188

    
189
gapDuration=       stimulusParameters.gapDuration;
190

    
191
rampDuration=      stimulusParameters.rampDuration;
192
AFCsilenceDuration=stimulusParameters.AFCsilenceDuration; % 2I2AFC gap
193
backgroundLevel=   stimulusParameters.backgroundLevel;
194

    
195
% Set level of within runs variable
196
% this is the first change to one of the values shown above
197
cmd=[stimulusParameters.WRVname '= withinRuns.variableValue;' ];
198
% e.g.: maskerLevel= withinRuns.variableValue;
199
eval(cmd);
200

    
201
% cue and test stimuli are identical except for a single difference
202
% depending on the paradigm
203
cueTestDifference= stimulusParameters.cueTestDifference;
204
%  cue characteristics before adding cue differences
205
cueTargetLevel=targetLevel;
206
cueMaskerFrequency=maskerFrequency;
207
cueMaskerDuration=maskerDuration;
208
cueMaskerLevel=maskerLevel;
209
cueTargetFrequency=targetFrequency;
210
cueGapDuration=gapDuration;
211

    
212
% ----------------------------paradigm sensitive cue and masker settings
213
% switch off unwanted components and base cue on target values
214
% for catch trials switch off the target
215
switch experiment.paradigm
216
    % OHIO is a temporary special arrangement
217
    case{'OHIOrand','OHIOspect','OHIOtemp','OHIOspectemp','OHIOabs'}
218
        % these values must be set in MAPparamsOHIO
219
        targetFrequency=experiment.OHIOfrequencies;
220
        numOHIOtones=stimulusParameters.numOHIOtones;
221
        maskerType='OHIO';
222
        targetType='OHIO';
223
        
224
        % globalStimParams.beginSilences says when each tone begins
225
        % targetFrequency specifies the frequency of each tone
226
        % targetLevel is the level of each tone
227
        switch experiment.paradigm
228
            case 'OHIOabs'
229
                % only one tone
230
                targetFrequency=targetFrequency(numOHIOtones);
231
                globalStimParams.beginSilences=0.01;
232
                targetDuration=0.01;
233
            case 'OHIOrand'
234
                % select random frequencies from the list
235
                x=rand(12,1); [x idx]=sort(x);
236
                targetFrequency=targetFrequency(idx(1:numOHIOtones));
237
                thresholds=experiment.OHIOthresholds;
238
                targetLevel=thresholds(idx(1:numOHIOtones))+targetLevel;
239
                globalStimParams.beginSilences=0.01:0.02:...
240
                    0.01+0.02*(numOHIOtones-1);
241
                targetDuration=numOHIOtones*0.02;
242
            case 'OHIOspect'
243
                % only one tone with multiple frequencies
244
                targetFrequency=fliplr(targetFrequency);
245
                targetFrequency=targetFrequency(1:numOHIOtones);
246
                thresholds=experiment.OHIOthresholds;
247
                thresholds=fliplr(thresholds);
248
                targetLevel=thresholds(1:numOHIOtones)+targetLevel;
249
                globalStimParams.beginSilences=...
250
                    repmat(0.01, 1, numOHIOtones);
251
                targetDuration=0.02;
252
            case 'OHIOspectemp'
253
                % only one tone with multiple frequencies
254
                targetFrequency=fliplr(targetFrequency);
255
                targetFrequency=targetFrequency(1:numOHIOtones);
256
                thresholds=experiment.OHIOthresholds;
257
                thresholds=fliplr(thresholds);
258
                targetLevel=thresholds(1:numOHIOtones)+targetLevel;
259
                globalStimParams.beginSilences=0.01:0.02:...
260
                    0.01+0.02*(numOHIOtones-1);
261
                targetDuration=numOHIOtones*0.02;
262
            case 'OHIOtemp'
263
                % use only one tone repeatedly
264
                tonesToUse=10;
265
                targetFrequency=targetFrequency(tonesToUse);
266
                targetFrequency=repmat(targetFrequency,1,numOHIOtones);
267
                thresholds=experiment.OHIOthresholds;
268
                thresholds=thresholds(tonesToUse);
269
                targetLevel=repmat(thresholds,1,numOHIOtones)+targetLevel;
270
                globalStimParams.beginSilences=0.01:0.02:...
271
                    0.01+0.02*(numOHIOtones-1);
272
                targetDuration=numOHIOtones*0.02;
273
        end
274
        % still in OHIO
275
        % Dummy values to make things work although no masker or cue used
276
        % target values have changed and this affects the cue values
277
        cueMaskerLevel=targetLevel;
278
        cueTargetLevel=targetLevel;
279
        cueTargetFrequency=targetFrequency;
280
        cueMaskerFrequency=targetFrequency;
281
        maskerFrequency=targetFrequency;
282
        maskerLevel=targetLevel;
283
        disp(['OHIO frequencies= ' num2str(targetFrequency)])
284
end
285

    
286
% --- set cueTarget level according to assessment method
287
% cue-test difference applies only with singleInterval
288
switch experiment.threshEstMethod
289
    case {'2I2AFC++', '2I2AFC+++'}
290
        % For 2IFC the cue stimulus (masker + probe) is the 'no' window
291
        % and the target stimulus (masker+probe) is the 'yes' window
292
        % the order of presentation is decided at the last minute.
293
        cueTargetLevel=-100;    % the target is never in the 'no' window
294
        cueMaskerLevel=maskerLevel; % masker level is the same in both
295
    otherwise
296
        % 'single interval' or max likelihood
297
        switch experiment.paradigm
298
            % cue target is more audible
299
            case {'training','absThreshold', 'absThreshold_8',  ...
300
                    'TENtest', 'threshold_duration','discomfort',...
301
                    'overShoot','overShootB','overShootMB1', ...
302
                    'overShootMB2', 'OHIO','OHIOabs','OHIOspect'}
303
                cueTargetLevel=targetLevel+cueTestDifference;
304
                
305
            case {'forwardMasking','forwardMaskingD','trainingIFMC', ...
306
                    'TMC','TMC_16ms', 'TMC - ELP', 'IFMC','IFMC_8ms', 'FMreProbe'}
307
                % cue masker is weaker to make target more audible
308
                cueMaskerLevel=maskerLevel-cueTestDifference;
309
        end
310
end
311

    
312
% ----------------------------- catch trial
313
if withinRuns.catchTrial
314
    targetLevel=-100;	% no target
315
end
316

    
317
% ----------------------------- calibration of sound output
318
calibrationCorrectiondB=stimulusParameters.calibrationdB;
319
if calibrationCorrectiondB<-50
320
    if maskerFrequency==targetFrequency
321
        load 'calibrationFile'  % calibrationFrequency calibrationAttenutation
322
        idx=find(calibrationFrequency==targetFrequency);
323
        if isempty(idx)
324
            error('Calibration bty file; frequency not found')
325
        else
326
            calibrationCorrectiondB=calibrationAttenutation(idx)
327
        end
328
    else
329
        error('calibration by file requested but masker frequency is not the same as target')
330
    end
331
end
332

    
333

    
334
% -------------------------------------- Checks on excessive signal level
335

    
336
% clipping is relevant only for soundcard use (not modelling)
337
switch experiment.ear
338
    case {'left', 'right', 'diotic',...
339
            'dichotic', 'dioticLeft', 'dichoticRight'}
340
        experiment.headphonesUsed=1;
341
    otherwise
342
        experiment.headphonesUsed=0;
343
end
344

    
345
% NB calibration *reduces* the level of the soundCard output
346
switch experiment.ear
347
    case {'left', 'right', 'diotic',...
348
            'dichotic', 'dioticLeft', 'dichoticRight'}
349
        clippingLevel=91+calibrationCorrectiondB;
350
        soundCardMinimum=clippingLevel-20*log10(2^24);
351
    otherwise
352
        clippingLevel=inf;
353
        soundCardMinimum=-inf;
354
end
355

    
356
% Check for extreme WRV values and abort if necessary
357
% WRVname specifies the value that changes from trial to trial
358
withinRuns.forceThreshold=[];
359
switch stimulusParameters.WRVname
360
    % check for extreme values. Note that one of the tones might be switched off
361
    case 'maskerLevel'
362
        upperLevel=stimulusParameters.WRVlimits(2);
363
        lowerLevel=stimulusParameters.WRVlimits(1);
364
        if max(maskerLevel, cueMaskerLevel)> upperLevel
365
            errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ...
366
                ') is too high ***'];
367
            withinRuns.forceThreshold=upperLevel;
368
            withinRuns.forceThreshold=NaN;
369
            return
370
        end
371
        if max(maskerLevel, cueMaskerLevel)< lowerLevel
372
            errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ...
373
                ') is too low ***'];
374
            withinRuns.forceThreshold=lowerLevel;
375
            withinRuns.forceThreshold=NaN;
376
            return
377
        end
378
        
379
        if max(maskerLevel, cueMaskerLevel)> clippingLevel
380
            errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ...
381
                ') is clipping ***'];
382
            withinRuns.forceThreshold=clippingLevel;
383
            withinRuns.forceThreshold=NaN;
384
            return
385
        end
386
        
387
    case 'targetLevel'
388
        upperLevel=stimulusParameters.WRVlimits(2);
389
        lowerLevel=stimulusParameters.WRVlimits(1);
390
        if ~withinRuns.catchTrial
391
            if max(targetLevel, cueTargetLevel)> upperLevel
392
                errormsg=['target level (' ...
393
                    num2str(max(targetLevel, cueTargetLevel)) ...
394
                    ')  is too high ***'];
395
                withinRuns.forceThreshold=upperLevel;
396
                withinRuns.forceThreshold=NaN;
397
                return
398
            end
399
            if max(targetLevel, cueTargetLevel)< lowerLevel
400
                errormsg=['target level (' ...
401
                    num2str(max(targetLevel, cueTargetLevel)) ...
402
                    ')  is too low ***'];
403
                withinRuns.forceThreshold=lowerLevel;
404
                withinRuns.forceThreshold=NaN;
405
                return
406
            end
407
            if max(targetLevel, cueTargetLevel)> clippingLevel
408
                errormsg=['target level (' ...
409
                    num2str(max(targetLevel, cueTargetLevel)) ...
410
                    ')  is clipping ***'];
411
                withinRuns.forceThreshold=upperLevel;
412
                withinRuns.forceThreshold=NaN;
413
                return
414
            end
415
        end
416
    case 'maskerDuration'
417
        % this is odd! but harmless
418
        if max(maskerDuration, cueMaskerDuration)> ...
419
                stimulusParameters.WRVlimits(2)
420
            errormsg=['maskerDuration (' ...
421
                num2str(max(maskerDuration, cueMaskerDuration))...
422
                ') is too long ***'];
423
            withinRuns.forceThreshold=stimulusParameters.WRVlimits(2);
424
            withinRuns.forceThreshold=NaN;
425
            return
426
        end
427
        
428
        if min(maskerDuration, cueMaskerDuration)...
429
                < stimulusParameters.WRVlimits(1)
430
            errormsg=['maskerDuration (' num2str(maskerLevel) ...
431
                ') too short ***'];
432
            withinRuns.forceThreshold=stimulusParameters.WRVlimits(1);
433
            withinRuns.forceThreshold=NaN;
434
            return
435
        end
436
        
437
        % legacy programming
438
    case 'gapDuration'
439
        if gapDuration<0
440
            errormsg=['gapDuration (' num2str(gapDuration) ...
441
                ') is less than zero  ***'];
442
            return
443
        end
444
        
445
    case 'maskerFrequency'
446
        switch experiment.paradigm
447
            case 'bandwidth'
448
                frequency=maskerFrequency';
449
                if stimulusParameters.WRVstep<0
450
                    lowerLevel=stimulusParameters.targetFrequency;
451
                    upperLevel=stimulusParameters.targetFrequency*2;
452
                else
453
                    lowerLevel=stimulusParameters.targetFrequency/3;
454
                    upperLevel=stimulusParameters.targetFrequency;
455
                end
456
                
457
                if frequency(1)>upperLevel || frequency(1)<lowerLevel
458
                    errormsg=['frequency out of range: ' ...
459
                        num2str(frequency)];
460
                    withinRuns.forceThreshold=frequency;
461
                    return
462
                end
463
            otherwise
464
        end
465
        
466
    case 'maskerRelativeFrequency'
467
        if  maskerRelativeFrequency<stimulusParameters.WRVlimits(1)
468
            errormsg=['masker frequency (' ...
469
                num2str(frequencyDifference) ...
470
                ')  is outside WRV limits ***'];
471
            withinRuns.forceThreshold=stimulusParameters.WRVlimits(1) ;
472
            return
473
        end
474
        if   maskerRelativeFrequency>stimulusParameters.WRVlimits(2)
475
            errormsg=['masker frequency (' ...
476
                num2str(frequencyDifference) ...
477
                ')  is outside WRV limits ***'];
478
            withinRuns.forceThreshold=stimulusParameters.WRVlimits(2) ;
479
            return
480
        end
481
        
482
end
483

    
484
% --------------------------------Ear ----------------------------------
485
globalStimParams.ears='specified';
486
% ear: 1=left, 2=right
487
switch experiment.ear
488
    case 'left'
489
        maskerEar=1;
490
        targetEar=1;
491
    case 'right'
492
        maskerEar=2;
493
        targetEar=2;
494
    case 'dichoticLeft'
495
        maskerEar=2;
496
        targetEar=1;
497
    case 'dichoticRight'
498
        maskerEar=1;
499
        targetEar=2;
500
    case 'diotic'
501
        maskerEar=1;
502
        targetEar=1;
503
        globalStimParams.ears='diotic';
504
    case {'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh', 'MAPmodelListen',...
505
            'statsModelLogistic', 'statsModelRareEvent'}
506
        maskerEar=1;
507
        targetEar=1;
508
end
509

    
510
backgroundType=stimulusParameters.backgroundType;
511
switch stimulusParameters.backgroundType
512
    case {'noiseDich', 'pinkNoiseDich'}
513
        %     case 'Dich'
514
        % dich means put the background in the ear opposite to the target
515
        backgroundType=backgroundType(1:end-4);
516
        switch targetEar
517
            case 1
518
                backgroundEar=2;
519
            case 2
520
                backgroundEar=1;
521
        end
522
    otherwise
523
        %             case {'none','noise', 'pinkNoise', 'TEN','babble'}
524
        backgroundEar=targetEar;
525
end
526

    
527
% ------------------------------- Make Stimulus -------------------
528
% single interval up/down plays cue then target stimulus
529
% 2IFC uses cue stimulus as interval with no target
530
globalStimParams.FS=stimulusParameters.sampleRate;
531
dt=1/stimulusParameters.sampleRate;
532
globalStimParams.dt=dt;
533
stimulusParameters.dt=dt; % for use later
534

    
535

    
536

    
537
globalStimParams.audioOutCorrection=10^(calibrationCorrectiondB/20);
538
% the output will be reduced by this amount in stimulusCreate
539
% i.e.  audio=audio/globalStimParams.audioOutCorrection
540
% A 91 dB level will yield a peak amp of 1 for calibration=0
541
% A 91 dB level will yield a peak amp of 0.4467 for calibration=7
542
% A 98 dB level will yield a peak amp of 1 for calibration=7
543

    
544
precedingSilence=stimulusParameters.stimulusDelay;
545
% all stimuli have 20 ms terminal silence.
546
% this is clearance for modelling late-ringing targets
547
terminalSilence=.03;
548

    
549
% Now compute overall duration of the stimulus
550
% note that all endsilence values are set to -1
551
%  so that they will fill with terminal silence as required to make
552
%  components equal in length
553
% We need to find the longest possible duration
554
duration(1)=precedingSilence+maskerDuration+cueGapDuration...
555
    +targetDuration+terminalSilence;
556
duration(2)=precedingSilence+maskerDuration+gapDuration...
557
    +targetDuration+ terminalSilence;
558
% If the gap is negative we need to ignore it when estimating total length
559
duration(3)=precedingSilence+maskerDuration+ terminalSilence;
560
globalStimParams.overallDuration=max(duration);
561
globalStimParams.nSignalPoints=...
562
    round(globalStimParams.overallDuration/globalStimParams.dt);
563

    
564
%           ----------------------------------------------cue stimulus
565
% cue masker
566
componentNo=1;
567
precedingSilence=stimulusParameters.stimulusDelay;
568
stimComponents(maskerEar,componentNo).type=maskerType;
569
stimComponents(maskerEar,componentNo).toneDuration=cueMaskerDuration;
570
stimComponents(maskerEar,componentNo).frequencies=cueMaskerFrequency;
571
stimComponents(maskerEar,componentNo).amplitudesdB=cueMaskerLevel;
572
stimComponents(maskerEar,componentNo).beginSilence=precedingSilence;
573
stimComponents(maskerEar,componentNo).endSilence=-1;
574
stimComponents(maskerEar,componentNo).AMfrequency=0;
575
stimComponents(maskerEar,componentNo).AMdepth=0;
576
if rampDuration<maskerDuration
577
    % ramps must be shorter than the signal
578
    stimComponents(maskerEar,componentNo).rampOnDur=rampDuration;
579
    stimComponents(maskerEar,componentNo).rampOffDur=rampDuration;
580
else
581
    % or squeeze the ramp in
582
    stimComponents(maskerEar,componentNo).rampOnDur=maskerDuration/2;
583
    stimComponents(maskerEar,componentNo).rampOffDur=maskerDuration/2;
584
end
585
stimComponents(maskerEar,componentNo).phases=...
586
    stimulusParameters.maskerPhase;
587
stimComponents(maskerEar,componentNo).niterations=0;    % for IRN only
588
% stimComponents(targetEar,componentNo)
589

    
590
% cue target
591
componentNo=2;
592
precedingSilence=precedingSilence + maskerDuration+cueGapDuration;
593
stimComponents(targetEar,componentNo).type=targetType;
594
stimComponents(targetEar,componentNo).toneDuration=targetDuration;
595
stimComponents(targetEar,componentNo).frequencies=cueTargetFrequency;
596
stimComponents(targetEar,componentNo).amplitudesdB=cueTargetLevel;
597
stimComponents(targetEar,componentNo).beginSilence=precedingSilence;
598
stimComponents(targetEar,componentNo).endSilence=-1;
599
stimComponents(targetEar,componentNo).AMfrequency=0;
600
stimComponents(targetEar,componentNo).AMdepth=0;
601
if rampDuration<targetDuration
602
    % ramps must be shorter than the signal
603
    stimComponents(targetEar,componentNo).rampOnDur=rampDuration;
604
    stimComponents(targetEar,componentNo).rampOffDur=rampDuration;
605
else
606
    stimComponents(targetEar,componentNo).rampOnDur=0;
607
    stimComponents(targetEar,componentNo).rampOffDur=0;
608
end
609
stimComponents(targetEar,componentNo).phases=...
610
    stimulusParameters.targetPhase;
611
% stimComponents(targetEar,componentNo)
612

    
613
% background same ear as target
614
componentNo=3;
615
stimComponents(backgroundEar,componentNo).type=backgroundType;
616
switch backgroundType
617
    case 'TEN'
618
        fileName=['..' filesep '..' filesep ...
619
            'multithresholdResources' filesep ...
620
            'backgrounds and maskers'...
621
            filesep 'ten.wav'];
622
        [tenNoise, FS]=wavread(fileName);
623
        tenNoise=resample(tenNoise, globalStimParams.FS, FS);
624
        stimComponents(backgroundEar,componentNo).type='file';
625
        stimComponents(backgroundEar,componentNo).stimulus=tenNoise';
626
end
627
stimComponents(backgroundEar,componentNo).toneDuration=...
628
    globalStimParams.overallDuration;
629
stimComponents(backgroundEar,componentNo).amplitudesdB=backgroundLevel;
630
stimComponents(backgroundEar,componentNo).beginSilence=0;
631
stimComponents(backgroundEar,componentNo).endSilence=-1;
632
stimComponents(backgroundEar,componentNo).AMfrequency=0;
633
stimComponents(backgroundEar,componentNo).AMdepth=0;
634
stimComponents(backgroundEar,componentNo).rampOnDur=rampDuration;
635
stimComponents(backgroundEar,componentNo).rampOffDur=rampDuration;
636

    
637
[cueStimulus, errormsg]=...
638
    stimulusCreate(globalStimParams, stimComponents, 0);
639
if ~isempty(errormsg)     % e.g. limits exceeded
640
    errormsg
641
    return
642
end
643

    
644
%               ------------------------------------------ test stimulus
645
% masker
646
componentNo=1;
647
precedingSilence=stimulusParameters.stimulusDelay;
648
stimComponents(maskerEar,componentNo).type=maskerType;
649
stimComponents(maskerEar,componentNo).toneDuration=maskerDuration;
650
stimComponents(maskerEar,componentNo).frequencies=maskerFrequency;
651
stimComponents(maskerEar,componentNo).amplitudesdB=maskerLevel;
652
stimComponents(maskerEar,componentNo).beginSilence=precedingSilence;
653
stimComponents(maskerEar,componentNo).endSilence=-1;
654
stimComponents(maskerEar,componentNo).AMfrequency=0;
655
stimComponents(maskerEar,componentNo).AMdepth=0;
656
if rampDuration<maskerDuration
657
    % ramps must be shorter than the signal
658
    stimComponents(maskerEar,componentNo).rampOnDur=rampDuration;
659
    stimComponents(maskerEar,componentNo).rampOffDur=rampDuration;
660
else
661
    stimComponents(maskerEar,componentNo).rampOnDur=maskerDuration/2;
662
    stimComponents(maskerEar,componentNo).rampOffDur=maskerDuration/2;
663
end
664
stimComponents(maskerEar,componentNo).phases=...
665
    stimulusParameters.maskerPhase;
666
stimComponents(maskerEar,componentNo).niterations=0;    % for IRN only
667

    
668
% target
669
componentNo=2;
670
targetDelay=precedingSilence+ maskerDuration+ gapDuration;
671
stimComponents(targetEar,componentNo).type=targetType;
672
stimComponents(targetEar,componentNo).toneDuration=targetDuration;
673
stimComponents(targetEar,componentNo).frequencies=targetFrequency;
674
stimComponents(targetEar,componentNo).amplitudesdB=targetLevel;
675
stimComponents(targetEar,componentNo).beginSilence=targetDelay;
676
stimComponents(targetEar,componentNo).endSilence=-1;
677
stimComponents(targetEar,componentNo).AMfrequency=0;
678
stimComponents(targetEar,componentNo).AMdepth=0;
679
if rampDuration<targetDuration
680
    % ramps must be shorter than the signal
681
    stimComponents(targetEar,componentNo).rampOnDur=rampDuration;
682
    stimComponents(targetEar,componentNo).rampOffDur=rampDuration;
683
else
684
    stimComponents(targetEar,componentNo).rampOnDur=0;
685
    stimComponents(targetEar,componentNo).rampOffDur=0;
686
end
687
stimComponents(targetEar,componentNo).phases=stimulusParameters.targetPhase;
688
% stimComponents(targetEar,componentNo)
689

    
690
% background same ear as target
691
componentNo=3;
692
stimComponents(backgroundEar,componentNo).type=backgroundType;
693
switch backgroundType
694
    case 'TEN'
695
        fileName=['..' filesep '..' filesep ...
696
            'multithresholdResources' filesep ...
697
            'backgrounds and maskers'...
698
            filesep 'ten.wav'];
699
        [tenNoise, FS]=wavread(fileName);
700
        
701
        tenNoise=resample(tenNoise, globalStimParams.FS, FS);
702
        stimComponents(backgroundEar,componentNo).type='file';
703
        stimComponents(backgroundEar,componentNo).stimulus=tenNoise';
704
end
705
stimComponents(backgroundEar,componentNo).toneDuration=...
706
    globalStimParams.overallDuration;
707
stimComponents(backgroundEar,componentNo).amplitudesdB=backgroundLevel;
708
stimComponents(backgroundEar,componentNo).beginSilence=0;
709
stimComponents(backgroundEar,componentNo).endSilence=-1;
710
stimComponents(backgroundEar,componentNo).rampOnDur=rampDuration;
711
stimComponents(backgroundEar,componentNo).rampOffDur=rampDuration;
712
stimComponents(backgroundEar,componentNo).AMfrequency=0;
713
stimComponents(backgroundEar,componentNo).AMdepth=0;
714

    
715
% timings used when evaluating MAP peripheral model
716
% this is the Slope during which spikes are counted
717
switch experiment.paradigm
718
    case 'gapDetection'
719
        % gap is the 'target' in this case
720
        stimulusParameters.testTargetBegins=...
721
            stimulusParameters.stimulusDelay...
722
            +stimulusParameters.maskerDuration;
723
        stimulusParameters.testTargetEnds=...
724
            stimulusParameters.testTargetBegins+withinRuns.variableValue;
725
        %     case 'SRT'
726
        %         set(handles.editdigitInput,'visible','off')
727
    otherwise
728
        stimulusParameters.testTargetBegins=targetDelay;
729
        stimulusParameters.testTargetEnds=targetDelay+targetDuration;
730
end
731

    
732
% ------------------------------------------------------------- play!
733
% Create and play stimulus (as required by different paradigms)
734
switch experiment.ear
735
    case {'statsModelLogistic', 'statsModelRareEvent'}
736
        audio=[0;0];            % no need to compute stimulus
737
        
738
    otherwise                   % create the stimulus
739
        [targetStimulus, errormsg]= ...
740
            stimulusCreate(globalStimParams, stimComponents, 0);
741
        
742
        if ~isempty(errormsg)   % e.g. limits exceeded
743
            errormsg
744
            return
745
        end
746
        
747
        switch experiment.ear
748
            case {'MAPmodel' , 'MAPmodelMultiCh', 'MAPmodelSingleCh', 'MAPmodelListen'}
749
                % model requires no calibration correction;
750
                %  signal is already in Pascals
751
                globalStimParams.audioOutCorrection=1;
752
                % use only the targetStimulus for the MAP model
753
                audio=targetStimulus;
754
                
755
            otherwise           % left, right diotic dichotic
756
                if stimulusParameters.includeCue
757
                    audio= [cueStimulus;  targetStimulus];
758
                else            % no cue
759
                    audio=targetStimulus;
760
                end
761
        end
762
        
763
        % playtime
764
        % order of the cue and test stimuli varies for 2AFC
765
        switch experiment.threshEstMethod
766
            case {'2I2AFC++', '2I2AFC+++'}
767
                % intervening silence (currently none; masking delay serves this purpose)
768
                IAFCinterveningSilence=zeros(round(AFCsilenceDuration/dt),2);
769
                if rand>0.5				% put test stimulus first
770
                    stimulusParameters.testTargetBegins=targetDelay ;
771
                    stimulusParameters.testTargetEnds= ...
772
                        targetDelay+targetDuration;
773
                    stimulusParameters.testNonTargetBegins=...
774
                        length(cueStimulus)*dt ...
775
                        + AFCsilenceDuration +targetDelay ;
776
                    stimulusParameters.testNonTargetEnds=...
777
                        length(cueStimulus)*dt ...
778
                        + AFCsilenceDuration+targetDelay+targetDuration;
779
                    
780
                    set(handles.pushbutton1,'backgroundcolor','r'), drawnow
781
                    y=audioplayer(targetStimulus, globalStimParams.FS, 24);
782
                    playblocking(y)
783
                    set(handles.pushbutton1,'backgroundcolor',...
784
                        get(0,'defaultUicontrolBackgroundColor')), drawnow
785
                    y=audioplayer(IAFCinterveningSilence, ...
786
                        globalStimParams.FS, 24);
787
                    playblocking(y)
788
                    set(handles.pushbutton2,'backgroundcolor','r'), drawnow
789
                    y=audioplayer(cueStimulus, globalStimParams.FS, 24);
790
                    playblocking(y)
791
                    set(handles.pushbutton2,'backgroundcolor',...
792
                        get(0,'defaultUicontrolBackgroundColor')), drawnow
793
                    withinRuns.stimulusOrder='targetFirst';
794
                    audio= [targetStimulus; IAFCinterveningSilence; ...
795
                        cueStimulus];   % for plotting purposes later
796
                    
797
                else					% put test stimulus second
798
                    stimulusParameters.testTargetBegins=...
799
                        length(cueStimulus)*dt ...
800
                        + AFCsilenceDuration +targetDelay ;
801
                    stimulusParameters.testTargetEnds=...
802
                        length(cueStimulus)*dt ...
803
                        + AFCsilenceDuration+targetDelay+targetDuration;
804
                    stimulusParameters.testNonTargetBegins=targetDelay ;
805
                    stimulusParameters.testNonTargetEnds=...
806
                        targetDelay+targetDuration;
807
                    
808
                    set(handles.pushbutton1,'backgroundcolor','r'),drawnow
809
                    y=audioplayer(cueStimulus, globalStimParams.FS, 24);
810
                    playblocking(y)
811
                    set(handles.pushbutton1,'backgroundcolor',...
812
                        get(0,'defaultUicontrolBackgroundColor')), drawnow
813
                    y=audioplayer(IAFCinterveningSilence, ...
814
                        globalStimParams.FS, 24);
815
                    playblocking(y)
816
                    set(handles.pushbutton2,'backgroundcolor','r'), drawnow
817
                    y=audioplayer(targetStimulus, globalStimParams.FS, 24);
818
                    playblocking(y)
819
                    set(handles.pushbutton2,'backgroundcolor',...
820
                        get(0,'defaultUicontrolBackgroundColor')), drawnow
821
                    withinRuns.stimulusOrder='targetSecond';
822
                    audio= [cueStimulus; IAFCinterveningSilence; ...
823
                        targetStimulus];    % for plotting purposes later
824
                end
825
            otherwise                       % singleInterval
826
                if strcmp(experiment.ear,'MAPmodel') ...
827
                        || strcmp(experiment.ear,'MAPmodelMultiCh') ...
828
                        || strcmp(experiment.ear,'MAPmodelSingleCh') ...
829
                        ||strcmp(experiment.ear,'MAPmodelListen')
830
                    % don't play for MAPmodel
831
                    switch experiment.ear
832
                        % except on special request
833
                        case {'MAPmodelListen'}
834
                            y=audioplayer(audio, globalStimParams.FS, 24);
835
                            playblocking(y) % suspends operations until completed
836
                    end
837
                else
838
                    y=audioplayer(audio, globalStimParams.FS, 24);
839
                    playblocking(y)
840
                end	  %   if experiment.ear
841
        end	% switch experiment.threshEstMethod
842
end % switch experiment.ear
843

    
844

    
845
% switch experiment.ear
846
%     case	{'MAPmodel', 'MAPmodelListen',  'MAPmodelMultiCh','MAPmodelSingleCh'}
847
%         % save audio for later reference or for input to MAP model
848
%         wavwrite(audio/max(audio), globalStimParams.FS,32,'stimulus')
849
% end
850

    
851
% Panel 1
852
% graphical presentation of the stimulus
853
% NB shown *after* the stimulus has been presented
854
axes(expGUIhandles.axes1), cla
855
% plot is HW rectified and plotted as dB re 28e-6
856
% calibration is ignored
857
t=dt:dt:dt*length(audio);
858
plot(t,stimulusParameters.calibrationdB+20*log10((abs(audio)+1e-10)/28e-6))
859
% set(gca,'xtick',[])
860
ylim([-20 100])
861
ylabel('stimulus (dB SPL)')
862
xlim([0 t(end)])
863
grid on
864
header=[betweenRuns.variableName1  ': ' ...
865
    num2str(betweenRuns.var1Sequence(betweenRuns.runNumber))];
866
header=[header '      ' num2str(...
867
    betweenRuns.var2Sequence(betweenRuns.runNumber)) ':' ...
868
    betweenRuns.variableName2 ];
869
title(header)
870