如何从QAudioInput实时采集音频存为NumPy数组并传入SciPy FFT
实现方案
你原有代码报错的核心原因是调用QByteArray.toUInt()是尝试将整段二进制音频数据转换为单个无符号整数,而你的需求是把每个采样点对应一个数值、组成数组,所以转换逻辑完全不对。
完整可运行实现代码
from PyQt5.QtWidgets import QMainWindow, QApplication from PyQt5.QtMultimedia import QAudioDeviceInfo, QAudioFormat, QAudioInput import numpy as np from scipy.fft import fft, fftfreq import sys class Window(QMainWindow): def __init__(self): super().__init__() info = QAudioDeviceInfo() input_device = info.defaultInputDevice() if input_device.isNull(): print("There is no audio input device available.") exit(-1) audio_format = QAudioFormat() audio_format.setSampleRate(44100) audio_format.setSampleSize(8) audio_format.setChannelCount(1) audio_format.setCodec("audio/pcm") audio_format.setSampleType(QAudioFormat.UnSignedInt) if sys.byteorder == "little": audio_format.setByteOrder(QAudioFormat.LittleEndian) else: audio_format.setByteOrder(QAudioFormat.BigEndian) # 可选:设置音频缓冲区大小,控制每次回调的音频长度 self.audioInput = QAudioInput(input_device, audio_format, self) self.ioDevice = self.audioInput.start() self.ioDevice.readyRead.connect(self.read_audio) def read_audio(self): # 读取原始二进制音频数据 data = self.ioDevice.readAll() # 转为Python原生bytes类型 raw_bytes = data.data() # 按你设置的音频格式转为numpy数组,这里对应8位无符号整型 audio_np = np.frombuffer(raw_bytes, dtype=np.uint8) # 可选:去除8位无符号采样的直流偏置,0点对齐到128,FFT结果更准确 audio_np = audio_np - 128 # 传递给scipy做FFT计算 if len(audio_np) > 0: yf = fft(audio_np) xf = fftfreq(len(audio_np), 1 / 44100) # 这里可以自行处理FFT结果,示例打印前10个频率点的幅值 print(np.abs(yf[:10])) if __name__ == "__main__": app = QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())
注意事项和优化点
- 如果你调整了音频格式参数,只需要同步修改
np.frombuffer的dtype即可:比如16位有符号整型对应np.int16,32位浮点对应np.float32,不需要改动其他逻辑 - 要解决每次读取音频长度不固定导致FFT输出长度变化的问题,可以加一行
self.audioInput.setBufferSize(44100 * 2)固定每次读取2秒的音频数据,保证每次FFT输入长度固定 - 要减少频谱泄露可以在FFT前加窗:
from scipy.signal import hann audio_np = audio_np * hann(len(audio_np))
把以上代码插入到计算FFT的代码之前即可。
内容的提问来源于stack exchange,提问作者Jaime02
相关产品推荐
相关产品推荐

