rmeddis@28: function errormsg=nextStimulus(handles) rmeddis@28: % Handles everything concerned with the stimulus presentation rmeddis@28: % called from startNewRun in subjGUI rmeddis@28: rmeddis@28: global experiment stimulusParameters withinRuns betweenRuns rmeddis@28: experiment.status='presentingStimulus'; rmeddis@28: errormsg=''; rmeddis@28: rmeddis@28: % interrupt by 'stop' button rmeddis@28: if experiment.stop rmeddis@28: disp('******** experiment manually stopped *****************') rmeddis@28: experiment.status= 'waitingForStart'; rmeddis@28: addToMsg('manually stopped',1) rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % -----------------------------------------choose catch trials at random rmeddis@28: % catch trials are for subject threshold measurements only rmeddis@28: % this is the only place where withinRuns.catchTrial is set rmeddis@28: if experiment.allowCatchTrials rmeddis@28: if withinRuns.trialNumber==1; rmeddis@28: % first trial is never a catch trial rmeddis@28: withinRuns.catchTrial=0; rmeddis@28: withinRuns.catchTrialCount=0; % reset count on first trial rmeddis@28: elseif withinRuns.trialNumber==2 ... rmeddis@28: && withinRuns.catchTrialCount==0 rmeddis@28: % second trial is always a catch trial rmeddis@28: withinRuns.catchTrial=1; rmeddis@28: withinRuns.catchTrialCount=1; % this must be the first rmeddis@28: elseif withinRuns.thisIsRepeatTrial rmeddis@28: % for requested repeats do not change catch trial status rmeddis@28: withinRuns.thisIsRepeatTrial=0; % reset toggle rmeddis@28: else rmeddis@28: % choose whether or not to have a catch trial rmeddis@28: R=rand; rmeddis@28: if R upperLevel rmeddis@28: errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... rmeddis@28: ') is too high ***']; rmeddis@28: withinRuns.forceThreshold=upperLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: if max(maskerLevel, cueMaskerLevel)< lowerLevel rmeddis@28: errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... rmeddis@28: ') is too low ***']; rmeddis@28: withinRuns.forceThreshold=lowerLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: if max(maskerLevel, cueMaskerLevel)> clippingLevel rmeddis@28: errormsg=['Level(' num2str(max(maskerLevel,cueMaskerLevel)) ... rmeddis@28: ') is clipping ***']; rmeddis@28: withinRuns.forceThreshold=clippingLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: case 'targetLevel' rmeddis@28: upperLevel=stimulusParameters.WRVlimits(2); rmeddis@28: lowerLevel=stimulusParameters.WRVlimits(1); rmeddis@28: if ~withinRuns.catchTrial rmeddis@28: if max(targetLevel, cueTargetLevel)> upperLevel rmeddis@28: errormsg=['target level (' ... rmeddis@28: num2str(max(targetLevel, cueTargetLevel)) ... rmeddis@28: ') is too high ***']; rmeddis@28: withinRuns.forceThreshold=upperLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: if max(targetLevel, cueTargetLevel)< lowerLevel rmeddis@28: errormsg=['target level (' ... rmeddis@28: num2str(max(targetLevel, cueTargetLevel)) ... rmeddis@28: ') is too low ***']; rmeddis@28: withinRuns.forceThreshold=lowerLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: if max(targetLevel, cueTargetLevel)> clippingLevel rmeddis@28: errormsg=['target level (' ... rmeddis@28: num2str(max(targetLevel, cueTargetLevel)) ... rmeddis@28: ') is clipping ***']; rmeddis@28: withinRuns.forceThreshold=upperLevel; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: end rmeddis@28: case 'maskerDuration' rmeddis@28: % this is odd! but harmless rmeddis@28: if max(maskerDuration, cueMaskerDuration)> ... rmeddis@28: stimulusParameters.WRVlimits(2) rmeddis@28: errormsg=['maskerDuration (' ... rmeddis@28: num2str(max(maskerDuration, cueMaskerDuration))... rmeddis@28: ') is too long ***']; rmeddis@28: withinRuns.forceThreshold=stimulusParameters.WRVlimits(2); rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: if min(maskerDuration, cueMaskerDuration)... rmeddis@28: < stimulusParameters.WRVlimits(1) rmeddis@28: errormsg=['maskerDuration (' num2str(maskerLevel) ... rmeddis@28: ') too short ***']; rmeddis@28: withinRuns.forceThreshold=stimulusParameters.WRVlimits(1); rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % legacy programming rmeddis@28: case 'gapDuration' rmeddis@28: if gapDuration<0 rmeddis@28: errormsg=['gapDuration (' num2str(gapDuration) ... rmeddis@28: ') is less than zero ***']; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: case 'maskerFrequency' rmeddis@28: switch experiment.paradigm rmeddis@28: case 'bandwidth' rmeddis@28: frequency=maskerFrequency'; rmeddis@28: if stimulusParameters.WRVstep<0 rmeddis@28: lowerLevel=stimulusParameters.targetFrequency; rmeddis@28: upperLevel=stimulusParameters.targetFrequency*2; rmeddis@28: else rmeddis@28: lowerLevel=stimulusParameters.targetFrequency/3; rmeddis@28: upperLevel=stimulusParameters.targetFrequency; rmeddis@28: end rmeddis@28: rmeddis@28: if frequency(1)>upperLevel || frequency(1)stimulusParameters.WRVlimits(2) rmeddis@28: errormsg=['masker frequency (' ... rmeddis@28: num2str(frequencyDifference) ... rmeddis@28: ') is outside WRV limits ***']; rmeddis@28: withinRuns.forceThreshold=stimulusParameters.WRVlimits(2) ; rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: end rmeddis@28: rmeddis@28: % --------------------------------Ear ---------------------------------- rmeddis@28: globalStimParams.ears='specified'; rmeddis@28: % ear: 1=left, 2=right rmeddis@28: switch experiment.ear rmeddis@28: case 'left' rmeddis@28: maskerEar=1; rmeddis@28: targetEar=1; rmeddis@28: case 'right' rmeddis@28: maskerEar=2; rmeddis@28: targetEar=2; rmeddis@28: case 'dichoticLeft' rmeddis@28: maskerEar=2; rmeddis@28: targetEar=1; rmeddis@28: case 'dichoticRight' rmeddis@28: maskerEar=1; rmeddis@28: targetEar=2; rmeddis@28: case 'diotic' rmeddis@28: maskerEar=1; rmeddis@28: targetEar=1; rmeddis@28: globalStimParams.ears='diotic'; rmeddis@28: case {'MAPmodel', 'MAPmodelMultiCh', 'MAPmodelSingleCh', 'MAPmodelListen',... rmeddis@28: 'statsModelLogistic', 'statsModelRareEvent'} rmeddis@28: maskerEar=1; rmeddis@28: targetEar=1; rmeddis@28: end rmeddis@28: rmeddis@28: backgroundType=stimulusParameters.backgroundType; rmeddis@28: switch stimulusParameters.backgroundType rmeddis@28: case {'noiseDich', 'pinkNoiseDich'} rmeddis@28: % case 'Dich' rmeddis@28: % dich means put the background in the ear opposite to the target rmeddis@28: backgroundType=backgroundType(1:end-4); rmeddis@28: switch targetEar rmeddis@28: case 1 rmeddis@28: backgroundEar=2; rmeddis@28: case 2 rmeddis@28: backgroundEar=1; rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % case {'none','noise', 'pinkNoise', 'TEN','babble'} rmeddis@28: backgroundEar=targetEar; rmeddis@28: end rmeddis@28: rmeddis@28: % ------------------------------- Make Stimulus ------------------- rmeddis@28: % single interval up/down plays cue then target stimulus rmeddis@28: % 2IFC uses cue stimulus as interval with no target rmeddis@28: globalStimParams.FS=stimulusParameters.sampleRate; rmeddis@28: dt=1/stimulusParameters.sampleRate; rmeddis@28: globalStimParams.dt=dt; rmeddis@28: stimulusParameters.dt=dt; % for use later rmeddis@28: rmeddis@28: rmeddis@28: rmeddis@28: globalStimParams.audioOutCorrection=10^(calibrationCorrectiondB/20); rmeddis@28: % the output will be reduced by this amount in stimulusCreate rmeddis@28: % i.e. audio=audio/globalStimParams.audioOutCorrection rmeddis@28: % A 91 dB level will yield a peak amp of 1 for calibration=0 rmeddis@28: % A 91 dB level will yield a peak amp of 0.4467 for calibration=7 rmeddis@28: % A 98 dB level will yield a peak amp of 1 for calibration=7 rmeddis@28: rmeddis@28: precedingSilence=stimulusParameters.stimulusDelay; rmeddis@28: % all stimuli have 20 ms terminal silence. rmeddis@28: % this is clearance for modelling late-ringing targets rmeddis@28: terminalSilence=.03; rmeddis@28: rmeddis@28: % Now compute overall duration of the stimulus rmeddis@28: % note that all endsilence values are set to -1 rmeddis@28: % so that they will fill with terminal silence as required to make rmeddis@28: % components equal in length rmeddis@28: % We need to find the longest possible duration rmeddis@28: duration(1)=precedingSilence+maskerDuration+cueGapDuration... rmeddis@28: +targetDuration+terminalSilence; rmeddis@28: duration(2)=precedingSilence+maskerDuration+gapDuration... rmeddis@28: +targetDuration+ terminalSilence; rmeddis@28: % If the gap is negative we need to ignore it when estimating total length rmeddis@28: duration(3)=precedingSilence+maskerDuration+ terminalSilence; rmeddis@28: globalStimParams.overallDuration=max(duration); rmeddis@28: globalStimParams.nSignalPoints=... rmeddis@28: round(globalStimParams.overallDuration/globalStimParams.dt); rmeddis@28: rmeddis@28: % ----------------------------------------------cue stimulus rmeddis@28: % cue masker rmeddis@28: componentNo=1; rmeddis@28: precedingSilence=stimulusParameters.stimulusDelay; rmeddis@28: stimComponents(maskerEar,componentNo).type=maskerType; rmeddis@28: stimComponents(maskerEar,componentNo).toneDuration=cueMaskerDuration; rmeddis@28: stimComponents(maskerEar,componentNo).frequencies=cueMaskerFrequency; rmeddis@28: stimComponents(maskerEar,componentNo).amplitudesdB=cueMaskerLevel; rmeddis@28: stimComponents(maskerEar,componentNo).beginSilence=precedingSilence; rmeddis@28: stimComponents(maskerEar,componentNo).endSilence=-1; rmeddis@28: stimComponents(maskerEar,componentNo).AMfrequency=0; rmeddis@28: stimComponents(maskerEar,componentNo).AMdepth=0; rmeddis@28: if rampDuration0.5 % put test stimulus first rmeddis@28: stimulusParameters.testTargetBegins=targetDelay ; rmeddis@28: stimulusParameters.testTargetEnds= ... rmeddis@28: targetDelay+targetDuration; rmeddis@28: stimulusParameters.testNonTargetBegins=... rmeddis@28: length(cueStimulus)*dt ... rmeddis@28: + AFCsilenceDuration +targetDelay ; rmeddis@28: stimulusParameters.testNonTargetEnds=... rmeddis@28: length(cueStimulus)*dt ... rmeddis@28: + AFCsilenceDuration+targetDelay+targetDuration; rmeddis@28: rmeddis@28: set(handles.pushbutton1,'backgroundcolor','r'), drawnow rmeddis@28: y=audioplayer(targetStimulus, globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton1,'backgroundcolor',... rmeddis@28: get(0,'defaultUicontrolBackgroundColor')), drawnow rmeddis@28: y=audioplayer(IAFCinterveningSilence, ... rmeddis@28: globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton2,'backgroundcolor','r'), drawnow rmeddis@28: y=audioplayer(cueStimulus, globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton2,'backgroundcolor',... rmeddis@28: get(0,'defaultUicontrolBackgroundColor')), drawnow rmeddis@28: withinRuns.stimulusOrder='targetFirst'; rmeddis@28: audio= [targetStimulus; IAFCinterveningSilence; ... rmeddis@28: cueStimulus]; % for plotting purposes later rmeddis@28: rmeddis@28: else % put test stimulus second rmeddis@28: stimulusParameters.testTargetBegins=... rmeddis@28: length(cueStimulus)*dt ... rmeddis@28: + AFCsilenceDuration +targetDelay ; rmeddis@28: stimulusParameters.testTargetEnds=... rmeddis@28: length(cueStimulus)*dt ... rmeddis@28: + AFCsilenceDuration+targetDelay+targetDuration; rmeddis@28: stimulusParameters.testNonTargetBegins=targetDelay ; rmeddis@28: stimulusParameters.testNonTargetEnds=... rmeddis@28: targetDelay+targetDuration; rmeddis@28: rmeddis@28: set(handles.pushbutton1,'backgroundcolor','r'),drawnow rmeddis@28: y=audioplayer(cueStimulus, globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton1,'backgroundcolor',... rmeddis@28: get(0,'defaultUicontrolBackgroundColor')), drawnow rmeddis@28: y=audioplayer(IAFCinterveningSilence, ... rmeddis@28: globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton2,'backgroundcolor','r'), drawnow rmeddis@28: y=audioplayer(targetStimulus, globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: set(handles.pushbutton2,'backgroundcolor',... rmeddis@28: get(0,'defaultUicontrolBackgroundColor')), drawnow rmeddis@28: withinRuns.stimulusOrder='targetSecond'; rmeddis@28: audio= [cueStimulus; IAFCinterveningSilence; ... rmeddis@28: targetStimulus]; % for plotting purposes later rmeddis@28: end rmeddis@28: otherwise % singleInterval rmeddis@28: if strcmp(experiment.ear,'MAPmodel') ... rmeddis@28: || strcmp(experiment.ear,'MAPmodelMultiCh') ... rmeddis@28: || strcmp(experiment.ear,'MAPmodelSingleCh') ... rmeddis@28: ||strcmp(experiment.ear,'MAPmodelListen') rmeddis@28: % don't play for MAPmodel rmeddis@28: switch experiment.ear rmeddis@28: % except on special request rmeddis@28: case {'MAPmodelListen'} rmeddis@28: y=audioplayer(audio, globalStimParams.FS, 24); rmeddis@28: playblocking(y) % suspends operations until completed rmeddis@28: end rmeddis@28: else rmeddis@28: y=audioplayer(audio, globalStimParams.FS, 24); rmeddis@28: playblocking(y) rmeddis@28: end % if experiment.ear rmeddis@28: end % switch experiment.threshEstMethod rmeddis@28: end % switch experiment.ear rmeddis@28: rmeddis@28: rmeddis@28: % switch experiment.ear rmeddis@28: % case {'MAPmodel', 'MAPmodelListen', 'MAPmodelMultiCh','MAPmodelSingleCh'} rmeddis@28: % % save audio for later reference or for input to MAP model rmeddis@28: % wavwrite(audio/max(audio), globalStimParams.FS,32,'stimulus') rmeddis@28: % end rmeddis@28: rmeddis@28: % Panel 1 rmeddis@28: % graphical presentation of the stimulus rmeddis@28: % NB shown *after* the stimulus has been presented rmeddis@28: axes(expGUIhandles.axes1), cla rmeddis@28: % plot is HW rectified and plotted as dB re 28e-6 rmeddis@28: % calibration is ignored rmeddis@28: t=dt:dt:dt*length(audio); rmeddis@28: plot(t,stimulusParameters.calibrationdB+20*log10((abs(audio)+1e-10)/28e-6)) rmeddis@28: % set(gca,'xtick',[]) rmeddis@28: ylim([-20 100]) rmeddis@28: ylabel('stimulus (dB SPL)') rmeddis@28: xlim([0 t(end)]) rmeddis@28: grid on rmeddis@28: header=[betweenRuns.variableName1 ': ' ... rmeddis@28: num2str(betweenRuns.var1Sequence(betweenRuns.runNumber))]; rmeddis@28: header=[header ' ' num2str(... rmeddis@28: betweenRuns.var2Sequence(betweenRuns.runNumber)) ':' ... rmeddis@28: betweenRuns.variableName2 ]; rmeddis@28: title(header) rmeddis@28: