单击录音按钮后,PyQt6 和 pyaudio 的音频录音停止工作
Audio recording with PyQt6 and pyaudio stops working after clicking recording button
我想制作一个带有录制和停止按钮的程序,并在顶部显示一个标签,以显示它是仍在录制还是已完成录制。
我从another question中获取了基本的录音代码结构,我重写它的方式可能有错误。
现在我的问题是:寡妇正在打开它看起来像我想要的样子,但是只要我点击 'record' 按钮后的任何东西,程序就会自行挂起 (标签更改为 'recording...')。有一次我也等了大约 10 分钟,看看我是不是太不耐烦了 (那不是问题) 直到 Windows 说 'Python is not responding' 因为我在 window.
上点击了退出按钮/太多次
before clicking -> recording, stop button clicked
正在使用:Python 3.10.1 & VSCode 1.63.2
任何帮助将不胜感激!
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
import pyaudio as pa
import wave
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Rec Audio")
self.stoped = False
vbox = QVBoxLayout()
self.labelRec = QLabel('')
self.labelRec.setFixedSize(130, 15)
hbox = QHBoxLayout()
self.recbtn = QPushButton('▶ record')
self.recbtn.setFixedSize(90, 30)
self.recbtn.clicked.connect(self.recAudio)
self.stopbtn = QPushButton('▪')
self.stopbtn.setFixedSize(40, 30)
self.stopbtn.clicked.connect(self.stopRec)
hbox.addWidget(self.recbtn)
hbox.addWidget(self.stopbtn)
vbox.addWidget(self.labelRec)
vbox.addLayout(hbox)
self.setLayout(vbox)
def recAudio(self):
audio = pa.PyAudio()
frames = []
stream = audio.open(format=pa.paInt16, channels=1, rate=44100, input=True, frames_per_buffer=1024)
self.stoped = False
self.labelRec.setText('◉ recording...')
self.repaint()
while self.stoped == False:
data = stream.read(1024)
frames.append(data)
stream.close()
self.labelRec.setText('recording stopped')
wf = wave.open('test_recording.wav', 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pa.paInt16))
wf.setframerate(44100)
wf.writeframes(b''.join(frames))
wf.close()
def stopRec(self):
self.stoped = True
app = QApplication([])
win = Window()
win.show()
sys.exit(app.exec())
您的解决方案中的问题是由于您在与 GUI 相同的线程中录制音频造成的。
然后当你的代码到达录音部分时:
while self.stoped == False:
data = stream.read(1024)
frames.append(data)
GUI 卡住了,没有任何帮助,因为 GUI 无法处理其他任何东西。
PyQt 提供了非常好的机制来处理称为信号和槽的事件,我建议您阅读更多相关内容。这不是一个容易的话题,尤其是在开始的时候,所以请耐心等待...
我修改了您之前的代码并使其按照您的预期运行:
import sys
import wave
import pyaudio as pa
from PyQt6.QtCore import QThread, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import *
class RecordingThread(QThread):
stopped = False
sig_started = pyqtSignal()
sig_stopped = pyqtSignal()
def __init__(self, target_file):
self.target_file = target_file
super().__init__()
def run(self) -> None:
audio = pa.PyAudio()
frames = []
stream = audio.open(format=pa.paInt16, channels=1, rate=44100, input=True, frames_per_buffer=1024)
self.stopped = False
self.sig_started.emit()
while not self.stopped:
data = stream.read(1024)
frames.append(data)
stream.close()
self.sig_stopped.emit()
wf = wave.open(self.target_file, 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pa.paInt16))
wf.setframerate(44100)
wf.writeframes(b''.join(frames))
wf.close()
@pyqtSlot()
def stop(self):
self.stopped = True
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Rec Audio")
# Create recording thread and attach slots to its signals
self.recording_thread = RecordingThread(target_file='test_recording.wav')
self.recording_thread.sig_started.connect(self.recording_started)
self.recording_thread.sig_stopped.connect(self.recording_stopped)
vbox = QVBoxLayout()
self.labelRec = QLabel('')
self.labelRec.setFixedSize(130, 15)
hbox = QHBoxLayout()
self.recbtn = QPushButton('▶ record')
self.recbtn.setFixedSize(90, 30)
# Connect signal "recbtn.clicked" to the slot "recording_thread.start" of our QThread
# Never connect directly to the run, always to start!
self.recbtn.clicked.connect(self.recording_thread.start)
self.stopbtn = QPushButton('▪')
self.stopbtn.setDisabled(True)
self.stopbtn.setFixedSize(40, 30)
# Connect signal "stopbtn.clicked" to the slot "recording_thread.stop" of our QThread
self.stopbtn.clicked.connect(self.recording_thread.stop)
hbox.addWidget(self.recbtn)
hbox.addWidget(self.stopbtn)
vbox.addWidget(self.labelRec)
vbox.addLayout(hbox)
self.setLayout(vbox)
@pyqtSlot()
def recording_started(self):
"""This slot is called when recording starts"""
self.labelRec.setText('◉ recording...')
self.stopbtn.setDisabled(False)
self.recbtn.setDisabled(True)
@pyqtSlot()
def recording_stopped(self):
"""This slot is called when recording stops"""
self.labelRec.setText('recording stopped')
self.recbtn.setDisabled(False)
self.stopbtn.setDisabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
app.exec()
不同之处在于我们现在使用 QThread,它将 运行 您的音频记录在单独的线程中并使您的 GUI 保持活动状态。
这个 QThread 还附加了两个信号 sig_started
和 sig_stopped
.
为了通知我们的 Window 记录正在进行或停止,我们将这些信号连接到插槽 recording_started
和 recording_stopped
.
现在应该可以正确记录您的文件并将其存储到文件中。
我想制作一个带有录制和停止按钮的程序,并在顶部显示一个标签,以显示它是仍在录制还是已完成录制。 我从another question中获取了基本的录音代码结构,我重写它的方式可能有错误。
现在我的问题是:寡妇正在打开它看起来像我想要的样子,但是只要我点击 'record' 按钮后的任何东西,程序就会自行挂起 (标签更改为 'recording...')。有一次我也等了大约 10 分钟,看看我是不是太不耐烦了 (那不是问题) 直到 Windows 说 'Python is not responding' 因为我在 window.
上点击了退出按钮/太多次before clicking -> recording, stop button clicked
正在使用:Python 3.10.1 & VSCode 1.63.2
任何帮助将不胜感激!
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
import pyaudio as pa
import wave
import sys
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Rec Audio")
self.stoped = False
vbox = QVBoxLayout()
self.labelRec = QLabel('')
self.labelRec.setFixedSize(130, 15)
hbox = QHBoxLayout()
self.recbtn = QPushButton('▶ record')
self.recbtn.setFixedSize(90, 30)
self.recbtn.clicked.connect(self.recAudio)
self.stopbtn = QPushButton('▪')
self.stopbtn.setFixedSize(40, 30)
self.stopbtn.clicked.connect(self.stopRec)
hbox.addWidget(self.recbtn)
hbox.addWidget(self.stopbtn)
vbox.addWidget(self.labelRec)
vbox.addLayout(hbox)
self.setLayout(vbox)
def recAudio(self):
audio = pa.PyAudio()
frames = []
stream = audio.open(format=pa.paInt16, channels=1, rate=44100, input=True, frames_per_buffer=1024)
self.stoped = False
self.labelRec.setText('◉ recording...')
self.repaint()
while self.stoped == False:
data = stream.read(1024)
frames.append(data)
stream.close()
self.labelRec.setText('recording stopped')
wf = wave.open('test_recording.wav', 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pa.paInt16))
wf.setframerate(44100)
wf.writeframes(b''.join(frames))
wf.close()
def stopRec(self):
self.stoped = True
app = QApplication([])
win = Window()
win.show()
sys.exit(app.exec())
您的解决方案中的问题是由于您在与 GUI 相同的线程中录制音频造成的。
然后当你的代码到达录音部分时:
while self.stoped == False:
data = stream.read(1024)
frames.append(data)
GUI 卡住了,没有任何帮助,因为 GUI 无法处理其他任何东西。
PyQt 提供了非常好的机制来处理称为信号和槽的事件,我建议您阅读更多相关内容。这不是一个容易的话题,尤其是在开始的时候,所以请耐心等待...
我修改了您之前的代码并使其按照您的预期运行:
import sys
import wave
import pyaudio as pa
from PyQt6.QtCore import QThread, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import *
class RecordingThread(QThread):
stopped = False
sig_started = pyqtSignal()
sig_stopped = pyqtSignal()
def __init__(self, target_file):
self.target_file = target_file
super().__init__()
def run(self) -> None:
audio = pa.PyAudio()
frames = []
stream = audio.open(format=pa.paInt16, channels=1, rate=44100, input=True, frames_per_buffer=1024)
self.stopped = False
self.sig_started.emit()
while not self.stopped:
data = stream.read(1024)
frames.append(data)
stream.close()
self.sig_stopped.emit()
wf = wave.open(self.target_file, 'wb')
wf.setnchannels(1)
wf.setsampwidth(audio.get_sample_size(pa.paInt16))
wf.setframerate(44100)
wf.writeframes(b''.join(frames))
wf.close()
@pyqtSlot()
def stop(self):
self.stopped = True
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Rec Audio")
# Create recording thread and attach slots to its signals
self.recording_thread = RecordingThread(target_file='test_recording.wav')
self.recording_thread.sig_started.connect(self.recording_started)
self.recording_thread.sig_stopped.connect(self.recording_stopped)
vbox = QVBoxLayout()
self.labelRec = QLabel('')
self.labelRec.setFixedSize(130, 15)
hbox = QHBoxLayout()
self.recbtn = QPushButton('▶ record')
self.recbtn.setFixedSize(90, 30)
# Connect signal "recbtn.clicked" to the slot "recording_thread.start" of our QThread
# Never connect directly to the run, always to start!
self.recbtn.clicked.connect(self.recording_thread.start)
self.stopbtn = QPushButton('▪')
self.stopbtn.setDisabled(True)
self.stopbtn.setFixedSize(40, 30)
# Connect signal "stopbtn.clicked" to the slot "recording_thread.stop" of our QThread
self.stopbtn.clicked.connect(self.recording_thread.stop)
hbox.addWidget(self.recbtn)
hbox.addWidget(self.stopbtn)
vbox.addWidget(self.labelRec)
vbox.addLayout(hbox)
self.setLayout(vbox)
@pyqtSlot()
def recording_started(self):
"""This slot is called when recording starts"""
self.labelRec.setText('◉ recording...')
self.stopbtn.setDisabled(False)
self.recbtn.setDisabled(True)
@pyqtSlot()
def recording_stopped(self):
"""This slot is called when recording stops"""
self.labelRec.setText('recording stopped')
self.recbtn.setDisabled(False)
self.stopbtn.setDisabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
app.exec()
不同之处在于我们现在使用 QThread,它将 运行 您的音频记录在单独的线程中并使您的 GUI 保持活动状态。
这个 QThread 还附加了两个信号 sig_started
和 sig_stopped
.
为了通知我们的 Window 记录正在进行或停止,我们将这些信号连接到插槽 recording_started
和 recording_stopped
.
现在应该可以正确记录您的文件并将其存储到文件中。