10

我正在使用 FFT 从音频文件中提取每个频率分量的幅度。实际上,Audacity 中已经有一个叫做 Plot Spectrum 的功能可以帮助解决这个问题。以这个由 3kHz sine 和 6kHz sine 组成的音频文件为例,频谱结果如下图所示。你可以看到峰值在 3KHz 和 6kHz,没有额外的频率。

在此处输入图像描述

现在我需要实现相同的函数并在 Python 中绘制类似的结果。在 的帮助下,我已接近 Audacity 结果,rfft但在获得此结果后我仍有问题需要解决。

在此处输入图像描述

  1. 第二张图片中幅度的物理意义是什么?
  2. 如何像 Audacity 中的那样将幅度标准化为 0dB?
  3. 为什么超过 6kHz 的频率具有如此高的幅度(≥90)?我可以将这些频率缩放到相对较低的水平吗?

相关代码:

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 一样在一个标准规则中测量多个音频文件的频率分量。

我也在Audacity 论坛上开始讨论,但我仍然不清楚如何实现我的目的。

4

1 回答 1

9

在对 Audacity 源代码进行一些逆向工程之后,这里有一些答案。首先,他们使用Welch 算法来估计 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()

关于参数的一些必要说明:

  • wave 文件被读取为 16 位 PCM,为了与 Audacity 兼容,应将其缩放为 |A|<1.0
  • segment_size对应Size于 Audacity 的 GUI。
  • 默认窗口类型是“汉宁”,您可以根据需要更改它。
  • 重叠segment_size/2与 Audacity 代码中的一样。
  • 输出窗口的框架遵循 Audacity 风格。他们扔掉第一个低频箱,并把所有东西都削减到 -90dB 以下

在此处输入图像描述

第二张图片中幅度的物理意义是什么?

它基本上是频率仓中的能量。

如何像 Audacity 中的那样将幅度标准化为 0dB?

您需要选择一些参考点。以分贝为单位的图表总是与某事相关。当您选择最大能量箱作为参考时,您的 0db 点就是最大能量(显然)。可以设置为最大振幅的正弦波的参考能量。见ref变量。正弦信号中的功率只是 RMS 的平方,要获得 RMS,您只需要将幅度除以 sqrt(2)。所以比例因子只是 0.5。请注意,之前的因子log10是 10 而不是 20,这是因为我们处理的是信号功率而不是幅度。

我可以将低于-90dB的幅度处理得如此之低以至于可以忽略吗?

是的,低于 -40dB 的任何东西通常都可以忽略不计

于 2018-07-09T07:49:27.243 回答