Hi,
I would like some advice about the best pratice to present a stimulus during X ms. I have used 2 methods in the past, and each seems to have advantages and drawbacks. In addition, PsychoPy has evolved considerably during the past 2 years, and there might be more efficient methods.
Imagine that we want to present a stimulus during 100 ms.
Method 1: present the stimulus during X frames
framerate = win.getActualFrameRate()
frameDur = 1.0 /round(framerate)
resp = keyboard.Keyboard()
Nframes_100 = int(round(.1/frameDur))
for i in range (Nframes_100):
stim.draw()
win.flip()
Advantage: precision of presentation duration
Drawback: if you loose some frames for whatever reason, timing can be bad.
Method 2: determine presentation duration with a clock:
resp = keyboard.Keyboard()
win.callOnFlip(resp.clock.reset)
win.callOnFlip(resp.clearEvents, eventType='keyboard')
check = 0
While True:
if check == 1:
t = resp.clock.getTime()
if t >= .1:
break
stim.draw()
win.flip()
if check == 0:
check =1
Advantage : can maintain a reasonable timing, even if you drop frames
Would you then advise to use Method 2 ?
Is there a way to refine the above code for Method 2 to increase the precision of presentation duration ?
Thanks for the advice.
Hi, yes, in most cases it is probably best now to use the timing approach, as it is more robust to frame-dropping issues. A good guide can be to look at how the latest version fo PsychoPy Builder (>= 3.2.0) controls the timing of stimuli. In particular, search for tThisFlip, the estimated time of the next screen refresh, given by win.getFutureFlipTime(), and frameTolerance.
Frame counting can still be a useful approach in certain situations, for example if you want to guarantee that a stimulus (especially a very brief one) is actually displayed, despite something that may have intruded on keeping up with the overall absolute timeline.
@Michael I just studied the code generated by the Builder (> 3.2). Really cool. A few comments:
Since the win.getFutureFlipTime() is computed as win._lastFrameTime + refreshInterval, it looks like this approach is not robust to frame-dropping issues. If you drop a frame, the win.getFutureFlipTime() estimate will be inaccurate right ? I am worried that this approach is essentially similar to the traditional frame counting approach. Why not using a simple timing approach like:
resp = keyboard.Keyboard()
win.callOnFlip(resp.clock.reset)
win.callOnFlip(resp.clearEvents, eventType='keyboard')
check = 0
framerate = win.getActualFrameRate()
frameDur = 1.0 /round(framerate)
While True:
if check == 1:
t = resp.clock.getTime()
if t > (.1-frameDur/2.):
break
stim.draw()
win.flip()
if check == 0:
check =1