一尘不染

从潜在谐波中确定基频的算法

algorithm

我试图从声源中提取基本频率。也许有人在向麦克风唱歌A3,所以我想检测〜110Hz

我的方法是:

  • FFT 1024浮点数
  • 使用每个档位的相位来准确确定其精确频率
  • 确定峰(通常为50个左右)
  • 最先订购它们

(Peak [0] .power = 1063.343750,.freq = 2032.715088
(Peak [1] .power = 1047.764893,.freq = 3070.605225
(Peak [2] .power = 1014.986877,.fr​​eq = 5925.878418
(Peak [3] .power = 1011.707825,.freq = 6963.769043
(Peak [4] .power = 1009.152954,.freq = 4022.363037
(Peak [5] .power = 995.199585,.freq = 4974.120605
(Peak [6] .power = 987.243713,.freq = 8087.792480
(Peak [7] .power = 533.514832,.freq = 908.691833

  • (MARKER1)从最大声开始,然后将其与所有其余峰匹配,因此,如果我有N个峰,则此时我将拥有N-1个峰对
  • 检查每个峰对的谐波;即,它与某个分数a / b有多接近,即我们可以找到b <20的a / b使得| peakA.freq / peakB.freq-a / b | <0.01(这将匹配高达20的谐波)
  • 现在,我们有了一个完善的峰列表,这些峰被认为是相互谐波的

谐波峰值对:(0,1)= 2/3,错误:0.00468 => f0 @ 1019.946289
谐波峰值对:(0,2)= 1/3,错误:0.00969 => f0 @ 2004.003906
谐波峰值对:(0,3) = 2/7,错误:0.00618 => f0 @ 1005.590820
谐波峰值对:(0,4)= 1/2,错误:0.00535 => f0 @ 2021.948242
谐波峰值对:(0,5)= 2/5,错误:0.00866 => f0 @ 1005.590820
谐波峰值对:(0,6)= 1/4,错误:0.00133 => f0 @ 2027.331543
谐波峰值对:(0,7)= 9/4,错误:0.01303 => f0 @ 226.515106

我的问题是:如何设计一种算法,可以将上述基本信号正确地识别为〜1000Hz?

绝对不能保证〜1000处的值会比〜2000或〜3000等处的值更高。甚至不能保证会有〜1000项。我们可能有〜5000 x一个条目,〜4000
x三个条目,〜3000 x 2条目以及几个假值浮动,就像上面列表中的226一样。

我想我可以再次重复该过程,以清除建议的基本原理,这些基本原理与列表的其余部分不“协调”。这至少可以摆脱虚假的价值…

可能是我什至没有问正确的问题。也许这整个方法糟透了。但是我认为选择最强的峰并提取与该峰相关的一组谐波是有意义的。

理论上应该产生一定比例的负载,比如说如果最强的原始峰是三次谐波,那么这组峰应该包含3/1 3/2 3/3 3/4 3/5 3/6
3/7等…尽管可能会丢失一些。

实际上,我感觉它总是会成为强度最大的基波或一次谐波。但我不知道我是否可以依靠…

这么多的因素,这使我无法自拔。对于这样一个麻烦的问题,我事先表示歉意。希望我可以死后整理一下。


阅读 442

收藏
2020-07-28

共1个答案

一尘不染

我找到了一个相当简单的解决方案,它的计算能力很低。

它涉及给每个间隙一个接近度分数。

要计算间隙i的接近度得分,我只需对所有j!= i求和1 / {dist(gap_i,gap_j)+1}

我设置+1以避免被零除。这样,如果两个值几乎相等,则它们将获得1分,并且随着差距的增加,该得分将变为0

但是,如果间隙j几乎恰好是间隙i的两倍,则也应将其视为非常接近。所以我首先获得最接近gap_j / gap_i的整数(可能需要交换以使gap_j更大),然后将gap_j除以该整数,然后考虑dist(gap_i,fiddled_gap_j)

如果例如gap_j〜= 2 * gap_i,那么我要小心提高Gap_i的得分,而不是gap_j

这样可以很好地获得合理准确的候选人。再经过两次处理应该可以很容易地改进该值。我想的第一件事是根据成功的候选人对价值进行平均。然后大概一秒钟创建一个泛函(f)函数,并使用从先前计算的点开始的某种Newton-Raphson过程来隔离局部最大值。

无论如何,下面是代码:

    float ascertain_f0(PEAK * peaks, POTENTIAL_HARMONIC * pH, int phCount)
{
    int gaps = phCount;

    // enumerate GAPS
    GAP gap [ MAX_POTENTIAL_HARMONICS ];

    gap[0].len = peaks[pH[0].peakQ].freq;
    for (int i=1; i < gaps; i++)
    {
        int a = pH[i-1].peakQ, 
            b = pH[i].peakQ;

        gap[i].len = peaks[b].freq - peaks[a].freq;
    }

    for (int i=0; i < gaps; i++)
        gap[i].score = 0.;

    // find best scoring gap
    for (int i=0; i < gaps; i++)
        for (int j=0; j < gaps; j++)
        {
            if (i == j)
                continue;

            float a = gap[i].len;
            float B_ = gap[j].len;

            if (a > B_)
                continue;

            // a < B_
            int multiplier = closestInt(B_ / a);
            float b = B_ / multiplier;

            float dist = fabs(b - a);           
            float sc = 1. / (dist + 1.);

            gap[i].score += sc;
            if (multiplier == 1)
                gap[j].score += sc;
        }

    int best = 0;
    for (int i=1; i < gaps; i++)
        if (gap[i].score > gap[best].score)
            best = i;

    return gap[best].len;
}
2020-07-28