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