====== Creating experiments in MATLAB using Psychtoolbox 2.0 ====== The Psychophysics toolbox (abbreviated as “Psychtoolbox”) is a set of functions, all located in various subfolders of the [[\\Gall\Programs\User_Scripts\huettel\PsychToolbox\]] directory, which allow you to display many types of stimuli on a computer screen from a MATLAB script. The advantage of this is that the display can be interactive, changing based on subject responses, instead of an invariant movie. You can find the Psychtoolbox web page at [[http://psychtoolbox.org/]]. ===== Adding Psychtoolbox to your path ===== Copy the scripts ‘addpsychtoolbox.m’ and ‘rmpsychtoolbox.m’ from [[\\Gall\Programs\User_Scripts\huettel]] to your user directory (''\\Gall\Users\[username]''). Your user directory is always in your path, so you should be able to simply type addpsychtoolbox or rmpsychtoolbox at the command line to add or remove the correct directories to your path. \\ \\ There is an issue with using the function ‘copyfile’ while Psychtoolbox is added to your path. There is both a MATLAB version as well as a Psychtoolbox version and they’re somewhat incompatible. The problem will crop up whenever you use readmr (which calls the MATLAB copyfile) with Psychtoolbox added. Just remove psychtoolbox (using rmpsychtoolbox) before calling readmr. ===== The SCREEN function ===== This is probably the most important function in Psychtoolbox. It lets you display your stimuli on the screen, draw objects, etc. There are a whole lot of options and subfunctions in it; I will try to go through the commonly used ones. Note that none of these commands will work if you haven’t run addpsychtoolbox. ==== Getting help with SCREEN ==== At the command line, type screen and hit enter. This will list a lot of options and usage information. For help with subfunctions, type screen [subfuncname]? (Yes, include the question mark). This format will also work: screen(‘[subfuncname]?’) ==== Opening on- and off-screen windows ==== An on-screen window is visible on the monitor. An off-screen window exists only in memory and is usually copied to the on-screen window for display (see info about this in the ‘CopyWindow’ section below). === On-screen windows === You only need to open one of these once when you start your script. You will copy all of your stimuli and such to this window. Open it using the following command: [window,screenRect]=SCREEN(0,’OpenWindow’,[],[],32) The zero tells it to open on screen # zero, which is the monitor you’re currently using. The first set of brackets indicates color, and leaving it empty like this defaults to white. The second set of brackets indicates the size of the window(its “rect”), and leaving it empty defaults to the current screen’s size. 32 is the pixel size. So, this command opens a white on screen window (the whole size of the monitor) and creates two important variables. The variable ‘window’ is what you have now named your on-screen window and is where you will copy other screens and such. The variable ‘screenRect’ is a 1x4 matrix that indicates the pixel locations of the upper-left and lower-right corners of ‘window’. So, if you’re at 1024x768 resolution, screenRect = [0 0 1024 768]. For more information on rects, see below. === Off-screen windows === Psychtoolbox is able to copy from off- to on-screen windows very quickly. This means that you can preload a pretty large number of windows off-screen, then just quickly copy them to the on-screen window. Preloading like this will save time, especially if you’re doing things like putting jpegs on the screen. \\ To open an off-screen window, use something similar to the following example: \\ **blank_screen = SCREEN(window,’OpenOffscreenWindow’,[0 0 0],screenRect);** \\ This line will make an off-screen window called blank_screen. This screen is black, as indicated by the RGB color code [0 0 0] (see section on this below), and fills the entire size of the screen, as indicated by the use of screenRect. ==== Closing SCREEN windows ==== To close only one window type \\ **SCREEN([windowname],’Close’)** \\ \\ And to close all windows type: \\ **SCREEN(‘CloseAll’)** \\ ==== An explanation of rects ==== * Psychtoolbox uses “rects” (short for rectangles) to determine where to place things on your screens. They are always 1x4 matrices in the following format: [upperleft_x upperleft_y lowerright_x lowerright_y]. The values correspond to screen pixels. So, for example, a rect defined as [0 0 400 600] would cover the left half of a 800x600 window. * //Clarification//: X coordinates increase as you go from Left to Right on your monitor. Y coordinates increase as you go from Top to Bottom on your screen. * There are a number of helper functions for manipulating rects, although we seldom use them. An example of this is the CenterRect function, which is used to center one rect inside another. This could be useful for placing something small in the center of the screen, for example. To find out more about these type **help psychrects.** ==== An explanation of RGB color codes ==== Psychtoolbox uses standard RGB (red, green, blue) codes to indicate on-screen colors. What you’re doing is indicating the individual amounts of red, green, and blue you want to display. Values can range from 0 to 255, so [0 0 0] is black and [255 255 255] is white. Blue would then be [0 0 255], etc. [[http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html | Here's]] a random website I found that lists a ton of different colors and their RGB codes ===== Commonly used SCREEN subfunctions ===== There are a ton of these, but I’ll try to get through the common ones. Note that most of them have very similar syntax, so if you understand one, you’ll understand nearly all of them. ==== CopyWindow ==== As mentioned previously, you’ll use this a lot. If you’re copying full-screen sized windows to the on-screen window it’s really easy to use. If you’re copying smaller rects to the on-screen window it can get tricky, so I’ll try to explain. === Copying full-screen windows === Say, for example, that you’ve created a full size off-screen window with a circle in the center that represents your stimulus. This window will be called circle_screen , and to display it to the subject, you would use copywindow as such: \\ **SCREEN(‘CopyWindow’,circle_screen,window,screenRect,screenRect)** \\ This tells Psychtoolbox that your source screen is the off-screen ‘circle_screen’ and that you want to copy it to the on-screen ‘window’ (the destination). The two screenRect arguments indicate that you’re copying the information located in screenRect in your source to screenRect in your destination. === Copying smaller windows to the on-screen window === Ok, this is kinda confusing. I’ll go through an example to illustrate. The difficulty arises when you define an off-screen window that is smaller than screenRect. - Say you want to display different colored squares in the center of your screen because each color means something different to the subject. However, you have some instructions elsewhere on the screen that need to stay put while the color of the square changes. You could create a ton of different full-sized off-screen windows for each combination of instructions and square color, and then just copy the whole window. But maybe you have 20 different types of instructions and 5 colors…..that’s a lot of windows. So, it would be easier if you could just create 5 different off screen windows for the 5 different-colored squares and worry about the instructions separately. Got all that? Good. - So, first you’d create the 5 off-screen windows. Say you define the blue square window as such (we’ll say the squares are 100x100 pixels in size): \\ **blue_square = SCREEN(window,’OpenOffscreenwindow’,[0 0 0],[350 250 450 350]);\\ SCREEN(blue_square,’FillRect’,[0 0 255], [350 250 450 350]); ** \\ This puts a blue square in a rectangle defined as [350 250 450 350], which would be 100 pixels on a side in the center of an 800x600 screen. - You would think then, that when you go to copy this to the on-screen window, that you would define the source rect and destination rect as the same [350 250 450 350]. Unfortunately, you would be wrong. If you define an off-screen window as something smaller than screenRect, no matter where you define it, psychtoolbox will always act like you placed it in the upper-left hand corner at 0, 0. So, the correct way to copy this to the center of your on-screen window would be: \\ **SCREEN(‘CopyWindow’,blue_square,window,[0 0 100 100],[350 250 450 350]); **\\ - So, to eliminate (I hope) the confusion about this sort of thing, here’s the easiest way to go about this. No matter where you want your object to eventually end up on the display, ALWAYS define smaller-than-screenRect-sized off-screen windows in the upper left-hand corner. So, the two lines of code from above would change to this:\\ **blue_square = SCREEN(window,’OpenOffscreenwindow’,[0 0 0],[0 0 100 100]);\\ SCREEN(blue_square,’FillRect’,[0 0 255], [0 0 100 100]);** \\ This way there is consistency in all of your locations. Copying it over to the on-screen window is still the same as above, except now it makes conceptual sense…..you defined something in the rect [0 0 100 100], then you copy from that same rect to a different one of the same size ([350 250 450 350]). \\ **SCREEN(‘CopyWindow’,blue_square,window,[0 0 100 100],[350 250 450 350]); ** \\ Voila! ==== DrawText ==== This function draws text on whatever window you define. It’s pretty quick, so if you need to write stuff on the screen in real time it’s not a big deal (i.e. you don’t need to use CopyWindow). Call it like this: SCREEN(window,’DrawText’,’This is the string you want to display’,xstart,ystart,[R G B]) The xstart and ystart variables are the starting coordinates for the “pen”. It is the lower left hand corner of the text you are writing. == TextSize == Changes the size of the text written by DrawText. For example, change to 40pt font like this: \\ **SCREEN(window,’TextSize’,40)** == TextFont == Changes the font. Example, changing to Arial. \\ **SCREEN(window,’TextFont’,’Arial’)** \\ == Centering text using TextWidth == - Many times you’ll want to center your text in the middle of the screen. This is most easily done by taking advantage of the TextWidth function, which calculates the length of a given string. Example: \\ **widthofstring = SCREEN(window,’TextWidth’,’How long is this?’) ** \\ The variable widthofstring will now contain the length of the string “How long is this?” - Draw it in the middle of the screen. You can determine the center of the width of your screen using your screenRect like this: \\ **centerWidth = (screenRect(3)-screenRect(1))/2; ** \\ This is useful for creating a script that is interchangeable between resolutions. To draw it:\\ **SCREEN(window,’DrawText’,’How long is this?’,centerWidth-widthofstring/2,ystart,[R G B])** ==== FillRect ==== * This function draws solid rectangles (that is, they are “filled-in” with the color you specify. See the blue square example above in the CopyWindow section. * Note that FrameRect operates almost identically and draws rectangles that are NOT filled-in. FrameRect includes an extra argument or two to define “pen-width”….how wide the border of your rectangle will be. ==== FillOval ==== Similar to FillRect but draws ovals/circles. The shape (ovular or circular) is defined by the shape of the rectangle you specify. If the rectangle is a square, you get a circle. If your rectangle is actually rectangular you get an oval. Example of a red circle with radius 50 in the upper left corner: \\ **SCREEN(window,’FillOval’,[255 0 0],[0 0 100 100]);** \\ FrameOval operates in the same fashion as FrameRect. ==== FillArc ==== Kind of like FillOval in that it draws ovals and circles, but you have to specify the angles through which to draw. That is, it’s easy to draw portions of circles and ovals. Example of the top half of a circle in the upper left-hand corner of the screen: \\ ** SCREEN(window,’FillArc’,[255 0 0],[0 0 100 100],-90,180) ** \\ The first angle argument (-90) specifies the starting angle, with 0 being vertical. So, -90 is horizontal to the left. The second angle argument (180) specifies the angle sweep to draw. Thus, this will draw 180 degrees (half) of a circle.\\ There is also a FrameArc function that is similar. ==== Dealing with Images ==== Often times you’ll want to display .jpg images on the screen. Note that jpg’s work the best in our experience. Other formats like .bmp probably work ok, but GIF’s are definitely screwy. Avoid them or convert them to jpg’s (I’ll explain how to do that below). === Reading into MATLAB using imread === * This function loads your graphic into a MATLAB matrix. Use it like this: \\ **[img] = imread(‘filename.jpg’,’jpg’);**\\ You now have a variable called img that contains the information. * If you must deal with GIF’s, load them as such:\\ **[img,map] = imread(‘filename.gif’,’gif’);** \\ === Saving images using imwrite === - The most common reason to save images is if you need screenshots of your experiment for a poster or something (use in conjunction with GetImage; see below). Example: \\ **imwrite(img,’screenshot1.jpg’,’jpg’);**\\ This will write the image data in the variable img to a jpg file called screenshot1. - To convert a GIF into a jpg:\\ **imwrite(img,map,’newjpgimage.jpg’,’jpg’);**\\ === Putting images on the screen using PutImage === This is another SCREEN subfunction. Pretty easy to use: \\ **SCREEN(window,’PutImage’,img,[0 0 200 200])** \\ This would put the image data from img into the indicated rect, [0 0 200 200]. If the image is a different size than the specified rect it will be scaled to fit. === Grabbing stuff on the screen using GetImage === Couple this with the imwrite function above to grab and save screenshots. Example: \\ **img = SCREEN(window,’GetImage’,screenRect);** \\ This would grab the whole screen and save the image data in the variable img. You can also easily grab smaller rects if you wish. ===== Experiment Timing ===== ==== Functions to use for timing ==== Psychtoolbox has a few built-in functions for keeping track of experiment timing. == GetSecs == Gets the time in seconds since the computer started, with 4-decimal place precision. Thus, you will generally use it by first creating some sort of start time variable and using it as a reference. For example, you might want to record subject response time, so you could create a variable startsecs = getsecs right before they are prompted for a button press. Then, when the subject actually presses the button, you could record a new variable, responsetime = getsecs-startsecs. == WaitSecs == Waits for the number of seconds specified. This is good for when you need some sort of delay between two different aspects of your display. For example, you might show a box indicating the selection a subject made, and then two seconds later indicate that they won or lost money. You could use the command waitsecs(2); to wait for those two seconds. ==== Time locking to TR ==== This is just a snippet of code that will hold the program in a loop until the start of the next available TR. Assume in this example that the variable startsecs references the time at very beginning of the experiment, and that a variable TR has been defined as well (in seconds). Specifically this will wait until the getsecs clock is within .001 seconds of the next TR onset. while mod((getsecs-startsecs),TR) > .001 end ==== Overall Timing ==== For your experiment you will have a set run time (set time that the scanner will be collecting images) based upon the number of trials and such that you wish to accomplish per run. You will need to synch your script with this timing. This means it won’t be sufficient to just set up a for loop for the number of trials and let the thing go. Instead you will need a while loop that incorporates scan run time. \\ This while loop set up will be in the following general form: currenttrialnum = 0; while (getsecs-startsecs)