2011-02-18 108 views
17

我想爲iPhone構建一個吉他調音器應用程序。我的目標是找到吉他弦所產生的聲音的基本頻率。我已經使用Apple提供的aurioTouch示例中的一些代碼來計算頻譜,並且我找到了幅度最高的頻率。它對純音(僅具有一個頻率的音)起作用很好,但對於來自吉他弦的聲音它會產生錯誤的結果。我讀過這是因爲吉他弦產生的泛音可能比基本弦的幅度更大。我怎樣才能找到基本的頻率,所以它適用於吉他弦? C/C++/Obj-C中是否有開放源代碼庫用於聲音分析(或信號處理)?如何找到吉他絃音的基本頻率?

+1

我不是一個音樂家,但如果你想要做一個調音器,爲什麼你需要找出一個和絃?一次調整一個字符串不是更容易嗎? – mtrw 2011-02-18 22:30:13

+0

@mtrw這是拼寫錯誤......它應該是「繩索」,而不是「和絃」......我很抱歉誤會 – Mircea 2011-02-19 12:28:36

+6

所以編輯它來修復錯誤!並使用術語「字符串」來避免任何進一步的混淆。 – Clifford 2011-02-20 08:33:24

回答

45

您可以使用信號的自相關,它是DFT幅度平方的逆變換。如果以44100樣本/秒進行採樣,那麼82.4Hz基波約爲535個樣本,而1479.98Hz約爲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() 

Rxx and Sxx

輸出:

Average Power 
    p: 0.0410611012542 
Rxx: 0.0410611012542 
Sxx: 0.0410611012542 

峯值滯後是538,這是五百三十八分之四萬四千百= 81.97赫茲。第一遍非循環DFT顯示了在箱61處的基波,其爲82.10 +/- 0.67Hz。第二遍使用的窗口長度爲538 * 15 = 8070,所以DFT頻率包括字符串的基本週期和諧波。這使得無偏循環自相關能夠用於具有較少諧波擴展的改進的PSD估計(即,相關性可以週期性地圍繞窗口)。

編輯:更新爲使用韋爾奇方法來估計自相關。重疊窗口補償了漢明窗口。我還計算了海明窗的錐形偏差以去除自相關。

編輯:添加了循環相關的第2遍以清除功率譜密度。此通道使用3個不重疊的矩形窗口,長度爲538 * 15 = 8070(足夠短,幾乎靜止不動)。循環相關的偏差是一個常數,而不是漢明窗的錐形偏差。

4

找到一個和絃中的音樂節奏要比估計一次播放的單個字符串或音符的節奏困難得多。和絃中的多個音符的泛音可能都是重疊和交錯的。並且所有普通和絃的音符本身可以處於一個或多個不存在的低音音符的泛音頻率。

對於單音符,自相關是一些吉他調音器常用的技巧。但是對於自相關,你必須意識到一些潛在的八度不確定性,因爲吉他可能會產生不和諧和衰減的泛音,因此從音調週期到音高週期不完全匹配。 Cepstrum和Harmonic Product Spectrum是另外兩種基音估計方法,根據吉他和音符的不同,它們可能會有也可能不會有不同的問題。

RAPT似乎是一種公開的算法,用於更可靠的基音估計。陰是另一個。另外目標C是ANSI C的一個超集。因此,您可以使用您在Objective C應用程序中爲音高估計找到的任何C DSP例程。

3

使用libaubio (link)並很開心。這是我嘗試實現一個頻率估計器的最大時間。如果你想自己做,我建議你按照YINFFT的方法(link)