问题描述
我的代码通过 tkinter
构建 GUI,并在按下按钮时通过 sounddevice
处理音频数据。
我成功地使用线程类实时处理音频数据。
当我按下开始按钮时,来自麦克风的输入声音完美地输出到扬声器。
但是,停止按钮有问题。
当我按下停止按钮时,我的代码尝试终止线程,但会出现 GUI 冻结。而且线程不会死。
根据堆栈溢出的大量信息,我做了几次尝试,但都失败了。
请检查我的代码并给我一些建议。
这是我的代码:
import sounddevice as sd
import numpy as np
import tkinter as tk
from tkinter import ttk
from threading import Thread
class StreamThread(Thread):
def __init__(self):
super().__init__()
self.input_device_index = 0
self.output_device_index = 4
self.BLOCK_SHIFT = 128
self.SAMPLING_RATE = 16000
self.BLOCK_LEN = 512
self.soUND_DEVICE_LATENCY = 0.2
def run(self):
with sd.Stream(device=(self.input_device_index,self.output_device_index),samplerate=self.SAMPLING_RATE,blocksize=self.BLOCK_SHIFT,dtype=np.float32,latency=self.soUND_DEVICE_LATENCY,channels=1,callback=self.callback):
input() # Input start
def callback(indata,outdata,frames,time,status):
outdata[:] = indata
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Please Help Me")
self.geometry("400x300")
self.resizable(0,0)
start_button = tk.Button(self,overrelief="solid",width=15,command=lambda: start_button_clicked(),text="Start",repeatdelay=1000,repeatinterval=100)
start_button.grid(column=0,row=5)
stop_button = tk.Button(self,command=lambda: stop_button_clicked(),text="Stop",repeatinterval=100)
stop_button.grid(column=0,row=6)
def start_button_clicked():
stream_thead.start()
def stop_button_clicked():
# this is problem point
if stream_thead.isAlive():
sd.CallbackStop()
sd.CallbackAbort()
stream_thead.join()
if __name__ == "__main__":
stream_thead = StreamThread()
stream_thead.daemon = True # set Daemon thread
app = App()
app.mainloop()
解决方法
您的代码存在问题:
- 在 GUI 应用程序中使用控制台
input()
。据我了解,您使用input()
将线程任务置于等待状态。建议改用threading.Event.wait()
。 -
sd.CallbackStop()
和sd.CallbackAbort()
不能破坏input()
。使用threading.Event.set()
打破threading.Event.wait()
。 -
self
中缺少def callback(indata,...)
参数。应该是def callback(self,indata,...)
。
下面是修改代码以解决上述问题:
...
from threading import Thread,Event
...
class StreamThread(Thread):
...
def run(self):
self.event = Event()
with sd.Stream(device=(self.input_device_index,self.output_device_index),samplerate=self.SAMPLING_RATE,blocksize=self.BLOCK_SHIFT,dtype=np.float32,latency=self.SOUND_DEVICE_LATENCY,channels=1,callback=self.callback) as self.stream:
#input() # Input start
self.event.wait()
def terminate(self):
self.stream.abort() # abort the stream processing
self.event.set() # break self.event.wait()
def callback(self,outdata,frames,time,status):
outdata[:] = indata
...
def stop_button_clicked():
if stream_thread.is_alive():
stream_thread.terminate()
stream_thread.join()
...