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

在Python中尝试使用FFT分析音频信号,python,audio,signal-processing,Python,Audio,Signal Processing,我一直在尝试使用FFT来获得信号的频率,但处理起来有点困难。我在这里找到了一个关于使用FFT分析和绘制信号的站点: 但是我在用Python2.7实现它时遇到了一个问题。编辑我用改进的版本更新了代码。实际上,这一个可以工作,并将波形(稍微慢一点)绘制到图表上。我想知道这是否是读取帧的正确方法——我读到偶数数组索引用于左通道(因此奇数数组索引用于右通道,我想) 所以,我想我应该读多少帧,但除以采样宽度,然后每隔偶数帧对左声道进行采样,如果是立体声的话,嗯 import scipy import w

我一直在尝试使用FFT来获得信号的频率,但处理起来有点困难。我在这里找到了一个关于使用FFT分析和绘制信号的站点:

但是我在用Python2.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数据)。我该怎么做呢?

我很久没有使用scipy版本的numpy/numarray了,但是我在寻找函数。它比试图通过
struct.unpack
洗牌所有数据要容易得多。使用以下方法读取数据的示例:


请记住,wave文件中可能有不同的数据类型和多个通道,因此在解包时请注意这一点。

尝试执行
len(tempb)
检查。根据需要,它必须是正确的长度,
readframes
将读取“最多”
fft\u长度
字节。感谢您的建议。我尝试了这个,但是我得到了另一个错误-ValueError:操作数不能与shapes(512)(256)一起广播<还有其他建议吗?我可能应该从第一步开始,更多地了解将声音文件读入缓冲区的核心功能……我不知道如何从第一步开始,但最好了解一下您尝试使用的API以及它们的功能。要处理您提到的错误,您可以在数组上执行切片,然后适当地设置
.shape
属性。感谢Shane的建议,我确实读了一点,并设法使其正常工作。然而,我希望能够阅读单独的左声道和右声道-你知道这到底是如何工作的吗?我能够从文件中读取帧的唯一方法是将总帧数除以字节数和通道数,从而使其成为单声道。。。?或者生成的阵列仍然是立体声?波文件按每帧的顺序交错通道。分离通道的基本方法是改变阵列的形状。假设将
getsamplewidth()
与匹配的numpy数据类型相匹配,则可以设置生成的形状
res.shape=(-1,fp.getnchannels())
。然后,您可以使用
res[:,0]
获取频道0的数据,使用
res[:,1]
获取频道1的数据。嘿,也许您可以详细说明一下-我不知道如何获取不同的频道。“将getsamplewidth()函数与匹配的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')