【问题标题】:How to find the fundamental frequency of a guitar string sound?如何找到吉他弦音的基频?
【发布时间】:2011-06-29 23:31:22
【问题描述】:

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

【问题讨论】:

  • 我不是音乐家,但如果你想做调音器,为什么需要识别和弦?一次调一个弦不是更容易吗?
  • @mtrw 这是拼写错误……应该是“cord”而不是“chord”……我很抱歉造成误解
  • 所以编辑它来修复错误!并使用术语“字符串”以避免任何进一步的混淆。
  • 这个问答很有帮助。它的回答方式(几乎)与objective-C无关。如果您可以删除objective-c 标签并从问题中删除objective-C 以使更广泛的受众更容易找到它,那就太好了。
  • 如果你找到了iphone的解决方案,请帮帮我。

标签: audio signal-processing guitar


【解决方案1】:

您可以使用信号的自相关,即 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()

输出:

Average Power
  p: 0.0410611012542
Rxx: 0.0410611012542
Sxx: 0.0410611012542 

峰值延迟为 538,即 44100/538 = 81.97 Hz。第一遍非循环 DFT 显示 bin 61 处的基波,即 82.10 +/- 0.67 Hz。第二遍使用 538*15 = 8070 的窗口长度,因此 DFT 频率包括弦的基波周期和谐波。这实现了 ubiased 循环自相关,以改进 PSD 估计,具有更少的谐波扩展(即相关可以周期性地环绕窗口)。

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

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

【讨论】:

  • 当基波小于谐波(或完全缺失)时,这种技术是否有效?
  • @mtrw:如果你有一个 2 Hz 的泛音和一个 3 Hz 的泛音,那么看起来呈三角形的波形仍将每 1 秒重复一次,即使在零 1 的情况下,在足够长的窗口中也会发现这种自相关Hz 正弦含量。
【解决方案2】:

找出和弦中的音高比估计一次演奏的单个弦或音符的音高要困难得多。一个和弦中多个音符的泛音可能都是重叠和交错的。对于一个或多个不存在的低音音符,公共和弦中的所有音符本身都可能处于泛音频率。

对于单音,自相关是一些吉他调音师常用的技术。但是对于自相关,您必须注意一些潜在的八度不确定性,因为吉他可能会产生不和谐和衰减的泛音,因此在不同的音高周期之间并不完全匹配。 Cepstrum 和 Harmonic Product Spectrum 是另外两种音高估计方法,它们可能有也可能没有不同的问题,具体取决于吉他和音符。

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

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

【讨论】:

  • @eryksun 你说得对……我打算把“cord”写成“string”的同义词
  • 感谢您的回答,对于“chord”-“cord”的错误,我深表歉意
【解决方案3】:

使用 libaubio (link) 并快乐。尝试实施基本频率估计器是我最大的时间损失之一。如果你想自己做,我建议你按照 YINFFT 方法(link)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-28
    • 2012-03-15
    • 1970-01-01
    • 2012-12-30
    • 2011-07-24
    • 2013-03-01
    • 2016-05-07
    • 2012-05-27
    相关资源
    最近更新 更多