某种节拍器逻辑不准确 - C#

Some kind of Metronome logic inaccuracy - C#

我正在尝试实现某种节拍器(敲击速度)逻辑,每次单击一个键,计算每次单击之间的间隔, 测量平均 BPM(每分钟节拍)

例如,如果我每秒点击一个键,我预计 BPM 为 60。

我介绍了一些使用点击次数的代码,我注意到有些延迟,平均为 62,

我认为我的手动点击不对, 所以我引入了一个 Timer 对象,它每秒都会“点击”一次, 而且,我得到的是 62 而不是 60 bpm

这是为什么呢?怎样才能更准确?

这是我的代码:

对不起,这是意大利面条,我只是为了好玩才尝试的

public class Program
{
    static Stopwatch sw = new Stopwatch();
    static List<double> nums = new List<double>();

    private static void TimerOnElapsed(object sender, ElapsedEventArgs e)
    {
        EvaluateTime(sw, nums);
        sw.Restart();
    }

    // Driver program
    public static void Main()
    {
        
        Console.WriteLine("App is ready");
        Timer timer = new Timer();
        timer.Interval = 1000;
        timer.Elapsed += TimerOnElapsed;
        timer.AutoReset = true;
        sw.Restart();
        timer.Start();
        while (true)
        {
            Console.WriteLine(sw.Elapsed.TotalSeconds);
            var x = Console.ReadKey();

            if (x.Key != ConsoleKey.B)
            {
                EvaluateTime(sw, nums);
            }

            sw.Restart();
            if (x.Key == ConsoleKey.S)
            {
                return;
            }
        }
    }
    
    private static void EvaluateTime(Stopwatch sw, List<double> nums)
    {
        nums.Add(sw.Elapsed.TotalSeconds);
        Console.Clear();
        Console.WriteLine($"Average: {Math.Round(60 / (nums.Sum() / nums.Count), 2)}");
    }
}

我发现重启秒表的开销导致了延迟,

我宁愿只使用秒表的时间并计算每次点击之间的间隔

通过检查 elapsed seconds minus the sum of all previous elements

最终代码如下所示:

 public class Program
{
    static readonly Stopwatch sw = new Stopwatch();
    static readonly List<double> nums = new List<double>();

    // Driver program
    public static void Main()
    {
        Console.WriteLine("App is ready");
        while (true)
        {
            Console.WriteLine(sw.Elapsed.TotalSeconds);
            var x = Console.ReadKey();
            if (!sw.IsRunning) sw.Start();
            else EvaluateTime(sw, nums);

            switch (x.Key)
            {
                case ConsoleKey.S:
                    return;
                case ConsoleKey.R:
                    sw.Reset();
                    nums.Clear();
                    Console.WriteLine("waiting for input");
                    break;
            }
        }
    }

    private static void EvaluateTime(Stopwatch sw, List<double> nums)
    {
        Console.WriteLine(
            $"{Math.Round(sw.Elapsed.TotalSeconds, 2)} - {Math.Round(nums.Sum(), 2)} = {Math.Round(sw.Elapsed.TotalSeconds, 2) - Math.Round(nums.Sum(), 2)}");

    nums.Add(Math.Round(sw.Elapsed.TotalSeconds - nums.Sum(), 2));
    Console.WriteLine($"The Average Is ====> {Math.Round(60 / (nums.Sum() / nums.Count), 2)}");
}

我再次尝试使用计时器,现在结果更加一致:

App is ready
0
1 - 0 = 1
The Average Is ====> 60
2 - 1 = 1
The Average Is ====> 60
3 - 2 = 1
The Average Is ====> 60
4 - 3 = 1
The Average Is ====> 60
5 - 4 = 1
The Average Is ====> 60
6 - 5 = 1
The Average Is ====> 60
6.99 - 6 = 0.9900000000000002
The Average Is ====> 60.09
8.01 - 6.99 = 1.0199999999999996 // my guess for this inconsitency is the increasing size of the sum calculation, but this is a small difference
The Average Is ====> 59.93
8.99 - 8.01 = 0.9800000000000004
The Average Is ====> 60.07