Вопрос : несколько say()
должны быть запущены / завершены на основе event
'finished-utterance'
Эта реализацияосновано на работающем драйвере-цикле событий из pyttsx3.readthedocs.io .
Примечание : TTSThread
начинается с момента его создания и работает вечно! .
Вам нужно позвонить .terminate()
в EXIT __main__
, чтобы предотвратить ожидание вечно!
# 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 : передайте одно предложение за другим, в зависимости от события 'Finised-Utterance' .
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