Search code examples
pythontimetimingbenchmarkingvizard

How to schedule action at various timings from a list in Python


I am trying to schedule an action (a beep sound) in Vizard using Python. However, I want the beep to happen at certain timings since onset of the trial.

What I have so far gives me (for example, after running it once) a list of times times: [ 1.89229142 5.2610474 9.86058804 11.43137033 13.87078666] and is playing the sound at what seems to be varying intervals and prints out the elements of the above mentioned timing list. It's just that it's not actually using these elements as seconds/timings at which to play.

My question is: How do I make Python know that these are not just numbers, but timings since onset of the function/trial? Probably I am supposed to use time.time() somewhere, but I am just not sure about the logic of how to get there.

import time
import numpy as np
import viztask                                              


### Start Vizard ### 
viz.go()


### Cue sound ###
cue = viz.addAudio('cues\dong.wav') 
cueDuration = cue.getDuration() 

### Timings ###

def uniform_min_range(a, b, n, min_dist):
    while True:
        times = np.random.uniform(a, b, size=n)
        np.sort(times)
        if np.all(np.diff(times) >= min_dist):
            return times

def timings():
    global times
    times = uniform_min_range(0, 20, 5, 1.0)
    print "times: ", times


def main():
    global times
    timesIndex =0
    for x in range(len(times)):
        cuetime = times[timesIndex]
        cue.play()
        print 'cue'
        print cuetime
        yield viztask.waitTime(cueDuration + cuetime)
        cue.stop()
        timesIndex = timesIndex + 1



timings()
viztask.schedule(main())

EDIT based on jacantebury's suggestion in the comment to his answer: This is the closes working code. I suppose the mismatch in timing is related to the framerate, as jacanterbury suggests.

import time
import viztask

viz.go()

times= [1.76493425, 3.10174059, 4.49576803, 10.99379224, 18.84178369] #at these times since onset of script, a text "hello world" should be printed
time_intervals=[]
prev=0 

for val in times: 
    time_intervals.append( val - prev ) 
    prev = val 
    print time_intervals

intervalsIndex = 1

start = time.time()

def main():
    global intervalsIndex
    yield viztask.waitTime(time_intervals[0])
    for x in range (len(time_intervals)):
        print ('hello world', '- now: ', time.time()- start)
        yield viztask.waitTime(time_intervals[intervalsIndex])
        intervalsIndex = intervalsIndex + 1

 viztask.schedule(main())

This now gives me ('hello world', '- now: ', 1.7820000648498535) ('hello world', '- now: ', 3.133000135421753) ('hello world', '- now: ', 4.5350000858306885) ('hello world', '- now: ', 11.040000200271606) ('hello world', '- now: ', 18.897000074386597) which is very close to my list elements (i.e. timings, and what I initially wanted).


Solution

  • busfault is correct about your use of the FOR loop.

    To test it using yield, you need to schedule the called function. e.g.

    import viz
    import viztask
    
    def myfunc():
    
        for cuetime in (2,3,4):
    
            mydata = yield viztask.waitTime( cuetime)
            print( 'Wait time: ' , cuetime, ' - Elapsed time: ', mydata.elapsed )
    
    viz.go()
    viztask.schedule( myfunc )
    

    which produces output like this ...

    ('Wait time: ', 2, ' - Elapsed time: ', 2.002306576051085)
    ('Wait time: ', 3, ' - Elapsed time: ', 3.016386045747815)
    ('Wait time: ', 4, ' - Elapsed time: ', 4.016321305293874)
    

    You'll find that the timing mismatch is usually related to your frame rate ( 60 Hz ~ 16.7ms)

    Following edit by OP: .. an easy way to test if the timing differences are due to the frame syncing is to turn off Vertical sync (V-Sync) for your graphics card and re-run it - I suspect you'll get much better timings, however you might get frame tearing on your display - then it's your call as to what is more important to you, image quality or timing accuracy. Another option to prove its todo wit refreshing is simply to change your refresh rate and see if the time differences vary accordingly.

    I notice you've gone back to using time.time() - this will be LESS accurate than using the return object from waitTime as per my demo (ie mydata = yield viztask.waitTime( cuetime) ... )