Python中如何实现实时FFT绘图并定位、绘制及输出峰值?
实时音频频谱峰值标记实时更新解决方案
问题描述
首次使用pyaudio和matplotlib库实现麦克风输入的实时音频频域绘图,按指定间距输出峰值。程序运行无报错、帧率正常,关闭窗口可终止,但存在旧峰值标记叠加显示的问题;尝试循环内清图导致画面完全空白,已解决峰值x坐标偏移问题,需实现峰值标记的实时更新。
解决方案
核心思路是复用峰值绘图对象,而非每次循环新增绘图元素;同时避免不必要的清图操作,只更新数据而非重绘整个画布。
修改后的完整代码
import pyaudio import wave import struct from scipy.fftpack import fft from scipy.signal import find_peaks import matplotlib.pyplot as plt import numpy as np class Wave: def __init__(self, file) -> None: self.CHUNK = 1024 * 4 self.obj = wave.open(file, "r") self.callback_output = [] self.data = self.obj.readframes(self.CHUNK) self.rate = 44100 # 初始化PyAudio实例 self.p = pyaudio.PyAudio() # 打开音频流 self.stream = self.p.open(format=pyaudio.paInt16, channels=self.obj.getnchannels(), rate=self.rate, output=True, input=True, frames_per_buffer=self.CHUNK) def fft_plot(self, distance: float): x_fft = np.linspace(0, self.rate, self.CHUNK) fig, ax = plt.subplots() # 初始化频谱线 line_fft, = ax.semilogx(x_fft, np.random.rand(self.CHUNK), "-", lw=2) # 初始化峰值标记(空对象,后续更新数据) peaks_plot, = ax.plot([], [], "x", color="red") # 设置坐标轴范围 ax.set_xlim(20, self.rate / 2) ax.set_ylim(0, 0.5) # 固定y轴范围,避免画面跳动 plt.show(block=False) # 绑定窗口关闭事件(只绑定一次) def on_close(event): self.stream.stop_stream() self.stream.close() self.p.terminate() plt.close() exit() fig.canvas.mpl_connect('close_event', on_close) while True: # 读取音频数据 data = self.stream.read(self.CHUNK) # 转换为整数数组(匹配paInt16格式) self.data_int = struct.unpack(str(self.CHUNK * self.obj.getnchannels()) + 'h', data) # 计算FFT并更新频谱线 yf = fft(self.data_int) line_data = np.abs(yf[0:self.CHUNK]) / (128 * self.CHUNK) line_fft.set_ydata(line_data) # 查找峰值 peaks, _ = find_peaks(line_data, distance=distance) # 更新峰值标记的坐标 peaks_plot.set_xdata(x_fft[peaks]) # 使用实际频率坐标而非索引 peaks_plot.set_ydata(line_data[peaks]) # 更新画布 fig.canvas.draw() fig.canvas.flush_events() test_file = "C:/Users/Tam/Documents/VScode/Final Project/PrismGuitars.wav" audio_test = Wave(test_file) audio_test.fft_plot(2000)
关键改动说明
- 复用峰值绘图对象:在初始化时创建空的峰值标记对象,每次循环仅更新其坐标数据,避免重复添加绘图元素导致叠加。
- 修正音频数据解析:匹配
paInt16格式使用'h'(短整数)解析,结合声道数计算解析长度,避免数据格式不匹配。 - 正确映射峰值频率:用
x_fft[peaks]获取峰值对应的实际频率值,确保坐标逻辑准确。 - 固定y轴范围:避免频谱跳动造成的视觉干扰,可根据实际需求调整范围值。
- 优化窗口关闭事件:仅绑定一次关闭逻辑,同时释放音频流和PyAudio资源,避免内存泄漏。
- 简化初始化流程:移除不必要的初始FFT计算,直接进入实时数据处理循环。
内容的提问来源于stack exchange,提问作者TamperedAether
相关产品推荐
相关产品推荐

