Audio 如何找到吉他弦声音的基频?

Audio 如何找到吉他弦声音的基频?,audio,signal-processing,guitar,Audio,Signal Processing,Guitar,我想为Iphone构建一个吉他调谐器应用程序。我的目标是找到吉他弦产生的声音的基本频率。我使用了苹果公司提供的aurioTouch样本中的一些代码来计算频谱,我找到了振幅最高的频率。它适用于纯声音(只有一个频率的声音),但对于来自吉他弦的声音,它会产生错误的结果。我读到这是因为吉他弦产生的泛音可能比基音的振幅更高。我怎样才能找到吉他弦的基频呢?C/C++/Obj-C中是否有用于声音分析(或信号处理)的开源库 您可以使用信号的自相关,即DFT幅值平方的逆变换。如果以44100个采样/秒的速度采样,

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

您可以使用信号的自相关,即DFT幅值平方的逆变换。如果以44100个采样/秒的速度采样,那么82.4 Hz的基本频率约为535个采样,而1479.98 Hz约为30个采样。查看该范围内的峰值正滞后(例如,从28到560)。确保您的窗口至少是最长基本周期的两个周期,这将是1070个样本。下一个二次方是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频率包括基波周期和弦的谐波。这使得基于泛化的循环自相关能够用于改进的PSD估计,具有更少的谐波扩展(即相关性可以周期性地环绕窗口)

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


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

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

对于单音符,自相关是一些吉他调谐器常用的技术。但对于自相关,你必须意识到一些潜在的倍频程不确定性,因为吉他可能会产生不和谐和衰减的泛音,因此在不同的音调周期之间并不完全匹配。倒谱和声积谱是另外两种基音估计方法,根据吉他和音符的不同,它们可能有不同的问题,也可能没有不同的问题

这似乎是一种更稳健的基音估计算法。尹是另一个


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

使用libaubio并感到高兴。这是我尝试实现基本频率估计器所损失的最大时间之一。如果你想自己做,我建议你遵循YINFFT方法,我不是音乐家,但如果你想做调谐器,为什么需要识别和弦?一次调一个字符串不是更容易吗?@mtrw这是拼写错误。。。它应该是“弦”而不是“弦”。。。很抱歉造成误解,请编辑它以修复错误!并使用“字符串”一词来避免进一步的混淆。这个问题和答案非常有用。答案(几乎)与objective-C无关。如果你能去掉objective-c标签,也能从问题中去掉objective-c,让更多的人更容易找到它,那就太好了。如果你找到了iphone的解决方案,请帮我解决。当基波小于谐波(或完全缺失)时,这项技术有效吗?@mtrw:如果你有一个2 Hz的泛音和一个3 Hz的泛音,产生的三角形波形仍然会每1秒重复一次,即使有0个1 Hz的正弦内容,在足够长的窗口内自相关也会发现。@eryksun你是对的。。。我打算写“cord”作为“string”的同义词谢谢你的回答,我为“chord”-“cord”的错误感到抱歉