用python生成一边录音一边播放一边显示FFT画布PyQt5有按键的多线程程序
时间: 2023-10-05 16:06:44 浏览: 89
以下是一个用Python生成一边录音一边播放一边显示FFT画布的PyQt5多线程程序,带有按键控制:
```python
import sys
import numpy as np
import pyaudio
import threading
import time
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton
from PyQt5.QtGui import QPainter, QPen
from PyQt5.QtCore import Qt
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
self.chunk = 1024
self.rate = 44100
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16, channels=1, rate=self.rate, input=True, output=True, frames_per_buffer=self.chunk)
self.is_recording = False
self.is_playing = False
self.is_stopped = False
self.thread_rec = None
self.thread_play = None
self.thread_fft = None
def initUI(self):
self.setGeometry(100, 100, 800, 600)
self.setWindowTitle('Real-time Recording/Playback/FFT')
self.lbl1 = QLabel('Real-time Recording/Playback/FFT', self)
self.lbl1.move(20, 20)
self.lbl1.resize(300, 20)
self.btn_start_rec = QPushButton('Start Recording', self)
self.btn_start_rec.move(20, 60)
self.btn_start_rec.resize(120, 30)
self.btn_start_rec.clicked.connect(self.start_recording)
self.btn_stop_rec = QPushButton('Stop Recording', self)
self.btn_stop_rec.move(150, 60)
self.btn_stop_rec.resize(120, 30)
self.btn_stop_rec.clicked.connect(self.stop_recording)
self.btn_start_play = QPushButton('Start Playback', self)
self.btn_start_play.move(20, 100)
self.btn_start_play.resize(120, 30)
self.btn_start_play.clicked.connect(self.start_playing)
self.btn_stop_play = QPushButton('Stop Playback', self)
self.btn_stop_play.move(150, 100)
self.btn_stop_play.resize(120, 30)
self.btn_stop_play.clicked.connect(self.stop_playing)
self.lbl2 = QLabel('Real-time FFT', self)
self.lbl2.move(20, 140)
self.lbl2.resize(300, 20)
self.canvas = QLabel(self)
self.canvas.move(20, 180)
self.canvas.resize(600, 300)
self.btn_start_fft = QPushButton('Start FFT', self)
self.btn_start_fft.move(20, 500)
self.btn_start_fft.resize(120, 30)
self.btn_start_fft.clicked.connect(self.start_fft)
self.btn_stop_fft = QPushButton('Stop FFT', self)
self.btn_stop_fft.move(150, 500)
self.btn_stop_fft.resize(120, 30)
self.btn_stop_fft.clicked.connect(self.stop_fft)
self.show()
def start_recording(self):
self.is_recording = True
self.thread_rec = threading.Thread(target=self.record)
self.thread_rec.start()
def stop_recording(self):
self.is_recording = False
def start_playing(self):
self.is_playing = True
self.thread_play = threading.Thread(target=self.play)
self.thread_play.start()
def stop_playing(self):
self.is_playing = False
def start_fft(self):
self.is_stopped = False
self.thread_fft = threading.Thread(target=self.fft)
self.thread_fft.start()
def stop_fft(self):
self.is_stopped = True
def record(self):
frames = []
while self.is_recording:
data = self.stream.read(self.chunk)
frames.append(data)
self.stream.stop_stream()
self.stream.close()
self.p.terminate()
self.frames_rec = frames
def play(self):
for data in self.frames_rec:
if not self.is_playing:
break
self.stream.write(data)
def fft(self):
while not self.is_stopped:
data = np.fromstring(self.stream.read(self.chunk), dtype=np.int16)
yf = np.fft.fft(data)
xf = np.linspace(0, self.rate/2, self.chunk/2)
fig = self.plot(xf, 2.0/self.chunk * np.abs(yf[:self.chunk//2]))
self.show_fft(fig)
def plot(self, x, y):
fig = np.zeros((300, 600, 3), dtype=np.uint8)
fig.fill(255)
pen = QPen(Qt.black, 1)
painter = QPainter()
painter.begin(fig)
painter.setPen(pen)
for i in range(len(x)):
x1 = int(x[i] / (self.rate/2) * 600)
y1 = 300 - int(y[i] * 300)
painter.drawPoint(x1, y1)
painter.end()
return fig
def show_fft(self, fig):
img = np.array(fig, dtype=np.uint8)
img = np.rot90(img, 1)
img = np.flipud(img)
img = img.tobytes()
qimg = QtGui.QImage(img, 600, 300, QtGui.QImage.Format_RGB888)
pixmap = QtGui.QPixmap.fromImage(qimg)
self.canvas.setPixmap(pixmap)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MyWindow()
sys.exit(app.exec_())
```
该程序使用了PyQt5库创建了一个简单的GUI界面,包括开始录音、停止录音、开始播放、停止播放、开始FFT、停止FFT等按钮,可以通过这些按钮控制程序的运行。
程序使用PyAudio库进行录音和播放,并使用Numpy库进行FFT计算。在进行FFT计算时,程序使用多线程实现了一边录音一边计算FFT,并将结果显示在GUI界面上。
由于PyAudio是一个C++库,因此需要先在系统中安装PortAudio库,然后使用pip install pyaudio安装PyAudio库。
请注意,在使用多线程进行录音和播放时,需要将PyAudio流对象放在每个线程的函数内部,以便在每个线程中使用不同的流对象,避免产生竞争条件。
阅读全文