音频文件是16位单声道PCM音频文件,具有不同的采样率,长度为10-30ms。
import struct
from pydub import AudioSegment
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
sound = AudioSegment.from_wav("3000hz.wav")
raw_data = sound.raw_data# needs to be mono
sample_rate = sound.frame_rate
sample_size = sound.sample_width
channels = sound.channels
fmt = "%ih" % sound.frame_count() * channels
amplitudes= struct.unpack(fmt, raw_data)
yVals = scipy.fftpack.fft(amplitudes)
plt.plot(abs(yVals[:(len(yVals)/2)-1]),'r')
plt.show()
带有3000hz wav文件的输出(来自在线正弦波发生器)产生了不错的FFT,但峰值为9000,而不是3000。在其他测试中,相差3倍是一致的。这个可以吗?代码正确吗?
最佳答案
通过仅使用plt.plot()
数组而没有对应的y
数组的x
调用,它将使用0, 1, ..., N-1
作为x
值。这不是我们真正想要的,我们想要x轴上的频率。
让我们用“bin index”表示您现在在图中看到的x
值。假设数组的长度为N
,采样频率为fs
。在计算FFT时,bin索引0
对应于0 Hz的频率。下一个二进制索引1
对应于频率fs / N
Hz。这是因为FFT将具有N
值,并且从0
Hz变为fs
Hz,因此每个步都是fs / N
Hz。因此,下一个bin对应于2 * fs / N
Hz,依此类推。最后一个bin N-1
是(N-1)/N * fs
Hz,所以几乎是fs
Hz。
如果要创建一个振幅谱与频率关系图,则需要手动创建一个频率 vector ,其中包含每个仓位索引的实际频率。幸运的是,scipy.fftpack
包含一个函数: fftfreq
:
freq = scipy.fftpack.fftfreq(n=N, d=1.0 / fs)
然后,我们可以修改对
plt.plot()
的调用,以将freq
用作x
值而不是0 ... N-1
:plt.plot(freq, abs(yVals), 'r')
这样,峰值应该在正确的位置。
如果您只想查看单面频谱,则可以像在问题代码中已经做过的那样裁剪
freq
和yVals
。关于python - 这是读取音频文件FFT的正确方法吗? (python + wav),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54522881/