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 · 34 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?

@cclauss
Copy link
Contributor

cclauss commented Nov 8, 2024

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

@ChillFam
Copy link

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'

Not sure if this will help you but I had the same problem and got around it by doing this

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

@willwade
Copy link
Collaborator

willwade commented Dec 21, 2024

@cclauss @nateshmbhat i think we should do a release from main and then triage bugs. Any thoughts? (Nb. There WILL be bugs. )

@nateshmbhat
Copy link
Owner

Sounds good @willwade.
Gimme a day. Will check this out.

@willwade
Copy link
Collaborator

Sounds good @willwade.
Gimme a day. Will check this out.

Cool. If you can some how tag it as an alpha or something that might be good. It might fail to release as tests might fail. That's ok - let me know outcome and I'll figure that out (or try to! With @cclauss !)

@nateshmbhat
Copy link
Owner

just held up with a ton of stuff at work. will check this hopefully in a day or 2.

@cclauss
Copy link
Contributor

cclauss commented Dec 25, 2024

On the aifc stuff, https://pypi.org/project/standard-aifc will provide a version that is compatible with Py3.13+.

@nateshmbhat
Copy link
Owner

I have published the current master branch to https://test.pypi.org/project/pyttsx3/2.99/
can you test it out and see if its good to go ahead and push to main pypi ?

@james-trayford
Copy link
Author

I have published the current master branch to https://test.pypi.org/project/pyttsx3/2.99/
can you test it out and see if its good to go ahead and push to main pypi ?

@nateshmbhat this is working for me on Mac OS 14.2 and Windows 10

@cclauss
Copy link
Contributor

cclauss commented Jan 2, 2025

@james-trayford
Copy link
Author

@nateshmbhat I did come across a problem on MacOS from a fresh virtual environment:

-> python3.13 -m pip install -i https://test.pypi.org/simple/ pyttsx3==2.99
Looking in indexes: https://test.pypi.org/simple/
Collecting pyttsx3==2.99
  Using cached https://test-files.pythonhosted.org/packages/97/67/17dbb2ece338a9a4b14daee515638c77ba65ee937141e5d6458595d2c1a2/pyttsx3-2.99-py3-none-any.whl.metadata (6.2 kB)
INFO: pip is looking at multiple versions of pyttsx3 to determine which version is compatible with other requirements. This could take a while.
ERROR: Could not find a version that satisfies the requirement pyobjc>=2.4; platform_system == "Darwin" (from pyttsx3) (from versions: none)
ERROR: No matching distribution found for pyobjc>=2.4; platform_system == "Darwin"

if i pip install pyobjc beforehand, however, the install works.

@willwade
Copy link
Collaborator

willwade commented Jan 2, 2025

That’s interesting. It’s not that bad you’ve got to install pyobjc but that’s fixable. I really must check Linux and windows.

@cclauss are the tests working ok on this? What’s failing? Something must be (Nb. I’m tied up a bit at the moment. Family health crisis stuff. Will try and look as quick as I can )

@cclauss
Copy link
Contributor

cclauss commented Jan 2, 2025

https://pypi.org/project/pyobjc is v10.3.2 but I but https://test.pypi.org/project/pyobjc is a 404 Error.

@james-trayford
Copy link
Author

@cclauss ah - seems likely to be the issue!

@james-trayford
Copy link
Author

@cclauss yes, so install with

pip install --no-cache-dir --extra-index-url https://test.pypi.org/simple/ pyttsx3==2.99

works fine as it gets the pyobjc packages from regular PyPI, just using test.pypi.org for pyttsx3 v2.99 (with --no-cache-dir to avoid using previously cached packages)

this suggests tom me if v2.99 was pushed to regular PyPI, my use case would work with just a pip install pyttsx3

@nateshmbhat
Copy link
Owner

@willwade were u able to test it in linux and windows ?

@james-trayford
Copy link
Author

I confirmed 2.99 worked for me on Windows, Linux and MacOS if that's helpful?

@james-trayford
Copy link
Author

james-trayford commented Jan 23, 2025

Is there anything I can do to help get this on regular PyPI?

@willwade
Copy link
Collaborator

Sorry! So my bad!

Basically. Run it all on windows. Run the tests. See if anything breaks. I’m sure it will. I’ve just been swamped with death in family stuff and various work crap..

@willwade
Copy link
Collaborator

I confirmed 2.99 worked for me on Windows, Linux and MacOS if that's helpful?

It is super helpful. Can you run the tests and see if anything breaks?

@james-trayford
Copy link
Author

james-trayford commented Jan 25, 2025

@willwade Sorry to hear it - hope things calm down for you. Thanks for the direction I've been able to run the tests. 5 of the tests are not skipped on which 2 of them fail, with RuntimeError: This means you probably do not have eSpeak or eSpeak-ng installed!. The problem I have is for Windows, I'm testing on our University system, and we have very limited privileges - getting software like espeak-ng installed is hard/slow to get done. I've been successful with my own use-case for pyttsx3 on windows running with the default windows voices.

I should note I'm not sure where the branch representing v2.99 is - I've been installing 2.99 from test-pypi so I've been running the tests in the current master branch (I also tried the most latest PR from @cclauss #396 with the same results).

`pytest` output
================================================= test session starts =================================================
platform win32 -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
rootdir: N:\Code\pyttsx
collected 20 items

tests\test_engines.py .FsssFss                                                                                   [ 40%]
tests\test_pyttsx3.py .sss.sssssss                                                                               [100%]

====================================================== FAILURES =======================================================
______________________________________________ test_engine_name[espeak] _______________________________________________

driverName = 'espeak', debug = False

    def init(driverName=None, debug=False):
        """
        Constructs a new TTS engine instance or reuses the existing instance for
        the driver name.

        @param driverName: Name of the platform specific driver to use. If
            None, selects the default driver for the operating system.
        @type: str
        @param debug: Debugging output enabled or not
        @type debug: bool
        @return: Engine instance
        @rtype: L{engine.Engine}
        """
        try:
>           eng = _activeEngines[driverName]

C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\__init__.py:22:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <WeakValueDictionary at 0x180554bccd0>, key = 'espeak'

    def __getitem__(self, key):
        if self._pending_removals:
            self._commit_removals()
>       o = self.data[key]()
E       KeyError: 'espeak'

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\Lib\weakref.py:136: KeyError

During handling of the above exception, another exception occurred:

driver_name = 'espeak'

    @pytest.mark.parametrize("driver_name", pyttsx3.engine.engines_by_sys_platform())
    def test_engine_name(driver_name) -> None:
>       engine = pyttsx3.init(driver_name)

tests\test_engines.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\__init__.py:24: in init
    eng = Engine(driverName, debug)
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\engine.py:59: in __init__
    self.proxy = driver.DriverProxy(weakref.proxy(self), self.driver_name, debug)
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\driver.py:41: in __init__
    self._module = importlib.import_module(f"pyttsx3.drivers.{driverName}")
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\Lib\importlib\__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1387: in _gcd_import
    ???
<frozen importlib._bootstrap>:1360: in _find_and_load
    ???
<frozen importlib._bootstrap>:1331: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:935: in _load_unlocked
    ???
<frozen importlib._bootstrap_external>:1026: in exec_module
    ???
<frozen importlib._bootstrap>:488: in _call_with_frames_removed
    ???
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\drivers\espeak.py:15: in <module>
    from . import _espeak
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    import time
    from ctypes import (
        CFUNCTYPE,
        POINTER,
        Structure,
        Union,
        c_char_p,
        c_int,
        c_long,
        c_short,
        c_ubyte,
        c_uint,
        c_ulong,
        c_void_p,
        c_wchar,
        cdll,
    )


    def cfunc(name, dll, result, *args):
        """Build and apply a ctypes prototype complete with parameter flags."""
        atypes = []
        aflags = []
        for arg in args:
            atypes.append(arg[1])
            aflags.append((arg[2], arg[0]) + arg[3:])
        return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))


    dll = None


    def load_library() -> bool:
        global dll  # noqa: PLW0603
        paths = (
            # macOS paths
            "/opt/homebrew/lib/libespeak-ng.1.dylib",
            "/usr/local/lib/libespeak-ng.1.dylib",
            "/usr/local/lib/libespeak.dylib",
            # Linux paths
            "libespeak-ng.so.1",
            "/usr/local/lib/libespeak-ng.so.1",
            "libespeak.so.1",
            # Windows paths
            r"C:\Program Files\eSpeak NG\libespeak-ng.dll",
            r"C:\Program Files (x86)\eSpeak NG\libespeak-ng.dll",
        )

        for path in paths:
            try:
                dll = cdll.LoadLibrary(path)
                return True
            except Exception:  # noqa: PERF203
                continue  # Try the next path
        return False


    try:
        if not load_library():
            msg = "This means you probably do not have eSpeak or eSpeak-ng installed!"
>           raise RuntimeError(msg)
E           RuntimeError: This means you probably do not have eSpeak or eSpeak-ng installed!

C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\drivers\_espeak.py:61: RuntimeError
_____________________________________________ test_espeak_voices[espeak] ______________________________________________

driverName = 'espeak', debug = False

    def init(driverName=None, debug=False):
        """
        Constructs a new TTS engine instance or reuses the existing instance for
        the driver name.

        @param driverName: Name of the platform specific driver to use. If
            None, selects the default driver for the operating system.
        @type: str
        @param debug: Debugging output enabled or not
        @type debug: bool
        @return: Engine instance
        @rtype: L{engine.Engine}
        """
        try:
>           eng = _activeEngines[driverName]

C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\__init__.py:22:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <WeakValueDictionary at 0x180554bccd0>, key = 'espeak'

    def __getitem__(self, key):
        if self._pending_removals:
            self._commit_removals()
>       o = self.data[key]()
E       KeyError: 'espeak'

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\Lib\weakref.py:136: KeyError

During handling of the above exception, another exception occurred:

driver_name = 'espeak'

    @pytest.mark.parametrize("driver_name", pyttsx3.engine.engines_by_sys_platform())
    def test_espeak_voices(driver_name) -> None:
        if driver_name != "espeak":
            pytest.skip(f"Skipping eSpeak-specific test for {driver_name}.")

>       engine = pyttsx3.init(driver_name)

tests\test_engines.py:40:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\__init__.py:24: in init
    eng = Engine(driverName, debug)
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\engine.py:59: in __init__
    self.proxy = driver.DriverProxy(weakref.proxy(self), self.driver_name, debug)
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\driver.py:41: in __init__
    self._module = importlib.import_module(f"pyttsx3.drivers.{driverName}")
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\Lib\importlib\__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1387: in _gcd_import
    ???
<frozen importlib._bootstrap>:1360: in _find_and_load
    ???
<frozen importlib._bootstrap>:1331: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:935: in _load_unlocked
    ???
<frozen importlib._bootstrap_external>:1026: in exec_module
    ???
<frozen importlib._bootstrap>:488: in _call_with_frames_removed
    ???
C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\drivers\espeak.py:15: in <module>
    from . import _espeak
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    import time
    from ctypes import (
        CFUNCTYPE,
        POINTER,
        Structure,
        Union,
        c_char_p,
        c_int,
        c_long,
        c_short,
        c_ubyte,
        c_uint,
        c_ulong,
        c_void_p,
        c_wchar,
        cdll,
    )


    def cfunc(name, dll, result, *args):
        """Build and apply a ctypes prototype complete with parameter flags."""
        atypes = []
        aflags = []
        for arg in args:
            atypes.append(arg[1])
            aflags.append((arg[2], arg[0]) + arg[3:])
        return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))


    dll = None


    def load_library() -> bool:
        global dll  # noqa: PLW0603
        paths = (
            # macOS paths
            "/opt/homebrew/lib/libespeak-ng.1.dylib",
            "/usr/local/lib/libespeak-ng.1.dylib",
            "/usr/local/lib/libespeak.dylib",
            # Linux paths
            "libespeak-ng.so.1",
            "/usr/local/lib/libespeak-ng.so.1",
            "libespeak.so.1",
            # Windows paths
            r"C:\Program Files\eSpeak NG\libespeak-ng.dll",
            r"C:\Program Files (x86)\eSpeak NG\libespeak-ng.dll",
        )

        for path in paths:
            try:
                dll = cdll.LoadLibrary(path)
                return True
            except Exception:  # noqa: PERF203
                continue  # Try the next path
        return False


    try:
        if not load_library():
            msg = "This means you probably do not have eSpeak or eSpeak-ng installed!"
>           raise RuntimeError(msg)
E           RuntimeError: This means you probably do not have eSpeak or eSpeak-ng installed!

C:\Users\Trayfoj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\site-packages\pyttsx3\drivers\_espeak.py:61: RuntimeError
=============================================== short test summary info ===============================================
FAILED tests/test_engines.py::test_engine_name[espeak] - RuntimeError: This means you probably do not have eSpeak or eSpeak-ng installed!
FAILED tests/test_engines.py::test_espeak_voices[espeak] - RuntimeError: This means you probably do not have eSpeak or eSpeak-ng installed!
======================================= 2 failed, 3 passed, 15 skipped in 1.55s =======================================

@cclauss
Copy link
Contributor

cclauss commented Jan 25, 2025

@james-trayford On the machines where you run pytest, can you try espeak-ng --version and let us know the output?

Do you need espeak or would the default Windows voices (sapi5) work for your use cases?

The title of this issue is about nss which is only compatible with Apple operating systems.

@james-trayford
Copy link
Author

@james-trayford On the machines where you run pytest, can you try espeak-ng --version and let us know the output?

@cclauss It's not installed atm so I just get

(venv) N:\Code\pyttsx>espeak-ng --version
'espeak-ng' is not recognized as an internal or external command,
operable program or batch file.

Do you need espeak or would the default Windows voices (sapi5) work for your use cases?

No I don't need espeak and sapi5 voices are perfectly fine for me - I was just implying I may not be able to fully validate windows compatibility without a form of espeak installed, in the case that was a requirement for pyttsx3==2.99 to be uploaded to regular pypi (which I'm primarily interested in)

The title of this issue is about nss which is only compatible with Apple operating systems.

yes - apologies - @nateshmbhat 's v2.99 fixed my original nss problem, and its morphed into a discussion about making 2.99 available on regular PyPI as I'm eager to package it as a dependency. If a separate issue is better I could open one

@james-trayford
Copy link
Author

Given it's working in the sapi5 case, does espeak-ng need further testing before putting 2.99 on regular PyPI?

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

5 participants