nateshmbhat / pyttsx3

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

AttributeError: 'NSSpeechDriver' object has no attribute '_current_text' #361

Open james-trayford opened 3 weeks ago

james-trayford commented 3 weeks ago

Hi, I've hit a problem trying to use pyttsx3 for speech synthesis on Mac OSX Sonoma 14.2

I can get the say function to run ok, but when I try the engine.save_to_file() method, this crashes on engine.runAndWait() with the error: AttributeError: 'NSSpeechDriver' object has no attribute '_current_text' (see trace at bottom). However, the output file appears to produce successfully.

If I catch this error in a try block, I can get my application to run first time and have the audio file I need, but I think this leaves the NSSS engine in an abortive state - If i run again I get the Error run loop already started. I've tried also adding:

if engine._inLoop:                                                                                                                                                                  
      engine.endLoop()

to try and force stop the loop but I get Stopper already registered for this runLoop.

Grateful for any insight!

Trace:

in the `except` block, but this

AttributeError                            Traceback (most recent call last)
Cell In[4], line 6
      3 # render at default 48 kHz rate
      4 soni = Sonification(score, events, generator, system,
      5                     caption=caption_en)
----> 6 soni.render()
      7 soni.notebook_display(show_waveform=0)

File ~/Documents/Code/strauss_dev/src/strauss/sonification.py:205, in Sonification.render(self, downsamp)
    203     else:
    204         pass
--> 205     render_caption(self.caption, self.samprate,
    206                self.ttsmodel, str(cpath))
    207 rate_in, wavobj = wavfile.read(cpath)
    208 wavobj = np.array(wavobj)

File ~/Documents/Code/strauss_dev/src/strauss/tts_caption.py:121, in render_caption(caption, samprate, model, caption_path)
    117 engine.save_to_file(caption, caption_path)
    119 try:
    120     # TODO: explore why NSS triggers error hear without catching
--> 121     engine.runAndWait()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/engine.py:183, in Engine.runAndWait(self)
    181 self._inLoop = True
    182 self._driverLoop = True
--> 183 self.proxy.runAndWait()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/driver.py:195, in DriverProxy.runAndWait(self)
    190 '''
    191 Called by the engine to start an event loop, process all commands in
    192 the queue at the start of the loop, and then exit the loop.
    193 '''
    194 self._push(self._engine.endLoop, tuple())
--> 195 self._driver.startLoop()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py:77, in NSSpeechDriver.startLoop(self)
     75 if nextfire is not None:
     76     nextfire = soon.earlierDate_(nextfire)
---> 77 if not runLoop.runMode_beforeDate_(NSDefaultRunLoopMode, nextfire):
     78     stopper.stop()
     79     break

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py:159, in NSSpeechDriver.speechSynthesizer_willSpeakWord_ofString_(self, tts, rng, text)
    158 def speechSynthesizer_willSpeakWord_ofString_(self, tts, rng, text):
--> 159     if self._current_text:
    160         current_word = self._current_text[rng.location:rng.location + rng.length]
    161     else:

AttributeError: 'NSSpeechDriver' object has no attribute '_current_text'
willwade commented 3 weeks ago

ok - mind giving me a code snippet fully on this? I have a feeling we have fixed this just not pushed a new release

james-trayford commented 3 weeks ago

Hey, here's a minimal snippet that triggers the original error for me:

import pyttsx3
engine = pyttsx3.init()
engine.save_to_file('test', 'test.wav')
engine.runAndWait()

giving:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[4], line 4
      2 engine = pyttsx3.init()
      3 engine.save_to_file('test', 'test.wav')
----> 4 engine.runAndWait()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/engine.py:183, in Engine.runAndWait(self)
    181 self._inLoop = True
    182 self._driverLoop = True
--> 183 self.proxy.runAndWait()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/driver.py:195, in DriverProxy.runAndWait(self)
    190 '''
    191 Called by the engine to start an event loop, process all commands in
    192 the queue at the start of the loop, and then exit the loop.
    193 '''
    194 self._push(self._engine.endLoop, tuple())
--> 195 self._driver.startLoop()

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py:73, in NSSpeechDriver.startLoop(self)
     71 PyObjCAppHelperRunLoopStopper.addRunLoopStopper_toRunLoop_(stopper, runLoop)
     72 while stopper.shouldRun():
---> 73     nextfire = runLoop.limitDateForMode_(NSDefaultRunLoopMode)
     74     soon = NSDate.dateWithTimeIntervalSinceNow_(0)  # maxTimeout in runConsoleEventLoop
     75     if nextfire is not None:

File ~/Documents/Code/strauss_dev/venv/lib/python3.11/site-packages/pyttsx3/drivers/nsss.py:159, in NSSpeechDriver.speechSynthesizer_willSpeakWord_ofString_(self, tts, rng, text)
    158 def speechSynthesizer_willSpeakWord_ofString_(self, tts, rng, text):
--> 159     if self._current_text:
    160         current_word = self._current_text[rng.location:rng.location + rng.length]
    161     else:

AttributeError: 'NSSpeechDriver' object has no attribute '_current_text'
willwade commented 3 weeks ago

Yeah ok - definitely then. thanks for confirming that - I can confirm we have fixed this already - just not released. Just give us a small while to release.. we have some snags with espeak that I'd love to fix first. Apologies.. Use the github url for the pip install if you are in a hurry. Sorry !

james-trayford commented 3 weeks ago

great that seems to work, thanks for your help! I'll use this and keep an eye out for the update - should we close this now or wait until the fix is published?

willwade commented 3 weeks ago

lets keep it open.. you never know it might take me forever to get espeak working against all our new tests! its looking that way..

cclauss commented 2 weeks ago

We are saving .aiff files, not .wav when using NSSpeechSynthesizer nsss on macOS because startSpeakingString:toURL synthesizes text into a sound (AIFF) file. https://github.com/nateshmbhat/pyttsx3/blob/d212b167cf722126de819ab9b5102aa40b640406/pyttsx3/drivers/nsss.py#L157 This causes us to skip this test on macOS. https://github.com/nateshmbhat/pyttsx3/blob/d212b167cf722126de819ab9b5102aa40b640406/tests/test_pyttsx3.py#L39

Converting a .aiff into a .wav could be done by ffmpg, pydub, or scipy.

If the eSpeak-NG driver was used on macOS instead of NSSpeechSynthesizer, the file would probably be a .wav.

willwade commented 2 weeks ago

We are saving .aiff files, not .wav when using NSSpeechSynthesizer nsss on macOS because startSpeakingString:toURL synthesizes text into a sound (AIFF) file.

https://github.com/nateshmbhat/pyttsx3/blob/d212b167cf722126de819ab9b5102aa40b640406/pyttsx3/drivers/nsss.py#L157

This causes us to skip this test on macOS. https://github.com/nateshmbhat/pyttsx3/blob/d212b167cf722126de819ab9b5102aa40b640406/tests/test_pyttsx3.py#L39

Converting a .aiff into a .wav could be done by ffmpg, pydub, or scipy.

If the eSpeak-NG driver was used on macOS instead of NSSpeechSynthesizer, the file would probably be a .wav.

Yeah - I felt uncomfortable adding a big dependency like pydub or ffmpeg.. What if we just dealt with it..

so end user save_to_file('file.wav")

nsss-> file.aiff

?

Or - check of ffmpeg installed - then output to wav. But dont add as a depenency -

cclauss commented 2 weeks ago

https://docs.python.org/3.12/library/aifc.html was removed in Python 3.13 as a dead battery https://peps.python.org/pep-0594/#aifc

Will we have identical problems with AVSynth https://developer.apple.com/documentation/avfaudio/avaudiofile ?

willwade commented 2 weeks ago

https://docs.python.org/3.12/library/aifc.html was removed in Python 3.13 as a dead battery https://peps.python.org/pep-0594/#aifc

Will we have identical problems with AVSynth https://developer.apple.com/documentation/avfaudio/avaudiofile ?

OOh i have a feeling we wont - but will need to bear this in mind.

james-trayford commented 2 weeks ago

Thanks both - yeah I noticed that my files were actually aiff, so I have catch in my strauss code that converts this towav with ffmpeg if it can't be read as a WAV - though didn't realise scipy could do this?

cclauss commented 2 weeks ago

Ask ChatGPT: How can I convert an AIFF file into a WAV file with Python on macOS?