audio - How to find the fundamental frequency of a guitar string sound? -
i want build guitar tuner app iphone. goal find fundamental frequency of sound generated guitar string. have used bits of code auriotouch sample provided apple calculate frequency spectrum , find frequency highest amplitude . works fine pure sounds (the ones have 1 frequency) sounds guitar string produces wrong results. have read because of overtones generate guitar string might have higher amplitudes fundamental one. how can find fundamental frequency works guitar strings? there open-source library in c/c++/obj-c sound analyzing (or signal processing)?
you can use signal's autocorrelation, inverse transform of magnitude squared of dft. if you're sampling @ 44100 samples/s, 82.4 hz fundamental 535 samples, whereas 1479.98 hz 30 samples. peak positive lag in range (e.g. 28 560). make sure window @ least 2 periods of longest fundamental, 1070 samples here. next power of 2 that's 2048-sample buffer. better frequency resolution , less biased estimate, use longer buffer, not long signal no longer approximately stationary. here's example in 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 sample of guitar playing open string 6 # fundamental frequency of 82.4 hz (in theory), # sample @ 81.97 hz g = fromstring(wave.open('dist_gtr_6.wav').readframes(-1), dtype='int16') g = g / float64(max(abs(g))) # normalize +/- 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 k in range(k): m = k * ( m - l) xt = w * x[m:m+m] # use rfft efficiency (assumes x real-valued) xsq = xsq + abs(rfft(xt, n)) ** 2 xsq = xsq / k wsq = abs(rfft(w, n)) ** 2 bias = irfft(wsq) # unbiasing rxx , sxx p = dot(x,x) / len(x) # avg power, used 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 side lobes in dft xsq, bias, p = welch(x, w, l, n) rxx = irfft(xsq) # acyclic autocorrelation rxx = rxx / bias # unbias (bias tapered) mp = argmax(rxx[28:561]) + 28 # index of 1st peak in 28 560 # 2nd pass: cyclic autocorrelation n = m = l - (l % mp) # window integer number of periods # shortened ~8192 stationarity x = g[mi:mi+k*m] # data 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 constant) mp = argmax(rxx[28:561]) + 28 # index of 1st peak in 28 560 sxx = xsq / bias[0] sxx[1:-1] = 2 * sxx[1:-1] # fold freq axis sxx = sxx / n # normalize s avg power n0 = n / mp np = argmax(sxx[n0-2:n0+3]) + n0-2 # bin of 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()
output:
average power p: 0.0410611012542 rxx: 0.0410611012542 sxx: 0.0410611012542
the peak lag 538, 44100/538 = 81.97 hz. first-pass acyclic dft shows fundamental @ bin 61, 82.10 +/- 0.67 hz. 2nd pass uses window length of 538*15 = 8070, dft frequencies include fundamental period , harmonics of string. enables ubiased cyclic autocorrelation improved psd estimate less harmonic spreading (i.e. correlation can wrap around window periodically).
edit: updated use welch's method estimate autocorrelation. overlapping windows compensates hamming window. calculate tapered bias of hamming window unbias autocorrelation.
edit: added 2nd pass cyclic correlation clean power spectral density. pass uses 3 non-overlapping, rectangular windows length 538*15 = 8070 (short enough stationary). bias cyclic correlation constant, instead of hamming window's tapered bias.
Comments
Post a Comment