2011-02-18 5 views
17

Iphone 용 기타 튜너 앱을 만들고 싶습니다. 내 목표는 기타 줄에서 발생하는 소리의 기본 빈도를 찾는 것입니다. Apple이 제공 한 aurioTouch 샘플의 코드를 사용하여 주파수 스펙트럼을 계산했으며 주파수가 가장 높은 진폭을 찾았습니다. 순수한 사운드 (하나의 주파수 만 갖는 사운드)에서는 잘 작동하지만 기타 스트링의 사운드에서는 잘못된 결과를 만들어냅니다. 나는 이것이 기본 스트링보다 높은 진폭을 가질 수있는 기타 스트링에 의해 오버 톤이 발생하기 때문이라고 읽었습니다. 기본 주파수를 어떻게 찾을 수있어 기타 줄에서 작동합니까? 사운드 분석 (또는 신호 처리)을위한 C/C++/Obj-C의 오픈 소스 라이브러리가 있습니까?기타 줄 소리의 기본 주파수를 찾는 방법은 무엇입니까?

+1

저는 음악가가 아니지만 튜너를하려한다면 왜 화음을 식별해야합니까? 한 번에 한 문자열을 쉽게 조정할 수 있습니까? – mtrw

+0

@mtrw 맞춤법 오류였습니다 ... "코드"대신 "코드"여야합니다 ... 오해를해서 죄송합니다. – Mircea

+6

그래서 오류를 수정하려면 편집하십시오! 그리고 더 혼란을 피하기 위해 "문자열"이라는 용어를 사용하십시오. – Clifford

답변

45

신호의 자기 상관 (DFT의 제곱의 역변환)을 사용할 수 있습니다. 44100 샘플/s에서 샘플링하는 경우 82.4 Hz 기본 신호는 약 535 샘플이지만 1479.98 Hz는 약 30 샘플입니다. 해당 범위에서 최대의 지연 (예 : 28에서 560)을 찾으십시오. 당신의 창문이 적어도 2 개의 기간이 가장 긴 기초의 1070 샘플이 될 것입니다. 2048 샘플 버퍼의 2의 다음 제곱으로. 더 나은 주파수 분해능과 편향된 추정치를 얻으려면 더 긴 버퍼를 사용하되 신호가 거의 정지하지 않을 정도로 오래 걸리지는 마십시오. 여기 파이썬 예제이다

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분의 44,100 = 81.97 Hz에서 인 538이다. 첫 번째 패스 비순환 DFT는 82.10 +/- 0.67 Hz 인 빈 (bin) 61에서 기본을 보여줍니다. 두 번째 패스는 창 길이가 538 * 15 = 8070이므로 DFT 주파수에는 기본주기와 문자열의 고조파가 포함됩니다. 이는보다 적은 고조파 확산 (즉, 상관 관계가 윈도우 주위를 주기적으로 랩핑 할 수 있음)을 갖는 개선 된 PSD 추정을 위해 ubiased주기 자기 상관을 가능하게한다.

편집 : Welch의 방법을 사용하여 자기 상관을 추정하도록 업데이트되었습니다. 창을 겹치게하면 해밍 창을 보완합니다. 나는 또한 자기 상관을 unbias하기 위해 해밍 창의 테이퍼 바이어스를 계산한다.

편집 : 주기적 상관 관계가있는 2 차 통과를 추가하여 전력 스펙트럼 밀도를 정리했습니다. 이 패스는 3 개의 겹치지 않는 직사각형 윈도우 길이 538 * 15 = 8070 (거의 고정 될만큼 충분히 짧음)을 사용합니다. 주기 상관 관계에 대한 바이어스는 해밍 창의 테이퍼 바이어스 대신에 일정합니다.

+5

+1 놀랍도록 풍부한 답변. – jv42

+1

이 기법은 기본 파가 고조파보다 작은 경우 (또는 모두 누락 된 경우) 작동합니까? – mtrw

+1

@mtrw : 2Hz의 오버 톤과 3Hz의 오버 톤이있는 경우 삼각파 모양의 파형이 1 초마다 반복됩니다. 1 초의 사인 곡선 콘텐츠가 포함 된 경우에도 충분히 긴 시간 동안의 자기 상관 관계가 나타납니다. – hotpaw2

4

코드에서 음악 피치를 찾는 것이 한 번에 재생되는 단일 문자열 또는 음표의 피치를 추정하는 것보다 훨씬 어렵습니다. 코드의 여러 음표에 대한 배음은 모두 겹쳐지고 삽입 될 수 있습니다. 그리고 공통 화음의 모든 음표는 하나 이상의 존재하지 않는 낮은 음정의 음표에 대해 자체적으로 오버 톤 주파수에있을 수 있습니다.

단일 음표의 경우 자기 상관은 일부 기타 튜너에서 사용되는 일반적인 기술입니다. 그러나 자기 상관법을 사용하면 기타가 옥타브의 잠재적 인 불확실성을 인식해야합니다. 기타가 비할 데없는 붕괴 된 배음을 생성 할 수 있으므로 피치주기와 피치주기가 정확히 일치하지 않습니다. Cepstrum 및 Harmonic Product Spectrum은 기타 및 음표에 따라 다른 문제가 있거나 없을 수있는 두 가지 피치 추정 방법입니다.

RAPT은보다 견고한 피치 추정을 위해 하나의 공개 된 알고리즘 인 것으로 나타난다. YIN은 또 하나입니다.

또한 Objective C는 ANSI C의 수퍼 세트입니다. 따라서 Objective C 애플리케이션에서 피치 추정을 위해 찾은 C DSP 루틴을 사용할 수 있습니다.

+0

@eryksun 맞습니다 ... "문자열"에 대한 동의어로 "코드"를 사용하려고했습니다. – Mircea

+0

답장을 보내 주셔서 감사 드리며 "코드"오류로 인해 유감스럽게 생각합니다. – Mircea

3

libaubio (link)을 사용하고 만족하십시오. 근본적인 주파수 추정기를 구현하려고하는 것이 가장 큰 시간이었습니다. 직접하고 싶다면 YINFFT 방식을 따르는 것이 좋습니다 (link)