2017-05-14 74 views
2

我想要檢測用吉他彈奏的B3音符的音高。音頻可以找到here單吉他音符Python的諧波產品譜

這是頻譜: spectrogram

正如你所看到的,它是可見的根本間距大約對應於B3注爲250Hz。

它還含有大量的諧波,這就是爲什麼我選擇使用here的HPS。我使用此代碼檢測間距:

def freq_from_hps(signal, fs): 
    """Estimate frequency using harmonic product spectrum 
    Low frequency noise piles up and overwhelms the desired peaks 
    """ 
    N = len(signal) 
    signal -= mean(signal) # Remove DC offset 

    # Compute Fourier transform of windowed signal 
    windowed = signal * kaiser(N, 100) 

    # Get spectrum 
    X = log(abs(rfft(windowed))) 

    # Downsample sum logs of spectra instead of multiplying 
    hps = copy(X) 
    for h in arange(2, 9): # TODO: choose a smarter upper limit 
     dec = decimate(X, h) 
     hps[:len(dec)] += dec 

    # Find the peak and interpolate to get a more accurate peak 
    i_peak = argmax(hps[:len(dec)]) 
    i_interp = parabolic(hps, i_peak)[0] 

    # Convert to equivalent frequency 
    return fs * i_interp/N # Hz 

我的採樣速率爲40000然而,而不是越來越接近250Hz的(B3注)的結果,我得到0.66Hz。這怎麼可能?

我也嘗試過使用自相關方法從相同的回購,但我也得到像10000Hz的不好的結果。

感謝一個答案我明白我必須應用濾波器來消除信號中的低頻。我怎麼做?有多種方法可以做到這一點,並推薦哪一種?

狀態更新:

通過回答提出的高通濾波器的工作。如果我在音頻信號的答案中應用該功能,則它會正確顯示約245Hz。但是,我想過濾整個信號,而不僅僅是它的一部分。一個音符可能位於信號的中間,或者一個信號包含多個音符(我知道一個解決方案是開始檢測,但我很想知道爲什麼這不起作用)。這就是爲什麼我編輯代碼返回filtered_audio

問題是,如果我這樣做,即使噪音已被正確刪除(見截圖)。結果我得到了0.05。

spectrogram

+0

你做了一個回送與你的聲卡/音頻接口或有其它已知的頻率源? - 其他人報告難以根據操作系統獲得所需的採樣率,驅動程序 – f5r5e5d

+0

當您嘗試計算整個剪輯的音高時,由於音高算法看不到頻譜圖,您會得到無意義的結果。頻譜圖顯示了隨時間變化的功率譜,但該算法僅適用於整個信號的平均功率譜。看看我的意思,看看整個信號的頻譜,並將它與音符的頻譜(0-1秒)進行比較。爲了解決剪輯中多個音符的可能性,我認爲你應該提出一個新問題。 – Michael

+0

謝謝,你是對的。我想我應該把信號分成多個信號(每播放一個音符,一個10-15幀的片段),然後爲每一個音符做HPS。你認爲這是一個合乎邏輯的方法嗎? – pk1914

回答

1

基於在頻譜中的諧波之間的距離,我會估計音調爲約150-200赫茲。那麼,爲什麼音高檢測算法沒有檢測到我們在頻譜圖中可以看到的音高?我有幾個猜測:

該筆記只持續幾秒鐘。一開始,有一個美麗的諧波疊加10個或更多的諧波!這些很快消失,5秒後甚至不可見。如果您試圖估計整個信號的音高,您的估計值可能會被5-12秒的「音高」所污染。 嘗試僅在前1-2秒內計算音調。

有太多的低頻噪聲。在頻譜圖中,您可以看到0到64 Hz之間的許多功率。這不是諧波的一部分,因此您可以嘗試使用高通濾波器將其刪除。

下面是一些代碼,沒有工作:

import numpy as np 
from scipy.io import wavfile 
from scipy import signal 
import matplotlib.pyplot as plt 

from frequency_estimator import freq_from_hps 
# downloaded from https://github.com/endolith/waveform-analyzer/ 

filename = 'Vocaroo_s1KZzNZLtg3c.wav' 
# downloaded from http://vocaroo.com/i/s1KZzNZLtg3c 

# Parameters 
time_start = 0 # seconds 
time_end = 1 # seconds 
filter_stop_freq = 70 # Hz 
filter_pass_freq = 100 # Hz 
filter_order = 1001 

# Load data 
fs, audio = wavfile.read(filename) 
audio = audio.astype(float) 

# High-pass filter 
nyquist_rate = fs/2. 
desired = (0, 0, 1, 1) 
bands = (0, filter_stop_freq, filter_pass_freq, nyquist_rate) 
filter_coefs = signal.firls(filter_order, bands, desired, nyq=nyquist_rate) 

# Examine our high pass filter 
w, h = signal.freqz(filter_coefs) 
f = w/2/np.pi * fs # convert radians/sample to cycles/second 
plt.plot(f, 20 * np.log10(abs(h)), 'b') 
plt.ylabel('Amplitude [dB]', color='b') 
plt.xlabel('Frequency [Hz]') 
plt.xlim((0, 300)) 

# Apply high-pass filter 
filtered_audio = signal.filtfilt(filter_coefs, [1], audio) 

# Only analyze the audio between time_start and time_end 
time_seconds = np.arange(filtered_audio.size, dtype=float)/fs 
audio_to_analyze = filtered_audio[(time_seconds >= time_start) & 
            (time_seconds <= time_end)] 

fundamental_frequency = freq_from_hps(audio_to_analyze, fs) 
print 'Fundamental frequency is {} Hz'.format(fundamental_frequency) 
+0

請參閱更新的問題。我添加了音頻文件,修復了聲譜圖並重構了問題。 – pk1914

+0

這看起來不錯。你介意解釋你所遵循的步驟嗎? 「filter_stop_freq」下的所有內容都被丟棄了嗎?我不太瞭解這些變量的大部分用法。 – pk1914

+0

我知道在這裏解釋它並不容易,所以參考將會很棒! :) – pk1914