用python写一边录音一边播音一边显示FFT波形PYQT5带开始按键的三线程程序
时间: 2023-10-05 08:04:35 浏览: 95
以下是一种用Python编写的三线程程序,可在录音时播放并显示FFT波形,带有一个开始按键。使用了PyQt5库进行界面设计和线程管理。
```
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import pyaudio
import numpy as np
import matplotlib.pyplot as plt
class RecordThread(QThread):
"""
录音线程类
"""
record_finished_signal = pyqtSignal(np.ndarray)
def __init__(self, CHUNK=1024, RATE=44100, RECORD_SECONDS=5):
super().__init__()
self.CHUNK = CHUNK
self.RATE = RATE
self.RECORD_SECONDS = RECORD_SECONDS
def run(self):
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=self.RATE,
input=True,
frames_per_buffer=self.CHUNK)
frames = []
for i in range(0, int(self.RATE / self.CHUNK * self.RECORD_SECONDS)):
data = stream.read(self.CHUNK)
frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
audio_data = np.frombuffer(b''.join(frames), dtype=np.int16)
self.record_finished_signal.emit(audio_data)
class PlayThread(QThread):
"""
播放线程类
"""
def __init__(self, audio_data, CHUNK=1024, RATE=44100):
super().__init__()
self.audio_data = audio_data
self.CHUNK = CHUNK
self.RATE = RATE
def run(self):
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=self.RATE,
output=True)
for i in range(0, len(self.audio_data), self.CHUNK):
chunk = self.audio_data[i:i + self.CHUNK]
stream.write(chunk)
stream.stop_stream()
stream.close()
p.terminate()
class FFTThread(QThread):
"""
FFT线程类
"""
fft_data_signal = pyqtSignal(np.ndarray)
def __init__(self, audio_data, CHUNK=1024, RATE=44100):
super().__init__()
self.audio_data = audio_data
self.CHUNK = CHUNK
self.RATE = RATE
def run(self):
fft_data = self.get_fft(self.audio_data, self.CHUNK, self.RATE)
self.fft_data_signal.emit(fft_data)
@staticmethod
def get_fft(audio_data, CHUNK, RATE):
data = audio_data * np.hanning(len(audio_data))
fft_data = np.abs(np.fft.fft(data)) / CHUNK * 2
freq = np.linspace(0, RATE, CHUNK + 1)[:CHUNK // 2]
return fft_data[:CHUNK // 2], freq
class MainWindow(QWidget):
"""
窗口类
"""
def __init__(self):
super().__init__()
self.initUI()
self.record_thread = None
self.play_thread = None
self.fft_thread = None
def initUI(self):
self.setWindowTitle('Record and Play Audio')
self.setGeometry(300, 300, 800, 600)
self.label1 = QLabel('Audio Waveform', self)
self.label1.setGeometry(50, 50, 300, 30)
self.label2 = QLabel('FFT Waveform', self)
self.label2.setGeometry(450, 50, 300, 30)
self.start_button = QPushButton('Start Recording', self)
self.start_button.setGeometry(50, 500, 150, 30)
self.start_button.clicked.connect(self.start_recording)
self.stop_button = QPushButton('Stop Recording', self)
self.stop_button.setGeometry(250, 500, 150, 30)
self.stop_button.clicked.connect(self.stop_recording)
self.stop_button.setEnabled(False)
self.play_button = QPushButton('Play Audio', self)
self.play_button.setGeometry(450, 500, 150, 30)
self.play_button.clicked.connect(self.play_audio)
self.play_button.setEnabled(False)
self.label3 = QLabel('Recording Time: 0s', self)
self.label3.setGeometry(50, 550, 150, 30)
self.label4 = QLabel('Playing Time: 0s', self)
self.label4.setGeometry(450, 550, 150, 30)
self.audio_waveform = QLabel(self)
self.audio_waveform.setGeometry(50, 100, 300, 300)
self.audio_waveform.setStyleSheet('background-color: white;')
self.fft_waveform = QLabel(self)
self.fft_waveform.setGeometry(450, 100, 300, 300)
self.fft_waveform.setStyleSheet('background-color: white;')
self.show()
def start_recording(self):
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True)
self.play_button.setEnabled(False)
self.record_thread = RecordThread()
self.record_thread.record_finished_signal.connect(self.record_finished)
self.fft_thread = FFTThread(np.array([]))
self.fft_thread.fft_data_signal.connect(self.update_fft_waveform)
self.record_thread.start()
self.fft_thread.start()
def stop_recording(self):
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.play_button.setEnabled(True)
self.record_thread.terminate()
self.fft_thread.terminate()
self.label3.setText('Recording Time: {}s'.format(self.record_thread.RECORD_SECONDS))
def play_audio(self):
self.start_button.setEnabled(False)
self.stop_button.setEnabled(False)
self.play_button.setEnabled(False)
self.play_thread = PlayThread(self.record_thread.audio_data)
self.play_thread.finished.connect(self.play_finished)
self.play_thread.start()
def record_finished(self, audio_data):
self.audio_data = audio_data
self.audio_waveform.setPixmap(self.get_waveform_pixmap(audio_data))
self.fft_thread.audio_data = audio_data
def update_fft_waveform(self, fft_data):
self.fft_waveform.setPixmap(self.get_fft_pixmap(fft_data))
def play_finished(self):
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.play_button.setEnabled(True)
self.label4.setText('Playing Time: {}s'.format(len(self.record_thread.audio_data) / self.record_thread.RATE))
@staticmethod
def get_waveform_pixmap(audio_data):
fig = plt.figure(figsize=(3, 3), dpi=80)
plt.plot(audio_data)
buf = fig.canvas.buffer_rgba()
plt.close(fig)
width, height = buf.width(), buf.height()
img = QImage(buf, width, height, QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
return pixmap.scaled(300, 300, Qt.KeepAspectRatio)
@staticmethod
def get_fft_pixmap(fft_data):
fig = plt.figure(figsize=(3, 3), dpi=80)
plt.plot(fft_data)
buf = fig.canvas.buffer_rgba()
plt.close(fig)
width, height = buf.width(), buf.height()
img = QImage(buf, width, height, QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
return pixmap.scaled(300, 300, Qt.KeepAspectRatio)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
```
该程序包括三个线程类:RecordThread、PlayThread和FFTThread。RecordThread用于录制音频数据,PlayThread用于播放录制好的音频数据,FFTThread用于计算音频数据的FFT并发送给主线程进行显示。
在主窗口类MainWindow中,通过UI设计创建了开始录制、停止录制、播放录制好的音频、显示录制音频波形和FFT波形的控件,并实现了相应的槽函数。录制音频时,实例化RecordThread和FFTThread并启动,当录制完成时,RecordThread将录制好的音频数据发送给主线程进行显示,并更新FFTThread的音频数据,FFTThread计算FFT并将结果发送给主线程进行显示。播放录制好的音频时,实例化PlayThread并启动,播放完成后发送finished信号,在槽函数中更新相应的控件状态。主线程通过get_waveform_pixmap和get_fft_pixmap方法将matplotlib绘制好的波形转换成QPixmap格式,并显示在相应的控件上。
阅读全文