尝试使用Python中的FFT分析音频信号

4
我一直在尝试使用FFT来获取信号的频率,但处理起来有些困难。我发现了一个网站,介绍了如何使用FFT来分析和绘制信号:http://macdevcenter.com/pub/a/python/2001/01/31/numerically.html?page=2。但是,我在Python 2.7中实现时遇到了问题。编辑后,我用改进版代码实现了它。这个版本可以工作,并将波形(有点慢)绘制到图表上。不过,我想知道读取帧的正确方法是什么——我读到说偶数数组索引是左声道的(奇数则为右声道)。所以,我应该读取多少帧,然后除以采样宽度,如果是立体声,则每隔一个偶数帧采样左声道,对吗?
import scipy
import wave
import struct
import numpy
import pylab

fp = wave.open('./music.wav', 'rb')

samplerate = fp.getframerate()
totalsamples = fp.getnframes()
fft_length = 256 # Guess
num_fft = (totalsamples / fft_length) - 2

#print (samplerate)

temp = numpy.zeros((num_fft, fft_length), float)

leftchannel = numpy.zeros((num_fft, fft_length), float)
rightchannel = numpy.zeros((num_fft, fft_length), float)

for i in range(num_fft):

tempb = fp.readframes(fft_length / fp.getnchannels() / fp.getsampwidth());

up = (struct.unpack("%dB"%(fft_length), tempb))

temp[i,:] = numpy.array(up, float) - 128.0

temp = temp * numpy.hamming(fft_length)

temp.shape = (-1, fp.getnchannels())

fftd = numpy.fft.fft(temp)

pylab.plot(abs(fftd[:,1]))

pylab.show()

我正在加载的音乐是我自己制作的。编辑:现在,我通过读取帧并将当前要读取的数字除以每帧的通道数和位数来读取音频文件。这样做会丢失任何数据吗?这是我唯一能获取任何数据的方法 - 否则文件处理程序无法将太多数据读入struct.unpack函数中。此外,我想分离左声道和右声道(获取每个声道的FFT数据)。我该如何做?

尝试实现对len(tempb)的检查。根据http://docs.python.org/library/struct.html#struct.unpack,它必须恰好是正确的长度,并且`readframes`将读取最多`fft_length`字节。 - bkconrad
1个回答

1

我已经很久没有使用scipy的numpy/numarray版本了,但是我会寻找frombuffer函数。它比尝试通过struct.unpack来处理所有数据要容易得多。以下是使用numpy读取数据的示例:

fp = wave.open('./music.wav', 'rb')
assert fp.getnchannels() == 1, "Assumed 1 channel"
assert fp.getsampwidth() == 2, "Assuming int16 data"
numpy.frombuffer(fp.getnframes(fp.readframes()), 'i2')

请记住,波形文件中可能包含不同的数据类型和多个通道,因此在解包时要注意。


谢谢您的建议。我尝试了这个方法,但是出现了另一个错误 - ValueError: operands could not be broadcast together with shapes (512) (256)。还有其他建议吗?也许我应该从头开始学习有关将声音文件读入缓冲区的核心功能... - SolarLune
我不知道从零开始,但是了解一下你要使用的API以及它们的功能会很有帮助。为了处理你提到的错误,你可以对数组执行切片操作,然后适当地设置.shape属性。 - Shane Holloway
感谢您的建议,Shane。我已经阅读了一些资料并成功地让它工作了。但是,我想能够读取单独的左右声道 - 您是否知道这是如何工作的?我从文件中读取帧的唯一方法是将总帧数除以字节数和通道数,从而使其成为单声道...?还是生成的数组仍然是立体声的? - SolarLune
Wave文件以每帧的顺序交错通道。分离通道的numpy方法是改变数组的形状。假设您将getsamplewidth()匹配到匹配的numpy数据类型,您可以设置结果形状res.shape = (-1, fp.getnchannels())。然后,您可以使用res[:,0]获取通道0的数据,并使用res[:,1]获取通道1的数据。 - Shane Holloway
嗨,也许你可以详细说明一下 - 我不清楚你是如何获得不同的通道的。你所说的“将getsamplewidth()函数与匹配的numpy数据类型进行匹配”是什么意思? - SolarLune

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接