簡體   English   中英

單擊錄制按鈕后,使用 PyQt6 和 pyaudio 錄制的音頻停止工作

[英]Audio recording with PyQt6 and pyaudio stops working after clicking recording button

我想制作一個帶有錄制和停止按鈕的程序,頂部有一個 label 以顯示它是否仍在錄制或已完成錄制。 我從另一個問題中獲取了基本的記錄代碼結構,我重寫它的方式可能存在錯誤。

現在我的問題是:寡婦正在打開,它看起來像我想要的樣子,但是只要我在“記錄”按鈕后單擊任何內容,程序就會自行掛起(label 更改為“記錄...”) . 有一次我也等了大約 10 分鍾,看看我是不是之前的時間太不耐煩了(這不是問題) ,直到 Windows 說“Python 沒有響應”,因為我點擊了退出按鈕/太多次window。

在點擊->錄制之前,點擊停止按鈕

使用: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_startedsig_stopped
為了通知我們的 Window 記錄正在進行或停止,我們將這些信號連接到插槽recording_startedrecording_stopped

現在應該正確記錄您的文件並將其存儲到文件中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM