diff --git a/ensemblesWrapper.m b/ensemblesWrapper.m new file mode 100755 index 0000000..16f8a50 --- /dev/null +++ b/ensemblesWrapper.m @@ -0,0 +1,86 @@ +function ensemblesWrapper(subjCode) + +if nargin<1, + % Subject info + prompt={'Participant code:'}; + def={'999'}; + title='Input Participant Code'; + lineNo=1; + userinput=inputdlg(prompt,title,lineNo,def); + subjCode=userinput{1}; +end + +assert(length(subjCode)==3,'subjCode must be 3 characters long.') +assert(ischar(subjCode),'Put that subjCode in single quotes!') + +% focus to the command window +commandwindow; + +% get into the correct folder +cd 'C:\Users\Seated4\Desktop\Matt Experiments\ensembles\tasks' + +% make sure helper files are in the path +addpath(['..' filesep 'helpers' filesep]); + +%% practice +% terminated by experimenter +pracCode = ['9' subjCode(2:3)]; +runHet('m',pracCode); + +%% experiment +% 408 equal count trials across 10 blocks +for i=1:10, +runHet('s',subjCode); + +[keyIsDown,~,keyCode]=KbCheck; +if keyIsDown + if strcmpi(KbName(keyCode),'q'), + return; + end +end +end + +% 408 unequal count trials across 10 blocks +for i=1:10, +runHet('d',subjCode); + +[keyIsDown,~,keyCode]=KbCheck; +if keyIsDown + if strcmpi(KbName(keyCode),'q'), + return; + end +end +end + +% 816 mixed trials across 20 blocks +for i=1:20, +runHet('m',subjCode); + +[keyIsDown,~,keyCode]=KbCheck; +if keyIsDown + if strcmpi(KbName(keyCode),'q'), + return; + end +end +end + +%% End of experiment notice +WhichScreen=max(Screen('Screens')); +shutdown.oldVDLevel = Screen('Preference', 'VisualDebugLevel', 2); +shutdown.oldVerbosity = Screen('Preference', 'Verbosity', 1); +shutdown.oldSkipSyncValue = Screen('Preference', 'SkipSyncTests', 1); +[w, ~] = Screen('OpenWindow',WhichScreen,[0 0 0]); +Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + +instText = ['You have reached the end of this part of the experiment!\n\n',... + 'Please tell the experimenter that you have finished']; +Screen('FillRect', w, [0 0 0]); % instruction screen background black +Screen('TextSize', w, 25); +DrawFormattedText(w, instText, 'Center', 'Center', [255 255 255]); +Screen('Flip', w); % display instructions + +WaitSecs(2); +KbWait([],2); + +ShutdownNicely(shutdown); \ No newline at end of file diff --git a/helpers/DrawFixation.m b/helpers/DrawFixation.m old mode 100644 new mode 100755 index cb2b1ac..1ae1b61 --- a/helpers/DrawFixation.m +++ b/helpers/DrawFixation.m @@ -11,8 +11,11 @@ rgb = [255 255 255]; % default to white cross end +[~, windowHeight]=Screen('WindowSize', window); + + %Screen('DrawLine', window, rgb, x - armLength, y, x + armLength, y); Screen('DrawLine', window, rgb, x - armLength, y+armLength, x + armLength, y-armLength); %Screen('DrawLine', window, rgb, x, y - armLength, x, y + armLength); Screen('DrawLine', window, rgb, x-armLength, y - armLength, x+armLength, y + armLength); -Screen('DrawLine', window, rgb, x, 1, x, 800); +Screen('DrawLine', window, rgb, x, 1, x, windowHeight); diff --git a/helpers/MakeTone.m b/helpers/MakeTone.m old mode 100644 new mode 100755 diff --git a/helpers/PlaceHalfWindowsLR.m b/helpers/PlaceHalfWindowsLR.m index 1469cf1..1f98431 100755 --- a/helpers/PlaceHalfWindowsLR.m +++ b/helpers/PlaceHalfWindowsLR.m @@ -9,5 +9,16 @@ % See also: DrawMirrored, in the whac repository. assert(nargin==4,'PlaceHalfWindowsLR: Need 4 arguments, got %d.\n',nargin) -Screen('DrawTexture',window,onehalf,[], [0 0 ScrRes(1)/2 ScrRes(2)], 0); -Screen('DrawTexture',window,otherhalf,[], [ScrRes(1)/2 0 ScrRes(1) ScrRes(2)], 0); + +ScaleFactor = 0.93; % Stimuli generated assuming 48px/deg, actual setup is 44.65 px/deg +[stimWidth, stimHeight] = Screen('WindowSize',onehalf); +origHalfRect = [0 0 stimWidth stimHeight]; +targetRect = round(origHalfRect .* ScaleFactor); +leftRect = CenterRect(targetRect, [0 0 ScrRes(1)/2 ScrRes(2)]); +rightRect = CenterRect(targetRect, [ScrRes(1)/2 0 ScrRes(1) ScrRes(2)]); + +% Screen('Resolution',window) +% Screen('DrawTexture',window,onehalf,[], [0 0 ScrRes(1)/2 ScrRes(2)], 0); +% Screen('DrawTexture',window,otherhalf,[], [ScrRes(1)/2 0 ScrRes(1) ScrRes(2)], 0); +Screen('DrawTexture',window,onehalf,[], leftRect, 0); +Screen('DrawTexture',window,otherhalf,[], rightRect, 0); diff --git a/helpers/ShutdownNicely.m b/helpers/ShutdownNicely.m old mode 100644 new mode 100755 diff --git a/helpers/convert2VisAngle.m b/helpers/convert2VisAngle.m old mode 100644 new mode 100755 diff --git a/helpers/equneq_instructions.m b/helpers/equneq_instructions.m index 9a9d980..39dcda7 100755 --- a/helpers/equneq_instructions.m +++ b/helpers/equneq_instructions.m @@ -1,28 +1,24 @@ -function equneq_instructions(w,keyl,keyr,BGCol,TxtCol) +function equneq_instructions(w,keyl,keyr) % EQUNEQ_INSTRUCTIONS Display instructions for ensembles task -% equneq_instructions(w,keyl,keyr,BGCol,TxtCol) % w (int): usable PsychToolbox window -% keyl, keyr (char): left/right key, eg 'h' and 'k' -% BGCol (int(3)): background color in [r g b] 0-255 -% TxtCol: same idea, for text color % blank screen -Screen('FillRect', w, BGCol); +Screen('FillRect', w, [0 0 0]); Screen('Flip', w); instText = ['Choose which side has LARGER mean size.\n\n' ... - sprintf('Press %s for left. Press %s for right.\n\n',keyl,keyr) ... + 'Press h for left. Press k for right.\n\n' ... 'Look at the cross in the midddle.\n\n\n\n' ... 'If you respond correctly, you will hear a HIGH beep. \n\n'... 'If you respond incorrectly, you will hear a LOW beep. \n\n\n'... 'If you do not respond in time, you will hear DOUBLE LOW beeps.\n\n' ... - sprintf('If you press anything than %s or %s, you will hear DOUBLE LOW beeps.\n\n\n\n',keyl,keyr) ... + 'If you press anything than h or k, you will hear DOUBLE LOW beeps.\n\n\n\n' ... 'Each block takes a few minutes, and contains about 40 trials.\n' ... - sprintf('To begin, press %s, and then press %s.',keyl,keyr) ]; + 'To begin, press h, and then press k.' ]; -Screen('FillRect', w, BGCol); % instruction screen background +Screen('FillRect', w, [0 0 0]); % instruction screen background black Screen('TextSize', w, 25); -DrawFormattedText(w, instText, 'Center', 'Center', TxtCol); +DrawFormattedText(w, instText, 'Center', 'Center', [255 255 255]); Screen('Flip', w); % display instructions nextKey = keyl; % first make them press left key diff --git a/helpers/fixedPositions.m b/helpers/fixedPositions.m old mode 100644 new mode 100755 diff --git a/helpers/generateSet.m b/helpers/generateSet.m old mode 100644 new mode 100755 diff --git a/helpers/getPos.m b/helpers/getPos.m old mode 100644 new mode 100755 diff --git a/helpers/makeAll.m b/helpers/makeAll.m old mode 100644 new mode 100755 diff --git a/helpers/makeDiff.m b/helpers/makeDiff.m old mode 100644 new mode 100755 diff --git a/helpers/makeEqual.m b/helpers/makeEqual.m old mode 100644 new mode 100755 diff --git a/helpers/makeMix.m b/helpers/makeMix.m old mode 100644 new mode 100755 diff --git a/helpers/makeSame.m b/helpers/makeSame.m old mode 100644 new mode 100755 diff --git a/helpers/makeStim.m b/helpers/makeStim.m old mode 100644 new mode 100755 diff --git a/helpers/makeUnequal.m b/helpers/makeUnequal.m old mode 100644 new mode 100755 diff --git a/helpers/mean2tot.m b/helpers/mean2tot.m old mode 100644 new mode 100755 diff --git a/helpers/pairSpacing.m b/helpers/pairSpacing.m old mode 100644 new mode 100755 diff --git a/helpers/psyPoints.m b/helpers/psyPoints.m old mode 100644 new mode 100755 diff --git a/helpers/setSpacing.m b/helpers/setSpacing.m old mode 100644 new mode 100755 diff --git a/helpers/totOfSet.m b/helpers/totOfSet.m old mode 100644 new mode 100755 diff --git a/tasks/runHet.m b/tasks/runHet.m index c49e411..9017691 100755 --- a/tasks/runHet.m +++ b/tasks/runHet.m @@ -1,14 +1,10 @@ function datafile = runHet(cond,subjCode) % RUNHET Run heterogeneous ensembles experiment -% Initially released Spring 2015 // Comments to Sasen Cain sasen@ucsd.edu -% Updates & task variations at https://github.com/sasen/equneq/ -% datafile = runHet(cond,subjCode) +% Spring 2015 // Comments to Sasen Cain sasen@ucsd.edu % cond (char) : condition 's', 'd', 'm' for same, different, or mixed trials -% subjCode (char(3)) : 3-character code for the subject, in quotes. 'dbg'-> debug mode -% datafile (str) : full path to textfile containing subject's saved results for this block -% Experiment description +%% Experiment description % -% Two ensembles of filled circles, to L and R of fixation. +% Two ensembles of filled circles, to L and R of fixation. % Keypress 2-AFC on which side has greater mean diameter. % Sets may have equal or unequal numbers of circles. assert(nargin==2,'Two arguments, the condition code, and subject code, are required.') @@ -17,7 +13,7 @@ stimfile = 'allStimuli123_6.mat'; load(stimfile) -if ~exist('trials') +if ~exist('trials','var') switch cond case 's' trials = sTr; @@ -40,12 +36,12 @@ subjFiles = what(pathdata); fIndex = find(strncmp(cond,subjFiles.mat,1)); % find the index for this condition else - fprintf(1,'Starting new subject. Stimulus file is: %s',stimfile) - sanityCheck = input('\n Are you happy with this? [y/n]:','s'); - if strcmp(sanityCheck,'n') - disp('OK, maybe you should fix that.') - return - end +% fprintf(1,'Starting new subject. Stimulus file is: %s',stimfile) +% sanityCheck = input('\n Are you happy with this? [y/n]:','s'); +% if strcmp(sanityCheck,'n') +% disp('OK, maybe you should fix that.') +% return +% end mkdir(pathdata); fIndex = []; end @@ -93,7 +89,6 @@ % Stimulus parameters % %%%%%%%%%%%%%%%%%%%%%%% black = [ 0 0 0]; -gray = [128 128 128]; white = [255 255 255]; fixationLength = 10; % length of lines in fixation cross tFixation = 0.500; % 500 ms fixation cross display @@ -110,7 +105,7 @@ maxTrials = 42; % upto 42 trials per block! numTrials = min([ (length(trials)-doneTrials), maxTrials]); % trials to do this time if numTrials==0 - fprintf(1,'\nDone with condition %s.\nPlease inform the experimenter.\n',cond) + fprintf(1,'\nDone with condition %s.\nPlease inform the experimenter.\n',cond) %#ok return end @@ -147,13 +142,16 @@ [xCen, yCen] = RectCenter(winRect); %% Open a half-size offscreen window for pre-drawing Left & Right stimuli -HalfScrRes = [ScrRes(1)/2 ScrRes(2)]; % half-screens split along horizontal side -woff1 = Screen('OpenOffScreenWindow',w,[BGCol 0], [0 0 HalfScrRes]); -woff2 = Screen('OpenOffScreenWindow',w,[BGCol 0], [0 0 HalfScrRes]); +% HalfScrRes = [ScrRes(1)/2 ScrRes(2)]; % half-screens split along horizontal side +% woff1 = Screen('OpenOffScreenWindow',w,[0 0 0 0], [0 0 HalfScrRes]); +% woff2 = Screen('OpenOffScreenWindow',w,[0 0 0 0], [0 0 HalfScrRes]); +originalHSR = [1280/2 800]; % Half of Sasen's original screen size +woff1 = Screen('OpenOffScreenWindow',w,[0 0 0 0], [0 0 originalHSR]); +woff2 = Screen('OpenOffScreenWindow',w,[0 0 0 0], [0 0 originalHSR]); % Display reminder of instructions (need to have shown demo already) % Make them press left key, then right key; that will call the KbCheck/KbName MEX files! -equneq_instructions(w, keymap.l, keymap.r, BGCol, TextColors{1}); +equneq_instructions(w, keymap.l, keymap.r); @@ -162,17 +160,17 @@ for i = doneTrials+1 : doneTrials+numTrials % Draw fixation to indicate the start of the trial - Screen('FillRect', w, BGCol); - DrawFixation(w, fixationLength, xCen, yCen, TextColors{1}); + Screen('FillRect', w, black); + DrawFixation(w, fixationLength, xCen, yCen); [~, tFixOnset] = Screen('Flip', w,[], 1); % dontClear =1 %% Show fixation, mark its onset time % Prepare stimuli on our offscreen half-windows - Screen('FillRect', woff1, BGCol); - Screen('FillRect', woff2, BGCol); - Screen('DrawDots',woff1,trials(i).Lcirs(:,1:2)',trials(i).Lcirs(:,3),TextColors{1},[],1); % 1=cir, 2=circ++ - Screen('DrawDots',woff2,trials(i).Rcirs(:,1:2)',trials(i).Rcirs(:,3),TextColors{1},[],1); + Screen('FillRect', woff1, black); + Screen('FillRect', woff2, black); + Screen('DrawDots',woff1,trials(i).Lcirs(:,1:2)',trials(i).Lcirs(:,3),white,[],1); % 1=cir, 2=circ++ + Screen('DrawDots',woff2,trials(i).Rcirs(:,1:2)',trials(i).Rcirs(:,3),white,[],1); PlaceHalfWindowsLR(w,woff1,woff2,ScrRes); % Put the stimuli on the window - DrawFixation(w, fixationLength, xCen, yCen, TextColors{1}); % Add fixation cross last + DrawFixation(w, fixationLength, xCen, yCen); % Add fixation cross last % Wait til the end of fixation period; then display stimuli. Mark stimulus onset time. [~, tStimulusOnset] = Screen('Flip', w, tFixOnset+tFixation); %%%%%%%%% <========== show stimuli! @@ -181,7 +179,7 @@ % imwrite(curImage,fname,'jpg'); %%% Stimulus offset: Blank screen until response - Screen('FillRect', w, BGCol); + Screen('FillRect', w, black); [~, tStimulusOffset] = Screen('Flip', w, tStimulusOnset+tDisplay, 1); % dontClear =1 % Get 2AFC response keypress. Keep this clean to have tight confidence on RT @@ -220,47 +218,47 @@ end % if keyisdown ITI if ~postTrialStuffDoneYet - % 1. Give real-time feedback in the form of sounds, record accuracy data - if isnan(RTs(i)) % didn't respond in time [we're characterizing response] - PsychPortAudio('FillBuffer', audiohandle,toneToolate); - elseif strcmp( trials(i).trialRightAnswers, meanEqualCode ); % trial has no right/wrong ans - % the means are actually equal. flip a coin to call it right or wrong - % note: accuracy is a lie here, but it saves WHAT WE TOLD THE SUBJECT!! - % FIXME: ideally, this would be standard for all subjects!! - if round(rand) % coinflip = 1; call it right - PsychPortAudio('FillBuffer', audiohandle,toneCorrect); - ACCs(i) = 1; - else % call it wrong - PsychPortAudio('FillBuffer', audiohandle,toneIncorrect); - ACCs(i) = 0; - end % if coin flip - elseif strcmp(choices{i}, keymap.l) || strcmp(choices{i}, keymap.r) % hit a valid key - if strcmp(choices{i}, keymap.(trials(i).trialRightAnswers)) % response was right - PsychPortAudio('FillBuffer', audiohandle,toneCorrect); - ACCs(i) = 1; - else % response was the wrong side - PsychPortAudio('FillBuffer', audiohandle,toneIncorrect); - ACCs(i) = 0; - end %-- if response was right - else % hit an invalid key. Note: ACCs(i) should stay NaN as initialized - PsychPortAudio('FillBuffer', audiohandle,toneToolate); % FIXME: giving too-late feedback ok? - end %-- isnan (characterize response) - PsychPortAudio('Start',audiohandle); % play feedback (program keeps going, i think) - - % 3. Record data for experimental parameters in .mat - expdata.trial(i) = i; - expdata.veridical(i) = trials(i).trialRightAnswers; - expdata.RTs(i) = RTs(i); - expdata.choices{i} = choices{i}; - expdata.afcL(i) = afcL(i); - expdata.ACCs(i) = ACCs(i); - expdata.trialType(i) = trials(i).trialType; - expdata.Lmean(i) = trials(i).Lmean; - expdata.Rmean(i) = trials(i).Rmean; - save(datafile, 'expdata'); - - postTrialStuffDoneYet = 1; % all intertrial business is finished - end %% doing post-trial stuff + % 1. Give real-time feedback in the form of sounds, record accuracy data + if isnan(RTs(i)) % didn't respond in time [we're characterizing response] + PsychPortAudio('FillBuffer', audiohandle,toneToolate); + elseif strcmp( trials(i).trialRightAnswers, meanEqualCode ); % trial has no right/wrong ans + % the means are actually equal. flip a coin to call it right or wrong + % note: accuracy is a lie here, but it saves WHAT WE TOLD THE SUBJECT!! + % FIXME: ideally, this would be standard for all subjects!! + if round(rand) % coinflip = 1; call it right + PsychPortAudio('FillBuffer', audiohandle,toneCorrect); + ACCs(i) = 1; + else % call it wrong + PsychPortAudio('FillBuffer', audiohandle,toneIncorrect); + ACCs(i) = 0; + end % if coin flip + elseif strcmp(choices{i}, keymap.l) || strcmp(choices{i}, keymap.r) % hit a valid key + if strcmp(choices{i}, keymap.(trials(i).trialRightAnswers)) % response was right + PsychPortAudio('FillBuffer', audiohandle,toneCorrect); + ACCs(i) = 1; + else % response was the wrong side + PsychPortAudio('FillBuffer', audiohandle,toneIncorrect); + ACCs(i) = 0; + end %-- if response was right + else % hit an invalid key. Note: ACCs(i) should stay NaN as initialized + PsychPortAudio('FillBuffer', audiohandle,toneToolate); % FIXME: giving too-late feedback ok? + end %-- isnan (characterize response) + PsychPortAudio('Start',audiohandle); % play feedback (program keeps going, i think) + + % 3. Record data for experimental parameters in .mat + expdata.trial(i) = i; + expdata.veridical(i) = trials(i).trialRightAnswers; + expdata.RTs(i) = RTs(i); + expdata.choices{i} = choices{i}; + expdata.afcL(i) = afcL(i); + expdata.ACCs(i) = ACCs(i); + expdata.trialType(i) = trials(i).trialType; + expdata.Lmean(i) = trials(i).Lmean; + expdata.Rmean(i) = trials(i).Rmean; + save(datafile, 'expdata'); + + postTrialStuffDoneYet = 1; % all intertrial business is finished + end %% doing post-trial stuff end %% waiting in ITI end %% main trial loop @@ -269,7 +267,7 @@ % Inform subjects that experiment is over, shutdown everything endDisplay = ['The block is over.\n\n'... 'Please rest now, and restart when ready.']; -DrawFormattedText(w, endDisplay, 'center', 'center', TextColors{1}); +DrawFormattedText(w, endDisplay, 'center', 'center', white); Screen('Flip', w); WaitSecs(2); ShutdownNicely(shutdown); % Close the program