Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
james-trayford opened this issue Nov 3, 2024 · 10 comments

Comments

@james-trayford
Copy link

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
Copy link
Collaborator

willwade commented Nov 3, 2024

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
Copy link
Author

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
Copy link
Collaborator

willwade commented Nov 3, 2024

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
Copy link
Author

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
Copy link
Collaborator

willwade commented Nov 4, 2024

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
Copy link
Contributor

cclauss commented Nov 7, 2024

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

self._tts.startSpeakingString_toURL_(text, url)

This causes us to skip this test on macOS.
@pytest.mark.xfail(

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
Copy link
Collaborator

willwade commented Nov 7, 2024

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

self._tts.startSpeakingString_toURL_(text, url)

This causes us to skip this test on macOS.

@pytest.mark.xfail(

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
Copy link
Contributor

cclauss commented Nov 7, 2024

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
Copy link
Collaborator

willwade commented Nov 7, 2024

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
Copy link
Author

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants