Lagging issues with conditional statements in python during time-sensitive trials (Windows 10, Integrated Intel GPU, Psychopy v1.9)
I have recently done an experiment, which included a presentation of spoken digits to participants (ppt) - a concurrent load task adopted from Wills et al. (2001). Here is a brief description:
At the beginning of the trial, participants needed to remember a random list of six digits presented before the main task. After feedback on the main task, they received a probe (a random number from the list of six digits), at which point they need to enter the number that followed the probe in the list from the beginning of the trial.
In order to implement the task, I repurposed some python code from previous student experiment, for the PsychoPy Builder, that seemed to have done a reliable-enough job in presenting participants the spoken digits. Until my experiment crashed with the error message:
138.2088 WARNING Couldn't measure a consistent frame rate. - Is your graphics card set to sync to vertical blank? - Are you running other processes on your computer? 138.5821 WARNING t of last frame was 31.24ms (=1/32) 138.6133 WARNING t of last frame was 31.24ms (=1/32) 138.6444 WARNING t of last frame was 31.14ms (=1/32) 138.6757 WARNING t of last frame was 31.29ms (=1/31) 138.7085 WARNING Multiple dropped frames have occurred - I'll stop bothering you about them! 139.5413 WARNING psychopy.sound.backend_pyo.init could not find microphone hardware; recording not available Traceback (most recent call last): File "C:\PATH\TO\EXPERIMENT\YOUREXPERIMENT_lastrun.py", line 1813, in <module> cur_num = num_list[num_count] IndexError: list index out of range
This was the code I added to the Routine in the Builder View. At the time it did not seem that problematic. It worked on most machines, although they were all running Linux. There was no error message while running the pilot experiment (although with limited number of participants).
Beginning of Routine
from numpy import arange import random full_list = range(1, 9) shuffle(full_list) num_list = full_list[0:6] load_timer = core.Clock() s_file = '1.wav' cur_num = 0 num_count = 0 p_index = randint(0,5) probe = num_list[p_index] ans = num_list[p_index+1]
if load_timer.getTime() > 0.33: load_timer.reset() cur_num = num_list[num_count] s_file = str(cur_num) + '.wav' load_sound = sound.Sound(s_file,secs=-1) load_sound.setVolume(0.5) load_sound.play() num_count = num_count + 1
After consulting with some fellow programmers, we managed to articulate
the problem. The
if statement is executed at every 330 ms in a Routine
lasting for 2310 ms, while Each Frame executes any code that is placed
there at every 10 ms.
If you are wondering why: the media files are 330 ms long and the first starts playing from 330 ms, so (1 + 6) * 330ms = 2310 ms, therefore 2310 ms.
It picks out a member of a vector with 6 elements that is randomised at
the beginning of each trial. In all cases, the error message popped up
after after all the spoken digits were presented. If some other
component loads slower compared to the
core.Clock() - when the
computer is lagging behind - Psychopy will execute the code for the
8th time. As a result, the program looks for
num_list, whereas the
last member is
num_list (python starts counting from 0), therefore
the message: out of bounds. It seems that it only occurs on machines
running Windows with an integrated Intel graphics. There is no fixing
it, unless you change the OS.
So I decided to write a code that does the exact same job without an if statement running at every 10 ms. So the presentations of the spoken digits are not conditional in a programming sense.
Here is my quick solution:
from numpy import arange import random # Create a randomised list of numbers full_list = range(1, 9) shuffle(full_list) num_list = full_list[0:6] # Select random probe p_index = randint(0,5) probe = num_list[p_index] # Store the right answer ans = num_list[p_index+1] # Convert number list to strings for later proc num_list = map(str, num_list) # Wait for monitor to update before executing code win.flip() # Play each element of num_list after 0.33 second for i in range(0, 6): core.wait(0.33) # wait .33 s files = str(num_list[i]) + '.wav' # select sound file load_sound = sound.Sound(files, secs=-1) # load sound file load_sound.setVolume(0.5) # set volume load_sound.play() # play sound
This was a quick fix and I am convinced that there is a more elegant way of doing it. Let me know if you can think of a better way to do this, I will be happy to add it to the post. Or you can just edit it, thanks to Creative Commons.
The error was only present on Windows 10 machines with Integrated Graphics and Psychopy v1.9. On Linux, the experiment runs flawlessly. So, it is possible that it is a driver issue, or the way memory is allocated for the integrated graphics in Windows.
My advice is to avoid conditional if statements in your experiment during time-sensitive trials. There are just too many variables (hardware, OS, alignment of the planets) that affect how well the program works. I am sure only the select few has enough money to upgrade the computers at every year or so. And while Linux is coming up, Windows is still the more prevalent.
Wills, A. J., Graham, S., Koh, Z., McLaren, I. P., & Rolland, M. D. (2011). Effects of concurrent load on feature-and rule-based generalization in human contingency learning. Journal of Experimental Psychology: Animal Behavior Processes, 37(3), 308.