【问题标题】:Some kind of Metronome logic inaccuracy - C#某种节拍器逻辑不准确 - C#
【发布时间】:2021-07-31 18:55:31
【问题描述】:

我正在尝试实现某种节拍器(节拍器)逻辑,每次点击一个键,计算每次点击之间的间隔, 测量平均 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)}");
    }
}

【问题讨论】:

  • 为什么每次通过主轮询循环都重新启动Stopwatch?此外,您似乎没有在任何地方使用 Timer 实例。
  • @KarlKnechtel,我正在重新启动秒表,因为我对间隔感兴趣,这对我来说与从记录的最后一个滴答声中减去当前滴答声相同。
  • @KarlKnechtel 并启动计时器(并注册到每秒发生的事件)
  • 看起来你每秒都会在列表中添加一个项目,然后当你按下 B 以外的任何键时会额外添加一个。这是否意味着你总是会比预期多得到一个?也最好打印总和和计数,以便您/我们可以看到发生了什么。
  • 我正在尝试使用\不点击进行测试,而无需点击我只使用计时器。我认为也许不重新启动秒表并实际检查经过的时间与之前的刻度总和会更好

标签: c# timing


【解决方案1】:

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

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

通过检查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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-22
    • 1970-01-01
    • 2014-11-15
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 2018-11-12
    • 1970-01-01
    相关资源
    最近更新 更多