From 1cd6a71ce07febc76ca8a495682506e2d88907f2 Mon Sep 17 00:00:00 2001 From: JS00000 Date: Fri, 21 Apr 2023 18:31:20 +0800 Subject: [PATCH] fix the bug of pytts in linux --- voice/pytts/pytts_voice.py | 43 +++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/voice/pytts/pytts_voice.py b/voice/pytts/pytts_voice.py index 072e28b..17cd6ff 100644 --- a/voice/pytts/pytts_voice.py +++ b/voice/pytts/pytts_voice.py @@ -2,8 +2,9 @@ pytts voice service (offline) """ +import os +import sys import time - import pyttsx3 from bridge.reply import Reply, ReplyType @@ -11,7 +12,6 @@ from common.log import logger from common.tmp_dir import TmpDir from voice.voice import Voice - class PyttsVoice(Voice): engine = pyttsx3.init() @@ -20,19 +20,42 @@ class PyttsVoice(Voice): self.engine.setProperty("rate", 125) # 音量 self.engine.setProperty("volume", 1.0) - for voice in self.engine.getProperty("voices"): - if "Chinese" in voice.name: - self.engine.setProperty("voice", voice.id) + if sys.platform == 'win32': + for voice in self.engine.getProperty("voices"): + if "Chinese" in voice.name: + self.engine.setProperty("voice", voice.id) + else: + self.engine.setProperty("voice", "zh") + # If the problem of espeak is fixed, using runAndWait() and remove this startLoop() + # TODO: check if this is work on win32 + self.engine.startLoop(useDriverLoop=False) def textToVoice(self, text): try: - wavFile = TmpDir().path() + "reply-" + str(int(time.time())) + ".wav" + # avoid the same filename + wavFileName = "reply-" + str(int(time.time())) + "-" + str(hash(text) & 0x7fffffff) + ".wav" + wavFile = TmpDir().path() + wavFileName + logger.info("[Pytts] textToVoice text={} voice file name={}".format(text, wavFile)) + self.engine.save_to_file(text, wavFile) - self.engine.runAndWait() - logger.info( - "[Pytts] textToVoice text={} voice file name={}".format(text, wavFile) - ) + + if sys.platform == 'win32': + self.engine.runAndWait() + else: + # In ubuntu, runAndWait do not really wait until the file created. + # It will return once the task queue is empty, but the task is still running in coroutine. + # And if you call runAndWait() and time.sleep() twice, it will stuck, so do not use this. + # If you want to fix this, add self._proxy.setBusy(True) in line 127 in espeak.py, at the beginning of the function save_to_file. + # self.engine.runAndWait() + + # Before espeak fix this problem, we iterate the generator and control the waiting by ourself. + # But this is not the canonical way to use it, for example if the file already exists it also cannot wait. + self.engine.iterate() + while self.engine.isBusy() or wavFileName not in os.listdir(TmpDir().path()): + time.sleep(0.1) + reply = Reply(ReplyType.VOICE, wavFile) + except Exception as e: reply = Reply(ReplyType.ERROR, str(e)) finally: