如何找到吉他弦音的基本频率?

时间:2011-02-18 17:08:47

标签: audio signal-processing guitar

我想为Iphone制作吉他调音器应用程序。我的目标是找到吉他弦产生的声音的基本频率。我使用了Apple提供的aurioTouch样本中的一些代码来计算频谱,我找到了幅度最高的频率。它适用于纯声音(只有一个频率的声音),但对于来自吉他弦的声音,它会产生错误的结果。我读过这是因为吉他弦产生的泛音可能比基本弦更高。如何找到基频,以便它适用于吉他弦? C / C ++ / Obj-C中是否有用于声音分析(或信号处理)的开源库?

3 个答案:

答案 0 :(得分:46)

您可以使用信号的自相关,这是DFT幅度平方的逆变换。如果您以44100个样本/秒进行采样,那么82.4 Hz基波是大约535个样本,而1479.98 Hz是大约30个样本。寻找该范围内的峰值正滞后(例如从28到560)。确保您的窗口至少是最长基本的两个周期,这里将是1070个样本。对于下一个2的幂,这是2048样本缓冲区。为了获得更好的频率分辨率和更少的偏差估计,请使用更长的缓冲区,但不要太久以至于信号不再是近似静止的。这是Python中的一个例子:

from pylab import *
import wave

fs = 44100.0   # sample rate
K = 3          # number of windows
L = 8192       # 1st pass window overlap, 50%
M = 16384      # 1st pass window length
N = 32768      # 1st pass DFT lenth: acyclic correlation

# load a sample of guitar playing an open string 6
# with a fundamental frequency of 82.4 Hz (in theory),
# but this sample is actually at about 81.97 Hz
g = fromstring(wave.open('dist_gtr_6.wav').readframes(-1),
               dtype='int16')
g = g / float64(max(abs(g)))    # normalize to +/- 1.0
mi = len(g) / 4                 # start index

def welch(x, w, L, N):
    # Welch's method
    M = len(w)
    K = (len(x) - L) / (M - L)
    Xsq = zeros(N/2+1)                  # len(N-point rfft) = N/2+1
    for k in range(K):
        m = k * ( M - L)
        xt = w * x[m:m+M]
        # use rfft for efficiency (assumes x is real-valued)
        Xsq = Xsq + abs(rfft(xt, N)) ** 2
    Xsq = Xsq / K
    Wsq = abs(rfft(w, N)) ** 2
    bias = irfft(Wsq)                   # for unbiasing Rxx and Sxx
    p = dot(x,x) / len(x)               # avg power, used as a check
    return Xsq, bias, p

# first pass: acyclic autocorrelation
x = g[mi:mi + K*M - (K-1)*L]        # len(x) = 32768
w = hamming(M)                      # hamming[m] = 0.54 - 0.46*cos(2*pi*m/M)
                                    # reduces the side lobes in DFT
Xsq, bias, p = welch(x, w, L, N)
Rxx = irfft(Xsq)                    # acyclic autocorrelation
Rxx = Rxx / bias                    # unbias (bias is tapered)
mp = argmax(Rxx[28:561]) + 28       # index of 1st peak in 28 to 560

# 2nd pass: cyclic autocorrelation
N = M = L - (L % mp)                # window an integer number of periods
                                    # shortened to ~8192 for stationarity
x = g[mi:mi+K*M]                    # data for K windows
w = ones(M); L = 0                  # rectangular, non-overlaping
Xsq, bias, p = welch(x, w, L, N)
Rxx = irfft(Xsq)                    # cyclic autocorrelation
Rxx = Rxx / bias                    # unbias (bias is constant)
mp = argmax(Rxx[28:561]) + 28       # index of 1st peak in 28 to 560

Sxx = Xsq / bias[0]
Sxx[1:-1] = 2 * Sxx[1:-1]           # fold the freq axis
Sxx = Sxx / N                       # normalize S for avg power
n0 = N / mp
np = argmax(Sxx[n0-2:n0+3]) + n0-2  # bin of the nearest peak power

# check
print "\nAverage Power"
print "  p:", p
print "Rxx:", Rxx[0]                # should equal dot product, p
print "Sxx:", sum(Sxx), '\n'        # should equal Rxx[0]

figure().subplots_adjust(hspace=0.5)
subplot2grid((2,1), (0,0))
title('Autocorrelation, R$_{xx}$'); xlabel('Lags')
mr = r_[:3 * mp]
plot(Rxx[mr]); plot(mp, Rxx[mp], 'ro')
xticks(mp/2 * r_[1:6])
grid(); axis('tight'); ylim(1.25*min(Rxx), 1.25*max(Rxx))

subplot2grid((2,1), (1,0))
title('Power Spectral Density, S$_{xx}$'); xlabel('Frequency (Hz)')
fr = r_[:5 * np]; f = fs * fr / N; 
vlines(f, 0, Sxx[fr], colors='b', linewidth=2)
xticks((fs * np/N  * r_[1:5]).round(3))
grid(); axis('tight'); ylim(0,1.25*max(Sxx[fr]))
show()

Rxx and Sxx

输出:

Average Power
  p: 0.0410611012542
Rxx: 0.0410611012542
Sxx: 0.0410611012542 

峰值滞后为538,即44100/538 = 81.97 Hz。第一次通过非循环DFT显示箱61处的基波,其为82.10 +/- 0.67Hz。第二遍使用的窗口长度为538 * 15 = 8070,因此DFT频率包括字符串的基本周期和谐波。这使得无偏的循环自相关能够以更少的谐波扩展来改善PSD估计(即,相关性可以周期性地环绕窗口)。

编辑:更新以使用Welch的方法来估计自相关。重叠窗口可以补偿汉明窗口。我还计算了汉明窗口的锥形偏差以消除自相关。

编辑:添加了具有循环相关性的第二遍以清除功率谱密度。此过程使用3个不重叠的矩形窗口,长度为538 * 15 = 8070(足够短,几乎静止不动)。循环相关的偏差是常数,而不是汉明窗口的锥形偏差。

答案 1 :(得分:4)

在估计和弦中的音高比估计单个弦的音高或一次播放的音符要困难得多。和弦中多个音符的泛音可能都是重叠和交错的。并且普通和弦中的所有音符本身可能处于一个或多个不存在的低音音符的泛音频率。

对于单音符,自相关是一些吉他调音器常用的技术。但是在自相关的情况下,你必须意识到一些潜在的八度不确定性,因为吉他可能会产生不和谐和衰减的泛音,因此从音高周期到音高周期并不完全匹配。倒谱和谐波产品频谱是另外两种音高估算方法,根据吉他和音符的不同,可能有也可能没有不同的问题。

RAPT似乎是一种已发布的算法,可用于更稳健的音高估计。尹是另一个。

此外,Objective C是ANSI C的超集。因此,您可以使用在Objective C应用程序中找到的任何C DSP例程进行音高估计。

答案 2 :(得分:3)

使用libaubio (link)并感到高兴。尝试实现一个有趣的频率估算器,这是我失去的最大时间之一。如果你想自己做,我建议你遵循YINFFT方法(link)