我正在开发一个GUI,它可以使用sounddevice和soundfile库在任意时间内记录音频。按下“ ctrl + c”组合键可停止录制过程。
我正在尝试使用“开始”和“结束”按钮来实现GUI。 “开始”按钮应调用记录功能,“结束”按钮应模拟“ ctrl + c”事件。我不知道如何将此事件实现为python中的函数。 实施想法很受赞赏。
如果使用Windows命令提示符python record.py
record.py 如下:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import queue
from PyQt5 import QtCore, QtGui, QtWidgets
import soundfile as sf
import sounddevice as sd
import mythreading
class Ui_MainWindow(object):
def __init__(self):
self.threadpool = QThreadPool()
print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(640, 480)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(280, 190, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton.clicked.connect(self.start_button_func)
self.pushButton_1 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_1.setGeometry(QtCore.QRect(380, 190, 75, 23))
self.pushButton_1.setObjectName("pushButton")
self.pushButton_1.clicked.connect(self.end_button_func)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 640, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Start"))
self.pushButton_1.setText(_translate("MainWindow", "End"))
def record(self):
self.q = queue.Queue()
self.s = sd.InputStream(samplerate=48000, channels=2, callback=self.callback)
try:
# Make sure the file is open before recording begins
with sf.SoundFile('check.wav', mode='x', samplerate=48000, channels=2, subtype="PCM_16") as file:
with self.s:
# 1 second silence before the recording begins
time.sleep(1)
print('START')
print('#' * 80)
print('press Ctrl+C to stop the recording')
while True:
file.write(self.q.get())
except OSError:
print('The file to be recorded already exists.')
sys.exit(1)
except KeyboardInterrupt:
print('The utterance is recorded.')
def callback(self, indata, frames, time, status):
"""
This function is called for each audio block from the record function.
"""
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
def start_button_func(self):
self.worker = mythreading.Worker(self.record)
self.threadpool.start(self.worker)
def end_button_func(self):
print('how to stop?')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
mythreading.py 如下:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Worker(QRunnable):
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
self.fn = fn
@pyqtSlot()
def run(self):
self.fn()
答案 0 :(得分:1)
如果命令
exit()
make应用程序没有响应,我们可以使用以下方式模拟ctrl事件
signal.CTRL_C_EVENT
在Windows中
和
signal.SIGINT
在Linux中
记住要导入信号 因此功能变为...
import signal
...
...
...
...
def end_button_func(self):
signal.SIGINT # if you are using ubuntu or mac
signal.CTRL_C_EVENT # if you are using windows
我有一台Mac,所以我没有尝试发送信号。CTRL_C_EVENT,无论如何都尝试
希望它能工作!
答案 1 :(得分:0)
查看声音设备的source code,看起来KeyboardInterrupt
事件在其示例中记录了rec_unlimited.py时调用了argparser的exit方法,
parser = argparse.ArgumentParser(description=__doc__)
...
try:
# Make sure the file is opened before recording anything:
with sf.SoundFile(args.filename, mode='x', samplerate=args.samplerate,
channels=args.channels, subtype=args.subtype) as file:
with sd.InputStream(samplerate=args.samplerate, device=args.device,
channels=args.channels, callback=callback):
print('#' * 80)
print('press Ctrl+C to stop the recording')
print('#' * 80)
while True:
file.write(q.get())
except KeyboardInterrupt:
print('\nRecording finished: ' + repr(args.filename))
parser.exit(0)
此示例对您有用吗?我认为在此退出无论如何只会调用系统退出。如果可行,请从那里开始,并确保您的GUI中的stop命令正在执行相同的操作(即,提高KeyboardInterrupt
或退出)。
如果sounddevice正在创建线程(queue.get建议这样做),则使用线程也可能会出现问题,因此exit调用不会终止所有线程。
如果该退出在Windows中不起作用,则也许调用sd.stop()
可能是跨平台的解决方案(尽管我怀疑离开with
块仍然可以这样做)。
基于讨论的更新:
由于示例运行良好,但是end_button_func
冻结了GUI,看来记录过程需要separate thread,因此您的GUI会响应,直到传递了停止它的信号为止。我认为最好的方法是传递一个参数,当按下停止按钮时,该参数会触发线程中的KeyboardInterrupt
异常 。要与线程通信,您需要像在answer或this中那样发送信号,并在此基础上提高线程中的KeyboardInterrupt
。
这个示例(PyQt: How to send a stop signal into a thread where an object is running a conditioned while loop?)似乎是最接近的,您可以在其中调整work
函数以引发异常,如下所示,
@pyqtSlot()
def work(self):
while self.running():
time.sleep(0.1)
print 'doing work...'
self.sgnFinished.emit()
raise KeyboardInterrupt
我这里没有PyQt5或Windows,因此无法进行进一步测试。请注意,您似乎需要使主类成为QObject
才能使用pyqtSignal。