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
Ensure Onecore synthDriver still loads when the configured voice is missing #7999
Conversation
…anguage from setLanguage into a new getWindowsLanguage function so that it can be called separately.
…d voice has been uninstalled. Also majorly speed up changing voices. Specifically: * When fetching the list of voices from Onecore speech, also tet the language for each voice. * Choose the best default voice on initialization, trying to match the user's Windows language as best as possible. If this fails, just choose the first valid voice it can find. * Validate and filter the list of available voices as early as possible. This ensures that available voices are not fetched more than once, as the actual call to get the voices from Onecore can sometimes take up to 600 ms or more. thus this was majorly slowing down changing voices.
I've tested the OneCore synthesizer on Windows Server 2016, which had no voices installed by that time. Initializing the synth failed with a weird WindowsError. I'd say this was expected, but may be you could check for available in the check function while at it? |
682b50b
to
a1e2a6d
Compare
Checking for availableVoices takes about 600 ms on some machines. Doing
this in check will be not practical.
|
source/languageHandler.py
Outdated
localeName=locale.windows_locale[windowsLCID] | ||
except KeyError: | ||
# #4203: some locale identifiers from Windows 8 don't exist in Python's list. | ||
# Therefore use window' own function to get the locale name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: misssing 's' in "window's"
source/languageHandler.py
Outdated
# #4203: some locale identifiers from Windows 8 don't exist in Python's list. | ||
# Therefore use window' own function to get the locale name. | ||
# Eventually this should probably be used all the time. | ||
buf=ctypes.create_unicode_buffer(32) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps use a variable for this value of 32
something like buffer_size
, then reuse it below.
source/languageHandler.py
Outdated
# Eventually this should probably be used all the time. | ||
buf=ctypes.create_unicode_buffer(32) | ||
try: | ||
ctypes.windll.kernel32.LCIDToLocaleName(windowsLCID,buf,32,0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do the same with 0
name it as dwFlags
this will get optimised away, and helps a reader to know what the parameter is without looking up the function.
source/synthDrivers/oneCore.py
Outdated
def _getDefaultVoice(self): | ||
""" | ||
Finds the best available voice that can be used as a default. | ||
It first tries finding a voice with the same language and country as the user's configured Windows language (E.g. en_AU), else one that matches just the language (E.g. en), else simply the first available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you split this line into two shorter ones?
language=language.replace('-','_') | ||
return VoiceInfo(ID,name,language=language) | ||
|
||
def _getAvailableVoices(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think this will be cached by default, is that a problem? It seems like this might be a slow operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is very slow, yes. However, the SynthDriver base class has an availableVoices property that by default calls _getAvailableVoices. This property is cached. As long as we never call _getAvailableVoices we are okay.
""" | ||
Fetches the locale name of the user's configured language in Windows. | ||
""" | ||
windowsLCID=ctypes.windll.kernel32.GetUserDefaultUILanguage() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we are no longer required to support Windows XP, I'd like to suggest using GetUserPreferredUILanguages
What do you see as the advantages to using GetUserPreferredUILanguages?
Are you imagining that for Onecore speech we might try each language
until we get a voice?
If we were only interested in the first language, I assume there is no
advantage to this function over UserDefaultUILanguage?
|
@michaelDCurran commented on 6 mrt. 2018 10:57 CET:
The advantage of using GetUserPreferredUILanguages is that you don't have to do a subsequent call to LCIDToLocaleName. The comments above that call suggest that it is not yet clear what to do by default, either quering the locale id in the locale.windows_locale dictionary, or calling LCIDToLocaleName. As part of #7949, I switched from GetDateFormat to GetDateFormatEx, because of Microsoft stating their preference of using a locale name over an locale id. From the GetDateFormat page is the following quote:
Note that I'm only suggesting this as an alternative, I'd be perfectly ok with it if you keep it as it is. |
Do you know if there is a function to convert an lcid to a localeName?
It would be nice if we are going to switch to the other function that we
also allow Windows itself to convert from lcid to locale name, such as
when receiving the language ID from MS Word, so that the results are the
same for both functions. Currently we map from lcid to localeName with a
hard-coded list.
Either way though, I'd be tempted to leave changing the functions at all
for this particular PR as I'm not totally convinced that Windows 7
actually contains a complete list of locale names. We'd have to write
some kind of test for that I guess, even if it was just to prove it once.
|
@michaelDCurran commented on 6 Mar 2018, 18:32 CET:
Yes, this is LCIDToLocaleName and it is already used in the languageHandler code.
Yes, but when that fails, the code falls back to LCIDToLocaleName.
Hmm yes. But that ought to be tested on a Windows 7 system. I agree, let's keep it as it is for this particular pr. |
Err... don't mind me. I was commenting in the middle of the night :)
But yes, in future we could switch to using getUserPreferredUILanguages
and also remove the hard-coded stuff from lcidToLocaleName.
|
@feerrenrut: review actions have been addressed. |
Incubating to give people a good chance to test before 2018.2. But @feerrenrut not entirely sure if you were had finished with this. I addressed all your review comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked over this again, and I am happy with the changes.
Link to issue number:
Fixes #7553
Summary of the issue:
If the Onecore synth driver is set to a particular voice, and then that voice is uninstalled (by the user or Windows itself), the synth driver will fail to load.
Description of how this pull request fixes the issue:
When the Onecore synth driver is initialized, it now sets a default voice that it knows definitely exists. To choose this voice, it tries to match the user's Windows language as best as possible. Failing this, it just chooses the first valid voice available in the list of voices.
To get the user's Windows language, some code has been split out from languageHandler.setLanguage into a new languageHandler.getWindowsLanguage function.
The refactoring of the voice handling for the Onecore synth driver also means that now changing the voice is much faster than it used to be, as all voices are only fetched once.
Testing performed:
Known issues with pull request:
Change log entry:
Bug fixes: