You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt6+PyAudio录音异常:点击按钮生成0时长WAV文件

解决PyQt6+PyAudio录音生成0秒音频的问题

你的代码能正常显示界面和更新状态,但生成的音频文件时长为0秒,核心原因是没有在录音过程中读取音频数据并存储到frames列表,同时存在几处小问题需要修正:

问题分析

  1. 录音流程缺失:打开音频流后,没有循环读取音频块数据到self.frames,导致最终写入文件时没有有效数据。
  2. 重复初始化PyAudio:每次点击录音按钮都重新创建PyAudio实例,会造成资源浪费和潜在的冲突。
  3. 主程序入口笔误:if __name__ == 'main': 应为if __name__ == '__main__':,否则程序可能无法正确启动(你当前能运行可能是IDLE的特殊处理,但这是标准错误)。
  4. UI阻塞风险:如果直接在主线程循环读取音频数据,会导致界面卡死,需要用线程来处理录音逻辑。

修复后的完整代码

import sys
import wave
from PyQt6.QtCore import QTimer, QThread, pyqtSignal
import pyaudio
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QWidget, QGridLayout

class RecorderThread(QThread):
    finished = pyqtSignal()
    def __init__(self, p, chunk, sample_format, channels, fs, frames):
        super().__init__()
        self.p = p
        self.chunk = chunk
        self.sample_format = sample_format
        self.channels = channels
        self.fs = fs
        self.frames = frames
        self.is_recording = True

    def run(self):
        # 打开音频流
        stream = self.p.open(format=self.sample_format,
                            channels=self.channels,
                            rate=self.fs,
                            frames_per_buffer=self.chunk,
                            input=True)
        # 循环读取音频数据直到停止
        while self.is_recording:
            data = stream.read(self.chunk)
            self.frames.append(data)
        # 停止并关闭流
        stream.stop_stream()
        stream.close()
        self.finished.emit()

class RecordingWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.chunk = 1024
        self.sample_format = pyaudio.paInt16
        self.channels = 1
        self.fs = 44100
        self.seconds = 3
        self.filename = "new.wav"
        self.p = pyaudio.PyAudio()
        self.recorder_thread = None

    def init_ui(self):
        self.setWindowTitle("Audio Recorder")
        self.setGeometry(100, 100, 400, 300)

        self.status_label = QLabel("Press 'Record' to start recording.\nMicrophone will record for three seconds.", self)

        self.record_button = QPushButton("Record", self)
        self.record_button.clicked.connect(self.start_recording)

        layout = QGridLayout()
        layout.addWidget(self.status_label, 0, 0, 2, 2)
        layout.addWidget(self.record_button, 2, 0, 1, 2)  # 调整布局避免控件重叠

        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

    def start_recording(self):
        self.status_label.setText("Recording... Please Speak Loudly!")
        self.record_button.setEnabled(False)

        self.frames = []
        # 创建并启动录音线程
        self.recorder_thread = RecorderThread(self.p, self.chunk, self.sample_format, self.channels, self.fs, self.frames)
        self.recorder_thread.finished.connect(self.stop_recording)
        self.recorder_thread.start()

        # 启动定时器,指定时间后停止录音
        self.timer = QTimer()
        self.timer.timeout.connect(self.stop_recording_thread)
        self.timer.start(self.seconds * 1000)

    def stop_recording_thread(self):
        # 停止线程的录音循环
        if self.recorder_thread and self.recorder_thread.is_recording:
            self.recorder_thread.is_recording = False
        self.timer.stop()

    def stop_recording(self):
        # 等待线程结束
        if self.recorder_thread:
            self.recorder_thread.wait()
            self.recorder_thread = None

        # 写入音频文件
        wf = wave.open(self.filename, 'wb')
        wf.setnchannels(self.channels)
        wf.setsampwidth(self.p.get_sample_size(self.sample_format))
        wf.setframerate(self.fs)
        wf.writeframes(b''.join(self.frames))
        wf.close()

        self.status_label.setText("Recording completed. Press 'Record' to record again.")
        self.record_button.setEnabled(True)

    def closeEvent(self, event):
        # 关闭前清理资源
        if self.recorder_thread and self.recorder_thread.isRunning():
            self.recorder_thread.is_recording = False
            self.recorder_thread.wait()
        if hasattr(self, 'p'):
            self.p.terminate()
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = RecordingWindow()
    window.show()
    sys.exit(app.exec())

关键修改说明

  1. 新增录音线程:创建RecorderThread类处理音频读取逻辑,避免阻塞UI主线程,保证界面响应正常。
  2. 补充音频读取循环:在线程的run方法中循环读取音频块并添加到frames列表,确保录音数据被正确存储。
  3. 修正PyAudio初始化:仅在窗口初始化时创建一次PyAudio实例,避免重复初始化导致的资源问题。
  4. 修复主程序入口:将if __name__ == 'main':改为标准的if __name__ == '__main__':。
  5. 完善资源清理:在窗口关闭时确保线程停止并释放PyAudio资源,避免内存泄漏。

macOS额外注意事项

确保你的Python程序已经获得麦克风权限:

  • 打开「系统设置」→「隐私与安全性」→「麦克风」,确认VSCode或IDLE已被授予权限。

内容的提问来源于stack exchange,提问作者finceth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 16:35:53