Mercurial > hg > map
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Copy_of_multithreshold 1.46/nextStimulus.m Fri Jul 01 12:59:47 2011 +0100 @@ -0,0 +1,870 @@ +function errormsg=nextStimulus(handles) +% Handles everything concerned with the stimulus presentation +% called from startNewRun in subjGUI + +global experiment stimulusParameters withinRuns betweenRuns +experiment.status='presentingStimulus'; +errormsg=''; + +% interrupt by 'stop' button +if experiment.stop + disp('******** experiment manually stopped *****************') + experiment.status= 'waitingForStart'; + addToMsg('manually stopped',1) + return +end + +% -----------------------------------------choose catch trials at random +% catch trials are for subject threshold measurements only +% this is the only place where withinRuns.catchTrial is set +if experiment.allowCatchTrials + if withinRuns.trialNumber==1; + % first trial is never a catch trial + withinRuns.catchTrial=0; + withinRuns.catchTrialCount=0; % reset count on first trial + elseif withinRuns.trialNumber==2 ... + && withinRuns.catchTrialCount==0 + % second trial is always a catch trial + withinRuns.catchTrial=1; + withinRuns.catchTrialCount=1; % this must be the first + elseif withinRuns.thisIsRepeatTrial + % for requested repeats do not change catch trial status + withinRuns.thisIsRepeatTrial=0; % reset toggle + else + % choose whether or not to have a catch trial + R=rand; + if R<stimulusParameters.catchTrialRate + % catch trial + withinRuns.catchTrial=1; + addToMsg('Catch Trial',1) + withinRuns.catchTrialCount=withinRuns.catchTrialCount+1; + else + % not a catch trial + withinRuns.catchTrial=0; + end + end +else + % no catch trials for statistical evaluations or 2AIFC or (poss) MAP + withinRuns.catchTrial=0; +end + +%------------ during stimulus presentation show appropriate button images +switch experiment.ear + case {'statsModelLogistic', 'statsModelRareEvent',... + 'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh'} + % no buttons shown + otherwise + switch experiment.threshEstMethod + case {'2I2AFC++', '2I2AFC+++'} + %Except for 2I2AFC + % For 2I2AFC the buttons on the screen ab initio + set(handles.frame1,'visible','off') + set(handles.pushbuttonGO,'visible','off') + set(handles.pushbuttoNotSure,'visible','off') + set(handles.pushbuttonWrongButton,'visible','off') + set(handles.pushbutton3,'visible','off') + set(handles.pushbutton0,'visible','off') + set(handles.pushbutton1,'visible','on') + set(handles.pushbutton2,'visible','on') + drawnow + otherwise + % i.e. single interval/ maxLikelihood + set(handles.frame1,'backgroundColor','w') + set(handles.frame1,'visible','off') + set(handles.pushbuttoNotSure,'visible','off') + set(handles.pushbuttonWrongButton,'visible','off') + set(handles.pushbutton3,'visible','off') + set(handles.pushbutton2,'visible','off') + set(handles.pushbutton1,'visible','off') + set(handles.pushbutton0,'visible','off') + pause(.1) + end +end + +set(handles.textMSG,'BackgroundColor','w', 'ForegroundColor', 'b') + +% Now the serious business of crafting and presenting the stimulus +errormsg= stimulusMakeAndPlay (handles); + +if ~isempty(errormsg) + % e.g. clipping. subjGUI will service the error + return +end + +% after playing the stimulus, reset the subjectGUI +switch experiment.ear + case {'statsModelLogistic', 'statsModelRareEvent',... + 'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh'} + % no changes required if model used + % NB these changes do occur is 'MAPmodelListen' is selected + otherwise + switch experiment.threshEstMethod + case {'2I2AFC++', '2I2AFC+++'} + % buttons already visible + otherwise + % single interval now make buttons visible + set(handles.frame1,'visible','on') + set(handles.pushbuttoNotSure,'visible','on') + % set(handles.pushbuttonWrongButton,'visible','on') + set(handles.pushbutton0,'visible','on') + set(handles.pushbutton1,'visible','on') + set(handles.pushbutton2,'visible','on') + set(handles.pushbutton3,'visible','on') + end +end + +switch experiment.paradigm + case 'SRT' + set(handles.frame1,'backgroundColor','w') + set(handles.frame1,'visible','off') + set(handles.pushbuttoNotSure,'visible','off') + set(handles.pushbuttonWrongButton,'visible','off') + set(handles.pushbutton3,'visible','off') + set(handles.pushbutton2,'visible','off') + set(handles.pushbutton1,'visible','off') + set(handles.pushbutton0,'visible','off') + set(handles.editdigitInput,'visible','on') + set(handles.editdigitInput,'string',[]) + pause(.2) + uicontrol(handles.editdigitInput) + + otherwise + set(handles.editdigitInput,'visible','off') +end + + +experiment.status='waitingForResponse'; +% home again + +% ------------------------------------------------------------------------------------------stimulusMakeAndPlay +function errormsg=stimulusMakeAndPlay (handles) +global experiment stimulusParameters betweenRuns withinRuns expGUIhandles audio +% creates the stimulus and plays it; there are two stimuli; cue and test +% called from nextStimulus + +errormsg=''; + +% first post the subjects instructions on subjGUI +set(handles.textMSG,'string', stimulusParameters.subjectText) + +% select the new levels of the between runs variables +num=betweenRuns.runNumber; +cmd=(['stimulusParameters.' betweenRuns.variableName1 '= ' ... + num2str(betweenRuns.var1Sequence(num)) ';']); +% e.g. stimulusParameters.targetFrequency= 1000; +eval(cmd); + +cmd=(['stimulusParameters.' betweenRuns.variableName2 '= ' ... + num2str(betweenRuns.var2Sequence(num)) ';']); +% e.g. stimulusParameters.targetDuration= 0.1; +eval(cmd); + +switch experiment.paradigm + % target level may vary between runs + case {'trainingIFMC', 'TMC','TMC_16ms', 'TMC - ELP', 'IFMC','IFMC_8ms','IFMC_16ms'} + idx=floor(num/length(betweenRuns.variableList1)-0.01)+1; + cmd=(['stimulusParameters.targetLevel = ' ... + num2str(stimulusParameters.targetLevels(idx)) ';']); + eval(cmd); + if withinRuns.trialNumber==1 + disp(['targetLevel=' num2str(stimulusParameters.targetLevel)]) + end +end + + +% for more readable code use shorter variable names; +% NB these may change below; these are only the starting values + +targetType= stimulusParameters.targetType; +targetDuration= stimulusParameters.targetDuration; +targetLevel= stimulusParameters.targetLevel; +targetFrequency= stimulusParameters.targetFrequency; + +maskerType= stimulusParameters.maskerType; +maskerDuration= stimulusParameters.maskerDuration; +maskerLevel= stimulusParameters.maskerLevel; +maskerRelativeFrequency= stimulusParameters.maskerRelativeFrequency; +maskerFrequency= maskerRelativeFrequency*targetFrequency; + +gapDuration= stimulusParameters.gapDuration; + +rampDuration= stimulusParameters.rampDuration; +AFCsilenceDuration=stimulusParameters.AFCsilenceDuration; % 2I2AFC gap +backgroundLevel= stimulusParameters.backgroundLevel; + +% Set level of within runs variable +% this is the first change to one of the values shown above +cmd=[stimulusParameters.WRVname '= withinRuns.variableValue;' ]; +% e.g.: maskerLevel= withinRuns.variableValue; +eval(cmd); + +% cue and test stimuli are identical except for a single difference +% depending on the paradigm +cueTestDifference= stimulusParameters.cueTestDifference; +% cue characteristics before adding cue differences +cueTargetLevel=targetLevel; +cueMaskerFrequency=maskerFrequency; +cueMaskerDuration=maskerDuration; +cueMaskerLevel=maskerLevel; +cueTargetFrequency=targetFrequency; +cueGapDuration=gapDuration; + +% ----------------------------paradigm sensitive cue and masker settings +% switch off unwanted components and base cue on target values +% for catch trials switch off the target +switch experiment.paradigm + % OHIO is a temporary special arrangement + case{'OHIOrand','OHIOspect','OHIOtemp','OHIOspectemp','OHIOabs'} + % these values must be set in MAPparamsOHIO + targetFrequency=experiment.OHIOfrequencies; + numOHIOtones=stimulusParameters.numOHIOtones; + maskerType='OHIO'; + targetType='OHIO'; + + % globalStimParams.beginSilences says when each tone begins + % targetFrequency specifies the frequency of each tone + % targetLevel is the level of each tone + switch experiment.paradigm + case 'OHIOabs' + % only one tone + targetFrequency=targetFrequency(numOHIOtones); + globalStimParams.beginSilences=0.01; + targetDuration=0.01; + case 'OHIOrand' + % select random frequencies from the list + x=rand(12,1); [x idx]=sort(x); + targetFrequency=targetFrequency(idx(1:numOHIOtones)); + thresholds=experiment.OHIOthresholds; + targetLevel=thresholds(idx(1:numOHIOtones))+targetLevel; + globalStimParams.beginSilences=0.01:0.02:... + 0.01+0.02*(numOHIOtones-1); + targetDuration=numOHIOtones*0.02; + case 'OHIOspect' + % only one tone with multiple frequencies + targetFrequency=fliplr(targetFrequency); + targetFrequency=targetFrequency(1:numOHIOtones); + thresholds=experiment.OHIOthresholds; + thresholds=fliplr(thresholds); + targetLevel=thresholds(1:numOHIOtones)+targetLevel; + globalStimParams.beginSilences=... + repmat(0.01, 1, numOHIOtones); + targetDuration=0.02; + case 'OHIOspectemp' + % only one tone with multiple frequencies + targetFrequency=fliplr(targetFrequency); + targetFrequency=targetFrequency(1:numOHIOtones); + thresholds=experiment.OHIOthresholds; + thresholds=fliplr(thresholds); + targetLevel=thresholds(1:numOHIOtones)+targetLevel; + globalStimParams.beginSilences=0.01:0.02:... + 0.01+0.02*(numOHIOtones-1); + targetDuration=numOHIOtones*0.02; + case 'OHIOtemp' + % use only one tone repeatedly + tonesToUse=10; + targetFrequency=targetFrequency(tonesToUse); + targetFrequency=repmat(targetFrequency,1,numOHIOtones); + thresholds=experiment.OHIOthresholds; + thresholds=thresholds(tonesToUse); + targetLevel=repmat(thresholds,1,numOHIOtones)+targetLevel; + globalStimParams.beginSilences=0.01:0.02:... + 0.01+0.02*(numOHIOtones-1); + targetDuration=numOHIOtones*0.02; + end + % still in OHIO + % Dummy values to make things work although no masker or cue used + % target values have changed and this affects the cue values + cueMaskerLevel=targetLevel; + cueTargetLevel=targetLevel; + cueTargetFrequency=targetFrequency; + cueMaskerFrequency=targetFrequency; + maskerFrequency=targetFrequency; + maskerLevel=targetLevel; + disp(['OHIO frequencies= ' num2str(targetFrequency)]) +end + +% --- set cueTarget level according to assessment method +% cue-test difference applies only with singleInterval +switch experiment.threshEstMethod + case {'2I2AFC++', '2I2AFC+++'} + % For 2IFC the cue stimulus (masker + probe) is the 'no' window + % and the target stimulus (masker+probe) is the 'yes' window + % the order of presentation is decided at the last minute. + cueTargetLevel=-100; % the target is never in the 'no' window + cueMaskerLevel=maskerLevel; % masker level is the same in both + otherwise + % 'single interval' or max likelihood + switch experiment.paradigm + % cue target is more audible + case {'training','absThreshold', 'absThreshold_8', ... + 'TENtest', 'threshold_duration','discomfort',... + 'overShoot','overShootB','overShootMB1', ... + 'overShootMB2', 'OHIO','OHIOabs','OHIOspect'} + cueTargetLevel=targetLevel+cueTestDifference; + + case {'forwardMasking','forwardMaskingD','trainingIFMC', ... + 'TMC','TMC_16ms', 'TMC - ELP', 'IFMC','IFMC_8ms', 'FMreProbe'} + % cue masker is weaker to make target more audible + cueMaskerLevel=maskerLevel-cueTestDifference; + end +end + +% ----------------------------- catch trial +if withinRuns.catchTrial + targetLevel=-100; % no target +end + +% ----------------------------- calibration of sound output +calibrationCorrectiondB=stimulusParameters.calibrationdB; +if calibrationCorrectiondB<-50 + if maskerFrequency==targetFrequency + load 'calibrationFile' % calibrationFrequency calibrationAttenutation + idx=find(calibrationFrequency==targetFrequency); + if isempty(idx) + error('Calibration bty file; frequency not found') + else + calibrationCorrectiondB=calibrationAttenutation(idx) + end + else + error('calibration by file requested but masker frequency is not the same as target') + end +end + + +% -------------------------------------- Checks on excessive signal level + +% clipping is relevant only for soundcard use (not modelling) +switch experiment.ear + case {'left', 'right', 'diotic',... + 'dichotic', 'dioticLeft', 'dichoticRight'} + experiment.headphonesUsed=1; + otherwise + experiment.headphonesUsed=0; +end + +% NB calibration *reduces* the level of the soundCard output +switch experiment.ear + case {'left', 'right', 'diotic',... + 'dichotic', 'dioticLeft', 'dichoticRight'} + clippingLevel=91+calibrationCorrectiondB; + soundCardMinimum=clippingLevel-20*log10(2^24); + otherwise + clippingLevel=inf; + soundCardMinimum=-inf; +end + +% Check for extreme WRV values and abort if necessary +% WRVname specifies the value that changes from trial to trial +withinRuns.forceThreshold=[]; +switch stimulusParameters.WRVname + % check for extreme values. Note that one of the tones might be switched off + case 'maskerLevel' + upperLevel=stimulusParameters.WRVlimits(2); + lowerLevel=stimulusParameters.WRVlimits(1); + if max(maskerLevel, cueMaskerLevel)> upperLevel + errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... + ') is too high ***']; + withinRuns.forceThreshold=upperLevel; + withinRuns.forceThreshold=NaN; + return + end + if max(maskerLevel, cueMaskerLevel)< lowerLevel + errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... + ') is too low ***']; + withinRuns.forceThreshold=lowerLevel; + withinRuns.forceThreshold=NaN; + return + end + + if max(maskerLevel, cueMaskerLevel)> clippingLevel + errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... + ') is clipping ***']; + withinRuns.forceThreshold=clippingLevel; + withinRuns.forceThreshold=NaN; + return + end + + case 'targetLevel' + upperLevel=stimulusParameters.WRVlimits(2); + lowerLevel=stimulusParameters.WRVlimits(1); + if ~withinRuns.catchTrial + if max(targetLevel, cueTargetLevel)> upperLevel + errormsg=['target level (' ... + num2str(max(targetLevel, cueTargetLevel)) ... + ') is too high ***']; + withinRuns.forceThreshold=upperLevel; + withinRuns.forceThreshold=NaN; + return + end + if max(targetLevel, cueTargetLevel)< lowerLevel + errormsg=['target level (' ... + num2str(max(targetLevel, cueTargetLevel)) ... + ') is too low ***']; + withinRuns.forceThreshold=lowerLevel; + withinRuns.forceThreshold=NaN; + return + end + if max(targetLevel, cueTargetLevel)> clippingLevel + errormsg=['target level (' ... + num2str(max(targetLevel, cueTargetLevel)) ... + ') is clipping ***']; + withinRuns.forceThreshold=upperLevel; + withinRuns.forceThreshold=NaN; + return + end + end + case 'maskerDuration' + % this is odd! but harmless + if max(maskerDuration, cueMaskerDuration)> ... + stimulusParameters.WRVlimits(2) + errormsg=['maskerDuration (' ... + num2str(max(maskerDuration, cueMaskerDuration))... + ') is too long ***']; + withinRuns.forceThreshold=stimulusParameters.WRVlimits(2); + withinRuns.forceThreshold=NaN; + return + end + + if min(maskerDuration, cueMaskerDuration)... + < stimulusParameters.WRVlimits(1) + errormsg=['maskerDuration (' num2str(maskerLevel) ... + ') too short ***']; + withinRuns.forceThreshold=stimulusParameters.WRVlimits(1); + withinRuns.forceThreshold=NaN; + return + end + + % legacy programming + case 'gapDuration' + if gapDuration<0 + errormsg=['gapDuration (' num2str(gapDuration) ... + ') is less than zero ***']; + return + end + + case 'maskerFrequency' + switch experiment.paradigm + case 'bandwidth' + frequency=maskerFrequency'; + if stimulusParameters.WRVstep<0 + lowerLevel=stimulusParameters.targetFrequency; + upperLevel=stimulusParameters.targetFrequency*2; + else + lowerLevel=stimulusParameters.targetFrequency/3; + upperLevel=stimulusParameters.targetFrequency; + end + + if frequency(1)>upperLevel || frequency(1)<lowerLevel + errormsg=['frequency out of range: ' ... + num2str(frequency)]; + withinRuns.forceThreshold=frequency; + return + end + otherwise + end + + case 'maskerRelativeFrequency' + if maskerRelativeFrequency<stimulusParameters.WRVlimits(1) + errormsg=['masker frequency (' ... + num2str(frequencyDifference) ... + ') is outside WRV limits ***']; + withinRuns.forceThreshold=stimulusParameters.WRVlimits(1) ; + return + end + if maskerRelativeFrequency>stimulusParameters.WRVlimits(2) + errormsg=['masker frequency (' ... + num2str(frequencyDifference) ... + ') is outside WRV limits ***']; + withinRuns.forceThreshold=stimulusParameters.WRVlimits(2) ; + return + end + +end + +% --------------------------------Ear ---------------------------------- +globalStimParams.ears='specified'; +% ear: 1=left, 2=right +switch experiment.ear + case 'left' + maskerEar=1; + targetEar=1; + case 'right' + maskerEar=2; + targetEar=2; + case 'dichoticLeft' + maskerEar=2; + targetEar=1; + case 'dichoticRight' + maskerEar=1; + targetEar=2; + case 'diotic' + maskerEar=1; + targetEar=1; + globalStimParams.ears='diotic'; + case {'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh', 'MAPmodelListen',... + 'statsModelLogistic', 'statsModelRareEvent'} + maskerEar=1; + targetEar=1; +end + +backgroundType=stimulusParameters.backgroundType; +switch stimulusParameters.backgroundType + case {'noiseDich', 'pinkNoiseDich'} + % case 'Dich' + % dich means put the background in the ear opposite to the target + backgroundType=backgroundType(1:end-4); + switch targetEar + case 1 + backgroundEar=2; + case 2 + backgroundEar=1; + end + otherwise + % case {'none','noise', 'pinkNoise', 'TEN','babble'} + backgroundEar=targetEar; +end + +% ------------------------------- Make Stimulus ------------------- +% single interval up/down plays cue then target stimulus +% 2IFC uses cue stimulus as interval with no target +globalStimParams.FS=stimulusParameters.sampleRate; +dt=1/stimulusParameters.sampleRate; +globalStimParams.dt=dt; +stimulusParameters.dt=dt; % for use later + + + +globalStimParams.audioOutCorrection=10^(calibrationCorrectiondB/20); +% the output will be reduced by this amount in stimulusCreate +% i.e. audio=audio/globalStimParams.audioOutCorrection +% A 91 dB level will yield a peak amp of 1 for calibration=0 +% A 91 dB level will yield a peak amp of 0.4467 for calibration=7 +% A 98 dB level will yield a peak amp of 1 for calibration=7 + +precedingSilence=stimulusParameters.stimulusDelay; +% all stimuli have 20 ms terminal silence. +% this is clearance for modelling late-ringing targets +terminalSilence=.03; + +% Now compute overall duration of the stimulus +% note that all endsilence values are set to -1 +% so that they will fill with terminal silence as required to make +% components equal in length +% We need to find the longest possible duration +duration(1)=precedingSilence+maskerDuration+cueGapDuration... + +targetDuration+terminalSilence; +duration(2)=precedingSilence+maskerDuration+gapDuration... + +targetDuration+ terminalSilence; +% If the gap is negative we need to ignore it when estimating total length +duration(3)=precedingSilence+maskerDuration+ terminalSilence; +globalStimParams.overallDuration=max(duration); +globalStimParams.nSignalPoints=... + round(globalStimParams.overallDuration/globalStimParams.dt); + +% ----------------------------------------------cue stimulus +% cue masker +componentNo=1; +precedingSilence=stimulusParameters.stimulusDelay; +stimComponents(maskerEar,componentNo).type=maskerType; +stimComponents(maskerEar,componentNo).toneDuration=cueMaskerDuration; +stimComponents(maskerEar,componentNo).frequencies=cueMaskerFrequency; +stimComponents(maskerEar,componentNo).amplitudesdB=cueMaskerLevel; +stimComponents(maskerEar,componentNo).beginSilence=precedingSilence; +stimComponents(maskerEar,componentNo).endSilence=-1; +stimComponents(maskerEar,componentNo).AMfrequency=0; +stimComponents(maskerEar,componentNo).AMdepth=0; +if rampDuration<maskerDuration + % ramps must be shorter than the signal + stimComponents(maskerEar,componentNo).rampOnDur=rampDuration; + stimComponents(maskerEar,componentNo).rampOffDur=rampDuration; +else + % or squeeze the ramp in + stimComponents(maskerEar,componentNo).rampOnDur=maskerDuration/2; + stimComponents(maskerEar,componentNo).rampOffDur=maskerDuration/2; +end +stimComponents(maskerEar,componentNo).phases=... + stimulusParameters.maskerPhase; +stimComponents(maskerEar,componentNo).niterations=0; % for IRN only +% stimComponents(targetEar,componentNo) + +% cue target +componentNo=2; +precedingSilence=precedingSilence + maskerDuration+cueGapDuration; +stimComponents(targetEar,componentNo).type=targetType; +stimComponents(targetEar,componentNo).toneDuration=targetDuration; +stimComponents(targetEar,componentNo).frequencies=cueTargetFrequency; +stimComponents(targetEar,componentNo).amplitudesdB=cueTargetLevel; +stimComponents(targetEar,componentNo).beginSilence=precedingSilence; +stimComponents(targetEar,componentNo).endSilence=-1; +stimComponents(targetEar,componentNo).AMfrequency=0; +stimComponents(targetEar,componentNo).AMdepth=0; +if rampDuration<targetDuration + % ramps must be shorter than the signal + stimComponents(targetEar,componentNo).rampOnDur=rampDuration; + stimComponents(targetEar,componentNo).rampOffDur=rampDuration; +else + stimComponents(targetEar,componentNo).rampOnDur=0; + stimComponents(targetEar,componentNo).rampOffDur=0; +end +stimComponents(targetEar,componentNo).phases=... + stimulusParameters.targetPhase; +% stimComponents(targetEar,componentNo) + +% background same ear as target +componentNo=3; +stimComponents(backgroundEar,componentNo).type=backgroundType; +switch backgroundType + case 'TEN' + fileName=['..' filesep '..' filesep ... + 'multithresholdResources' filesep ... + 'backgrounds and maskers'... + filesep 'ten.wav']; + [tenNoise, FS]=wavread(fileName); + tenNoise=resample(tenNoise, globalStimParams.FS, FS); + stimComponents(backgroundEar,componentNo).type='file'; + stimComponents(backgroundEar,componentNo).stimulus=tenNoise'; +end +stimComponents(backgroundEar,componentNo).toneDuration=... + globalStimParams.overallDuration; +stimComponents(backgroundEar,componentNo).amplitudesdB=backgroundLevel; +stimComponents(backgroundEar,componentNo).beginSilence=0; +stimComponents(backgroundEar,componentNo).endSilence=-1; +stimComponents(backgroundEar,componentNo).AMfrequency=0; +stimComponents(backgroundEar,componentNo).AMdepth=0; +stimComponents(backgroundEar,componentNo).rampOnDur=rampDuration; +stimComponents(backgroundEar,componentNo).rampOffDur=rampDuration; + +[cueStimulus, errormsg]=... + stimulusCreate(globalStimParams, stimComponents, 0); +if ~isempty(errormsg) % e.g. limits exceeded + errormsg + return +end + +% ------------------------------------------ test stimulus +% masker +componentNo=1; +precedingSilence=stimulusParameters.stimulusDelay; +stimComponents(maskerEar,componentNo).type=maskerType; +stimComponents(maskerEar,componentNo).toneDuration=maskerDuration; +stimComponents(maskerEar,componentNo).frequencies=maskerFrequency; +stimComponents(maskerEar,componentNo).amplitudesdB=maskerLevel; +stimComponents(maskerEar,componentNo).beginSilence=precedingSilence; +stimComponents(maskerEar,componentNo).endSilence=-1; +stimComponents(maskerEar,componentNo).AMfrequency=0; +stimComponents(maskerEar,componentNo).AMdepth=0; +if rampDuration<maskerDuration + % ramps must be shorter than the signal + stimComponents(maskerEar,componentNo).rampOnDur=rampDuration; + stimComponents(maskerEar,componentNo).rampOffDur=rampDuration; +else + stimComponents(maskerEar,componentNo).rampOnDur=maskerDuration/2; + stimComponents(maskerEar,componentNo).rampOffDur=maskerDuration/2; +end +stimComponents(maskerEar,componentNo).phases=... + stimulusParameters.maskerPhase; +stimComponents(maskerEar,componentNo).niterations=0; % for IRN only + +% target +componentNo=2; +targetDelay=precedingSilence+ maskerDuration+ gapDuration; +stimComponents(targetEar,componentNo).type=targetType; +stimComponents(targetEar,componentNo).toneDuration=targetDuration; +stimComponents(targetEar,componentNo).frequencies=targetFrequency; +stimComponents(targetEar,componentNo).amplitudesdB=targetLevel; +stimComponents(targetEar,componentNo).beginSilence=targetDelay; +stimComponents(targetEar,componentNo).endSilence=-1; +stimComponents(targetEar,componentNo).AMfrequency=0; +stimComponents(targetEar,componentNo).AMdepth=0; +if rampDuration<targetDuration + % ramps must be shorter than the signal + stimComponents(targetEar,componentNo).rampOnDur=rampDuration; + stimComponents(targetEar,componentNo).rampOffDur=rampDuration; +else + stimComponents(targetEar,componentNo).rampOnDur=0; + stimComponents(targetEar,componentNo).rampOffDur=0; +end +stimComponents(targetEar,componentNo).phases=stimulusParameters.targetPhase; +% stimComponents(targetEar,componentNo) + +% background same ear as target +componentNo=3; +stimComponents(backgroundEar,componentNo).type=backgroundType; +switch backgroundType + case 'TEN' + fileName=['..' filesep '..' filesep ... + 'multithresholdResources' filesep ... + 'backgrounds and maskers'... + filesep 'ten.wav']; + [tenNoise, FS]=wavread(fileName); + + tenNoise=resample(tenNoise, globalStimParams.FS, FS); + stimComponents(backgroundEar,componentNo).type='file'; + stimComponents(backgroundEar,componentNo).stimulus=tenNoise'; +end +stimComponents(backgroundEar,componentNo).toneDuration=... + globalStimParams.overallDuration; +stimComponents(backgroundEar,componentNo).amplitudesdB=backgroundLevel; +stimComponents(backgroundEar,componentNo).beginSilence=0; +stimComponents(backgroundEar,componentNo).endSilence=-1; +stimComponents(backgroundEar,componentNo).rampOnDur=rampDuration; +stimComponents(backgroundEar,componentNo).rampOffDur=rampDuration; +stimComponents(backgroundEar,componentNo).AMfrequency=0; +stimComponents(backgroundEar,componentNo).AMdepth=0; + +% timings used when evaluating MAP peripheral model +% this is the Slope during which spikes are counted +switch experiment.paradigm + case 'gapDetection' + % gap is the 'target' in this case + stimulusParameters.testTargetBegins=... + stimulusParameters.stimulusDelay... + +stimulusParameters.maskerDuration; + stimulusParameters.testTargetEnds=... + stimulusParameters.testTargetBegins+withinRuns.variableValue; + % case 'SRT' + % set(handles.editdigitInput,'visible','off') + otherwise + stimulusParameters.testTargetBegins=targetDelay; + stimulusParameters.testTargetEnds=targetDelay+targetDuration; +end + +% ------------------------------------------------------------- play! +% Create and play stimulus (as required by different paradigms) +switch experiment.ear + case {'statsModelLogistic', 'statsModelRareEvent'} + audio=[0;0]; % no need to compute stimulus + + otherwise % create the stimulus + [targetStimulus, errormsg]= ... + stimulusCreate(globalStimParams, stimComponents, 0); + + if ~isempty(errormsg) % e.g. limits exceeded + errormsg + return + end + + switch experiment.ear + case {'MAPmodel' , 'MAPmodelMultiCh', 'MAPmodelSingleCh', 'MAPmodelListen'} + % model requires no calibration correction; + % signal is already in Pascals + globalStimParams.audioOutCorrection=1; + % use only the targetStimulus for the MAP model + audio=targetStimulus; + + otherwise % left, right diotic dichotic + if stimulusParameters.includeCue + audio= [cueStimulus; targetStimulus]; + else % no cue + audio=targetStimulus; + end + end + + % playtime + % order of the cue and test stimuli varies for 2AFC + switch experiment.threshEstMethod + case {'2I2AFC++', '2I2AFC+++'} + % intervening silence (currently none; masking delay serves this purpose) + IAFCinterveningSilence=zeros(round(AFCsilenceDuration/dt),2); + if rand>0.5 % put test stimulus first + stimulusParameters.testTargetBegins=targetDelay ; + stimulusParameters.testTargetEnds= ... + targetDelay+targetDuration; + stimulusParameters.testNonTargetBegins=... + length(cueStimulus)*dt ... + + AFCsilenceDuration +targetDelay ; + stimulusParameters.testNonTargetEnds=... + length(cueStimulus)*dt ... + + AFCsilenceDuration+targetDelay+targetDuration; + + set(handles.pushbutton1,'backgroundcolor','r'), drawnow + y=audioplayer(targetStimulus, globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton1,'backgroundcolor',... + get(0,'defaultUicontrolBackgroundColor')), drawnow + y=audioplayer(IAFCinterveningSilence, ... + globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton2,'backgroundcolor','r'), drawnow + y=audioplayer(cueStimulus, globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton2,'backgroundcolor',... + get(0,'defaultUicontrolBackgroundColor')), drawnow + withinRuns.stimulusOrder='targetFirst'; + audio= [targetStimulus; IAFCinterveningSilence; ... + cueStimulus]; % for plotting purposes later + + else % put test stimulus second + stimulusParameters.testTargetBegins=... + length(cueStimulus)*dt ... + + AFCsilenceDuration +targetDelay ; + stimulusParameters.testTargetEnds=... + length(cueStimulus)*dt ... + + AFCsilenceDuration+targetDelay+targetDuration; + stimulusParameters.testNonTargetBegins=targetDelay ; + stimulusParameters.testNonTargetEnds=... + targetDelay+targetDuration; + + set(handles.pushbutton1,'backgroundcolor','r'),drawnow + y=audioplayer(cueStimulus, globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton1,'backgroundcolor',... + get(0,'defaultUicontrolBackgroundColor')), drawnow + y=audioplayer(IAFCinterveningSilence, ... + globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton2,'backgroundcolor','r'), drawnow + y=audioplayer(targetStimulus, globalStimParams.FS, 24); + playblocking(y) + set(handles.pushbutton2,'backgroundcolor',... + get(0,'defaultUicontrolBackgroundColor')), drawnow + withinRuns.stimulusOrder='targetSecond'; + audio= [cueStimulus; IAFCinterveningSilence; ... + targetStimulus]; % for plotting purposes later + end + otherwise % singleInterval + if strcmp(experiment.ear,'MAPmodel') ... + || strcmp(experiment.ear,'MAPmodelMultiCh') ... + || strcmp(experiment.ear,'MAPmodelSingleCh') ... + ||strcmp(experiment.ear,'MAPmodelListen') + % don't play for MAPmodel + switch experiment.ear + % except on special request + case {'MAPmodelListen'} + y=audioplayer(audio, globalStimParams.FS, 24); + playblocking(y) % suspends operations until completed + end + else + y=audioplayer(audio, globalStimParams.FS, 24); + playblocking(y) + end % if experiment.ear + end % switch experiment.threshEstMethod +end % switch experiment.ear + + +% switch experiment.ear +% case {'MAPmodel', 'MAPmodelListen', 'MAPmodelMultiCh','MAPmodelSingleCh'} +% % save audio for later reference or for input to MAP model +% wavwrite(audio/max(audio), globalStimParams.FS,32,'stimulus') +% end + +% Panel 1 +% graphical presentation of the stimulus +% NB shown *after* the stimulus has been presented +axes(expGUIhandles.axes1), cla +% plot is HW rectified and plotted as dB re 28e-6 +% calibration is ignored +t=dt:dt:dt*length(audio); +plot(t,stimulusParameters.calibrationdB+20*log10((abs(audio)+1e-10)/28e-6)) +% set(gca,'xtick',[]) +ylim([-20 100]) +ylabel('stimulus (dB SPL)') +xlim([0 t(end)]) +grid on +header=[betweenRuns.variableName1 ': ' ... + num2str(betweenRuns.var1Sequence(betweenRuns.runNumber))]; +header=[header ' ' num2str(... + betweenRuns.var2Sequence(betweenRuns.runNumber)) ':' ... + betweenRuns.variableName2 ]; +title(header) +