I'm developing a sort of music production app, which includes audio playback. I'm currently running into a problem where the playback is not consistent. The timer I use to progress through beats seems to skip or lag on some tick events. You can hear the pauses in the sample video: Video
I implement the timer and its event like so
private void Method()
{
///some other code here
//the math for the timer period is to figure out how many times the timer should tick in 1 minute.
int _period = (int)Math.Round(60000f / (float)NUD_ConfigBPM.Value, MidpointRounding.AwayFromZero)
_threadtimer = new System.Threading.Timer(_ => _threadtimer_Tick(), null, 0, _period);
}
private void _threadtimer_Tick()
{
//vorbis is a List<List<CachedSound>>
//each beat can have several sounds to play at once
foreach (var _sample in vorbis[_playbackbeat]) {
AudioPlaybackEngine.Instance.PlaySound(_sample);
}
//simulating a playhead by highlighting cells of the DataGridView
try {
trackEditor.ClearSelection();
trackEditor.Columns[_playbackbeat - 8].Selected = true;
} catch { }
_playbackbeat++;
//once playback has reached the end of the file, stop it.
if (_playbackbeat >= vorbis.Count) {
_threadtimer.Dispose();
_playing = false;
btnTrackPlayback.ForeColor = Color.Green;
btnTrackPlayback.Image = Properties.Resources.icon_play;
trackEditor.SelectionMode = DataGridViewSelectionMode.CellSelect;
}
}
AudioPlaybackEngine.Instance.PlaySound() is implemented from this NAudio article "Fire and Forget"
Where are these pauses coming from, and are there better timers or methods I could be using to solve this? The entire issue goes away at slower BPM (slower timer tick period).
Instead of a timer problem, could it be an NAudio problem, and the AudioPlaybackEngine is creating the delays?
I timed the spacing between every tick at 360BPM (_period is 167). Every tick was consistent in spacing (give or take a couple ms). Even for the beats with large audio delays, the tick event was on time.
I have tried using Multimedia timer, and the various high resolution timers in this thread. AccurateTimer and PrecisionRepeatActionOnIntervalAsync and returned the same result.
I also changed _playbacktimer_Tick() to be async, and got the same result.
Windows handles times internally with a 15.6ms resolution by default. So just about all methods at doing things in an interval will be limited by this resolution. In addition, Windows is not a real time OS, so it provides no real timing guarantees. Your thread could preempted at any time, or just not get scheduled.
If you just want to measure time the solution is simple, just use a stopwatch. This usually has a resolution of about 100ns.
To ensure that your thread is scheduled consistently, you might consider increasing the priority of the thread. But will probably not be able to do this with timers that raise events on the threadpool.
You can also increase the timer resolution in Windows, but this is a system wide change, and it does have effects on power consumption, so be sure to turn it down again when not needed. Or you could use the multi media timer. Unfortunately it does not have built-in managed wrapper. I'm not sure if the multimedia timer just increases the timer resolution, or does something else.
You can also use a spinwait and a stopwatch to do nothing between waits, but this will prevent the CPU core from doing anything else, so it's not an ideal solution.
The best solution is to use the audio playback system to avoid the need for high-resolution events, and delegate that to the library/OS/Hardware. But I'm not familiar with audio playback to provide any specific recommendations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With