Python 查找音频频率和幅度随时间变化情况
我想做的是这样的:我想要找出一个.wav文件中每1毫秒的音频频率和音量,并把这些数据保存到一个文件里。我已经画出了频率和音量的关系图,也画出了音量随时间变化的图,但我就是搞不清楚频率是如何随时间变化的。我的最终目标是能够读取这个文件,利用音量来调整一些变量,而频率则用来决定哪些变量被使用,这部分似乎比较简单。我一直在使用numpy、audiolab、matplotlib等工具,利用快速傅里叶变换(FFT),但就是无法解决这个问题,任何帮助都非常感谢!谢谢!
1 个回答
7
使用短时傅里叶变换(STFT)和重叠窗口来估计声谱图。为了省去自己动手的麻烦,你可以使用Matplotlib库中的specgram方法。选择一个足够小的窗口很重要,这样音频信号在这个窗口内可以被认为是稳定的,同时缓冲区的大小应该是2的幂,这样可以高效地使用常见的基2快速傅里叶变换(fft)。512个样本(在48 kHz采样率下大约是10.67毫秒;或者每个频率桶93.75赫兹)就足够了。对于48 kHz的采样率,重叠464个样本可以让你每1毫秒评估一次滑动窗口(也就是说,移动48个样本)。
补充说明:
这里有一个例子,使用mlab.specgram
处理一个8秒的信号,这个信号每秒有一个音调,从2 kHz到16 kHz。注意瞬态响应的表现。我在4秒处放大了细节,显示出更详细的响应。频率在4秒时发生了变化,但瞬态信号需要一个缓冲长度(512个样本;大约正负5毫秒)才能通过。这展示了由于非稳定过渡而导致的频谱/时间模糊现象。此外,即使信号是稳定的,数据窗口化也会导致频谱泄漏的问题。为了减少泄漏的旁瓣,使用了汉明窗窗口函数,但这也会使主瓣变宽。
import numpy as np
from matplotlib import mlab, pyplot
#Python 2.x:
#from __future__ import division
Fs = 48000
N = 512
f = np.arange(1, 9) * 2000
t = np.arange(8 * Fs) / Fs
x = np.empty(t.shape)
for i in range(8):
x[i*Fs:(i+1)*Fs] = np.cos(2*np.pi * f[i] * t[i*Fs:(i+1)*Fs])
w = np.hamming(N)
ov = N - Fs // 1000 # e.g. 512 - 48000 // 1000 == 464
Pxx, freqs, bins = mlab.specgram(x, NFFT=N, Fs=Fs, window=w,
noverlap=ov)
#plot the spectrogram in dB
Pxx_dB = np.log10(Pxx)
pyplot.subplots_adjust(hspace=0.4)
pyplot.subplot(211)
ex1 = bins[0], bins[-1], freqs[0], freqs[-1]
pyplot.imshow(np.flipud(Pxx_dB), extent=ex1)
pyplot.axis('auto')
pyplot.axis(ex1)
pyplot.xlabel('time (s)')
pyplot.ylabel('freq (Hz)')
#zoom in at t=4s to show transient
pyplot.subplot(212)
n1, n2 = int(3.991/8*len(bins)), int(4.009/8*len(bins))
ex2 = bins[n1], bins[n2], freqs[0], freqs[-1]
pyplot.imshow(np.flipud(Pxx_dB[:,n1:n2]), extent=ex2)
pyplot.axis('auto')
pyplot.axis(ex2)
pyplot.xlabel('time (s)')
pyplot.ylabel('freq (Hz)')
pyplot.show()