用python制作打开音频文件一边播音一边显示FFT波形pyqt5带按键的多线程程序
时间: 2024-05-03 08:19:59 浏览: 142
以下是一个基于PyQt5的多线程程序,可以打开音频文件,一边播音一边显示FFT波形,并带有按键控制。注意,由于FFT计算需要一定的计算量,如果音频文件过长,可能会导致程序运行缓慢。
```python
import sys
import os
import wave
import numpy as np
import pyqtgraph as pg
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QFileDialog, QHBoxLayout, QVBoxLayout
class AudioThread(QThread):
"""音频播放线程"""
signal_update_time = pyqtSignal(float) # 更新时间信号
signal_update_fft = pyqtSignal(list) # 更新FFT信号
def __init__(self, file_path):
super().__init__()
self.file_path = file_path
def run(self):
wf = wave.open(self.file_path, 'rb')
params = wf.getparams()
frame_rate = params[2]
nframes = params[3]
while True:
data = wf.readframes(frame_rate) # 每次读取1秒的数据
if not data:
break
data = np.frombuffer(data, dtype=np.int16) / 32768.0 # 将数据转换为-1到1之间的浮点数
self.signal_update_time.emit(wf.tell() / frame_rate) # 发送更新时间信号
fft_data = np.abs(np.fft.rfft(data)) # 计算FFT
self.signal_update_fft.emit(fft_data.tolist()) # 发送更新FFT信号
wf.close()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('音频播放器')
self.setWindowIcon(QIcon('icon.png'))
self.btn_select_file = QPushButton('选择文件')
self.btn_play = QPushButton('播放')
self.btn_pause = QPushButton('暂停')
self.btn_stop = QPushButton('停止')
self.btn_select_file.clicked.connect(self.select_file)
self.btn_play.clicked.connect(self.play)
self.btn_pause.clicked.connect(self.pause)
self.btn_stop.clicked.connect(self.stop)
self.btn_play.setEnabled(False)
self.btn_pause.setEnabled(False)
self.btn_stop.setEnabled(False)
self.plot_fft = pg.PlotWidget()
self.plot_fft.showGrid(x=True, y=True)
self.plot_fft.setLabel('left', '幅度')
self.plot_fft.setLabel('bottom', '频率')
h_layout = QHBoxLayout()
h_layout.addWidget(self.btn_select_file)
h_layout.addWidget(self.btn_play)
h_layout.addWidget(self.btn_pause)
h_layout.addWidget(self.btn_stop)
v_layout = QVBoxLayout()
v_layout.addLayout(h_layout)
v_layout.addWidget(self.plot_fft)
self.setLayout(v_layout)
self.audio_thread = None
self.fft_data = None
def select_file(self):
file_path, _ = QFileDialog.getOpenFileName(self, '选择文件', '.', '音频文件 (*.wav)')
if file_path:
self.file_path = file_path
self.btn_play.setEnabled(True)
def play(self):
if not self.audio_thread:
self.audio_thread = AudioThread(self.file_path)
self.audio_thread.signal_update_time.connect(self.update_time)
self.audio_thread.signal_update_fft.connect(self.update_fft)
self.audio_thread.start()
self.btn_play.setEnabled(False)
self.btn_pause.setEnabled(True)
self.btn_stop.setEnabled(True)
def pause(self):
self.btn_play.setEnabled(True)
self.btn_pause.setEnabled(False)
def stop(self):
if self.audio_thread:
self.audio_thread.terminate()
self.audio_thread = None
self.btn_play.setEnabled(True)
self.btn_pause.setEnabled(False)
self.btn_stop.setEnabled(False)
self.plot_fft.clear()
def update_time(self, time):
"""更新时间"""
print('time:', time)
def update_fft(self, fft_data):
"""更新FFT"""
self.fft_data = fft_data
self.plot_fft.plot(np.arange(len(fft_data)), fft_data, clear=True)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
```
在上面的程序中,我们定义了一个`AudioThread`类,用于在另一个线程中播放音频并计算FFT,同时发送信号给主线程更新时间和FFT波形。主程序窗口中有一个`QPushButton`控件用于选择音频文件,另外三个控件用于开始、暂停和停止播放。当用户选择了音频文件后,开始按钮将变为可用状态。用户点击开始按钮后,程序会在另一个线程中开始播放和计算FFT,并实时将FFT波形显示在主窗口中。用户可以点击暂停和停止按钮来暂停和停止播放。注意,停止按钮将清除FFT波形的显示。
阅读全文