Programming with PsychoPy, 8 Stimuli

Visual

Computer screens are updated according to their refresh rate. What happens then is that the whole screen is redrawn line by line all the time. For example, with a refresh rate of 60 Hz, the screen is redrawn 60 times per second (1000 ms) and the duration it takes to redraw it line by line is 16.67 ms (1000/60). The following video shows what it looks like:

When attempting to redraw the screen while it is currently already being updated (the lines are drawn) the result might lead to artifacts, since the update occurs immediately, leading to parts of both, the new and and the old screen content, being visible. The following image shows what happens if an image is updated twice while it is drawn to the screen:

Tear

What is even worse is that you will never know in which phase of the redrawing the new redraw was started. Thus, you cannot be sure when exactly the new content is fully visible on screen. The first step towards getting around this problem is to synchronize the actual redraw to the vertical retrace of the screen. This means that a change in content will never happen immediately, but always only when the retrace is at the top left position. When synchronizing to the vertical retrace, the graphics card is told to update the screen the next time it starts redrawing the first line. While this will solve the problem of artifacts, you will still face the problem of not knowing when exactly something was visible on the screen, since the graphic card handles this synchronization itself in the background.

PsychoPy solves this problem with the .flip() function. It allows waiting for the vertical retrace to actually happen before proceeding with the code that tells the graphic card to update the screen (this is also known as blocking on the vertical retrace). This means that whenever something should be presented on screen, no matter in which line the redraw is at this point in time, the graphic card will wait for the redraw to be in the first line and then present the stimulus. Since the code is blocking, the time presentation reports the stimulus to be presented on screen will always be the time when the redraw is starting at the first line.

It is important to switch off power saving schemes of your graphic card"s driver, and use a special graphical card provided by your technical support!

Audio

Warning, this example does not work on ISC computers. Try it in the lab or on your private computer

To play back audio you have to create an sound object from the sound module:

#!/usr/bin/env python
from psychopy import core, sound
s = sound.Sound(value="C", secs=0.5) 

s.play()
core.wait(4.0)

core.quit()

The play() function of auditory stimuli will return immediately. Therefore the experiment has to do a bit of waiting after calling the play function. You can also ask for input or do other things after calling the play function. Just make sure the experiment does not end there, because then you will not hear the sound.

Since the audio stream has to be sent to the hardware, there will still be a delay before the audio can be heard. Unfortunately, the latency of the sound onset is not 0 ms. However, it is assumed to be relatively stable over time. Tests results around 5 ms. And again use a special sound card provided by your technical support!

The sound object has many more options. It can play a tone, a complete stereo waveform or audio files.

Visualizing your experiment: A time line

In order to keep track of the desired timing of your experiment, it might be useful to visualize it by drawing a time line. A time line gives a nice overview how your experiment is build up and when and what has to happen. It is advised to draw a timeline of the experiment before you start to program. In this manner you can decompose your program into event pieces and start programming these step by step (or bit by bit ;-).

Timeline

In this example you start with creating a delay. Then you prepare your picture. When a button is pressed, the picture occurs on your screen. Programming done!

Assignment: time line

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from psychopy import core, visual, event
import csv, random, time

## Setup Section
win = visual.Window([800,600], fullscr=False, monitor="testMonitor", units="cm")

# turn the text strings into stimuli
textStimuli = []
for iTrial in range(10):
	# append this stimulus to the list of prepared stimuli
	text = "number: {}".format(iTrial)
	textStimuli.append(visual.TextStim(win, text=text)) 

# make a question stimulus
questionStimulus = visual.TextStim(win, text="Was this number larger than 4?")

# fixation cross
fixation = visual.ShapeStim(win, 
	vertices=((0, -0.5), (0, 0.5), (0,0), (-0.5,0), (0.5, 0)),
	lineWidth=5,
	closeShape=False,
	lineColor="white"
)

# open data output file
datafile = open("/tmp/datafile.csv", "wb")
# connect it with a csv writer
writer = csv.writer(datafile, delimiter=";")
# create output file header
writer.writerow([
	"number", 
	"response", 
	"responseTime",
	])

 
## Experiment Section
n = len(textStimuli)
for i in random.sample(range(n), n):
	# present fixation
	fixation.draw()
	win.flip()
	core.wait(0.980) # note how the real time will be very close to a multiple of the refresh time
	
	# present stimulus text and wait a maximum of 2 second for a response
	textStimuli[i].draw()
	win.flip()
	textTime = time.time()
	wait = random.uniform(0.100, 0.500)
	core.wait(wait)

	# ask wheter the number was larger than 4
	questionStimulus.draw()
	win.flip()
	key = event.waitKeys(2.000, ["y","n", "escape"])
	responseTime = time.time()

	# write result to data file
	if key==None:
		key=[]
		key.append("no key")
		
	writer.writerow([
		i, 
		key[0], 
		"{:.3f}".format(responseTime - textTime),
		])
	
	if key[0]=="escape":
		break
datafile.close()

## Closing Section
win.close()
core.quit()

Continue with the next lesson