2

Hey I have an rather specific issue.

I am building an metronom, and this metronom also shows boxes, which will change their color in the given tact in bpm. the highest possible bpm is 400bpm with 4/4 beats. that means the fastest change of color should happen every: 37,5 ms.

enter image description here

The change of color will last some milliseconds.

Until now i tried to realize that with a timer:

timer = new System.Timers.Timer() { AutoReset = true, SynchronizingObject = null };

        private async void timer1_Elapsed(object sender, ElapsedEventArgs e)
        {
            boxViewLast = (BoxView)beatDisplay.Children[(int)lastI];
            boxView = (BoxView)beatDisplay.Children[(int)i];
            boxViewLast.Color = boxColor;
            boxView.Color = transColor;
            await Task.Delay((int)BeatMilliseconds);
            boxView.Color = boxColor;
            lastI = i;
            i++;
            if (i >= numOfChildren)
            {
                i = 0;
            }

            timer.AutoReset = Tempo == 1 && Play;
        }

But in the UI I see a flickering some times if the color is changing fast and sometimes it is not even changing. I guess that resources are getting blocked and so the change can not be fullfilled in the given time. Is there a way to have UI change async and very performant? But I also need a very pricise timer

David Marquardt
  • 84
  • 2
  • 11
  • 2
    you should note that the OS probably have some fixed update frequency, often 60hz or 16ms, so trying to do anything UI related at high frequency will need to take this into account. – JonasH Jan 09 '22 at 19:28
  • 1
    I'm not sure Forms is the right tool to use for something that needs to be this responsive. SkiaSharp may be better. Or as @JonasH points out you may be running into a conflict with the underlying OS UI logic. At a minimum I would get rid of the Task.Delay and avoid doing two UI operations on the same element in the same Tick – Jason Jan 09 '22 at 19:31
  • Thanks for your comments. Why not Task.Delay? I used it for changing the color for a short time frame. first it should have transColor (white) and then after waiting (62) milli seconds it should bounce back to the original color. – David Marquardt Jan 09 '22 at 22:14
  • 2
    Task.Delay does not guarantee any accuracy. It only guarantee "at least N ms". Depending on the system timer resolution, thread pool workload, etc, the code after `Delay(100)` may be run after exactly 100ms or 2 minutes. As for timer resolution - as I know, on Windows it is 15ms. I do not know a resolution on iOS – Serg Jan 09 '22 at 23:22
  • Ah ok, so what is the best way to force to continue after 100 ms ? – David Marquardt Jan 10 '22 at 13:39
  • Sorry, I have no ready-to-use solution, usable for iOS, but this looks relevant https://stackoverflow.com/questions/9737877/how-to-get-a-accurate-timer-in-ios. – Serg Jan 10 '22 at 15:12

1 Answers1

0

No reason to await Task.Delay inside the callback. You can use the timer to define the interval between callbacks:

var lastTime = DateTime.Now;
var times = new List<TimeSpan>(1000);
var timer = new System.Timers.Timer() { Enabled = true, Interval = 32, AutoReset = true};
timer.Elapsed += TimerHandler;
Console.WriteLine("Starting timer");
timer.Start();
await Task.Delay(TimeSpan.FromSeconds(3));
timer.Stop();

foreach (var time in times)
{
    Console.WriteLine($"{time.TotalMilliseconds}ms passed");
}

void TimerHandler(object? sender, System.Timers.ElapsedEventArgs e)
{
    TimeSpan timePassed = e.SignalTime - lastTime;
    times.Add(timePassed);
    lastTime = DateTime.Now;
}

It's going to be somewhat accurate:

30.5771ms passed
31.0725ms passed
30.8268ms passed
31.0783ms passed
30.5758ms passed
31.4693ms passed
31.0038ms passed
32.1051ms passed
31.0289ms passed
31.1313ms passed
30.9087ms passed
31.0877ms passed
31.9015ms passed
30.7389ms passed
46.8875ms passed
31.4987ms passed
31.7549ms passed
asaf92
  • 1,557
  • 1
  • 19
  • 30
  • well, but still sometimes it can hit 46ms which should not happen in my case.. – David Marquardt Jan 10 '22 at 18:45
  • How about playing a video loop of flickering lights? – asaf92 Jan 11 '22 at 08:48
  • yea this would be the last resort, but i think its to heavy. because i have 10 different tact systems, so i would need ten different videos. and if i have choosen one tact system i need to control all videos so that they meet the exact beat – David Marquardt Jan 11 '22 at 12:31
  • I assume you can adjust the playback speed to match the BPM you want. I know Windows has some high resolution timers in the Multimedia API but It's not relevant for xamarin – asaf92 Jan 11 '22 at 13:31
  • Also, keep in mind that I ran the code on my laptop and in a main thread. Maybe the results will be better on a worker thread? – asaf92 Jan 11 '22 at 13:34