【问题标题】:How can I best calculate beats per min, given the time in between two beats?考虑到两次节拍之间的时间,我怎样才能最好地计算每分钟的节拍?
【发布时间】:2021-10-27 11:16:54
【问题描述】:

我正在创建一个应用程序,用于测试用户通过点击、鼠标点击/按钮按下等可以实现的节拍速度。

每次用户点击按钮时,我都会调用这个函数,beat();计算他们当前的 BPM 并用该 BPM 填充图形。我还想在最后计算一个平均值。请参阅下面我当前使用的代码。

我目前遇到的问题是,当用户点击时,BPM 到处都是,有时它是一致的,有时它是不准确的。查看我的代码让我感到困惑,因为我认为我实际上并没有在这里计算 BPM,或者至少不准确。

我还尝试在新旧 BPM 值之间添加 lerp,以平滑仪表,但似乎无法解决问题。

我在堆栈溢出中发现的最接近的问题是这个问题:Tap BPM code in Processing
但我仍然不确定这是否真的在准确计算用户 BPM。我非常感谢这里的一些帮助,特别是关于如何仅使用 2 个时间戳(一个旧节拍和一个新节拍)计算 BPM 背后的数学解释。

void beat()
{

    if (beat0 == null)
    {
        beat0 = DateTime.Now;
        return;
    }
    else if (beat1 == null)
    {
        beat1 = DateTime.Now;
    }
    else
    {
        beat0 = beat1;
        beat1 = DateTime.Now;
    }

    if (beat0 != null && beat1 != null)
    {
        double delta = (beat1 - beat0).TotalSeconds;
        double bpm = 60 / delta;
        curBpm = (int)bpm;

        if (oldBpm == -1)
        {
            oldBpm = curBpm;
            return;
        }

        lerpBpm = (int)Mathf.Lerp(oldBpm, curBpm, (float)delta);

        bpmText.text = lerpBpm.ToString();
        FillGauge(lerpBpm);
        oldBpm = lerpBpm;
    }
}

【问题讨论】:

  • DateTime 对任何事情的计时都很糟糕;系统时钟的分辨率太粗。使用秒表。在一个数组中有一个小缓冲区,例如 5 个读数,使用递增计数器(圆形数组)的模数访问,该计数器在单击时为 1ups,并将经过的秒表存储在数组中。做读数的平均值(最大值减去最小值除以数组长度)并从该平均值计算 bpm 否则它将无处不在
  • 您能否详细说明此评论,也许是在一个示例/一些伪代码的答案中?谢谢!
  • 我问是因为我有点困惑;平均值 = 最大值 - 最小值 / 数组长度?不是平均数组/数组长度中所有数字的总和吗?
  • 秒表不断计数,每次用户点击时,您都会将经过的时间存储在数组中,因此数组中的最大时间减去数组中的最小时间就是用户花费的时间最近的 5 次点击(如果它是一个 5 长的数组),didcide by 5 是点击之间的平均时间。如果将它们全部加起来,您将获得平均秒表读数,而不是从那时到现在的时间变化(您需要计算每分钟的点击次数。例如,每 1 秒 5 次点击是每分钟 300 次点击)
  • 您询问如何计算 BPM。如果每拍有 100 毫秒,每分钟有 60000 毫秒,那么你可以在一分钟内适应 600 批 100 毫秒的间隔。一旦你有了 ms/b,你可以通过除以 ms/m 得到 b/m由 ms/b .. 在基本数学术语中 (ms/b) / (ms/m) 与翻转 x/y 之一并将中间 / 更改为 * 相同。如果你翻转(ms/b) / (ms/m) 所以它是(b/ms) * (ms/m) 这是(b * ms) / (ms * m) 然后两个ms 和底部抵消只留下(b/m) 即每分钟节拍

标签: c# datetime time logic lerp


【解决方案1】:

我会有这样的东西..

一个用于例如 5 个样本的数组,以及一个用于知道哪个数组元素是“当前”的计数器

TimeSpan[] samples = new TimeSpan[5];
int index = 0;

我有一个秒表,正在跑步。这是.net的高分辨率计时类

当用户点击一个按钮时,我会读取秒表并将其存储在数组中:

var current = stopwatch.Elapsed;     //you'll see why I store in a temp variable in a moment 
samples[index] = current;

然后增加索引,使用模将其保持在数组边界内(循环数组)

index = (index +1)%samples.Length;

然后我需要查看数组中的最小值和最大值。您可以使用 LINQ 最小值/最大值,也可以只使用索引现在指向数组中最旧的项这一事实。

int oldest = samples[index];

现在,如果您执行current - oldest,您将得到采集 5 个样本所花费的时间,除以 5 得出过去 5 个样本中每个样本的平均时间

var ms = (current - oldest).TotalMilliseconds;

如果这是从一个节拍到下一个节拍的时间,您想知道在 60000 毫秒长的时间段内可以容纳多少节拍

var bpm = 60000 / ms;

现在我想剩下的就是检查一下最早的读数是否为 TimeSpan.Zero(表明它从未设置过),这样您就不会在前五次轻拍时开始计算疯狂的 bpm

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 2017-11-02
    • 1970-01-01
    • 2012-02-06
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    相关资源
    最近更新 更多