Mercurial > hg > map
comparison Copy_of_multithreshold 1.46/nextStimulus.m @ 28:02aa9826efe0
mainly multiThreshold
| author | Ray Meddis <rmeddis@essex.ac.uk> |
|---|---|
| date | Fri, 01 Jul 2011 12:59:47 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 27:d4a7675b0413 | 28:02aa9826efe0 |
|---|---|
| 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 |
