audio - 如何从FFT检测弦音

标签 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;
    }

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

但是,它无法判断字符串的主要频率,就像我预期的那样:

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

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

最佳答案

你在弦乐的频谱中看到的是一组谐波在

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



f0 是弦音的基频或音高。

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

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

序列 fft -> abs() -> fft-1 等价于计算 auto-correlation function (ACF) 感谢 Wiener–Khinchin theorem .

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

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

关于audio - 如何从FFT检测弦音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23807783/

相关文章:

algorithm - 使用FFT算法计算

java - 如何创建吉他和弦变奏?

java - 通过原始音频重新创建带有和声的吉他弦

audio - 吉他和弦识别算法?

audio - 在音频 session 中查找当前发言人

android - 选择最后连接的音频源android

python - NumPy 中 exp(-x^2) 的快速傅立叶变换

javascript - Web Audio API Analyzer节点无法按预期工作

android - 如何隐藏专辑封面,并在Android音乐播放器中显示可视化器,类似于Google Music Android应用?

signal-processing - 使用 FFT 检测小号的音高