Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

65 lines
2.4KB

  1. """
  2. pytts voice service (offline)
  3. """
  4. import os
  5. import sys
  6. import time
  7. import pyttsx3
  8. from bridge.reply import Reply, ReplyType
  9. from common.log import logger
  10. from common.tmp_dir import TmpDir
  11. from voice.voice import Voice
  12. class PyttsVoice(Voice):
  13. engine = pyttsx3.init()
  14. def __init__(self):
  15. # 语速
  16. self.engine.setProperty("rate", 125)
  17. # 音量
  18. self.engine.setProperty("volume", 1.0)
  19. if sys.platform == "win32":
  20. for voice in self.engine.getProperty("voices"):
  21. if "Chinese" in voice.name:
  22. self.engine.setProperty("voice", voice.id)
  23. else:
  24. self.engine.setProperty("voice", "zh")
  25. # If the problem of espeak is fixed, using runAndWait() and remove this startLoop()
  26. # TODO: check if this is work on win32
  27. self.engine.startLoop(useDriverLoop=False)
  28. def textToVoice(self, text):
  29. try:
  30. # Avoid the same filename under multithreading
  31. wavFileName = "reply-" + str(int(time.time())) + "-" + str(hash(text) & 0x7FFFFFFF) + ".wav"
  32. wavFile = TmpDir().path() + wavFileName
  33. logger.info("[Pytts] textToVoice text={} voice file name={}".format(text, wavFile))
  34. self.engine.save_to_file(text, wavFile)
  35. if sys.platform == "win32":
  36. self.engine.runAndWait()
  37. else:
  38. # In ubuntu, runAndWait do not really wait until the file created.
  39. # It will return once the task queue is empty, but the task is still running in coroutine.
  40. # And if you call runAndWait() and time.sleep() twice, it will stuck, so do not use this.
  41. # 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.
  42. # self.engine.runAndWait()
  43. # Before espeak fix this problem, we iterate the generator and control the waiting by ourself.
  44. # But this is not the canonical way to use it, for example if the file already exists it also cannot wait.
  45. self.engine.iterate()
  46. while self.engine.isBusy() or wavFileName not in os.listdir(TmpDir().path()):
  47. time.sleep(0.1)
  48. reply = Reply(ReplyType.VOICE, wavFile)
  49. except Exception as e:
  50. reply = Reply(ReplyType.ERROR, str(e))
  51. finally:
  52. return reply