我正在使用FFT从音频文件中提取每个频率分量的幅度。实际上,在Audacity中已经有一个称为Plot Spectrum的功能可以帮助解决问题。取由3kHz正弦和6kHz正弦组成的example audio file,频谱结果如下图所示。您可以看到峰值为3KHz和6kHz,没有多余的频率。
现在,我需要实现相同的功能并在Python中绘制相似的结果。在rfft
的帮助下,我接近Audacity的结果,但是在获得此结果后,仍然有很多问题需要解决。
相关代码:
import numpy as np
from pylab import plot, show
from scipy.io import wavfile
sample_rate, x = wavfile.read('sine3k6k.wav')
fs = 44100.0
rfft = np.abs(np.fft.rfft(x))
p = 20*np.log10(rfft)
f = np.linspace(0, fs/2, len(p))
plot(f, p)
show()
更新
我将汉宁窗与整个长度信号相乘(对吗?)。裙子的大部分振幅都在40以下。
按照@Mateen Ulhaq所说,将y轴缩放为分贝。结果更接近于Audacity。我可以将-90dB以下的振幅处理得如此之低以至于可以忽略吗?
更新的代码:
fs, x = wavfile.read('input/sine3k6k.wav')
x = x * np.hanning(len(x))
rfft = np.abs(np.fft.rfft(x))
rfft_max = max(rfft)
p = 20*np.log10(rfft/rfft_max)
f = np.linspace(0, fs/2, len(p))
关于赏金
有了上面更新中的代码,我可以以分贝为单位测量频率分量。最高可能值为0dB。但是该方法仅适用于特定的音频文件,因为它使用了此音频的rfft_max
。我想像Audacity一样,在一个标准规则中测量多个音频文件的频率分量。
我也started a discussion也在Audacity论坛上,但是我仍然不清楚如何实现我的目的。
答案 0 :(得分:4)
在对Audacity源代码进行一些反向工程之后,这里有一些答案。首先,他们使用Welch algorithm来估计PSD。简而言之,它将信号拆分为重叠的段,应用一些窗口函数,应用FFT并对结果求平均值。主要是因为存在噪音时,这有助于获得更好的结果。无论如何,在提取必要的参数之后,这里是近似于Audacity频谱图的解决方案:
import numpy as np
from scipy.io import wavfile
from scipy import signal
from matplotlib import pyplot as plt
segment_size = 512
fs, x = wavfile.read('sine3k6k.wav')
x = x / 32768.0 # scale signal to [-1.0 .. 1.0]
noverlap = segment_size / 2
f, Pxx = signal.welch(x, # signal
fs=fs, # sample rate
nperseg=segment_size, # segment size
window='hanning', # window type to use
nfft=segment_size, # num. of samples in FFT
detrend=False, # remove DC part
scaling='spectrum', # return power spectrum [V^2]
noverlap=noverlap) # overlap between segments
# set 0 dB to energy of sine wave with maximum amplitude
ref = (1/np.sqrt(2)**2) # simply 0.5 ;)
p = 10 * np.log10(Pxx/ref)
fill_to = -150 * (np.ones_like(p)) # anything below -150dB is irrelevant
plt.fill_between(f, p, fill_to )
plt.xlim([f[2], f[-1]])
plt.ylim([-90, 6])
# plt.xscale('log') # uncomment if you want log scale on x-axis
plt.xlabel('f, Hz')
plt.ylabel('Power spectrum, dB')
plt.grid(True)
plt.show()
一些必要的参数说明:
segment_size
对应于Audacity的GUI中的Size
。segment_size/2
,如Audacity代码中一样。第二张图片中振幅的物理含义是什么?
基本上是频率仓中的能量。
如何像Audacity中一样将幅度归一化为0dB?
您需要选择一些参考点。分贝图总是与某物相关。当选择最大能量箱作为参考时,您的0db点就是最大能量(显然)。可以将最大振幅的正弦波设置为参考能量。请参见ref
变量。正弦信号的功率仅是RMS的平方,而要获得RMS,只需将幅度除以sqrt(2)。因此比例因子仅为0.5。请注意,log10
之前的系数是10而不是20,这是因为我们处理信号的功率而不是振幅。
我可以将-90dB以下的幅度处理得如此之低以至于可以忽略吗?
是的,任何低于-40dB的声音通常被认为是可疏忽的