这是读取音频文件 FFT 的正确方法吗? (python + wav)

Posted

技术标签:

【中文标题】这是读取音频文件 FFT 的正确方法吗? (python + wav)【英文标题】:Is this the correct way to read FFT of a audio file? (python + wav) 【发布时间】:2019-02-04 19:17:42 【问题描述】:

音频文件是一个 16 位单声道 PCM 音频文件,具有不同的采样率和 10-30 毫秒的长度。

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 倍。这个可以吗?代码是否正确?

【问题讨论】:

乍一看,还可以。您可以绘制“幅度”来验证读取 wav 文件是否正确完成。 哦,请确保您正确解释了轴值。如果采样率 = 1000 并且 FFT 峰值为 100,这并不意味着实际频率为 100 Hz :-) @HenkvanderLaak 感谢您的帮助,它似乎给了我真正的频率 * 样本长度(以秒为单位)这是一个好的相关性吗? 【参考方案1】:

通过仅使用y 数组而没有对应的x 数组调用plt.plot(),它将使用0, 1, ..., N-1 作为x 值。这不是我们真正想要的,我们想要的是 x 轴上的频率。

让我们用“bin index”表示您现在在图中看到的x 值。设数组长度为N,采样频率为fs。在计算 FFT 时,bin 索引0 对应于 0 Hz 的频率。下一个 bin 索引 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。

如果我们想要创建一个具有幅度谱与频率的图,那么我们需要手动创建一个频率向量,其中包含每个 bin 索引的实际频率。幸运的是,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')

这样,峰值应该在正确的位置。

如果您只想查看单面光谱,则可以像问题中的代码一样裁剪 freqyVals

【讨论】:

以上是关于这是读取音频文件 FFT 的正确方法吗? (python + wav)的主要内容,如果未能解决你的问题,请参考以下文章

wav音频文件解析读取 定点转浮点分析 幅值提取(C语言实现)

如何使用 AudioToolbox 框架读取音频文件?

区分注释FFT算法

PHP 变量到 Java 小程序?

在 fft 中应用窗口函数的正确方法

返回 Numpys FFT 的数据并在音频文件中找到每秒的幅度和频率