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 / nextStimulus.m @ 31:c54a34161e4a

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  
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
%     errormsg='manually stopped';
14
%     addToMsg(errormsg,1)
15
%     return
16
% end
17

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

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

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

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

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

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

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

    
136

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

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

    
146
errormsg='';
147

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

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

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

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

    
175

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

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

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

    
190
gapDuration=       stimulusParameters.gapDuration;
191

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

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

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

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

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

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

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

    
334

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

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

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

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

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

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

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

    
536

    
537

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

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

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

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

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

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

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

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

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

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

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

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

    
845

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

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