如何从FFT中检测弦乐音调

时间:2014-05-22 13:09:24

标签: audio fft guitar tuner

我从傅里叶变换得到了谱。它看起来像这样: sound spektrogram created by passing police
警察刚刚经过附近

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

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

sound spektrogram of EHGDAE tuned guitar 电吉他直接插入麦克风(标准调音)

真正糟糕的是,正如你所看到的那样,没有强烈的强度 - 有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;
    }

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

然而,它无法分辨字符串的主要频率,就像我预期的那样:

Guitar strings now with highlighted strongest frequency
红点突出显示最强的峰

Here's a jsFiddle了解它是如何运作的(或者说不起作用)。

1 个答案:

答案 0 :(得分:2)

您在弦乐音调中看到的是

处的谐波集
  

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

f0是你的弦乐音调的基频或音高

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

这样做的一个非常好的方法是(abs,real)频谱的第二(逆)FFT。这会在t0 == 1 / f0处生成一条强行。

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

这种方法的精确度取决于FFT(或ACF)的长度和采样率。如果你插入&#34;真实的&#34;你可以大大改善精确度。结果的采样点之间的最大值使用sinc function

为了获得更好的效果,您可以校正中间频谱:大多数声音具有平均粉红色光谱。如果你在反向FFT之前放大更高的频率(根据反向粉红色光谱),ACF将更好地&#34; (它会更多地考虑更高的谐波,提高精确度。)