我使用线程作为绕过pyttsx3的"运行并等待"默认功能的一种方式,这样我就可以在讲话中打断别人说的话。然而,这会阻止库的回调,所以我无法检测一段文本何时结束——我必须用秒的长度来猜测。
我想要检测的回调是已完成的话语,它与self.engine.connect('finished-utterance', self.onEnd)
相连。这遵循了文档中给出的示例。当我在我的真实程序中使用这个时,一个文本块可以有数百个单词长,下一个只能有几个单词长。我想知道一句话什么时候完成,这样程序就可以自动进入下一个"说"功能。
我读到一个人通过使用多处理成功地绕过了这一点,但我不知道如何做到这一点。有没有一种方法可以让回调与线程一起工作?我正在使用Windows 10
from threading import Event, Thread
import pyttsx3
class Voice(object):
def __init__(self, skip, play_beep):
self.t = None
self._running = False
self.engine = pyttsx3.init()
self.skip = skip
self.engine.connect('finished-utterance', self.onEnd)
def onEnd(self, name, completed):
print('finishing : ', name, completed)
self.stop()
def on_finished_utterance(self, name, completed):
print('END')
t = Thread(target=self.killme, args=(self.engine), daemon=True)
t.start()
def process_speech(self, text):
self.engine.say(str(text))
self.engine.startLoop(False)
while self._running:
self.engine.iterate()
def say(self, text, length=2):
# check if thread is running
if self.t and self._running:
# stop it if it is
self.stop()
# iterate speech in a thread
self.t = Thread(target=self.process_speech, args=(text,), daemon=True)
self._running = True
self.t.start()
elapsed_seconds = 0
poll_interval = .1
while not self.skip.is_set() and elapsed_seconds < length:
self.skip.wait(poll_interval)
elapsed_seconds += poll_interval
def stop(self):
self._running = False
try:
self.engine.endLoop()
except:
pass
try:
self.t.join()
except Exception as e:
pass
skip = Event()
myVoice = Voice(skip, 0)
myVoice.say("test", 2)
myVoice.say("test two", 2)
问题:基于
event
'finished-utterance'
要激发/结束多个say()
此实现基于从pyttsx3.readthedocs.io.运行-a-driver-event-loop
注意:
TTSThread
从实例化开始并永远运行
您必须在EXIT __main__
呼叫.terminate()
,以防止等待
# TTS.py
import threading, time, pyttsx3
class TTSThread(threading.Thread):
def __init__(self, rate=115, event=None):
super().__init__()
if event:
setattr(self, event, threading.Event())
self._cancel = threading.Event()
self.rate = rate
self.engine = None
self._say = threading.Event()
self._text_lock = threading.Lock()
self._text = []
self._is_alive = threading.Event()
self._is_alive.set()
self.start()
def _init_engine(self, rate):
engine = pyttsx3.init()
engine.setProperty('rate', rate) # setting up new voice rate
engine.connect('finished-utterance', self._on_completed)
engine.connect('started-word', self._on_cancel)
return engine
def say(self, text, stop=None):
if self._is_alive.is_set():
self._cancel.clear()
if isinstance(text, str):
text = [(text, stop)]
if isinstance(text, (list, tuple)):
for t in text:
if isinstance(t, str):
t = t, None
with self._text_lock:
self._text.append(t)
self._say.set()
def cancel(self):
self._cancel.set()
def _on_cancel(self, name, location, length):
if self._cancel.is_set():
self.stop()
def stop(self):
self.engine.stop()
time.sleep(0.5)
self.engine.endLoop()
def _on_completed(self, name, completed):
if completed:
self.engine.endLoop()
self.on_finished_utterance(name, completed)
def on_finished_utterance(self, name, completed):
pass
def terminate(self):
self._is_alive.clear()
self._cancel.set()
self.join()
def run(self):
self.engine = engine = self._init_engine(self.rate)
while self._is_alive.is_set():
while self._say.wait(0.1):
self._say.clear()
while not self._cancel.is_set() and len(self._text):
with self._text_lock:
engine.say(*self._text.pop(0))
engine.startLoop()
用法1:一次传递多个句子,并为所有句子自动运行
enging.say(...)
。
from TTS import TTSThread
SAY = ["Use your head to save your feet.", "Time will tell.", "Strike while the iron is hot."]
class Voice(TTSThread):
def __init__(self):
super().__init__(rate=115)
if __name__ == "__main__":
voice = Voice()
voice.say(SAY)
# Simulate __main__.is_alive
count = 0.0
while True:
time.sleep(0.1)
count += 1
if count >= 100:
voice.terminate()
break
print('EXIT __main__'.format())
用法2:一句接一句地传递,取决于事件"完成的话语">。
from TTS import TTSThread
import time
class Voice(TTSThread):
def __init__(self):
self.completed = None
super().__init__(rate=115, event='completed')
def on_finished_utterance(self, name, completed):
"""
Overloads `TTSThread.on_finished_utterance`
which is connected to event 'finished-utterance'
"""
if len(SAY):
print('finishing[{}], delay next sentence {} sec.'.format(count, 1.5))
time.sleep(1.5)
self.completed.set()
else:
print('finishing')
if __name__ == "__main__":
voice = Voice()
# Start simulation, `say(...)` while __main__ is running
voice.completed.set()
_terminate = 100
count = 0.0
while True:
time.sleep(0.1)
count += 1
if voice.completed.is_set():
voice.completed.clear()
if len(SAY):
print('.say add[{}]: "{}..."'.format(count, SAY[0][:10]))
voice.say(SAY.pop(0))
if count % 20 == 0:
print('__main__ {}'.format(count))
if count >= _terminate:
voice.terminate()
print('EXIT __main__'.format())
输出:
.say add[1.0]: "Use your h..." finishing[18.0], delay next sentence 1.5 sec. __main__ 20.0 .say add[34.0]: "Time will ..." __main__ 40.0 finishing[51.0], delay next sentence 1.5 sec. __main__ 60.0 .say add[67.0]: "Strike whi..." __main__ 80.0 finishing __main__ 100.0 EXIT __main__
用Python测试:3.5-pyttsx3版本:2.71