Search Unity

Super accurate millisecond timing

Discussion in 'Scripting' started by coshea, Oct 5, 2015.

  1. coshea

    coshea

    Joined:
    Dec 20, 2012
    Posts:
    319
    I want to run a timer that says play a sound sample every 600 milliseconds. The accuracy is really important, as when played against a backing metronome track, any slight slip in time you can hear easily.

    I've tried this:
    Code (csharp):
    1.  
    2. float timeLastBeat;
    3. Update(){
    4.  
    5. if(time.timeSinceLevelLoad - timeLastBeat >= 0.6f){
    6. MasterAudio.play()
    7. timeLastBeat = time.timeSinceLevelLoad;
    8. }
    9.  
    i've also tried using Stopwatch

    Code (csharp):
    1.  
    2. usingSystem.Diagnostics;
    3. float timeLastBeat;
    4.  
    5. Start(){
    6. timer = newStopwatch();
    7. timer.start();
    8. }
    9. Update(){
    10.  
    11. if(timer.ElapsedMilliseconds - timeLastBeat >= 600.0f){
    12. MasterAudio.play()
    13. timeLastBeat = timer.ElapsedMilliseconds;
    14. }
    15.  
    both create slight variations in gaps between beats, 600ms, 612ms, 650ms etc. I'm guessing this is an Update speed/frequency issue and even a coroutine would be the same.

    I found this
    http://docs.unity3d.com/ScriptReference/AudioSettings-dspTime.html

    but I don't want to generate a sound from script, I want to play one using MasterAudio. I can't put a MasterAudio call inside OnAudioFilterRead.

    Any thoughts?

    Thanks
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    The Update loop is only called once a frame, and there's really no way around that. Even if there was, anything that is built on Unity's own audio components will almost certainly not have any timing better than a per-frame level. I see two options:

    A) If a ~15 ms variance is acceptable, focus on performance; if you can maintain a constant 60fps your timing will be within 16 ms. The toughest thing here would be eliminating all garbage collection, because a "bump" in a frame where the GC runs is what will really kill you.

    B) Go outside of Unity, both for the timing and the audio playback. It may be possible to get Mono to trigger some timed events that aren't dependent on Unity's Update cycle, but audio is a whole other can of worms. You'd probably have to write a native plugin.
     
  3. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    You can use dspTime outside of OnAudioFilterRead() - it's just used in that example to create procedural sounds.

    However, as StarManta stated, Update() is called every frame, meaning that there is no guarantee that using .dspTime will land you right on the 600ms mark - in fact, that will never happen. You can't use a separate thread to do the timing either, as you need to call the Unity functions on its main thread. You can use Unity's PlayScheduled() to to what you require - there is a nice post on that topic here.
     
    Last edited: Oct 5, 2015
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Huh, I did not realize that PlayScheduled exists. That's exactly what's needed for this. Good to know!
     
    Thomas-Mountainborn likes this.
  5. coshea

    coshea

    Joined:
    Dec 20, 2012
    Posts:
    319
    Thanks!

    Tom Betts on Twitter also recommended the same blog post mentioned above
    https://twitter.com/TomNullpointer/status/651150130528194560
    https://twitter.com/TomNullpointer/status/651148867052810241

    I've implemented the code from Christian Floisand, there is still a very slight delay, but at least the delay is consistent not like the Update method I was trying.

    See the attached image of the audio waveform, you can see the click track and the sample I'm trying to play on beat.

    I wonder if thats because the object you are notifying is still running this inside Update() :

    Code (csharp):
    1.  
    2. if ((beatObserver.beatMask & BeatType.OnBeat) == BeatType.OnBeat) {
    3. }
    4.  
    Thanks
     

    Attached Files: