【问题标题】:How to detect string tone from FFT如何从 FFT 中检测弦音
【发布时间】:2014-07-11 13:08:21
【问题描述】:

我从傅立叶变换中得到了光谱。它看起来像这样:
警察刚刚从附近经过

颜色代表强度。
X 轴是时间。
Y 轴是频率 - 其中 0 位于顶部。

虽然口哨或警笛只留下一条痕迹,但许多其他音调似乎包含很多谐波频率。

电吉他直接插入麦克风(标准调音)

真正糟糕的是,正如您所见,没有主要强度 - 有 2-3 个频率几乎相等。
我写了一个峰值检测算法来突出最显着的峰值:

    function findPeaks(data, look_range, minimal_val) {
      if(look_range==null)
        look_range = 10;
      if(minimal_val == null)
        minimal_val = 20;
      //Array of peaks            
      var peaks = [];
      //Currently the max value (that might or might not end up in peaks array)
      var max_value = 0;
      var max_value_pos = 0;
      //How many values did we check without changing the max value
      var smaller_values = 0;
      //Tmp variable for performance
      var val;
      var lastval=Math.round(data.averageValues(0,4));
      //console.log(lastval);
      for(var i=0, l=data.length; i<l; i++) {
        //Remember the value for performance and readibility
        val = data[i];

        //If last max value is larger then the current one, proceed and remember
        if(max_value>val) {
          //iterate the ammount of values that are smaller than our champion
          smaller_values++;
          //If there has been enough smaller values we take this one for confirmed peak
          if(smaller_values > look_range) {
            //Remember peak
            peaks.push(max_value_pos);
            //Reset other variables
            max_value = 0;
            max_value_pos = 0;
            smaller_values = 0;
          }
        }
        //Only take values when the difference is positive (next value is larger)
        //Also aonly take values that are larger than minimum thresold
        else if(val>lastval && val>minimal_val) {
          //Remeber this as our new champion
          max_value = val;
          max_value_pos = i;
          smaller_values = 0;
          //console.log("Max value: ", max_value);
        }           
        //Remember this value for next iteration
        lastval = val;
      }
      //Sort peaks so that the largest one is first
      peaks.sort(function(a, b) {return -data[a]+data[b];});
      //if(peaks.length>0)
      //  console.log(peaks);
      //Return array
      return peaks;
    }

我的想法是,我遍历数据并记住一个大于阈值minimal_val 的值。如果下一个look_range 值小于所选值,则将其视为峰值。这个算法不是很聪明,但是很容易实现。

但是,它无法分辨出哪个是字符串的主要频率,就像我预期的那样:


红点突出最强峰

Here's a jsFiddle 看看它是如何工作的(或者更确切地说是不工作)。

【问题讨论】:

标签: audio fft guitar tuner


【解决方案1】:

你在弦音的频谱中看到的是一组泛音

f0, 2*f0, 3*f0, ...

f0 是弦音的基本频率或 音高

要从频谱估计 f0(FFT 的输出,abs 值,可能是对数),您不应该寻找最强的分量,而是寻找所有这些谐波之间的距离

一个非常好的方法是对(绝对,实数)频谱进行第二次(逆)FFT。这会在 t0 == 1/f0 处产生一条强线。

由于Wiener–Khinchin theorem,序列fft -> abs() -> fft-1 相当于计算auto-correlation function (ACF)。

这种方法的精度取决于 FFT(或 ACF)的长度和您的采样率。如果使用sinc function 在结果的采样点之间插入“真实”最大值,则可以大大提高精度。

为了获得更好的结果,您可以校正中间频谱:大多数声音的平均粉红色频谱。如果您在反 FFT 之前放大较高频率(根据反粉红色频谱),则 ACF 将“更好”(它会更多地考虑高次谐波,从而提高准确性)。

【讨论】:

  • 我现在对此表示赞同,但我稍后会对其进行测试。感谢您的意见。
  • 请您详细说明“使用 sinc 函数在结果的采样点之间插入“真实”最大值”是什么意思?我不确定“树苗点之间的最大值”指的是什么。请注意,我没有任何大学学位。
  • 基本公式在这里:eng.tau.ac.il/~yaro/RecentPublications/ps&pdf/…,您只需要 4 或 6 个 FFT 值并将它们与 sinc 函数进行卷积。听起来很复杂,最终的算法很简单。
  • 如何选择值?我认为选择随机的或恒定的偏移量不是我想要的。同样通过公式,您的意思是使用无限和的 Sinc 插值? alpha、x 和 k 在那里分别代表什么?
  • 您的问题的答案不适合评论部分。请打开另一个问题。
猜你喜欢
  • 2011-05-28
  • 2011-09-23
  • 2023-03-19
  • 2012-03-25
  • 2011-01-15
  • 1970-01-01
  • 2010-11-30
  • 2012-01-12
  • 1970-01-01
相关资源
最近更新 更多