用Python(MatPlotLib)进行实时FFT绘图

3
我正在通过麦克风接收音频流,使用pyaudio读取数据,对该数据执行FFT计算,并希望将FFT幅度数据显示在Y轴上,将FFT频率数据显示在X轴上,并且每秒更新20次(以20fps为例),基本上要像这样(https://www.youtube.com/watch?v=Tu8p2pywJAs&t=93s),但是低频率在左边,高频率在右边。 我的代码只完成到以下步骤。我是新手,连Python都不熟悉,希望得到帮助。请尽量用易懂的术语,如果需要详细解释,请尊重地提出。非常感谢您的时间!
import pyaudio
import numpy as np
import time
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib import style

pa = pyaudio.PyAudio()

callback_output = []

def callback(in_data, frame_count, time_info, flag):
    audio_data = np.fromstring(in_data, dtype=np.int16)
    callback_output.append(audio_data)
    return None,pyaudio.paContinue


stream = pa.open(format=pyaudio.paInt16,
                 channels=1,
                 rate=44100,
                 output=False,
                 input=True,
                 stream_callback=callback)

stream.start_stream()

fig = plt.gcf()
fig.show()
fig.canvas.draw()

while stream.is_active():
    fft_data = np.fft.fft(callback_output)
    fft_freq = np.fft.fftfreq(len(fft_data))
    plt.plot(fft_freq,fft_data)
    plt.xlim(min(fft_freq),max(fft_freq))
    fig.canvas.draw()
    plt.pause(0.05)
    fig.canvas.flush_events()
    fig.clear()

stream.close()
pa.terminate()
2个回答

3

我无法为您生成数据,但我写了一个示例,在循环中更新matplotlib图表:

import matplotlib.pyplot as plt
import numpy as np
import time


plt.ion() # Stop matplotlib windows from blocking

# Setup figure, axis and initiate plot
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = ax.plot([], [], 'ro-')

while True:
    time.sleep(0.5)

    # Get the new data
    xdata = np.arange(10)
    ydata = np.random.random(10)

    # Reset the data in the plot
    ln.set_xdata(xdata)
    ln.set_ydata(ydata)

    # Rescale the axis so that the data can be seen in the plot
    # if you know the bounds of your data you could just set this once
    # so that the axis don't keep changing
    ax.relim()
    ax.autoscale_view()

    # Update the window
    fig.canvas.draw()
    fig.canvas.flush_events()

在循环中,您只需更改分配xdata和ydata的行即可使其适用于您的数据。

如果您想在左侧获取低频率,则可能希望查看在fftfreq和fftdata上使用np.fft.fftshift:https://docs.scipy.org/doc/numpy/reference/generated/numpy.fft.fftshift.html


嗨,我尝试了这个方法以及其他方法,但是它们给我一些非常奇怪的图形,它们似乎在实时更新,但从视觉上来看毫无意义。你能否使用我创建的代码和你的解决方案告诉我是否也得到了奇怪的结果? - m e m e
可能会有一些问题。1)您正在将内容添加到回调输出列表中,而不是覆盖 - 这是您想要的行为吗?2)您没有向fftfreq提供样本间距,这可能会导致问题。您应该在fftfreq调用中传递d = 1/44100,以便您获得fft_freq = np.fft.fftfreq(len(fft_data),d = 1/44100)。 - James Downs
我现在正在使用audio_data作为全局变量,它确实可以正确清除图形,但是我得到的结果有些线条向上或向下延伸得非常远,这是轴线变化了,但我不知道如何将其设置为固定轴 :( 另外,自从您的回复后,我已经设置了采样率,只是轴线在变化,还有一些垂直线(我认为是振幅),始终在0频率值上很远的正负两侧绘制,只有Y轴在变化,这是我的fft_data(而不是我的fft_freq)。我也不确定如何在评论中发布照片。 - m e m e
啊,我用ShareX上传了一个绘图的gif,链接为https://i.imgur.com/iVDVwS8.gifv。当有两条大竖线时,是我在吹口哨,最后2或3秒钟,其余部分我认为是背景噪音,但有一些非常奇怪的异常情况,我认为轴正在重新调整,如何使它固定? - m e m e
当我按照你说的方法只设置一次轴缩放时,它只会根据音频数据的第一个块变成随机比例,所以这并不起作用 :( - m e m e
显示剩余2条评论

0
请尝试这段代码:
while stream.is_active():
    fft_data = np.fft.rfft(callback_output) # rfft removes the mirrored part that fft generates
    fft_freq = np.fft.rfftfreq(len(callback_output), d=1/44100) # rfftfreq needs the signal data, not the fft data
    plt.plot(fft_freq, np.absolute(fft_data)) # fft_data is a complex number, so the magnitude is computed here
    plt.xlim(np.amin(fft_freq), np.amax(fft_freq))
    fig.canvas.draw()
    plt.pause(0.05)
    fig.canvas.flush_events()
    fig.clear()

谢谢您的评论,但这个问题已经解决了。我完成了我的项目,并在此过程中学到了很多东西。希望这条评论能帮助正在寻找类似解决方案的其他人。 - m e m e

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