function LOGFILE = presenter(inputFile, TR, operationMode, keyboardID) % function LOGFILE = presenter(inputFile, TR, operationMode, keyboardID) % % inputFile - Text file containing presentation schedule % TR - TR used in the experiment % operationMode - 0 operate using equal sign symbol from scanner % 1 operate using internal clock, % keyboardID - Use PsychHIDTest to identify the keyboard or input % device used to trigger the script % % Created 11-16-2006 Itamar Kahn % Revised 11-16-2006 Itamar Kahn try if nargin < 4, fprintf(1,'\nNot enough input arguments. Aborting\n\nSee Usage:\n'); help presenter return; end % ===================================================================== % Initalize PMD1208FS device=initPMD1208FS; if isnumeric(device) && device < 0, fprintf(1,'Not sending output; PMD1208FS Device not found\n'); FastDaqDout = inline('','device', 'port', 'data'); else fprintf(1,'Initalizing PMD1208FS for output\n'); device = device.index; err=DaqDConfigPort(device,0,0); % err = DaqDConfigPort(device,port,direction) % port = 0 direction = 0 FastDaqDout = inline('PsychHID(''SetReport'', device, 2, hex2dec(''04''), uint8([0 port data]))','device', 'port', 'data'); end % ===================================================================== % Set operation mode (triggered by internal clock or by an external % device (e.g., MRI) if operationMode nextTrialFunc = 'while GetSecs - trialStart < trialDuration(ii-1), end; trialStart = trialStart + trialDuration(ii-1);'; fprintf(1,'\nUsing internal clock to trigger trials\n'); else nextTrialFunc = 'while GetSecs - trialStart < trialDuration(ii-1)-.1, end;while 1, [keyIsDown,secs,keyCode] = PsychHID(''KbCheck'',keyboardID);if keyCode(46),break;end,end;trialStart = GetSecs;'; fprintf(1,'\nUsing external USB device to trigger trials\n'); end global LOGFILE; LOGFILE = []; start_dir = pwd; % ===================================================================== % Open file containing presentation schedule fid = fopen(inputFile, 'r'); if (fid == -1), fprintf('\nCould not find the file %s. Aborting\n',inputFile); return; end imageNames = {}; ii=1; rotateString = '|/-\|'; fprintf(1,'Loading experiment schedule and images '); while 1 currTrialDuration = fscanf(fid, '%f',1); % Read trial duration (in TRs) if isempty(currTrialDuration) break; end trialDuration(ii) = currTrialDuration * TR; %trial duration is in seconds imageNames{ii} = fscanf(fid, '%s', 1); % Image name intervalOne(ii) = fscanf(fid, '%f',1); % Image presentation duration intervalTwo(ii) = fscanf(fid, '%f',1); % Red Fixation duration intervalThree(ii) = fscanf(fid, '%f',1); % White Fixation duration (ISI) eventCode{ii} = fscanf(fid, '%s',1); % Additional Information (not used for presentation) ii = ii + 1; if (mod(ii,5) == 0 ), fprintf(1,'\b%c',rotateString(mod(ii/5,5)+1)); end; end fprintf(1,'\b Done.\n'); imageNamesUnique = unique(sort(imageNames)); % ===================================================================== % Initalize PTB OS X % Disable alert messages related to multiple displays Screen('Preference','VisualDebugLevel',1); commandwindow; % moves commandwindow up front, handy in case of a break % This script calls Psychtoolbox commands available only in OpenGL- % based versions of the Psychtoolbox. The Psychtoolbox command % AssertPsychOpenGL will issue an error message if someone tries to % execute this script on a computer without an OpenGL Psychtoolbox AssertOpenGL; % Get the list of screens and choose the one with the highest screen % number. Screen 0 is, by definition, the display with the menu bar. % Often when two monitors are connected the one without the menu bar is % used as the stimulus display. Choosing the display with the highest % dislay number is a best guess about where you want the stimulus % displayed. screenNumber=max(Screen('Screens')); % Open a double buffered fullscreen window and draw a black background % and front and back buffers. [w, wRect]=Screen('OpenWindow',screenNumber, 0,[],32,2); % Find the color values which correspond to white and black. Though on % OS X we currently only support true color and thus, for scalar color % arguments, black is always 0 and white 255, this rule is not true on % other platforms will not remain true on OS X after we add other color % depth modes. white=WhiteIndex(screenNumber); black=BlackIndex(screenNumber); gray=GrayIndex(screenNumber); % returns the mean gray value of screen % ===================================================================== % Import images, convert and stor in MATLAB matrix, into a % Psychtoolbox OpenGL texture using 'MakeTexture' fprintf(1,'Loading images '); for ii=1:length(imageNamesUnique), fprintf(1,'\b%c',rotateString(mod(ii,4)+1)); imdata{ii}=imread(['stimuli/' imageNamesUnique{ii}], 'jpg'); % crop image if it is larger then screen size. There's no image % scaling in maketexture [iy, ix, id]=size(imdata{ii}); [wW, wH]=WindowSize(w); if ix>wW | iy>wH disp('Image size exceeds screen size'); disp('Image will be cropped'); end if ix>wW cl=round((ix-wW)/2); cr=(ix-wW)-cl; else cl=0; cr=0; end if iy>wH ct=round((iy-wH)/2); cb=(iy-wH)-ct; else ct=0; cb=0; end imagetex{ii}=Screen('MakeTexture', w, imdata{ii}(1+ct:iy-cb, 1+cl:ix-cr,:)); tRect=Screen('Rect', imagetex{ii}); end fprintf(1,'\b Done.\n'); [ctRect, dx, dy]=CenterRect(tRect, wRect); % Now, point to actual textures with the image structures for ii=1:length(trialDuration), jj = strmatch(imageNames{ii},imageNamesUnique,'exact'); imageTexture(ii) = imagetex{jj}; if ~strcmp(imageNames{ii}, 'FIXATION') & trialDuration(ii) > 0, imageTrial(ii) = 1; else imageTrial(ii) = 0; end end % Generate textures for Fixation, Response Interval Fixation, and % Instruction Screens jj = strmatch('FIXATION',imageNamesUnique,'exact'); fixationScreen = imagetex{jj}; jj = strmatch('RESPONSEFIXATION', imageNamesUnique,'exact'); responseFixationScreen = imagetex{jj}; jj = strmatch('InstScreen', imageNamesUnique); if ~isempty(jj), instructionScreen = imagetex{jj}; Screen('DrawTexture', w, instructionScreen); else TextSize = 32; [ScreenCenter(1) ScreenCenter(2)] = WindowCenter(w); [oldFontName,oldFontNumber]=Screen('TextFont', w ,'Courier'); oldTextSize=Screen('TextSize', w, TextSize); oldTextColor=Screen('TextColor', w , [255 255 255 255]); [rect] = CenterRectOnPoint([0 0 round(.60 * TextSize * 13) round(TextSize * 1.2)],ScreenCenter(1), ScreenCenter(2)); Screen('DrawText', w, 'Get Ready ...', rect(1), rect(2)) end Screen('Flip',w); % ===================================================================== % Start Experiment % We are now ready to start the experiment. Wait for trigger. Screen('HideCursorHelper',0); while 1 [keyIsDown,secs,keyCode] = KbCheck(keyboardID); if (keyCode(46) | keyCode(98) | keyCode(39) | keyCode(44)) break; end end trialStart = GetSecs; expStart = trialStart; Screen('DrawTexture', w, fixationScreen); % The experiment must start with a Fixation Screen Screen('Flip', w); fprintf(1,'%.3f\t%s\t%3.1f\n',trialStart - expStart, imageNames{ii}, trialDuration(ii)); RT_imageResp = 'NoResp'; RT_image = -Inf; LOGFILE(1).eventCode = []; LOGFILE(1).RT = -Inf; LOGFILE(1).trialTime = trialStart - expStart; LOGFILE(1).trialStructure = [intervalOne(ii) intervalTwo(ii) intervalThree(ii)]; LOGFILE(1).imageName = imageNames{ii}; for ii=2:length(trialDuration), eval(nextTrialFunc); % Busy wait loop before starting the next Trial if imageTrial(ii), % Start a new event RT = 0; trialOnset = GetSecs; Screen('DrawTexture', w, imageTexture(ii)); % Interval One Screen('Flip', w); while GetSecs - trialOnset < intervalOne(ii) [keyIsDown,secs,keyCode] = KbCheck(keyboardID); if keyIsDown & ~RT & (~keyCode(46) | length(find(keyCode)) > 1), keyCode(46)=0; RT_image = GetSecs - trialOnset; RT_imageResp = num2str(find(keyCode)); RT = 1; end end lastIntervalEnded = GetSecs; Screen('DrawTexture', w, responseFixationScreen); % Interval Two Screen('Flip', w); while GetSecs - lastIntervalEnded < intervalTwo(ii) [keyIsDown,secs,keyCode] = KbCheck(keyboardID); if keyIsDown & ~RT & (~keyCode(46) | length(find(keyCode)) > 1), keyCode(46)=0; RT_image = GetSecs - trialOnset; RT_imageResp = num2str(find(keyCode)); RT = 1; end end lastIntervalEnded = GetSecs; Screen('DrawTexture', w, fixationScreen); % Interval Three (ISI) Screen('Flip', w); while GetSecs - lastIntervalEnded < intervalThree(ii); [keyIsDown,secs,keyCode] = KbCheck(keyboardID); if keyIsDown & ~RT & (~keyCode(46) | length(find(keyCode)) > 1), keyCode(46)=0; RT_image = GetSecs - trialOnset; RT_imageResp = num2str(find(keyCode)); RT = 1; end end lastIntervalEnded = GetSecs; LOGFILE(ii).eventCode = eventCode{ii}; LOGFILE(ii).RT = RT_image; LOGFILE(ii).RTresp = RT_imageResp; LOGFILE(ii).trialTime = trialStart - expStart; LOGFILE(ii).trialStructure = [intervalOne(ii) intervalTwo(ii) intervalThree(ii)]; LOGFILE(ii).imageName = imageNames{ii}; fprintf(1,'%.3f\t%s %s %s(%.3f)\t%3.1f\n',trialStart - expStart, eventCode{ii},imageNames{ii}, RT_imageResp, RT_image, trialDuration(ii)); RT_imageResp = 'NoResp'; RT_image = -Inf; else % Start a Fixation Event Screen('DrawTexture', w, fixationScreen); Screen('Flip', w); LOGFILE(ii).eventCode = []; LOGFILE(ii).RT = -Inf; LOGFILE(ii).RTresp = []; LOGFILE(ii).trialTime = trialStart - expStart; LOGFILE(ii).trialStructure = [intervalOne(ii) intervalTwo(ii) intervalThree(ii)]; LOGFILE(ii).imageName = imageNames{ii}; fprintf(1,'%.3f\t%s\n',trialStart - expStart, imageNames{ii}); end end; % For the last event use internal clock regardless of operation mode while GetSecs - trialStart < trialDuration(ii), end; fprintf(1,'%.3f\tExperiment ended\n',GetSecs - expStart); % ===================================================================== % Experiment ended. PTB3 cleanup and save LOGFILE % The same commands which closes onscreen and offscreen windows also % closes textures. Screen('CloseAll'); ShowCursor; Priority(0); % Set log filename and save responses currdate = strrep(datestr(now,29),'-',''); currdate = currdate(3:end); tmp1 = strfind(inputFile,'/'); tmp2 = strfind(inputFile,'.'); unix('mkdir -p subjects'); fname = ['subjects/' currdate '_' inputFile(tmp1+1:tmp2-1) '.mat']; if exist(fname,'file'), currdate = strrep(datestr(now,30),'T','_'); currdate = currdate(3:end); fname = ['subjects/' currdate '_' inputFile(tmp1+1:tmp2-1) '.mat']; end save(fname, 'LOGFILE'); fprintf(1, 'Logfile %s saved\n',fname); catch % ===================================================================== % An error has occurred. PTB3 cleanup and save LOGFILE % The "catch" section executes in case of an error in the "try" section % above. Importantly, it closes the onscreen window if its open. Screen('CloseAll'); ShowCursor; Priority(0); rethrow(lasterror); % Set log filename and save responses currdate = strrep(datestr(now,29),'-',''); currdate = currdate(3:end); tmp1 = strfind(inputFile,'/'); tmp2 = strfind(inputFile,'.'); unix('mkdir -p subjects'); fname = ['subjects/' currdate '_' inputFile(tmp1+1:tmp2-1) '.mat']; if exist(fname,'file'), currdate = strrep(datestr(now,30),'T','_T'); currdate = currdate(3:end); fname = ['subjects/' currdate '_' inputFile(tmp1+1:tmp2-1) '.mat']; end save(fname, 'LOGFILE'); fprintf(1, 'Logfile %s saved\n',fname); end %try..catch.. fprintf(1, 'presenter finished\n\n'); commandwindow; return; % ===================================================================== % DAQ trigger output operations (MEG/EEG/TMS) function d=initPMD1208FS(); daq=DaqDeviceIndex; switch length(daq) case 0, fprintf('Sorry. Couldn''t find a PMD-1208FS box connected to your computer.\n'); d = -1; return; case 1, fprintf('You have a PMD-1208FS daq: \n'); case 2, fprintf('You have two PMD-1208FS daqs: \n'); otherwise, fprintf('You have %d PMD-1208FS daqs: \n',length(daq)); end devices=PsychHID('Devices'); for i=1:length(daq) d=devices(daq(i)); fprintf('device %d, serialNumber %s\n',d.index,d.serialNumber); end function err=DaqDOut(device,port,data) % err=DaqDOut(device,port,data) % PMD-1208FS: Write digital port. This command writes data to the DIO port % bits that are configured as outputs. % "device" is a small integer, the array index specifying which HID % device in the array returned by PsychHID('Devices') is interface 0 % of the desired PMD-1208FS box. % "port" 0 = port A, 1 = port B % "data" 8-bit value, 0 to 255. % See also Daq, DaqPins, TestDaq, TestPsychHid. % 4/15/05 dgp Wrote it. if ~ismember(port,0:1) error('"port" must be 0 or 1.'); end if ~ismember(data,0:255) error('"data" must be in range 0:255.'); end err=PsychHID('SetReport',device,2,hex2dec('04'),uint8([0 port data])); % DOut if err.n fprintf('DaqDOut error 0x%s. %s: %s\n',hexstr(err.n),err.name,err.description); end function err=DaqDConfigPort(device,port,direction) % err=DaqDConfigPort(device,port,direction) % PMD-1208FS: Configure digital port. This command sets the direction of % the DIO port to input or output. % "device" is a small integer, the array index specifying which HID % device in the array returned by PsychHID('Devices') is interface 0 % of the desired PMD-1208FS box. % "port" 0 = port A, 1 = port B % "direction" 0 = output, 1 = input % See also Daq, TestDaq, TestPsychHid. % 4/15/05 dgp Wrote it. if ~ismember(port,0:1) error('"port" must be 0 or 1.'); end if ~ismember(direction,0:1) error('"direction" must be 0 (out) or 1 (in).'); end report=uint8([0 port direction]); err=PsychHID('SetReport',device,2,hex2dec('01'),report); % Configure digital port. if err.n fprintf('DaqDConfigPort error 0x%s. %s: %s\n',hexstr(err.n),err.name,err.description); end return