2

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).

Spica
  • 127
  • 2
  • 10
  • What is not working currently? – Tom Myddeltyn May 16 '16 at 14:32
  • Hi busfault! It does not seem as if it is treating the elements of the list "times" as elapsed time since onset. Maybe I need to define an onset of some sort but then not sure what to do with it.. – Spica May 16 '16 at 14:41
  • Is there any particular reason for using `global`? I'm not sure if it has anything to do with your issue. – Tom Myddeltyn May 16 '16 at 20:45

2 Answers2

2

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) ... )

jacanterbury
  • 1,435
  • 1
  • 26
  • 36
  • Thank you jacanterbury! I have changed the print command to print( 'Now: ' , viz.tick(), 'Wait time: ' , cuetime, ' - Elapsed time: ', mydata.elapsed ). Just to show that the printing itself does not happen at the timings in the list. I wonder why I get ('Now: ', 2.376125239373963, 'Wait time: ', 2, ' - Elapsed time: ', 2.0099786548416336) ('Now: ', 5.378527175519809, 'Wait time: ', 3, ' - Elapsed time: ', 3.002428450099741) ('Now: ', 9.3816914591142, 'Wait time: ', 4, ' - Elapsed time: ', 4.003159436957659) – Spica May 17 '16 at 10:38
  • This means that the "Now" and the "Elapsed time" are not the same, even though the "elapsed time" and the "wait time" are equivalent. – Spica May 17 '16 at 10:39
  • From the help pages : .tick() : This command will return the current time, in seconds, since the script started. elapsed time is the time the waitTime() waited for – jacanterbury May 17 '16 at 10:42
  • But that means that it's not waitTime I need, because, what I want is to schedule a beep at, say 2,3 and 4 secodns after the script started. So I wonder how else to schedule something, if not using the viztask.waitTime? – Spica May 17 '16 at 10:52
  • well, there're probably loads of ways to tackle it - you could make a second list with the time intervals : times=(1,2,4,7,11) time_intervals=[] prev=0 for val in times: time_intervals.append( val - prev ) prev = val print time_intervals – jacanterbury May 17 '16 at 11:04
  • Thanks jacanterbury, indeed that code gives me a list of intervals [1, 1, 2, 3, 4]. So you are probably saying that I should use that and the waitTime. to schedule the timings. – Spica May 17 '16 at 11:24
  • I have edited my question based to the suggestion in your last comment, jacanterbury. I think it's the closest I can get to my original timings! – Spica May 17 '16 at 13:54
0

I think your issue is here: with a for loop you do not need to iterate, it does it automatically. What this will do is iterate through your list times each time setting cuetime to the specific value

Also, not that cue.stop() will not be called until all yielding is done, I think? You may want to check that with a print call after the yield as I am not as familiar with yield

def main():
    global times
    for cuetime in times:
        cue.play()
        print 'cue'
        print cuetime
        yield viztask.waitTime(cueDuration + cuetime)
        cue.stop()
Tom Myddeltyn
  • 1,307
  • 1
  • 13
  • 27
  • Thanks busfault, the function and loop look cleaner now! And you are right about the yield. But the problem is still that it does not treat those elements as timings (play saound at those seconds since start of trial). I am now tryign to wrap my head around timer functions. Maybe I need to tell it somewhere that those elements are onsets. – Spica May 17 '16 at 10:01