pyttsx3 icon indicating copy to clipboard operation
pyttsx3 copied to clipboard

Manually installed voice missing after compilation with PyInstaller

Open TypicalHog opened this issue 6 years ago • 12 comments

After I finally got my program to work with PyInstaller with the help of this, another issue arose. When I run the .py file there are 3 voices (2nd one has been manually installed), but when I run the .exe created by PyInstaller there are only 2 default voices (as seen in the images below).

image

This piece of code runs fine, just refuses to find the missing voice:

try:
    tts_engine = pyttsx3.init()
    for i in tts_engine.getProperty('voices'):
        if i.name not in combobox_voice['values']:
            combobox_voice['values'] += (i.name,)
except:
    listbox_logs.insert(END, f"ERROR: Couldn't get TTS voices.")
    listbox_logs.see(END)

I would greatly appreciate any suggestions that may lead to a fix or at least a better understanding of this problem.

TypicalHog avatar May 19 '19 12:05 TypicalHog

That code snippet doesn't work for me. I ended up doing the following:

        try:
            tts_engine = pyttsx3.init()
            temp_list = []
            for i in tts_engine.getProperty('voices'):
                if i.name not in self.combobox_voice['values']:
                    temp_list.append(i.name)
            self.combobox_voice['values'] = temp_list
        except:
            print('error')

It seems to me that you're improperly assigning the voices to the combobox.

Perhaps you could supply me with a complete example and I could be of further help.

josephalway avatar May 22 '19 21:05 josephalway

Ok, I got 3 voices when I ran this program.

import pyttsx3
from time import sleep

try:
    tts_engine = pyttsx3.init()
    for i in tts_engine.getProperty('voices'):
        print(i.name)
except:
    print('error')

sleep(60)

However, when I compiled it with PyInstaller I got only 2 voices (as I anticipated). I built it with these parameters:

pyinstaller -F test2.py --hidden-import=pyttsx3.drivers --hidden-import=pyttsx3.drivers.dummy --hidden-import=pyttsx3.drivers.espeak --hidden-import=pyttsx3.drivers.nsss --hidden-import=pyttsx3.drivers.sapi5

TypicalHog avatar May 30 '19 13:05 TypicalHog

Why are you using hidden imports? Those shouldn't be necessary.

I would highly recommend using a .spec file and building your program like that. Below are examples I use for a fun project I've been working on that uses pyttsx3, with generic my_app names. You won't be able to use it as a drop-in replacement for what you're doing, but it should give you something to work with.

Example my_app_build.bat file for building the executable: pyinstaller --clean --onefile --windowed my_app_build.spec

Example my_app_build.spec file referenced by my build file:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['my_app.py'],
             pathex=['C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\x86'],
             binaries=[],
             datas=[('my_app_icon.ico', '.')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
		  Tree('Images', prefix='Images\\'),
		  Tree('Sound', prefix='Sound\\'),
          a.zipfiles,
          a.datas,
          name='name_of_my_application',
          debug=False,
          strip=False,
          upx=False,
          console=False, icon='my_app_icon.ico' )

josephalway avatar Jun 04 '19 20:06 josephalway

Hmm..., I don't specifically import pyttsx3 or it's submodules in my project, but I have apparently done that in an example or two I've run.

Why don't you try the following:

Create a pyttsx_speech_example.py file with the following code:

import pyttsx3

try:
    tts_engine = pyttsx3.init()
    for i in tts_engine.getProperty('voices'):
        print(i.name)
    input('Press enter to exit.')
except:
    print('error')

Create a pyttsx_speech_example.bat file with this command: pyinstaller --onefile --windowed pyttsx_speech_example.spec --clean

Create a pyttsx_speech_example.spec file with these parameters:

# -*- mode: python -*-
from PyInstaller.utils.hooks import collect_submodules
block_cipher = None

my_hidden_imports = collect_submodules('pyttsx3')

a = Analysis(['pyttsx_speech_example.py'],
             pathex=['C:\\Program Files (x86)\\Windows Kits\\10\\Redist\\ucrt\\DLLs\\x64'],
             binaries=[],
             datas=[],
             hiddenimports=my_hidden_imports,
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='pyttsx_speech_example',
          debug=False,
          strip=False,
          upx=False,
          console=True)

You can remove the contents of the pathex parameter, if you don't have the Windows 10 SDK installed. Your program just won't likely run correctly on other computers.

josephalway avatar Jun 04 '19 20:06 josephalway

The code you posted above does work, but the 3rd voice is still not there when I run the .exe.

TypicalHog avatar Jun 05 '19 11:06 TypicalHog

Assuming, the code works, and it lists the 3rd voice correctly.

That would make me take a serious look at pyinstaller as the code works, but the compiled executable doesn't.

I would recommend reviewing this page https://pythonhosted.org/PyInstaller/hooks.html. Then post an issue here https://github.com/pyinstaller/pyinstaller/issues. In the event that someone there might be able to help you track down the issue.

josephalway avatar Jun 07 '19 15:06 josephalway

I've reviewed the code as pertaining to how it uses the SAPI driver for Windows and loads the voice(s). The SAPI5.py file in the driver folder specifies a few common voices, but those variables aren't used to load the voice(s).

You may need to create a hook, or add the additional voice as a data file and load it manually, or something else. As far as I can tell it should "just work" with the additional voice(s).

josephalway avatar Jun 07 '19 15:06 josephalway

I successfully installed the English (Great Britain) language-pack and it added the Hazel voice to my list of voices. https://support.microsoft.com/en-us/help/22805 I had to go into the language settings and choose download language pack to get it to install.

When I go to Settings->Ease of Access->Narrator->Choose a Voice I get several voices to choose from. Including George and Susan.

Interestingly enough, only Microsoft David, Zira, and Hazel show up when I run the code mentioned previously. I'm guessing, because those are the only SAPI5 voices that are installed.

When I compile the code with no special additions, etc. with pyinstaller. It lists those 3 voices.

Microsoft David Desktop - English (United States)
Microsoft Hazel Desktop - English (Great Britain)
Microsoft Zira Desktop - English (United States)
Press enter to exit.

Example that prints the above and uses the female british voice to say "This is just a test":

import pyttsx3

try:
    tts_engine = pyttsx3.init()
    voices = tts_engine.getProperty('voices')
    for voice in voices:
        print(voice.name)
    tts_engine.setProperty('voice', voices[1].id)
    tts_engine.say('This is just a test.')
    tts_engine.runAndWait()
    input('Press enter to exit.')
except:
    print('error')

josephalway avatar Jun 07 '19 16:06 josephalway

Maybe it has something to do with the fact the voice is from a 3rd party and not officially from Microsoft. Anyway, thank You so much for all the time you took to look into this problem. I might try to find a solution by messing with PyInstaller some more in the future.

TypicalHog avatar Jun 08 '19 11:06 TypicalHog

Ok, I got 3 voices when I ran this program.

import pyttsx3
from time import sleep

try:
    tts_engine = pyttsx3.init()
    for i in tts_engine.getProperty('voices'):
        print(i.name)
except:
    print('error')

sleep(60)

However, when I compiled it with PyInstaller I got only 2 voices (as I anticipated). I built it with these parameters:

pyinstaller -F test2.py --hidden-import=pyttsx3.drivers --hidden-import=pyttsx3.drivers.dummy --hidden-import=pyttsx3.drivers.espeak --hidden-import=pyttsx3.drivers.nsss --hidden-import=pyttsx3.drivers.sapi5

thxxxxxx you are king

Aviv05423 avatar May 02 '21 20:05 Aviv05423

I successfully installed the English (Great Britain) language-pack and it added the Hazel voice to my list of voices. https://support.microsoft.com/en-us/help/22805 I had to go into the language settings and choose download language pack to get it to install.

When I go to Settings->Ease of Access->Narrator->Choose a Voice I get several voices to choose from. Including George and Susan.

Interestingly enough, only Microsoft David, Zira, and Hazel show up when I run the code mentioned previously. I'm guessing, because those are the only SAPI5 voices that are installed.

When I compile the code with no special additions, etc. with pyinstaller. It lists those 3 voices.

Microsoft David Desktop - English (United States)
Microsoft Hazel Desktop - English (Great Britain)
Microsoft Zira Desktop - English (United States)
Press enter to exit.

Example that prints the above and uses the female british voice to say "This is just a test":

import pyttsx3

try:
    tts_engine = pyttsx3.init()
    voices = tts_engine.getProperty('voices')
    for voice in voices:
        print(voice.name)
    tts_engine.setProperty('voice', voices[1].id)
    tts_engine.say('This is just a test.')
    tts_engine.runAndWait()
    input('Press enter to exit.')
except:
    print('error')

I have also encountered this problem and I found the solution

https://www.ghacks.net/2018/08/11/unlock-all-windows-10-tts-voices-system-wide-to-get-more-of-them/

starflash avatar May 24 '23 20:05 starflash