psychopy.org | Reference | Downloads | Github

Pause Function for Movie Stimulus

Hello,

I am having trouble with adding in coding for pausing a movie stimulus in the Builder View. I am new to coding so I am not completely sure on how things work. Although I add coding, the key that is specified in the coding does not actually pause the movie.

This is the coding that I have tried to use in my experiment which I extracted from the demo MovieStim2.py:

# Check for action keys.....
for key in event.getKeys():
    if key in ['escape', 'q']:
        win.close()
        core.quit()
    elif key in ['p', ]:
        # To pause the movie while it is playing....
        if mov.status == visual.PLAYING:
            mov.pause()
        elif mov.status == visual.PAUSED:
            # To /unpause/ the movie if pause has been called....
            mov.play()
            text.draw()
            win.flip()

No error message is given when I run the experiment with this coding. It seems that the key is not connected with the action that was specified as when I press the key ‘p’ nothing happens and the movie keeps on playing. If there is anyone who can give some advice on how to correct this issue, it would be much appreciated.

Thank you.

We probably need to see that code in context. i.e. in your drawing loop if you are using your own code, or if it is a Builder experiment, how this code is incorporated in it.

Hello,

It is a Builder experiment. I have the code implemented in ‘each frame’ of a 2 back task in which a movie is playing at the same time as the letters and fixation cross are appearing on the screen.
image

For a start, please remove the win.flip() line. Builder calls that itself once per drawing cycle, so by calling it manually, you’ll cause a bit of a stutter in timing and interrupt the normal flow of Builder code.

Otherwise, insert some (temporary) print() statements for debugging pauses, e.g.

for key in event.getKeys():
    if key in ['escape', 'q']:
        win.close()
        core.quit()
    elif key in ['p', ]:
        # To pause the movie while it is playing....
        if mov.status == visual.PLAYING:
            print('PAUSING')
            mov.pause()
        elif mov.status == visual.PAUSED:
            # To /unpause/ the movie if pause has been called....
            print('UN-PAUSING')
            mov.play()

That will show whether or not the code is flowing properly. If the pause code runs properly but no result is seen, I do wonder, by quickly scanning through the source code, if you might also need to set the autoDraw property of the movie to False when it you are pausing it, and to True who you start playing it again.

Builder scripts make extensive use of autodraw, which probably doesn’t occur in that demo code.

Thank you very much for your response. That coding does seem to work as I am now able to pause the movie.

However, the experiment closes down when I press the same key to try to un-pause it and an error message appears. Would I be able to correct this issue by making the changes to the autoDraw property that you had suggested?

image

Sorry, that screenshot is not really legible. It would be useful to see the error in full plus the line of code it is referring to (actually, perhaps show all of the revised section of code, and indicate the one the error is thrown on).

I can read that, ‘module has no attribute PAUSED’. Yep, that tracks. You shouldn’t be calling ‘visual.PAUSED’ and ‘visual.PLAYING’, these are not part of the psychopy.visual module, or at least they won’t behave like that in this context.

I don’t 100% know what the builder scripts import, but if you can, add a code element to run once at the very start of your experiment that looks like this:

from psychopy.constants import (PLAYING, PAUSED) 

This will allow you to simply refer to these statuses directly throughout the experiment. In truth you might not even need them, the builder might well just grab them as-is, but this will make sure you have EXACTLY what you need.

Then, for your code:

for key in event.getKeys():
    if key in ['escape', 'q']:
        win.close()
        core.quit()
    elif key in ['p', ]:
        # To pause the movie while it is playing....
        if mov.status == PLAYING:
            print('PAUSING')
            mov.pause()
        elif mov.status == PAUSED:
            # To /unpause/ the movie if pause has been called....
            print('UN-PAUSING')
            mov.play()

Dropping the visual. should just fix that part of it. If not, I’ve also had success encasing the status in single-quotes, so, 'PAUSED' and 'PLAYING'

I’m still slightly concerned about whether it will register that p is pressed on consecutive frames, but that’s because I don’t remember how getKeys works. If it clears the buffer when you call it, then that won’t be an issue. Otherwise it may act like you’re pressing p on consecutive frames and toggle pause on and off, which would be annoying. Still, let’s make sure you can get it to read the statuses properly first.

1 Like

Yes, that worked out great! I am now able to pause and un-pause the movie without any errors. Thank you so much, I appreciate the help.

Would you also happen to know any ways that I can resolve another issue that I have been having with my experiment? It involves the syncing of the audio and the movie in the Builder Component as the audio seems to repeat itself every 2.5 seconds. I have gone into more detail about it in another discussion named Audio Match Up with Movie during N-back task . I also seem to have the same issue with the pause that occurs in the same routine: if I pause the movie, it resumes playing after the 2.5 seconds.

I think there’s a few different things going on there. Here’s one thing you can do that will probably help:

If you’re on PsychoPy 3.0.5 or later, switch to SoundDevice for playing audio. In the PsychoPy preferences (file -> preferences), go to ‘hardware’, and on the line that says ‘audio library’ remove everything between the brackets and put ‘sounddevice’. So, it should look like this:
['sounddevice']

At the start of your code, where you import constants, also add these lines:

import prefs
prefs.hardware['audioLib'] = ['sounddevice']

(This belt-and-suspenders thing is because of a bug I just noticed and am about to fix.)

That will make it so that the audio is at least less likely to loop at weird times. That was due to Pyo basically re-loading the entire sound part of the movie every time it stopped or restarted. SoundDevice is much more memory-efficient and just…better.

I don’t know if that will solve all of your problems because I think there might be something else going on with how the movie object in the builder behaves, but that’s my best guess.

1 Like

Thank you again for your help!

I have implemented that coding into the experiment. I received an error when trying to use the coding on its own but it was able to run when I added in ‘from psychopy’ before it. The audio and movie are now synced, however, now I am unable to pause. It seems to record the key presses of ‘p’ as the output shows un-pause and pause. Although the presses are recorded, the pauses do not actually occur while the movie is playing.

Could the pause function not working correctly be caused by the way that I have ordered the coding?

Yes. The elif statement at the end, and everything under it, needs to be moved back four spaces. The last elif and the last if should be aligned.

I have made the changes to the coding and the pausing function is working now.

However, I still come across the same issue in which the pause will undo itself (without a key press) once the 2.5 second duration has passed. This duration is what I have found will keep the movie playing continuously while the letters are changing in a 2-back task without any errors. Do you believe that the pause resetting is caused by the settings of the movie itself or would I need to change the coding or add in coding so the pause will not be affected by the duration?
image

This is due to how the duration system works for builder components like this movie. Basically, it’s just a stopwatch. It isn’t paying attention to whether the movie is playing or not, it just counts 2.5 seconds from the start of the movie and then does something to try to end it, which probably in this case un-pauses it.

Change the stop type to ‘Frame(N)’ or ‘duration(frames)’ and figure out how many frames are in 2.5 seconds of your movie (which should just be 2.5* movie framerate, which is probably either 30 or 60). Looking briefly at the underlying code, that should make it so that it won’t behave in this ‘stop-watch’ manner, but actually pay attention to how far the movie has progressed, and only end when it reaches the frame that occurs 2.5 seconds into the movie.

1 Like

Unfortunately, those changes do not seem to work as I still come across the same issue. Even if I try different amounts, the pause still resets. I have tried 60 (frame rate of 24*2.5), 207 (the actual length of the movie in seconds), and 517.5 (207 *2.5) in either Frame(N) or duration(frames). Using 60 seems to be too short as it makes the movie disappear from sight within a second or so until the next letter is shown and then it appears and resumes playing for the same amount of time. On the other hand, 207 and 517.5 have let the movie keep playing continuously but the pause resets once the letters change.

I am beginning to think that the issue is that the coding and the task are in the same routine rather than it being an issue with the movie component or the coding themselves. The coding and movie seem to work fine on their own. However, as I have the letters and fixation crosses change every 2.5 seconds, the coding is only able to be used for those 2.5 seconds and then resets itself as the letters change since they are set as every repeat.

Are there any methods or changes I can implement in the experiment so that the movie’s state of paused can remain constant throughout the duration of the task and is not affected by the set duration of the letters? Or would you recommend trying to implement a split screen so that the movie and coding are separate from the settings of the task to try to correct this issue?

OK, so this seems to be running at 60 frames per second, if putting it at 60 frames made it run for 1 second. 2.5 seconds exactly would therefore be 150 frames, so try duration(frames) 150.

I think you’re basically right about what’s going on. If the other elements of the trial have their own duration settings, separate from the movie, those are just proceeding on their own stopwatches and ending the trial when they hit their end. You could potentially just give the other pieces of the trial indefinite duration and set the movie to end the trial using the “force end of routine” checkbox in the movie object. That should make it so the timing of the whole trial is tied to when the movie ends, and it will be responsive to the pausing if you use the frame counter.