rmeddis@0: function varargout = subjGUI_MT(varargin) rmeddis@0: rmeddis@0: % Begin initialization code - DO NOT EDIT rmeddis@0: gui_Singleton = 1; rmeddis@0: gui_State = struct('gui_Name', mfilename, ... rmeddis@0: 'gui_Singleton', gui_Singleton, ... rmeddis@0: 'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ... rmeddis@0: 'gui_OutputFcn', @subjGUI_MT_OutputFcn, ... rmeddis@0: 'gui_LayoutFcn', [] , ... rmeddis@0: 'gui_Callback', []); rmeddis@0: if nargin && isstr(varargin{1}) rmeddis@0: gui_State.gui_Callback = str2func(varargin{1}); rmeddis@0: end rmeddis@0: rmeddis@0: if nargout rmeddis@0: [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); rmeddis@0: else rmeddis@0: gui_mainfcn(gui_State, varargin{:}); rmeddis@0: end rmeddis@0: % End initialization code - DO NOT EDIT rmeddis@0: rmeddis@0: % --- Executes just before subjGUI_MT is made visible. rmeddis@0: function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin) rmeddis@0: rmeddis@0: % Choose default command line output for subjGUI_MT rmeddis@0: handles.output = hObject; rmeddis@0: initializeGUI(handles) rmeddis@0: guidata(hObject, handles); rmeddis@0: rmeddis@0: function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles) rmeddis@0: % Get default command line output from handles structure rmeddis@0: varargout{1} = handles.output; rmeddis@0: rmeddis@0: % -----------------------------------------------------initializeGUI rmeddis@0: function initializeGUI(handles) rmeddis@0: global experiment rmeddis@0: global subjectGUIHandles expGUIhandles rmeddis@28: addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ... rmeddis@0: ['..' filesep 'parameterStore'], ['..' filesep 'wavFileStore'],... rmeddis@0: ['..' filesep 'testPrograms']) rmeddis@0: rmeddis@0: dbstop if error rmeddis@0: rmeddis@0: % subjectGUI size and location % [left bottom width height] rmeddis@0: scrnsize=get(0,'screensize'); rmeddis@0: set(0, 'units','pixels') rmeddis@0: switch experiment.ear rmeddis@0: % use default size unless... rmeddis@0: case {'MAPmodel', 'MAPmodelMultich', 'MAPmodelSingleCh', ... rmeddis@0: 'statsModelLogistic','statsModelRareEvent'} rmeddis@0: % subjectGUI not needed for modelling so minimize subject GUI rmeddis@0: set(gcf, 'units','pixels') rmeddis@0: y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; rmeddis@0: set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@0: rmeddis@0: case 'MAPmodelListen', rmeddis@0: % subjectGUI is needed for display purposes. Make it large rmeddis@0: set(gcf, 'units','pixels') rmeddis@0: y=[.665*scrnsize(3) 0.02*scrnsize(4) ... rmeddis@0: 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside rmeddis@0: set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@0: end rmeddis@0: rmeddis@0: switch experiment.ear rmeddis@0: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@0: % Look to see if the button box exists and, if so, initialise it rmeddis@0: buttonBoxIntitialize % harmless if no button box attached rmeddis@0: end rmeddis@0: rmeddis@0: % clear display of previous mean values. This is a new measurement series rmeddis@28: axes(expGUIhandles.axes4), cla rmeddis@0: reset (expGUIhandles.axes4) rmeddis@0: rmeddis@0: % handles needed in non-callback routines below rmeddis@0: subjectGUIHandles=handles; rmeddis@0: rmeddis@0: % start immediately rmeddis@0: startNewExperiment(handles, expGUIhandles) rmeddis@0: % This is the end of the experiment. Exit here and return to ExpGUI. rmeddis@0: rmeddis@0: % ----------------------------------------------------- startNewExperiment rmeddis@0: function startNewExperiment(handles, expGUIhandles) rmeddis@0: % An experiment consists of a series of 'runs'. rmeddis@0: % Resets all relevant variables at the beginning of a new experiment. rmeddis@0: global experiment stimulusParameters betweenRuns rmeddis@0: rmeddis@0: % 'start new experiment' button is the only valid action now rmeddis@0: experiment.status='waitingForStart'; rmeddis@0: rmeddis@0: switch experiment.threshEstMethod rmeddis@0: % add appropriate labels to subject GUI buttons rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: set(handles.pushbutton3,'string','') rmeddis@0: set(handles.pushbutton2,'string','2') rmeddis@0: set(handles.pushbutton1,'string','1') rmeddis@0: set(handles.pushbutton0,'string','0') rmeddis@0: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@0: if stimulusParameters.includeCue rmeddis@0: set(handles.pushbutton3,'string','') rmeddis@0: set(handles.pushbutton2,'string','2') rmeddis@0: set(handles.pushbutton1,'string','1') rmeddis@0: set(handles.pushbutton0,'string','0') rmeddis@0: else rmeddis@0: set(handles.pushbutton3,'string','') rmeddis@0: set(handles.pushbutton2,'string','YES') rmeddis@0: set(handles.pushbutton1,'string','NO') rmeddis@0: set(handles.pushbutton0,'string','') rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: switch experiment.paradigm rmeddis@0: case 'discomfort' rmeddis@0: set(handles.pushbutton3,'string','') rmeddis@0: set(handles.pushbutton2,'string','uncomfortable') rmeddis@0: set(handles.pushbutton1,'string','loud') rmeddis@0: set(handles.pushbutton0,'string','comfortable') rmeddis@28: experiment.allowCatchTrials=0; rmeddis@0: end rmeddis@0: rmeddis@0: % experiment.subjGUIfontSize is set on expGUI rmeddis@0: set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.pushbutton1,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.pushbutton0,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.pushbuttoNotSure,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.pushbuttonGO,'FontSize',experiment.subjGUIfontSize) rmeddis@0: set(handles.textMSG,'FontSize',experiment.subjGUIfontSize) rmeddis@0: rmeddis@0: set(handles.pushbutton19,'visible','off') % unused button rmeddis@0: rmeddis@0: % start command window summary of progress rmeddis@0: fprintf(' \n ----------- NEW MEASUREMENTS\n') rmeddis@0: disp(['paradigm: ' experiment.paradigm]) rmeddis@0: cla(expGUIhandles.axes1) rmeddis@0: cla(expGUIhandles.axes2) rmeddis@0: cla(expGUIhandles.axes4) rmeddis@0: cla(expGUIhandles.axes5) rmeddis@0: rmeddis@0: experiment.stop=0; % status of 'stop' button rmeddis@0: experiment.pleaseRepeat=0; % status of 'repeat' button rmeddis@0: experiment.buttonBoxStatus='not busy'; rmeddis@0: rmeddis@0: % date and time and replace ':' with '_' rmeddis@28: date=datestr(now);idx=findstr(':',date);date(idx)='_'; rmeddis@0: experiment.date=date; rmeddis@0: timeNow=clock; betweenRuns.timeNow= timeNow; rmeddis@0: experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))]; rmeddis@28: experiment.minElapsed=0; rmeddis@0: rmeddis@28: % unpack catch trial rates. The rate declines from the start rate rmeddis@0: % to the base rate using a time constant. rmeddis@0: stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1); rmeddis@0: stimulusParameters.catchTrialBaseRate=... rmeddis@0: stimulusParameters.catchTrialRates(2); rmeddis@0: stimulusParameters.catchTrialTimeConstant=... rmeddis@0: stimulusParameters.catchTrialRates(3); rmeddis@0: if stimulusParameters.catchTrialBaseRate==0 rmeddis@0: stimulusParameters.catchTrialRate=0; rmeddis@0: end rmeddis@0: rmeddis@0: % for human measurements only, identify the start catch trial rate rmeddis@0: switch experiment.ear rmeddis@0: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@0: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@0: stimulusParameters.catchTrialRate) rmeddis@0: end rmeddis@0: rmeddis@0: % Reset betweenRuns parameters. this occurs only at experiment start rmeddis@0: % withinRuns values are reset in 'startNewRun' rmeddis@0: % this approach creates a more readable structure summary printout. rmeddis@0: betweenRuns.thresholds=[]; rmeddis@0: betweenRuns.thresholds_mean=[]; rmeddis@0: betweenRuns.thresholds_median=[]; rmeddis@0: betweenRuns.forceThresholds=[]; rmeddis@0: betweenRuns.observationCount=[]; rmeddis@0: betweenRuns.catchTrials=[]; rmeddis@0: betweenRuns.timesOfFirstReversals=[]; rmeddis@0: betweenRuns.bestThresholdTracks=[]; rmeddis@0: betweenRuns.levelTracks=[]; rmeddis@0: betweenRuns.responseTracks=[]; rmeddis@0: betweenRuns.slopeKTracks=[]; rmeddis@0: betweenRuns.gainTracks=[]; rmeddis@0: betweenRuns.VminTracks=[]; rmeddis@0: betweenRuns.bestGain=[]; rmeddis@0: betweenRuns.bestVMin=[]; rmeddis@0: betweenRuns.bestPaMin=[]; rmeddis@0: betweenRuns.bestLogisticM=[]; rmeddis@0: betweenRuns.bestLogisticK=[]; rmeddis@0: betweenRuns.resets=0; rmeddis@0: betweenRuns.runNumber=0; rmeddis@0: rmeddis@0: % Up to two parameters can be changed between runs rmeddis@0: % Find the variable parameters and randomize them rmeddis@0: % e.g. 'variableList1 = stimulusParameters.targetFrequency;' rmeddis@0: eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']); rmeddis@0: eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']); rmeddis@0: nVar1=length(variableList1); rmeddis@0: nVar2=length(variableList2); rmeddis@0: rmeddis@28: % Create two sequence vectors to represent the sequence of var1 and var2 rmeddis@0: % values. 'var1' changes most rapidly. rmeddis@0: switch betweenRuns.randomizeSequence rmeddis@30: % {'randomize within blocks', 'fixed sequence',... rmeddis@30: % 'randomize across blocks'} rmeddis@0: case 'fixed sequence' rmeddis@0: var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); rmeddis@0: var2Sequence=reshape(repmat(betweenRuns.variableList2, ... rmeddis@0: nVar1,1),1,nVar1*nVar2); rmeddis@0: case 'randomize within blocks' rmeddis@0: % the blocks are not randomized rmeddis@0: var1Sequence=betweenRuns.variableList1; rmeddis@0: ranNums=rand(1, length(var1Sequence)); [x idx]=sort(ranNums); rmeddis@0: var1Sequence=var1Sequence(idx); rmeddis@0: betweenRuns.variableList1=variableList1(idx); rmeddis@0: var1Sequence=repmat(var1Sequence, 1,nVar2); rmeddis@0: var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1)... rmeddis@0: ,1,nVar1*nVar2); rmeddis@0: case 'randomize across blocks' rmeddis@0: var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); rmeddis@0: var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1),... rmeddis@0: 1,nVar1*nVar2); rmeddis@0: ranNums=rand(1, nVar1*nVar2); rmeddis@0: [x idx]=sort(ranNums); rmeddis@0: var1Sequence=var1Sequence(idx); rmeddis@0: var2Sequence=var2Sequence(idx); rmeddis@28: % there should be one start value for every combination rmeddis@28: % of var1/ var2. In principle this allows these values to be rmeddis@0: % programmed. Not currently in use. rmeddis@0: stimulusParameters.WRVstartValues=... rmeddis@0: stimulusParameters.WRVstartValues(idx); rmeddis@0: end rmeddis@0: betweenRuns.var1Sequence=var1Sequence; rmeddis@0: betweenRuns.var2Sequence=var2Sequence; rmeddis@0: rmeddis@0: % caught out vector needs to be linked to the length of the whole sequence rmeddis@0: betweenRuns.caughtOut=zeros(1,length(var1Sequence)); rmeddis@0: rmeddis@0: disp('planned sequence:') rmeddis@0: if min(var1Sequence)>1 rmeddis@0: % use decidaml places only if necessary rmeddis@28: disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f') ]) rmeddis@0: else rmeddis@28: disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f') ]) rmeddis@0: end rmeddis@0: if min(var1Sequence)>1 rmeddis@28: disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ]) rmeddis@0: else rmeddis@28: disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ]) rmeddis@0: end rmeddis@0: rmeddis@0: fprintf('\nvariable1 \t variable2\t \n') rmeddis@0: fprintf('%s \t %s\t Threshold \n',betweenRuns.variableName1,... rmeddis@0: betweenRuns.variableName2) rmeddis@0: rmeddis@0: % Light up 'GO' on subjGUI and advise. rmeddis@0: set(handles.editdigitInput,'visible','off') rmeddis@0: switch experiment.ear rmeddis@0: case {'statsModelLogistic', 'statsModelRareEvent',... rmeddis@0: 'MAPmodel', 'MAPmodelMultiCh','MAPmodelSingleCh'} rmeddis@0: % no changes required if model used rmeddis@0: otherwise rmeddis@0: set(handles.pushbuttonGO,'backgroundcolor','y') rmeddis@0: set(handles.pushbuttonGO,'visible','on') rmeddis@0: set(handles.frame1,'visible','off') rmeddis@0: set(handles.textMSG,'backgroundcolor', 'w') rmeddis@0: msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}]; rmeddis@0: set(handles.textMSG,'string', msg) rmeddis@28: rmeddis@0: set(handles.pushbuttoNotSure,'visible','off') rmeddis@0: set(handles.pushbuttonWrongButton,'visible','off') rmeddis@0: set(handles.pushbutton3,'visible','off') rmeddis@0: set(handles.pushbutton2,'visible','off') rmeddis@0: set(handles.pushbutton1,'visible','off') rmeddis@0: set(handles.pushbutton0,'visible','off') rmeddis@0: pause(.1) % to allow display to be drawn rmeddis@0: end rmeddis@0: rmeddis@0: % Selecting the 'GO' button is the only valid operation action now rmeddis@0: experiment.status='waitingForGO'; % i.e. waiting for new run rmeddis@0: rmeddis@0: % control is now either manual, model (MAP) or randomization rmeddis@0: switch experiment.ear rmeddis@0: case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'} % MAP model is now the subject rmeddis@0: stimulusParameters.calibrationdB=0; % Pascals required! rmeddis@0: MAPmodelRunsGUI(handles) rmeddis@0: % model is now the subject rmeddis@28: case {'statsModelLogistic', 'statsModelRareEvent'} rmeddis@0: % no catch trials for the statistical model rmeddis@28: stimulusParameters.catchTrialBaseRate=0; rmeddis@0: stimulusParameters.catchTrialRate=0; rmeddis@0: statsModelRunsGUI(handles) rmeddis@0: otherwise rmeddis@0: %manual operation; wait for user to click on 'GO' rmeddis@0: end rmeddis@0: rmeddis@0: % Experiment complete (after MAP or randomization) rmeddis@0: % return to 'initializeGUI' and then back to expGUI rmeddis@0: % Manual control finds its own way home. Program control assumed when rmeddis@0: % the user hits the GO button rmeddis@0: rmeddis@0: % ----------------------------------------------------------------- startNewRun rmeddis@0: function startNewRun(handles) rmeddis@0: % There are many ways to arrive here. rmeddis@0: % Under manual control this is achieved by hitting the GO button rmeddis@0: % either via the button box or a mouse click rmeddis@0: % MAP and randomization methods call this too rmeddis@0: rmeddis@0: global experiment stimulusParameters betweenRuns withinRuns expGUIhandles rmeddis@28: global LevittControl rareEvent errormsg rmeddis@0: rmeddis@0: figure(handles.figure1) % guarantee subject GUI visibility rmeddis@0: rmeddis@0: % ignore call if program is not ready rmeddis@0: if ~strcmp(experiment.status,'waitingForGO'), return, end rmeddis@0: rmeddis@0: set(handles.pushbuttonGO,'visible','off') rmeddis@0: rmeddis@0: % append message to expGUI message box to alert experimenter that the user rmeddis@0: % is active rmeddis@0: addToMsg('Starting new trial',0) rmeddis@0: rmeddis@0: cla(expGUIhandles.axes1), title(''); % stimulus rmeddis@0: cla(expGUIhandles.axes2), title(''); % WRV track rmeddis@0: drawnow rmeddis@0: rmeddis@0: betweenRuns.runNumber=betweenRuns.runNumber + 1; rmeddis@0: rmeddis@0: withinRuns.trialNumber=1; rmeddis@0: withinRuns.variableValue=... rmeddis@0: stimulusParameters.WRVstartValues(betweenRuns.runNumber); rmeddis@0: % add random jitter to start level rmeddis@0: if ~experiment.singleShot rmeddis@0: % SS or single shot allows the user to precisely set the WRV rmeddis@0: withinRuns.variableValue=withinRuns.variableValue +... rmeddis@0: (rand-0.5)*stimulusParameters.jitterStartdB; rmeddis@0: end rmeddis@0: rmeddis@0: withinRuns.peaks=[]; rmeddis@0: withinRuns.troughs=[]; rmeddis@0: withinRuns.levelList=[]; rmeddis@0: withinRuns.meanEstTrack=[]; rmeddis@0: withinRuns.bestSlopeK=[]; rmeddis@0: withinRuns.bestGain=[]; rmeddis@0: withinRuns.bestVMin=[]; rmeddis@0: withinRuns.forceThreshold=NaN; rmeddis@0: withinRuns.responseList=[]; rmeddis@0: withinRuns.caughtOut=0; rmeddis@0: withinRuns.wrongButton=0; rmeddis@0: withinRuns.catchTrialCount=0; rmeddis@0: withinRuns.thresholdEstimateTrack=[]; rmeddis@0: rmeddis@0: withinRuns.beginningOfPhase2=0; rmeddis@0: withinRuns.nowInPhase2=0; rmeddis@0: withinRuns.thisIsRepeatTrial=0; rmeddis@0: rmeddis@0: rareEvent.Euclid=NaN; rmeddis@0: rareEvent.bestGain=NaN; rmeddis@0: rareEvent.bestVMin=NaN; rmeddis@0: rareEvent.thresholddB=0; rmeddis@0: rareEvent.bestPaMindB=NaN; rmeddis@0: rareEvent.predictionLevels=[]; rmeddis@0: rareEvent.predictionsRE=[]; rmeddis@0: rmeddis@0: LevittControl.sequence=[]; rmeddis@0: rmeddis@0: % on-screen count of number of runs still to complete rmeddis@0: trialsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; rmeddis@0: set(handles.toGoCounter,'string', trialsToGo); rmeddis@0: rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: % For 2I2AFC the buttons need to be on the screen ab initio rmeddis@0: Levitt2 % inititalize Levitt2 procedure rmeddis@0: end rmeddis@0: rmeddis@0: switch experiment.ear rmeddis@0: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@0: % allow subject time to recover from 'go' press rmeddis@0: pause(experiment.clickToStimulusPause) rmeddis@0: end rmeddis@0: rmeddis@0: errormsg=nextStimulus(handles); % get the show on the road rmeddis@0: rmeddis@0: % terminate if there is any kind of problem rmeddis@0: if ~isempty(errormsg) rmeddis@0: % e.g. limits exceeded, clipping rmeddis@0: disp(errormsg) rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: % return route is variable (see intro to this function) rmeddis@0: rmeddis@0: % -----------------------------------------------------buttonBox_callback rmeddis@0: function buttonBox_callback(obj, info) rmeddis@28: % deals with a button press on the button box. rmeddis@28: rmeddis@0: global experiment rmeddis@0: global serobj rmeddis@0: global subjectGUIHandles rmeddis@0: rmeddis@0: % do not accept callback if one is already in process rmeddis@0: if strcmp(experiment.buttonBoxStatus,'busy') rmeddis@0: disp(' ignored button press') rmeddis@28: return % to quiescent state rmeddis@0: end rmeddis@0: experiment.buttonBoxStatus='busy'; rmeddis@0: rmeddis@0: % identify the code of the button pressed rmeddis@0: buttonPressedNo = fscanf(serobj,'%c',1); rmeddis@0: rmeddis@0: % This is the map from the button to the Cedrus codes rmeddis@0: switch experiment.buttonBoxType rmeddis@0: case 'horizontal' rmeddis@28: pbGo='7'; pb0='1'; rmeddis@28: pb1='2'; pb2='3'; rmeddis@0: pbRepeat='4'; pbWrong='6'; pbBlank='5'; rmeddis@0: case 'square' rmeddis@28: pbGo='7'; pb0='1'; rmeddis@28: pb1='3'; pb2='4'; rmeddis@0: pbRepeat='8'; pbWrong='6'; pbBlank='5'; rmeddis@0: end rmeddis@0: rmeddis@0: % decide what to do rmeddis@0: switch experiment.status rmeddis@0: case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ... rmeddis@0: 'endOfExperiment'} rmeddis@0: disp(' ignored button press') rmeddis@28: rmeddis@0: case 'waitingForGO' rmeddis@0: % i.e. waiting for new run rmeddis@0: if strcmp(buttonPressedNo,pbGo) % only GO button accepted rmeddis@0: startNewRun(subjectGUIHandles) rmeddis@0: else rmeddis@0: disp(' ignored button press') rmeddis@0: end rmeddis@28: rmeddis@0: case 'waitingForResponse' rmeddis@0: % response to stimuli rmeddis@0: switch buttonPressedNo rmeddis@0: case pb0 % button 0 (top left) rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: disp(' ignored button press') rmeddis@0: otherwise rmeddis@0: set(subjectGUIHandles.pushbutton0,... rmeddis@0: 'backgroundcolor','r') rmeddis@0: pause(.1) rmeddis@0: set(subjectGUIHandles.pushbutton0,... rmeddis@0: 'backgroundcolor',get(0,... rmeddis@0: 'defaultUicontrolBackgroundColor')) rmeddis@0: userSelects0or1(subjectGUIHandles) rmeddis@0: end rmeddis@28: rmeddis@0: case pb1 % button 1 (bottom left) rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: userSelects0or1(subjectGUIHandles) rmeddis@0: otherwise rmeddis@0: set(subjectGUIHandles.pushbutton1,... rmeddis@0: 'backgroundcolor','r') rmeddis@0: pause(.1) rmeddis@0: set(subjectGUIHandles.pushbutton1,... rmeddis@0: 'backgroundcolor',get(0,... rmeddis@0: 'defaultUicontrolBackgroundColor')) rmeddis@0: userSelects0or1(subjectGUIHandles) rmeddis@0: end rmeddis@28: rmeddis@0: case pb2 % button 2 (bottom right) rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: userSelects2 (subjectGUIHandles) rmeddis@0: otherwise rmeddis@0: set(subjectGUIHandles.pushbutton2,... rmeddis@0: 'backgroundcolor','r') rmeddis@0: pause(.1) rmeddis@0: set(subjectGUIHandles.pushbutton2,... rmeddis@0: 'backgroundcolor',get(0,... rmeddis@0: 'defaultUicontrolBackgroundColor')) rmeddis@0: userSelects2 (subjectGUIHandles) rmeddis@0: end rmeddis@28: rmeddis@0: case pbRepeat % extreme right button rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: disp(' ignored button press') rmeddis@0: otherwise rmeddis@28: rmeddis@0: set(subjectGUIHandles.pushbuttoNotSure,... rmeddis@0: 'backgroundcolor','r') rmeddis@0: pause(.1) rmeddis@0: set(subjectGUIHandles.pushbuttoNotSure,... rmeddis@0: 'backgroundcolor',get(0,... rmeddis@0: 'defaultUicontrolBackgroundColor')) rmeddis@0: userSelectsPleaseRepeat (subjectGUIHandles) rmeddis@0: end rmeddis@28: rmeddis@0: case {pbWrong, pbBlank} rmeddis@0: disp(' ignored button press') rmeddis@28: rmeddis@0: otherwise % unrecognised button rmeddis@0: disp('ignored button press') rmeddis@0: end % end (button press number) rmeddis@0: otherwise rmeddis@0: disp('ignored button press') rmeddis@0: end % experiment status rmeddis@0: rmeddis@0: % button box remains 'busy' until after the stimulus has been presented rmeddis@0: experiment.buttonBoxStatus='not busy'; rmeddis@0: rmeddis@0: % -------------------------------------------------- pushbuttonGO_Callback rmeddis@0: function pushbuttonGO_Callback(hObject, eventdata, handles) rmeddis@0: % This is a mouse click path rmeddis@0: % GO function is also called directly from button box rmeddis@0: % and from MAP model and stats model rmeddis@0: rmeddis@0: set(handles.pushbuttonGO,'visible','off') rmeddis@0: startNewRun(handles) rmeddis@0: rmeddis@0: % ---------------------------------------------------pushbutton0_Callback rmeddis@0: function pushbutton0_Callback(hObject, eventdata, handles) rmeddis@0: global experiment rmeddis@0: % This is a mouse click path rmeddis@0: rmeddis@0: % ignore 0 button if 2I2AFC used rmeddis@28: if findstr(experiment.threshEstMethod,'2I2AFC') rmeddis@28: return % to quiescent state rmeddis@28: end rmeddis@0: rmeddis@0: % userDoesNotHearTarget(handles) % only possible interpretation rmeddis@0: userDecides(handles, false) rmeddis@0: rmeddis@0: % -------------------------------------------------- pushbutton1_Callback rmeddis@0: function pushbutton1_Callback(hObject, eventdata, handles) rmeddis@0: userSelects0or1(handles) % also called from buttonBox rmeddis@0: rmeddis@0: % ---------------------------------------------------pushbutton2_Callback rmeddis@0: function pushbutton2_Callback(hObject, eventdata, handles) rmeddis@0: userSelects2(handles) % also called from buttonBox rmeddis@0: rmeddis@0: % --------------------------------------------- pushbuttoNotSure_Callback rmeddis@0: function pushbuttoNotSure_Callback(hObject, eventdata, handles) rmeddis@0: userSelectsPleaseRepeat(handles) % also called from buttonBox rmeddis@0: rmeddis@0: % -------------------------------------------------- pushbutton3_Callback rmeddis@0: function pushbutton3_Callback(hObject, eventdata, handles) rmeddis@0: rmeddis@0: % ------------------------------------------------- pushbutton19_Callback rmeddis@0: function pushbutton19_Callback(hObject, eventdata, handles) rmeddis@0: % should be invisible (ignore) rmeddis@0: rmeddis@0: % --------------------------------------- pushbuttonWrongButton_Callback rmeddis@0: function pushbuttonWrongButton_Callback(hObject, eventdata, handles) rmeddis@0: userSelectsWrongButton(handles) rmeddis@0: rmeddis@0: % --------------------------------------- editdigitInput_Callback rmeddis@0: function editdigitInput_Callback(hObject, eventdata, handles) rmeddis@0: userSelects0or1(handles) % after digit string input rmeddis@0: rmeddis@0: rmeddis@0: rmeddis@0: % ----------------------------------------------------- userSelects0or1 rmeddis@0: function userSelects0or1(handles) rmeddis@0: global experiment withinRuns rmeddis@0: rmeddis@28: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: switch withinRuns.stimulusOrder rmeddis@0: case 'targetFirst'; rmeddis@0: % userHearsTarget(handles) rmeddis@0: userDecides(handles, true) rmeddis@0: otherwise rmeddis@0: % userDoesNotHearTarget(handles) rmeddis@0: userDecides(handles, false) rmeddis@0: end rmeddis@0: otherwise rmeddis@0: % single interval rmeddis@0: % 0 or 1 are treated as equivalent (i.e. target is not heard) rmeddis@0: userDecides(handles, false) rmeddis@0: end rmeddis@0: % return to pushButton1 callback rmeddis@0: rmeddis@0: % ----------------------------------------------------- userSelects2 rmeddis@0: function userSelects2(handles) rmeddis@0: global experiment withinRuns rmeddis@28: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: switch withinRuns.stimulusOrder rmeddis@0: case 'targetSecond'; rmeddis@0: % userDoesNotHearTarget(handles) rmeddis@0: userDecides(handles, true) rmeddis@0: otherwise rmeddis@0: % userHearsTarget(handles) rmeddis@0: userDecides(handles, false) rmeddis@0: end rmeddis@0: otherwise rmeddis@0: % single interval (2 targets heard) rmeddis@0: userDecides(handles, true) rmeddis@0: end rmeddis@0: % return to pushButton2 callback rmeddis@0: rmeddis@0: % ----------------------------------------------------- ---- userDecides rmeddis@0: function userDecides(handles, saidYes) rmeddis@0: global experiment stimulusParameters betweenRuns withinRuns rmeddis@28: global rareEvent logistic psy levelsBinVector errormsg rmeddis@0: rmeddis@0: if experiment.singleShot rmeddis@28: return % not clear why this should be here rmeddis@0: end rmeddis@0: rmeddis@0: % ignore click if not 'waitingForResponse' rmeddis@0: if ~strcmp(experiment.status,'waitingForResponse') rmeddis@0: disp('ignored click') rmeddis@28: return % to userSelects rmeddis@0: end rmeddis@0: rmeddis@0: % speech reception threshold rmeddis@0: if strcmp(stimulusParameters.targetType,'digitStrings') rmeddis@28: % read triple digits from userGUI rmeddis@0: digitsInput=get(handles.editdigitInput,'string'); rmeddis@0: % must be three digits rmeddis@0: if ~(length(digitsInput)==3) rmeddis@0: addToMsg(['error message: Wrong no of digits'], 0, 1) rmeddis@0: set(handles.textMSG,'string', 'Wrong no of digits', ... rmeddis@0: 'BackgroundColor','r', 'ForegroundColor', 'w') rmeddis@0: set(handles.editdigitInput,'string','') rmeddis@0: return rmeddis@0: end rmeddis@0: % obtain correct answer from file name rmeddis@0: x=stimulusParameters.digitString; rmeddis@0: idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero rmeddis@0: disp([x ' ' digitsInput]) rmeddis@0: if x==digitsInput rmeddis@28: saidYes=1; % i.e. correct response rmeddis@0: else rmeddis@28: saidYes=0; % i.e wrong response rmeddis@0: end rmeddis@28: set(handles.editdigitInput,'string','') rmeddis@28: set(handles.editdigitInput,'visible','off') rmeddis@28: pause(0.1) rmeddis@0: end rmeddis@0: rmeddis@0: % no button presses accepted while processing rmeddis@0: experiment.status='processingResponse'; rmeddis@0: rmeddis@0: % catch trials. Restart trial if caught rmeddis@0: if withinRuns.catchTrial rmeddis@0: if saidYes rmeddis@0: disp('catch trial - caught out') rmeddis@0: withinRuns.caughtOut=withinRuns.caughtOut+1; rmeddis@28: rmeddis@28: % special: estimate caught out rate by allowing the trial rmeddis@0: % to continue after catch rmeddis@0: if stimulusParameters.catchTrialBaseRate==0.5 rmeddis@28: % To use this facility, set the catchTrialRate and the rmeddis@0: % catchTrialBaseRate both to 0.5 rmeddis@0: % update false positive rate rmeddis@0: betweenRuns.caughtOut(betweenRuns.runNumber)=... rmeddis@28: withinRuns.caughtOut; rmeddis@0: plotProgressThisTrial(handles) rmeddis@0: nextStimulus(handles); rmeddis@0: return rmeddis@0: end rmeddis@28: rmeddis@28: % Punishment: caught out restarts the trial rmeddis@0: set(handles.frame1,'backgroundColor','r') rmeddis@0: set(handles.pushbuttonGO, ... rmeddis@0: 'visible','on', 'backgroundcolor','y') % and go again rmeddis@0: msg=[{'Start again: catch trial error'}, {' '},... rmeddis@0: {'Please,click on the GO button'}]; rmeddis@0: set(handles.textMSG,'string',msg) rmeddis@0: [y,fs]=wavread('ding.wav'); rmeddis@35: if ispc rmeddis@35: wavplay(y/100,fs) rmeddis@35: else rmeddis@35: sound(y/100,fs) rmeddis@35: end rmeddis@35: rmeddis@28: rmeddis@28: % raise catch trial rate temporarily. rmeddis@0: % this is normally reduced on each new trial (see GO) rmeddis@0: stimulusParameters.catchTrialRate=... rmeddis@0: stimulusParameters.catchTrialRate+0.1; rmeddis@28: if stimulusParameters.catchTrialRate>0.5 rmeddis@28: stimulusParameters.catchTrialRate=0.5; rmeddis@0: end rmeddis@0: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@0: stimulusParameters.catchTrialRate) rmeddis@28: rmeddis@0: betweenRuns.caughtOut(betweenRuns.runNumber)=... rmeddis@0: 1+betweenRuns.caughtOut(betweenRuns.runNumber); rmeddis@0: betweenRuns.runNumber=betweenRuns.runNumber-1; rmeddis@0: experiment.status='waitingForGO'; rmeddis@0: return % unwind and wait for button press rmeddis@0: else % (said No) rmeddis@28: % user claims not to have heard target. rmeddis@28: % This is good as it was not present. rmeddis@28: % So, repeat the stimulus (possibly with target) rmeddis@0: % and behave as if the last trial did not occur rmeddis@0: errormsg=nextStimulus(handles); rmeddis@28: rmeddis@0: % terminate if there is any kind of problem rmeddis@0: if ~isempty(errormsg) rmeddis@0: % e.g. limits exceeded, clipping rmeddis@0: disp(['Error nextStimulus: ' errormsg]) rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: return % no further action - next trial rmeddis@0: end rmeddis@28: end % of catch trial rmeddis@0: rmeddis@28: % Real target: analyse the response, make tracks and define next stim. rmeddis@0: rmeddis@0: % Define response and update response list rmeddis@0: if saidYes rmeddis@0: % target was heard, so response=1; rmeddis@0: withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!' rmeddis@0: else rmeddis@0: % target was not hear heard, so response=0; rmeddis@0: withinRuns.responseList=[withinRuns.responseList 0]; rmeddis@0: end rmeddis@0: withinRuns.levelList=[withinRuns.levelList withinRuns.variableValue]; rmeddis@0: trialNumber=length(withinRuns.responseList); rmeddis@0: rmeddis@0: % keep track of peaks and troughs; rmeddis@0: % identify direction of change during initial period rmeddis@0: if saidYes rmeddis@0: % default step size before first reversal rmeddis@28: WRVinitialStep=-stimulusParameters.WRVinitialStep; rmeddis@0: WRVsmallStep=-stimulusParameters.WRVsmallStep; rmeddis@0: % if the previous direction was 'less difficult', this must be a peak rmeddis@0: if strcmp(withinRuns.direction,'less difficult') ... rmeddis@0: && length(withinRuns.levelList)>1 rmeddis@0: withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue]; rmeddis@0: end rmeddis@0: withinRuns.direction='more difficult'; rmeddis@0: else rmeddis@0: % said 'no' rmeddis@0: % default step size before first reversal rmeddis@28: WRVinitialStep=stimulusParameters.WRVinitialStep; rmeddis@0: WRVsmallStep=stimulusParameters.WRVsmallStep; rmeddis@28: rmeddis@0: % if the previous direction was 'up', this must be a peak rmeddis@0: if strcmp(withinRuns.direction,'more difficult') ... rmeddis@0: && length(withinRuns.levelList)>1 rmeddis@0: withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue]; rmeddis@0: end rmeddis@0: withinRuns.direction='less difficult'; rmeddis@0: end rmeddis@0: rmeddis@28: % phase 2 is all the levels after and incuding the first reversal rmeddis@0: % plus the level before that rmeddis@28: % Look for the end of phase 1 rmeddis@0: if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ... rmeddis@0: length(withinRuns.troughs)>0 rmeddis@0: % define phase 2 rmeddis@0: withinRuns.beginningOfPhase2=trialNumber-1; rmeddis@0: withinRuns.nowInPhase2=1; rmeddis@0: WRVsmallStep=WRVinitialStep/2; rmeddis@0: end rmeddis@0: rmeddis@0: if withinRuns.nowInPhase2 rmeddis@0: % keep a record of all levels and responses in phase 2 only rmeddis@0: withinRuns.levelsPhaseTwo=... rmeddis@0: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@0: withinRuns.responsesPhaseTwo=... rmeddis@0: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@0: else rmeddis@0: withinRuns.levelsPhaseTwo=[]; rmeddis@0: end rmeddis@0: rmeddis@0: % get (or substitute) threshold estimate rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: % for plotting psychometric function only rmeddis@0: if withinRuns.beginningOfPhase2>0 rmeddis@0: [psy, levelsBinVector, logistic, rareEvent]= ... rmeddis@0: bestFitPsychometicFunctions... rmeddis@0: (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); rmeddis@0: end rmeddis@28: rmeddis@0: if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs) rmeddis@0: thresholdEstimate= ... rmeddis@0: mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]); rmeddis@0: else rmeddis@0: thresholdEstimate=NaN; rmeddis@0: end rmeddis@28: rmeddis@0: otherwise rmeddis@0: % single interval methods rmeddis@0: try rmeddis@0: % using the s trial after the first reversal rmeddis@0: [psy, levelsBinVector, logistic, rareEvent]= ... rmeddis@0: bestFitPsychometicFunctions(withinRuns.levelsPhaseTwo,... rmeddis@0: withinRuns.responsesPhaseTwo); rmeddis@0: catch rmeddis@0: logistic.bestThreshold=NaN; rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: if withinRuns.nowInPhase2 rmeddis@0: % save tracks of threshold estimates for plotting andprinting rmeddis@0: switch experiment.functionEstMethod rmeddis@0: case {'logisticLS', 'logisticML'} rmeddis@0: if withinRuns.nowInPhase2 rmeddis@0: withinRuns.meanEstTrack=... rmeddis@0: [withinRuns.meanEstTrack ... rmeddis@0: mean(withinRuns.levelsPhaseTwo)]; rmeddis@0: withinRuns.thresholdEstimateTrack=... rmeddis@0: [withinRuns.thresholdEstimateTrack ... rmeddis@0: logistic.bestThreshold]; rmeddis@0: end rmeddis@0: case 'rareEvent' rmeddis@0: withinRuns.meanEstTrack=... rmeddis@0: [withinRuns.meanEstTrack rareEvent.thresholddB]; rmeddis@0: withinRuns.thresholdEstimateTrack=... rmeddis@0: [withinRuns.thresholdEstimateTrack logistic.bestThreshold]; rmeddis@0: case 'peaksAndTroughs' rmeddis@0: withinRuns.meanEstTrack=... rmeddis@0: [withinRuns.meanEstTrack thresholdEstimate]; rmeddis@0: withinRuns.thresholdEstimateTrack=... rmeddis@0: [withinRuns.thresholdEstimateTrack thresholdEstimate]; rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % special discomfort condition rmeddis@0: % run is completed when subject hits '2' button rmeddis@0: switch experiment.paradigm rmeddis@0: case 'discomfort' rmeddis@0: if saidYes rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % choose the next level for the stimulus rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: if saidYes rmeddis@0: [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue); rmeddis@0: else rmeddis@0: [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue); rmeddis@0: end rmeddis@28: rmeddis@0: % empty message means continue as normal rmeddis@28: if ~isempty(msg) rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: newWRVvalue=withinRuns.variableValue-WRVinitialStep; rmeddis@28: rmeddis@0: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: % run completed by virtue of number of trials rmeddis@0: % or restart because listener is in trouble rmeddis@0: if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials rmeddis@0: % Use bonomial test to decide if there is an imbalance in the rmeddis@0: % number of 'yes'es and 'no's rmeddis@0: yesCount=sum(withinRuns.responseList); rmeddis@0: noCount=length(withinRuns.responseList)-yesCount; rmeddis@0: z=abs(yesCount-noCount)/(yesCount+noCount)^0.5; rmeddis@0: if z>1.96 rmeddis@0: betweenRuns.resets=betweenRuns.resets+1; rmeddis@0: disp([ 'reset / z= ' num2str( z) ... rmeddis@0: ' Nresets= ' num2str( betweenRuns.resets) ] ) rmeddis@0: withinRuns.peaks=[]; rmeddis@0: withinRuns.troughs=[]; rmeddis@0: withinRuns.levelList=withinRuns.levelList(end); rmeddis@0: withinRuns.meanEstTrack=withinRuns.meanEstTrack(end); rmeddis@0: withinRuns.forceThreshold=NaN; rmeddis@0: withinRuns.responseList=withinRuns.responseList(end); rmeddis@0: withinRuns.beginningOfPhase2=0; rmeddis@0: withinRuns.nowInPhase2=0; rmeddis@0: withinRuns.thresholdEstimateTrack=... rmeddis@0: withinRuns.thresholdEstimateTrack(end); rmeddis@0: else rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: end rmeddis@28: rmeddis@0: % set new value for WRV rmeddis@0: if withinRuns.nowInPhase2 rmeddis@0: % phase 2 rmeddis@0: currentMeanEst=withinRuns.thresholdEstimateTrack(end); rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case 'MaxLikelihood' rmeddis@28: newWRVvalue=currentMeanEst; rmeddis@0: case {'oneIntervalUpDown'} rmeddis@0: newWRVvalue=withinRuns.variableValue+WRVsmallStep; rmeddis@0: end rmeddis@0: else rmeddis@0: % phase 1 rmeddis@0: if withinRuns.variableValue+2*WRVinitialStep>... rmeddis@0: stimulusParameters.WRVlimits(2) rmeddis@0: % use smaller steps when close to maximum rmeddis@0: WRVinitialStep=WRVinitialStep/2; rmeddis@0: end rmeddis@0: newWRVvalue=withinRuns.variableValue+WRVinitialStep; rmeddis@0: end rmeddis@0: otherwise rmeddis@0: error( 'assessment method not recognised') rmeddis@0: end rmeddis@0: rmeddis@0: switch experiment.paradigm rmeddis@0: % prevent unrealistic gap durations 'gapDetection' tasks. rmeddis@0: % Note that the gap begins when the ramp ends not when stimulus ends rmeddis@0: case 'gapDetection' rmeddis@0: if newWRVvalue<-2*stimulusParameters.rampDuration rmeddis@0: newWRVvalue=-2*stimulusParameters.rampDuration; rmeddis@0: addToMsg('gap duration fixed at - 2 * ramp!',1, 1) rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: withinRuns.variableValue=newWRVvalue; rmeddis@0: withinRuns.trialNumber=withinRuns.trialNumber+1; rmeddis@0: rmeddis@0: % Trial continues rmeddis@0: plotProgressThisTrial(handles) rmeddis@0: rmeddis@0: % next stimulus and so the cycle continues rmeddis@0: errormsg=nextStimulus(handles); rmeddis@0: % after the stimulus is presented, control returns here and the system rmeddis@0: % waits for user action. rmeddis@0: rmeddis@0: % terminate if there is any kind of problem rmeddis@0: if ~isempty(errormsg) rmeddis@0: % e.g. limits exceeded, clipping rmeddis@0: disp(['Error nextStimulus: ' errormsg]) rmeddis@0: runCompleted(handles) rmeddis@0: return rmeddis@0: end rmeddis@0: rmeddis@0: % ------------------------------------------------ userSelectsPleaseRepeat rmeddis@0: function userSelectsPleaseRepeat(handles) rmeddis@0: global experiment withinRuns rmeddis@0: % ignore click if not 'waitingForResponse' rmeddis@0: if ~strcmp(experiment.status,'waitingForResponse') rmeddis@0: disp('ignored click') rmeddis@0: return rmeddis@0: end rmeddis@28: % Take no action other than to make a rmeddis@0: % tally of repeat requests rmeddis@0: experiment.pleaseRepeat=experiment.pleaseRepeat+1; rmeddis@0: withinRuns.thisIsRepeatTrial=1; rmeddis@0: nextStimulus(handles); rmeddis@0: rmeddis@0: % ------------------------------------------------ userSelectsWrongButton rmeddis@0: function userSelectsWrongButton(handles) rmeddis@0: global withinRuns experiment rmeddis@0: % restart is the simplest solution for a 'wrong button' request rmeddis@0: withinRuns.wrongButton=withinRuns.wrongButton+1; rmeddis@28: set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y') rmeddis@0: msg=[{'Start again: wrong button pressed'}, {' '},... rmeddis@0: {'Please,click on the GO button'}]; rmeddis@0: set(handles.textMSG,'string',msg) rmeddis@0: experiment.status='waitingForGO'; rmeddis@0: rmeddis@0: % ------------------------------------------------- plotProgressThisTrial rmeddis@0: function plotProgressThisTrial(handles) rmeddis@28: % updates GUI: used for all responses rmeddis@0: rmeddis@0: global experiment stimulusParameters betweenRuns withinRuns expGUIhandles rmeddis@0: global psy levelsBinVector binFrequencies rareEvent logistic statsModel rmeddis@0: rmeddis@0: % plot the levelTrack and the threshold track rmeddis@0: rmeddis@0: % Panel 2 rmeddis@0: % plot the levelList rmeddis@0: axes(expGUIhandles.axes2); cla rmeddis@0: plot( withinRuns.levelList,'o','markerFaceColor','k'), hold on rmeddis@0: % plot the best threshold estimate tracks rmeddis@0: if length(withinRuns.meanEstTrack)>=1 rmeddis@0: % The length of the levelList is 2 greater than number of thresholds rmeddis@0: ptr=withinRuns.beginningOfPhase2+1; rmeddis@0: plot(ptr: ptr+length(withinRuns.meanEstTrack)-1, ... rmeddis@0: withinRuns.meanEstTrack, 'r') rmeddis@0: plot( ptr: ptr+length(withinRuns.thresholdEstimateTrack)-1, ... rmeddis@0: withinRuns.thresholdEstimateTrack, 'g') rmeddis@0: hold off rmeddis@0: estThresh=withinRuns.thresholdEstimateTrack(end); rmeddis@0: switch experiment.threshEstMethod rmeddis@0: % add appropriate labels to subject GUI buttons rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: title([stimulusParameters.WRVname ' = ' ... rmeddis@0: num2str(withinRuns.variableValue, '%5.1f')]) rmeddis@0: otherwise rmeddis@0: title([stimulusParameters.WRVname ' = ' ... rmeddis@0: num2str(withinRuns.variableValue, '%5.1f') ... rmeddis@38: '; TH= ' num2str(estThresh, '%5.1f') ' dB']) rmeddis@0: end rmeddis@0: end rmeddis@0: xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]); rmeddis@0: ylim(stimulusParameters.WRVlimits) rmeddis@38: ylabel ('dB SPL') rmeddis@0: grid on rmeddis@0: rmeddis@0: % Panel 4: Summary of threshold estimates (not used here) rmeddis@28: % Estimates from previous runs are set in 'runCompleted' rmeddis@28: % It is only necessary to change title showing runs/trials remaining rmeddis@0: axes(expGUIhandles.axes4) rmeddis@0: runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; rmeddis@0: if withinRuns.beginningOfPhase2>0 rmeddis@0: trialsToGo= experiment.singleIntervalMaxTrials(1) ... rmeddis@0: + withinRuns.beginningOfPhase2- withinRuns.trialNumber; rmeddis@0: title(['trials remaining = ' num2str(trialsToGo) ... rmeddis@0: ': runs to go= ' num2str(runsToGo)]) rmeddis@0: end rmeddis@0: rmeddis@0: % plot psychometric function - panel 5 rmeddis@0: axes(expGUIhandles.axes5), cla rmeddis@0: plot(withinRuns.levelList, withinRuns.responseList,'b.'), hold on rmeddis@0: ylim([0 1]) rmeddis@0: title('') rmeddis@0: rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@0: if withinRuns.beginningOfPhase2>0 rmeddis@0: % display only when in phase 2. rmeddis@0: withinRuns.levelsPhaseTwo=... rmeddis@0: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@0: withinRuns.responsesPhaseTwo=... rmeddis@0: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@28: rmeddis@0: % organise data as psychometric function rmeddis@0: [psy, levelsBinVector, binFrequencies]= ... rmeddis@0: psychometricFunction(withinRuns.levelsPhaseTwo,... rmeddis@0: withinRuns.responsesPhaseTwo, experiment.psyBinWidth); rmeddis@28: rmeddis@0: % Plot the function rmeddis@0: % point by point with circles of appropiate weighted size rmeddis@0: hold on, rmeddis@0: for i=1:length(psy) rmeddis@0: plot(levelsBinVector(i), psy(i), 'ro', ... rmeddis@0: 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) rmeddis@0: end rmeddis@0: % save info for later rmeddis@0: betweenRuns.psychometicFunction{betweenRuns.runNumber}=... rmeddis@0: [levelsBinVector; psy]; rmeddis@28: rmeddis@0: % fitPsychometric functions is computed in 'userDecides' rmeddis@0: % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k') rmeddis@0: plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') rmeddis@0: plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') rmeddis@0: if ~isnan(logistic.bestThreshold ) rmeddis@0: xlim([ 0 100 ]) rmeddis@28: title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '... rmeddis@28: num2str(rareEvent.bestGain,'%6.3f') ' A=' ... rmeddis@28: num2str(rareEvent.bestVMin,'%8.1f')]) rmeddis@0: else rmeddis@0: title(' ') rmeddis@0: end rmeddis@28: rmeddis@0: switch experiment.ear rmeddis@0: %plot green line for statsModel a priori model rmeddis@0: case 'statsModelLogistic' rmeddis@0: % plot proTem logistic (green) used by stats model rmeddis@0: p= 1./(1+exp(-statsModel.logisticSlope... rmeddis@0: *(levelsBinVector-logistic.bestThreshold))); rmeddis@0: if experiment.psyFunSlope<0, p=1-p;end rmeddis@0: titleText=[ ', statsModel: logistic']; rmeddis@0: hold on, plot(levelsBinVector, p,'g') rmeddis@0: case 'statsModelRareEvent' rmeddis@0: pressure=28*10.^(levelsBinVector/20); rmeddis@0: p=1-exp(-stimulusParameters.targetDuration... rmeddis@0: *(statsModel.rareEvenGain... rmeddis@0: * pressure-statsModel.rareEventVmin)); rmeddis@0: p(p<0)=0; rmeddis@0: if experiment.psyFunSlope<0, p=1-p;end rmeddis@0: hold on, plot(levelsBinVector, p,'g') rmeddis@0: end %(estMethod) rmeddis@0: end rmeddis@28: rmeddis@0: otherwise % 2A2IFC rmeddis@0: message3= ... rmeddis@0: ([ 'peaks=' num2str(withinRuns.peaks) ... rmeddis@0: 'troughs=' num2str(withinRuns.troughs)]); rmeddis@0: ylimRM([-0.1 1.1]) % 0=no / 1=yes rmeddis@0: set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'}) rmeddis@0: ylabel('psychometric function'), xlabel('target level') rmeddis@0: if length(levelsBinVector)>1 rmeddis@0: xlim([ min(levelsBinVector) max(levelsBinVector)]) rmeddis@0: xlim([ 0 100]) rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % command window summary rmeddis@0: % Accumulate things to say in the message window rmeddis@0: message1= (['responses: ' num2str(withinRuns.responseList,'%9.0f')]); rmeddis@0: switch experiment.paradigm rmeddis@0: % more decimal places needed on GUI rmeddis@0: case { 'gapDetection', 'frequencyDiscrimination', 'forwardMaskingD'} rmeddis@0: message2= ([stimulusParameters.WRVname ... rmeddis@0: ': ' num2str(withinRuns.levelList,'%7.3f')]); rmeddis@0: message3= (['Thresh (logistic mean): ' ... rmeddis@0: num2str(withinRuns.thresholdEstimateTrack,'%7.3f')]); rmeddis@0: otherwise rmeddis@0: message2= ([stimulusParameters.WRVname ': ' ... rmeddis@0: num2str(withinRuns.levelList,'%7.1f')]); rmeddis@0: message3= (['Thresh (logistic mean): ' ... rmeddis@0: num2str(withinRuns.thresholdEstimateTrack,'%7.1f')]); rmeddis@0: end rmeddis@0: rmeddis@0: addToMsg(str2mat(message1, message2, message3), 0) rmeddis@0: rmeddis@0: % -----------------------------------------------------runCompleted rmeddis@0: function runCompleted(handles) rmeddis@0: % Used at the end of each run rmeddis@0: global experiment stimulusParameters betweenRuns withinRuns rmeddis@0: global rareEvent expGUIhandles rmeddis@0: % disp('run completed') rmeddis@0: rmeddis@0: experiment.status='runCompleted'; rmeddis@28: % quick update after final trial just to make sure rmeddis@0: plotProgressThisTrial(handles) rmeddis@0: rmeddis@0: switch experiment.ear rmeddis@0: case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... rmeddis@0: 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} rmeddis@0: % no changes required if model used rmeddis@0: otherwise rmeddis@0: set(handles.frame1,'visible','off') rmeddis@0: set(handles.pushbuttoNotSure,'visible','off') rmeddis@0: set(handles.pushbuttonWrongButton,'visible','off') rmeddis@0: set(handles.pushbutton3,'visible','off') rmeddis@0: set(handles.pushbutton2,'visible','off') rmeddis@0: set(handles.pushbutton1,'visible','off') rmeddis@0: set(handles.pushbutton0,'visible','off') rmeddis@0: set(handles.pushbuttonGO,'visible','off') rmeddis@0: end rmeddis@0: rmeddis@0: if isnan(withinRuns.forceThreshold) rmeddis@0: % the experiment has been aborted for some reason rmeddis@0: threshold=withinRuns.forceThreshold; rmeddis@0: stdev=NaN; rmeddis@0: logistic.bestK=NaN; rmeddis@0: logistic.bestThreshold=NaN; rmeddis@0: medianThreshold=NaN; rmeddis@0: meanThreshold=NaN; rmeddis@0: else rmeddis@0: % use only phase 2 levels and responses for calculating thresholds rmeddis@0: withinRuns.levelsPhaseTwo=... rmeddis@0: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@0: withinRuns.responsesPhaseTwo=... rmeddis@0: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@0: [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ... rmeddis@0: bestFitPsychometicFunctions... rmeddis@0: (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); rmeddis@28: rmeddis@0: % plot final psychometric function rmeddis@0: axes(expGUIhandles.axes5),cla rmeddis@0: hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') rmeddis@0: hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') rmeddis@0: % organise data as psychometric function rmeddis@0: [psy, levelsBinVector, binFrequencies]= ... rmeddis@0: psychometricFunction(withinRuns.levelsPhaseTwo,... rmeddis@0: withinRuns.responsesPhaseTwo, experiment.psyBinWidth); rmeddis@0: % point by point with circles of appropiate weighted size rmeddis@0: hold on, rmeddis@0: for i=1:length(psy) rmeddis@0: plot(levelsBinVector(i), psy(i), 'ro', ... rmeddis@0: 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) rmeddis@0: end rmeddis@0: rmeddis@0: % experimental rmeddis@0: medianThreshold=median(withinRuns.levelsPhaseTwo); rmeddis@0: warning off rmeddis@0: meanThreshold=mean(withinRuns.levelsPhaseTwo); rmeddis@28: rmeddis@0: % identify the current threshold estimate rmeddis@0: switch experiment.paradigm rmeddis@0: case 'discomfort' rmeddis@0: % most recent value (not truely a mean value) rmeddis@0: threshold=withinRuns.levelList(end); rmeddis@28: stdev=NaN; rmeddis@0: otherwise rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@0: % last value in the list rmeddis@28: % threshold=withinRuns.meanEstTrack(end); rmeddis@0: threshold=withinRuns.thresholdEstimateTrack(end); rmeddis@0: stdev=NaN; rmeddis@28: rmeddis@0: case {'2I2AFC++', '2I2AFC+++'} rmeddis@0: % use peaks and troughs rmeddis@0: try % there may not be enough values to use rmeddis@0: peaksUsed=experiment.peaksUsed; rmeddis@0: threshold=... rmeddis@0: mean(... rmeddis@0: [withinRuns.peaks(end-peaksUsed+1:end) ... rmeddis@0: withinRuns.troughs(end-peaksUsed+1:end)]); rmeddis@0: stdev=... rmeddis@0: std([withinRuns.peaks(end-peaksUsed +1:end) ... rmeddis@0: withinRuns.troughs(end-peaksUsed:end)]); rmeddis@0: catch rmeddis@0: threshold=NaN; rmeddis@0: stdev=NaN; rmeddis@0: end rmeddis@0: end rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % Store thresholds rmeddis@0: betweenRuns.thresholds=[betweenRuns.thresholds threshold]; rmeddis@0: betweenRuns.thresholds_mean=[betweenRuns.thresholds_mean meanThreshold]; rmeddis@0: betweenRuns.thresholds_median=... rmeddis@0: [betweenRuns.thresholds_median medianThreshold]; rmeddis@0: betweenRuns.forceThresholds=... rmeddis@0: [betweenRuns.forceThresholds withinRuns.forceThreshold]; rmeddis@0: rmeddis@0: % count observations after the startup phase for record keeping rmeddis@0: betweenRuns.observationCount=... rmeddis@0: [betweenRuns.observationCount length(withinRuns.levelList)]; rmeddis@0: betweenRuns.timesOfFirstReversals=... rmeddis@0: [betweenRuns.timesOfFirstReversals withinRuns.beginningOfPhase2]; rmeddis@0: betweenRuns.catchTrials=... rmeddis@0: [betweenRuns.catchTrials withinRuns.catchTrialCount]; rmeddis@0: rmeddis@0: % add variable length tracks to cell arrays rmeddis@0: if withinRuns.beginningOfPhase2>0 rmeddis@0: betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=... rmeddis@28: withinRuns.thresholdEstimateTrack; rmeddis@0: betweenRuns.levelTracks{length(betweenRuns.thresholds)}=... rmeddis@0: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@0: betweenRuns.responseTracks{length(betweenRuns.thresholds)}=... rmeddis@0: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@0: else rmeddis@0: betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=[]; rmeddis@0: betweenRuns.levelTracks{length(betweenRuns.thresholds)}=[]; rmeddis@0: betweenRuns.responseTracks{length(betweenRuns.thresholds)}=[]; rmeddis@0: end rmeddis@0: rmeddis@0: betweenRuns.bestGain=[betweenRuns.bestGain rareEvent.bestGain]; rmeddis@0: betweenRuns.bestVMin=[betweenRuns.bestVMin rareEvent.bestVMin]; rmeddis@0: betweenRuns.bestPaMin=[betweenRuns.bestPaMin rareEvent.bestPaMindB]; rmeddis@0: betweenRuns.bestLogisticM=... rmeddis@0: [betweenRuns.bestLogisticM logistic.bestThreshold]; rmeddis@0: betweenRuns.bestLogisticK=[betweenRuns.bestLogisticK logistic.bestK]; rmeddis@0: rmeddis@0: resultsSoFar=[betweenRuns.var1Sequence(betweenRuns.runNumber)'... rmeddis@0: betweenRuns.var2Sequence(betweenRuns.runNumber)'... rmeddis@0: betweenRuns.thresholds(betweenRuns.runNumber)' rmeddis@0: ]; rmeddis@0: rmeddis@0: fprintf('%10.3f \t%10.3f \t%10.1f \n', resultsSoFar') rmeddis@0: rmeddis@0: switch experiment.ear rmeddis@0: case {'left', 'right', 'diotic', 'dichoticLeft','dichoticRight'} rmeddis@0: disp(['caught out= ' num2str(betweenRuns.caughtOut)]) rmeddis@0: end rmeddis@0: rmeddis@0: % plot history of thresholds in panel 4 rmeddis@0: axes(expGUIhandles.axes4), cla rmeddis@0: plotColors='rgbmckywrgbmckyw'; rmeddis@0: for i=1:length(betweenRuns.thresholds) rmeddis@0: faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1); rmeddis@0: switch betweenRuns.variableName1 rmeddis@0: case {'targetFrequency', 'maskerRelativeFrequency'} rmeddis@0: if min(betweenRuns.var1Sequence)>0 rmeddis@0: semilogx(betweenRuns.var1Sequence(i), ... rmeddis@0: betweenRuns.thresholds(i), 'o', ... rmeddis@0: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@0: else rmeddis@0: plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... rmeddis@0: betweenRuns.thresholds, 'o', ... rmeddis@0: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@0: plot(betweenRuns.var1Sequence(i), ... rmeddis@0: betweenRuns.thresholds(i), 'o', ... rmeddis@0: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@0: end rmeddis@0: otherwise rmeddis@0: plot(betweenRuns.var1Sequence(i), ... rmeddis@0: betweenRuns.thresholds(i), 'o', 'markerSize', 5,... rmeddis@0: 'markerFaceColor',faceColor) rmeddis@0: end rmeddis@0: hold on rmeddis@0: end rmeddis@0: xlimRM([ min(betweenRuns.variableList1) max(betweenRuns.variableList1) ]) rmeddis@0: ylim(stimulusParameters.WRVlimits) rmeddis@38: ylabel('thresholds (dB)') rmeddis@0: xlabel(betweenRuns.variableName1) rmeddis@0: set(gca,'ytick', [0 20 40 60 80 100]) rmeddis@0: try rmeddis@0: % problems if only one x value rmeddis@0: set(gca,'XTick', sort(betweenRuns.variableList1)) rmeddis@0: catch rmeddis@0: end rmeddis@0: grid on, set(gca,'XMinorGrid', 'off') rmeddis@0: rmeddis@28: % final run? rmeddis@0: if betweenRuns.runNumber==length(betweenRuns.var1Sequence) rmeddis@0: % yes, end of experiment rmeddis@0: fileName=['savedData/' experiment.name experiment.date ... rmeddis@0: experiment.paradigm]; rmeddis@0: % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl') rmeddis@0: disp('Experiment completed') rmeddis@28: rmeddis@0: % update subject GUI to acknowledge end of run rmeddis@0: subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}]; rmeddis@0: set(handles.textMSG,'string', subjGUImsg ) rmeddis@0: % play 'Tada' rmeddis@0: [y,fs,nbits]=wavread('TADA.wav'); rmeddis@0: musicGain=10^(stimulusParameters.musicLeveldB/20); rmeddis@0: y=y*musicGain; rmeddis@35: if ispc, wavplay(y/100,fs, 'async'), else sound(y/100,fs, 'async'), end rmeddis@28: rmeddis@0: % update experimenter GUI rmeddis@0: addToMsg('Experiment completed.',1) rmeddis@28: rmeddis@38: set(expGUIhandles.pushbuttonSave,'visible','on') rmeddis@0: printReport rmeddis@28: experiment.status='endOfExperiment'; rmeddis@0: return rmeddis@0: else rmeddis@0: % No, hang on. rmeddis@0: switch experiment.ear rmeddis@0: case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... rmeddis@0: 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} rmeddis@0: % no changes required if model used rmeddis@0: otherwise rmeddis@0: % decrement catchTrialRate towards baseRate rmeddis@0: stimulusParameters.catchTrialRate=... rmeddis@0: stimulusParameters.catchTrialBaseRate + ... rmeddis@0: (stimulusParameters.catchTrialRate... rmeddis@0: -stimulusParameters.catchTrialBaseRate)... rmeddis@0: *(1-exp(-stimulusParameters.catchTrialTimeConstant)); rmeddis@0: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@0: stimulusParameters.catchTrialRate) rmeddis@28: rmeddis@0: % and go again rmeddis@28: set(handles.pushbuttonGO,'backgroundcolor','y') rmeddis@0: set(handles.frame1,'visible','off') rmeddis@0: set(handles.pushbuttonGO,'visible','on') rmeddis@0: msg=[{'Ready to start new trial'}, {' '},... rmeddis@0: {'Please,click on the GO button'}]; rmeddis@0: set(handles.textMSG,'string',msg) rmeddis@0: end rmeddis@0: experiment.status='waitingForGO'; rmeddis@0: % fprintf('\n') rmeddis@28: rmeddis@0: [y,fs,nbits]=wavread('CHIMES.wav'); rmeddis@0: musicGain=10^(stimulusParameters.musicLeveldB/20); rmeddis@0: y=y*musicGain; rmeddis@35: if ispc, wavplay(y/100,fs, 'async'), else sound(y/100,fs, 'async'), end rmeddis@35: rmeddis@0: end rmeddis@0: rmeddis@0: % -----------------------------------------------------MAPmodelRunsGUI rmeddis@0: % The computer presses the buttons rmeddis@0: function MAPmodelRunsGUI(handles) rmeddis@0: global experiment stimulusParameters method expGUIhandles rmeddis@0: global AN_IHCsynapseParams rmeddis@0: method=[]; rmeddis@0: rmeddis@28: while strcmp(experiment.status,'waitingForGO') rmeddis@0: % no catch trials for MAP model rmeddis@0: experiment.allowCatchTrials=0; rmeddis@28: rmeddis@28: % initiates run and plays first stimulus and it returns rmeddis@0: % without waiting for button press rmeddis@28: startNewRun(handles) rmeddis@0: rmeddis@29: % show sample Rate on GUI; it must be set in MAPparams ##?? rmeddis@0: set(expGUIhandles.textsampleRate,'string',... rmeddis@0: num2str(stimulusParameters.sampleRate)) rmeddis@28: rmeddis@29: if experiment.singleShot % ##?? rmeddis@0: AN_IHCsynapseParams.plotSynapseContents=1; rmeddis@0: else rmeddis@0: AN_IHCsynapseParams.plotSynapseContents=0; rmeddis@0: end rmeddis@28: rmeddis@0: % continuous loop until the program stops itself rmeddis@0: while strcmp(experiment.status,'waitingForResponse') rmeddis@0: % NB at this point the stimulus has been played rmeddis@0: pause(0.1) % to allow interrupt with CTRL/C rmeddis@28: rmeddis@0: switch experiment.ear rmeddis@0: case { 'MAPmodelListen'} rmeddis@28: % flash the buttons to show model response rmeddis@28: set(handles.pushbutton1,'backgroundcolor','y','visible','on') rmeddis@28: set(handles.pushbutton2,'backgroundcolor','y','visible','on') rmeddis@0: end rmeddis@0: rmeddis@28: % Analayse the current stimulus using MAP rmeddis@28: [modelResponse earObject]= MAPmodel; rmeddis@0: rmeddis@0: if experiment.stop || experiment.singleShot rmeddis@0: % trap for single trial or user interrupt using 'stop' button. rmeddis@0: experiment.status= 'waitingForStart'; rmeddis@33: % experiment.stop=0; rmeddis@28: errormsg='manually stopped'; rmeddis@28: addToMsg(errormsg,1) rmeddis@0: return rmeddis@0: end rmeddis@28: rmeddis@0: switch modelResponse rmeddis@0: case 1 rmeddis@0: % userDoesNotHearTarget(handles) rmeddis@0: switch experiment.ear rmeddis@0: case {'MAPmodelListen'} rmeddis@0: % illuminate appropriate button rmeddis@0: set(handles.pushbutton1,... rmeddis@0: 'backgroundcolor','r','visible','on') rmeddis@0: set(handles.pushbutton2,'backgroundcolor','y') rmeddis@0: end rmeddis@0: userDecides(handles, false) rmeddis@0: if experiment.singleShot, return, end rmeddis@0: rmeddis@0: case 2 rmeddis@0: % userHearsTarget(handles) rmeddis@0: switch experiment.ear rmeddis@0: case {'MAPmodelListen'} rmeddis@0: % illuminate appropriate button (DEMO only) rmeddis@0: set(handles.pushbutton2,'backgroundcolor',... rmeddis@0: 'r','visible','on') rmeddis@0: set(handles.pushbutton1,'backgroundcolor','y') rmeddis@0: end rmeddis@28: rmeddis@0: switch experiment.paradigm rmeddis@0: case 'discomfort' rmeddis@0: % always treat discomfort as 'not heard' rmeddis@0: userDecides(handles, false) rmeddis@0: otherwise rmeddis@0: userDecides(handles, true) rmeddis@0: end rmeddis@0: otherwise rmeddis@0: % probably an abort rmeddis@0: return rmeddis@0: end rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@28: % -------------------------------------------------------MAPmodel rmeddis@28: function [modelResponse, MacGregorResponse]=MAPmodel rmeddis@9: rmeddis@9: global experiment stimulusParameters audio withinRuns rmeddis@29: % global outerMiddleEarParams DRNLParams AN_IHCsynapseParams rmeddis@38: global ICoutput dtSpikes dt savedBFlist ANprobRateOutput expGUIhandles rmeddis@29: global paramChanges rmeddis@9: rmeddis@9: savePath=path; rmeddis@24: addpath(['..' filesep 'MAP'], ['..' filesep 'utilities']) rmeddis@9: modelResponse=[]; rmeddis@9: MacGregorResponse=[]; rmeddis@9: rmeddis@9: % mono only (column vector) rmeddis@9: audio=audio(:,1)'; rmeddis@9: rmeddis@9: % if stop button pressed earlier rmeddis@28: if experiment.stop rmeddis@28: errormsg='manually stopped'; rmeddis@28: addToMsg(errormsg,1) rmeddis@28: return rmeddis@28: end rmeddis@9: rmeddis@28: % ---------------------------------------------- run Model rmeddis@9: MAPparamsName=experiment.name; rmeddis@9: showPlotsAndDetails=experiment.MAPplot; rmeddis@29: rmeddis@29: % important buried constant ##?? rmeddis@9: AN_spikesOrProbability='spikes'; rmeddis@32: % AN_spikesOrProbability='probability'; rmeddis@9: rmeddis@9: % [response, method]=MAPsequenceSeg(audio, method, 1:8); rmeddis@28: rmeddis@28: if sum(strcmp(experiment.ear,{'MAPmodelMultiCh', 'MAPmodelListen'})) rmeddis@28: % use BFlist specified in MAPparams file rmeddis@28: BFlist= -1; rmeddis@28: else rmeddis@28: BFlist=stimulusParameters.targetFrequency; rmeddis@28: end rmeddis@28: paramChanges=get(expGUIhandles.editparamChanges,'string'); rmeddis@29: % convert from string to a cell array rmeddis@29: eval(paramChanges); rmeddis@28: rmeddis@28: MAP1_14(audio, stimulusParameters.sampleRate, BFlist,... rmeddis@28: MAPparamsName, AN_spikesOrProbability, paramChanges); rmeddis@28: rmeddis@9: if showPlotsAndDetails rmeddis@23: options.printModelParameters=0; rmeddis@9: options.showModelOutput=1; rmeddis@9: options.printFiringRates=1; rmeddis@9: options.showACF=0; rmeddis@38: options.showEfferent=0; rmeddis@23: options.surfProbability=0; rmeddis@25: showMapOptions.surfSpikes=0; rmeddis@23: UTIL_showMAP(options) rmeddis@9: end rmeddis@9: rmeddis@9: % No response, probably caused by hitting 'stop' button rmeddis@28: if strcmp(AN_spikesOrProbability,'spikes') && isempty(ICoutput) rmeddis@28: return rmeddis@28: end rmeddis@9: % ---------------------------------------------------------- end model run rmeddis@9: rmeddis@28: if strcmp(AN_spikesOrProbability,'spikes') rmeddis@28: MacGregorResponse= sum(ICoutput,1); % use IC rmeddis@38: dt=dtSpikes; rmeddis@28: time=dt:dt:dt*length(MacGregorResponse); rmeddis@28: else rmeddis@28: % for one channel, ANprobResponse=ANprobRateOutput rmeddis@28: % for multi channel take strongest in any epoch rmeddis@28: nChannels=length(savedBFlist); rmeddis@28: % use only HSR fibers rmeddis@28: ANprobRateOutput=ANprobRateOutput(end-nChannels+1:end,:); rmeddis@28: time=dt:dt:dt*length(ANprobRateOutput); rmeddis@28: end rmeddis@9: rmeddis@28: % group delay on unit response - these values are iffy rmeddis@28: windowOnsetDelay= 0.004; rmeddis@28: windowOffsetDelay= 0.022; % long ringing time rmeddis@9: rmeddis@38: % now find the response of the MacGregor model during rmeddis@38: % the target presentation + group delay rmeddis@9: switch experiment.threshEstMethod rmeddis@9: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: idx= time>stimulusParameters.testTargetBegins+windowOnsetDelay ... rmeddis@28: & timestimulusParameters.testNonTargetBegins+windowOnsetDelay ... rmeddis@28: & timeANprobRateOutput(end,1)+10 rmeddis@28: nSpikesDuringTarget=1; % i.e. at leastone MacG spike rmeddis@28: else rmeddis@28: nSpikesDuringTarget=0; rmeddis@28: end rmeddis@28: end rmeddis@9: % nSpikesDuringTarget is +ve when more spikes are found rmeddis@9: % in the target window rmeddis@9: difference= nSpikesTrueWindow-nSpikesFalseWindow; rmeddis@9: rmeddis@9: if difference>0 rmeddis@9: % hit rmeddis@9: nSpikesDuringTarget=experiment.MacGThreshold+1; rmeddis@9: elseif difference<0 rmeddis@9: % miss (wrong choice) rmeddis@9: nSpikesDuringTarget=experiment.MacGThreshold-1; rmeddis@9: else rmeddis@9: if rand>0.5 rmeddis@9: % hit (random choice) rmeddis@9: nSpikesDuringTarget=experiment.MacGThreshold+1; rmeddis@9: else rmeddis@9: % miss (random choice) rmeddis@9: nSpikesDuringTarget=experiment.MacGThreshold-1; rmeddis@9: end rmeddis@9: end rmeddis@9: disp(['level target dummy decision: ' ... rmeddis@9: num2str([withinRuns.variableValue nSpikesTrueWindow ... rmeddis@9: nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] ) rmeddis@28: otherwise rmeddis@38: rmeddis@38: % single interval up/down rmeddis@28: idx=find(time>stimulusParameters.testTargetBegins +windowOnsetDelay... rmeddis@28: & timeANprobRateOutput(end,1)+10 rmeddis@28: nSpikesDuringTarget=1; rmeddis@28: else rmeddis@28: nSpikesDuringTarget=0; rmeddis@28: end rmeddis@9: rmeddis@28: end rmeddis@9: end rmeddis@9: rmeddis@9: if experiment.MAPplot rmeddis@9: % add vertical lines to indicate target region rmeddis@9: figure(99), subplot(6,1,6) rmeddis@9: hold on rmeddis@9: yL=get(gca,'YLim'); rmeddis@28: plot([stimulusParameters.testTargetBegins + windowOnsetDelay ... rmeddis@28: stimulusParameters.testTargetBegins + windowOnsetDelay],yL,'r') rmeddis@28: plot([stimulusParameters.testTargetEnds + windowOffsetDelay ... rmeddis@28: stimulusParameters.testTargetEnds + windowOffsetDelay],yL,'r') rmeddis@9: end rmeddis@9: rmeddis@9: % specify unambiguous response rmeddis@28: if nSpikesDuringTarget>experiment.MacGThreshold rmeddis@28: modelResponse=2; % stimulus detected rmeddis@28: else rmeddis@28: modelResponse=1; % nothing heard (default) rmeddis@9: end rmeddis@9: rmeddis@9: path(savePath) rmeddis@0: rmeddis@0: % -----------------------------------------------------statsModelRunsGUI rmeddis@0: % The computer presses the buttons rmeddis@0: function statsModelRunsGUI(handles) rmeddis@0: % Decision are made at random using a prescribe statistical function rmeddis@0: % to set probabilities as a function of signal level. rmeddis@0: global experiment rmeddis@0: rmeddis@0: experiment.allowCatchTrials=0; rmeddis@0: rmeddis@0: while strcmp(experiment.status,'waitingForGO') rmeddis@0: % i.e. waiting for new run rmeddis@0: if experiment.stop rmeddis@0: % user has requested an abort rmeddis@0: experiment.status= 'waitingForStart'; rmeddis@0: addToMsg('manually stopped',1) rmeddis@0: return rmeddis@0: end rmeddis@28: rmeddis@0: % initiates run and plays first stimulus and it returns rmeddis@0: % without waiting for button press rmeddis@0: % NB stimulus is not actually generated (for speed) rmeddis@0: startNewRun(handles) rmeddis@28: rmeddis@0: while strcmp(experiment.status,'waitingForResponse') rmeddis@0: % create artificial response here rmeddis@0: modelResponse=statsModelGetResponse; rmeddis@0: switch modelResponse rmeddis@0: case 1 rmeddis@0: % userDoesNotHearTarget(handles) rmeddis@0: userDecides(handles, false) rmeddis@0: case 2 rmeddis@0: % userHearsTarget(handles) rmeddis@0: userDecides(handles, true) rmeddis@0: end rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % -----------------------------------------------------statsModelGetResponse rmeddis@0: function modelResponse=statsModelGetResponse(handles) rmeddis@0: global experiment withinRuns statsModel stimulusParameters rmeddis@0: % use the generating function to decide if a detection occurs or not rmeddis@0: rmeddis@0: % pause(0.1) % to allow stopping with CTRL/C but slows things down rmeddis@0: rmeddis@0: % first compute the probability that a detection occurs rmeddis@0: switch experiment.ear rmeddis@0: case {'statsModelLogistic'} rmeddis@0: prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean))); rmeddis@0: % if experiment.psyFunSlope<0, rmeddis@0: % prob=1-prob; rmeddis@0: % end rmeddis@28: rmeddis@0: case 'statsModelRareEvent' rmeddis@0: if experiment.psyFunSlope<0 rmeddis@0: addToMsg('statsModelRareEvent cannot be used with negative slope',0) rmeddis@0: error('statsModelRareEvent cannot be used with negative slope') rmeddis@0: end rmeddis@28: rmeddis@0: % standard formula is prob = 1 – exp(-d (g P – A)) rmeddis@0: % here A->A; To find Pmin use A/gain rmeddis@0: pressure=28*10^(withinRuns.variableValue/20); rmeddis@0: gain=statsModel.rareEvenGain; rmeddis@0: A=statsModel.rareEventVmin; rmeddis@0: d=stimulusParameters.targetDuration; rmeddis@0: gP_Vmin=gain*pressure-A; rmeddis@0: if gP_Vmin>0 rmeddis@0: prob=1-exp(-d*(gP_Vmin)); rmeddis@0: else rmeddis@0: prob=0; rmeddis@0: end rmeddis@0: end rmeddis@0: rmeddis@0: % Use the probability to choose whether or not a detection has occurred rmeddis@0: switch experiment.threshEstMethod rmeddis@0: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@0: if rand1 rmeddis@0: [r c]=size(headers); rmeddis@0: for no=1:r rmeddis@0: fprintf('%s\t',headers(no,:)) rmeddis@0: end rmeddis@0: fprintf('\n') rmeddis@0: end rmeddis@0: rmeddis@0: [r c]=size(M); rmeddis@0: rmeddis@0: for row=1:r rmeddis@0: for col=1:c rmeddis@0: if row==1 && col==1 && M(1,1)==-1000 rmeddis@0: % Print nothing (tab follows below) rmeddis@0: else rmeddis@0: fprintf('%s',num2str(M(row,col))) rmeddis@0: end rmeddis@0: if col