- Home /
I need a way to run code much more frequently than update. Ideally, every 3 milliseconds
I'm working on an interactive music tool. The ear is much more sensitive to timing issues than the eye, and while Update's frequency of 16 milliseconds is good enough for the eye, it's far too slow for music.
Things sound "in sync" if they are within a tolerance of 5 milliseconds, water tight if they're at 3.
So far, coroutines are too expensive to reach this speed, and changing the timestep isn't an option, as it would interfere with the rest of the game. Even OnAudioFilterRead is only about 20 ms.
Is there a way to have code called at a frequency of less than 5 milliseconds?
I don't know you can get it down to 5 milliseconds, nor how even the spacing, but InvokeRepeating() can be executed at rate faster than the frame rate, and it is more efficient that a coroutine.
Just FYI, Update doesn't have any set frequency. It's variable, depending on many factors, such as vsync, what the monitor refresh rate is if vsync is on, CPU/GPU load, etc. If you leave vsync off and the computer is fast enough for what you're doing, Update would certainly be able to hit 3ms (or lower, unless you used Application.targetFrameRate = 333 to limit it).
You don't need to go lower than 20ms (DSP speed = 1/outputSampleRate) though You can still reset the frequency of the DSP modifying the outputSampleRate.
Yes, I do need a faster update, unless I can call a function every time there's new data, I need looping code that runs faster than every 20 ms.
This tool needs to be able to trigger audio on the beat, on demand. With an update of ~20ms, a beat will often be late by 10-20 ms which is unacceptable.
public void WaitThenLoop ()
{
loopTimer = $$anonymous$$easureLoopTime();
//If the track has segments
if(currentTrack.Segments.Count > 0)
{
//gets the segment from track associated with the current state
Segment segment = currentTrack.Segments[currentState];
//If the audio is passed the current segment
if (audio.time > segment.End)
{
//either start the segment over or jump to the next one
currentState = (int)cuedState;
audio.time = currentTrack.Segments[(int)cuedState].Start;
}
}
}
IEnumerator AudioLoop()
{
while(true)
{
yield return new WaitForSeconds(0.0001f);
WaitThenLoop();
}
}
On a good computer disabling Vsync may give you 1000fps, so that's 1ms precision on Update().It is a solution if the tool you create is for your own use because fps depend on the abilities of the computer.
There are many possibilities to generate audio on the fly with unity, but you can't update at the output sample rate ( at least not using unity), you have to create a queue of data samples to anticipate the next beats and keep the audio continuity, that's what I do to generate audio. I think audio program$$anonymous$$g tools generates buffer/chunk of audio every update, thei don't send data sample by sample.
Answer by YourGamesBeOver · Jun 18, 2014 at 03:09 PM
If your tool doesn't use the physics engine for anything, you could use FixedUpdate. It has a more consistent timing, and you can change the rate by going to Edit->Project Settings->Time and changing the Fixed Timestep.
Thanks for your reply. This would most certainly work, but unfortunately the rest of the game uses physics pretty extensively.
Answer by Wisteso · Jun 14, 2014 at 05:03 AM
You could try just using a C#/Mono thread... However, it seems like a bad idea to assume that you'll be able to get ~333 calculations per second. On slower hardware especially...
You'll probably also have to use busy-waiting because sleeping a thread tends to be hit and miss if you want exact timings.
My question is why dont you calculate what you need ahead of time? If you're reacting to the music (rather than generating it) you have all the data you need before-hand. If you ARE generating the music on the fly, then would it be bad to have it be smooth but lagging behind by about 30ms?
I'll look into mono threads. I have to believe that it is possible to get such a fast refresh since it's basically how audio program$$anonymous$$g works.
Here's the reason that calculating ahead of time isn't a solution:
Suppose I want to loop the first four measures of my song, which is from exactly 0.000 s to 4.800 s. I can know this already, and tell unity exactly what to do.
Because of how slow Update() is, when it reaches 4.800, it could jump back within 4.803, but it's more likely to jump back at 4.812. Way way mondo inaccurate for musical purposes.
It's not just that things are behind (though that is a problem) it's also that things are inconsistent.
If everything was just 30 ms behind but dead on otherwise, there wouldn't be a problem. but ins$$anonymous$$d things are 5-25 ms behind, which for musical purposes is unacceptable.
Using a c# thread might be best for you then. If you do busy waiting, you'll use more CPU but you'll also have more accurate sleep times. You might be able to get by with normal (not busy) waiting, but then the thread may wake up early/late (depends on the underlying OS).
You could also possibly use a bigger buffer. It sounds like you're wanting to almost do this in real time, but a lot of audio playback programs work by passing big chunks of audio data to the sound device, rather than many tiny bits. Though this would prevent the game from having instantaneous music changes based on input - it would be delayed by a maybe half a second.
Ultimately, you're talking about creating something that's typically done in native code, and not behind tons of layers of abstraction. It might be doable though. It sounds like the biggest thing to worry about is accurate wait/sleep timers, but like I said, busy waiting can help that. General idea being...
while (DateTime.Now.Ticks < NextTime)
{
// comment out the line below for more expensive
// but more accurate ti$$anonymous$$g
Thread.Sleep(1);
}