nateshmbhat / pyttsx3

Offline Text To Speech synthesis for python
Mozilla Public License 2.0
2.11k stars 331 forks source link

runAndWait hangs on OSX High Sierra #22

Closed m9tech closed 3 years ago

m9tech commented 6 years ago

I searched quite a bit for an answer to this problem, to no avail. While running pyttsx3 on a Mac running OSX High Sierra, the script hangs (or never exits) the runAndWait() command. I found a few suggestions as to how to solve this issue (run it in a function or Class), neither of which worked for me.

Is this an active bug that is on a path to being fixed? Is there a library that needs updating? Any ideas as to how to get it to exit normally?

Thanks,

bluthen commented 6 years ago

I'm not 100% sure it is your exact problem, but you may need a newer version of pyobjc. At least that fixes it for me. See comments in the original project: https://github.com/RapidWareTech/pyttsx/issues/26

m9tech commented 6 years ago

Thanks for the comment, I have installed latest pyobjc, version 4.2.2, but that did not solve the issue.

anapaulagomes commented 5 years ago

I've got the same error using the latest versions of both.

jessejamesrich commented 5 years ago

Same.

dd121 commented 5 years ago

same

torxx666 commented 5 years ago

same

bananakiu commented 5 years ago

same

Pac-boy commented 5 years ago

Anyone fixed it or have other solution ?

bluthen commented 5 years ago

I finally ended up using this in a product I work on OSX. I ended up using multiprocessing. Whenever a say is stopped, we kill the process. Also we freeze it using PyInstaller and had to run multiprocessing.freeze_support() and multiprocessing.set_start_method('forkserver')

I can't remember if the forkserver was required even without freezing (probably is). In anycase it seems to run reliably after doing all that.

We use multiprocessing queue to communicate back and forth to the process started with multiprocessing. The queue is used to send word event info back to the main process, and to to tell the tts process what to say from the main process.

nateshmbhat commented 3 years ago

Is this still happening? This was a very old issue.

Closing this until any furthur activity.

Rainer2465 commented 3 years ago

yep, happening again

esnyder commented 1 month ago

Came here with something similar, but with a few more details. I know that I'm using a comically out of date Mac OS (10.14.6, Mojave). I installed the latest pyttsx3 via pip with pyobjc pinned at 9.0.1 like

--- requirements.txt ---
pygame==2.6.0
pyobjc==9.0.1; sys_platform == 'darwin'
pyttsx3
-------------------------

Did python3 -m venv ./venv; pip install --upgrade pip; pip install -r requirements.txt

Then, in a repl, when I try to do

import pyttsx3
engine = pyttsx3.init()
engine.say("some words")
engine.runAndWait()

I get audible output, but the runAndWait never returns. I can't Ctrl-D or Ctrl-C out of it at the repl either; I have to do kill <python pid> to get out.

I added some debugging to the nsss.py startLoop method, and it looks like the problem is that the runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, nextfire) is never exiting. The nextfire value I'm getting back from the runLoop.limitDateForMode_(NSDefaultRunLoopMode) is very far in the future (4001-01-01 00:00:00). If I replace it with a time just a second or two in the future then the while loop exits on the stopper.shouldRun() check failing, like so (with some debug print's added):

(venv) Emiles-MacBook-Pro:typer emile$ python
Python 3.9.0 (v3.9.0:9cf6752276, Oct  5 2020, 11:29:23) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyttsx3
>>> engine = pyttsx3.init()
>>> engine.say("testing testing")
driver::_pump
>>> engine.runAndWait()
driver::runAndWait
driver::_pump
onPumpFirst_ called with timer <__NSCFTimer: 0x7fe8b4fe8760>
driver::_pump
driver::_pump popped (<bound method NSSpeechDriver.say of <NSSpeechDriver: 0x7fe8b261edd0>>, ('testing testing',), None)
driver::notify called for topic started-utterance with kwargs {}
  nextfire 4001-01-01 00:00:00 +0000 .timeIntervalSinceDate_(soon 2024-09-03 20:28:26 +0000) == 62366815893.200516 at iteration 1
  about to call runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, 2024-09-03 20:28:26 +0000) while runLoop.currentMode() == None
driver::notify called for topic started-word with kwargs {'location': 0, 'length': 7}
driver::notify called for topic started-word with kwargs {'location': 8, 'length': 7}
  runLoop.runMode_beforeDate_ done for 1
  nextfire 4001-01-01 00:00:00 +0000 .timeIntervalSinceDate_(soon 2024-09-03 20:28:27 +0000) == 62366815892.695145 at iteration 2
  about to call runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, 2024-09-03 20:28:27 +0000) while runLoop.currentMode() == None
  runLoop.runMode_beforeDate_ done for 2
  nextfire 4001-01-01 00:00:00 +0000 .timeIntervalSinceDate_(soon 2024-09-03 20:28:27 +0000) == 62366815892.18962 at iteration 3
  about to call runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, 2024-09-03 20:28:27 +0000) while runLoop.currentMode() == None
driver::notify called for topic finished-utterance with kwargs {'completed': True}
driver::_pump
driver::_pump popped (<bound method Engine.endLoop of <pyttsx3.engine.Engine object at 0x107ae0880>>, (), None)
driver::endLoop
nsss::stop called
nsss::endLoop called
  runLoop.runMode_beforeDate_ done for 3
NSSpeechDriver::startLoop while finished
>>> 

If I don't replace nextfire with a time a second in the future, I get this:

(venv) Emiles-MacBook-Pro:typer emile$ python
Python 3.9.0 (v3.9.0:9cf6752276, Oct  5 2020, 11:29:23) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyttsx3
>>> engine = pyttsx3.init()
>>> engine.say("testing testing")
driver::_pump
>>> engine.runAndWait()
driver::runAndWait
driver::_pump
onPumpFirst_ called with timer <__NSCFTimer: 0x7fd00fab4dd0>
driver::_pump
driver::_pump popped (<bound method NSSpeechDriver.say of <NSSpeechDriver: 0x7fd00d5449b0>>, ('testing testing',), None)
driver::notify called for topic started-utterance with kwargs {}
  nextfire 4001-01-01 00:00:00 +0000 .timeIntervalSinceDate_(soon 2024-09-03 20:33:57 +0000) == 62366815562.67524 at iteration 1
  about to call runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, 4001-01-01 00:00:00 +0000) while runLoop.currentMode() == None
driver::notify called for topic started-word with kwargs {'location': 0, 'length': 7}
driver::notify called for topic started-word with kwargs {'location': 8, 'length': 7}
driver::notify called for topic finished-utterance with kwargs {'completed': True}
driver::_pump
driver::_pump popped (<bound method Engine.endLoop of <pyttsx3.engine.Engine object at 0x10e05e880>>, (), None)
driver::endLoop
nsss::stop called
nsss::endLoop called

where it hangs as in the unmodified version.

I have no Objective C experience at all, but am happy to try things out if anyone has thoughts for how this should work differently? Also happy to open a PR w/ my debugging changes if it would help to have something more concrete to talk about.

Thanks!