单吉他音符Python的谐波产品谱

时间:2017-05-14 12:57:16

标签: python signal-processing pitch-tracking

我正试图检测用吉他演奏的B3音符的音高。可以找到音频here

这是频谱图: spectrogram

正如您所看到的,基本音高约为250Hz,与B3音符相对应。

它还包含大量的谐波,这就是我选择使用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

1 个答案:

答案 0 :(得分:1)

根据频谱图中谐波之间的距离,我估计音高约为150-200 Hz。那么,为什么音高检测算法没有检测到我们在频谱图中可以通过眼睛看到的音高呢?我有一些猜测:

音符只持续几秒钟。一开始,有一个漂亮的谐波叠加,有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)