Skip to content

Commit 4042bf9

Browse files
authored
Merge 486c5b0 into 0dbab4d
2 parents 0dbab4d + 486c5b0 commit 4042bf9

4 files changed

Lines changed: 58 additions & 37 deletions

File tree

source/languageHandler.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
# languageHandler.py
21
# A part of NonVisual Desktop Access (NVDA)
3-
# Copyright (C) 2007-2018 NV access Limited, Joseph Lee
2+
# Copyright (C) 2007-2021 NV access Limited, Joseph Lee
43
# This file is covered by the GNU General Public License.
54
# See the file COPYING for more details.
65

@@ -16,6 +15,7 @@
1615
import gettext
1716
import globalVars
1817
from logHandler import log
18+
from typing import Optional
1919

2020
#a few Windows locale constants
2121
LOCALE_SLANGUAGE=0x2
@@ -291,7 +291,8 @@ def setLocale(localeName: str) -> None:
291291
def getLanguage() -> str:
292292
return curLang
293293

294-
def normalizeLanguage(lang):
294+
295+
def normalizeLanguage(lang) -> Optional[str]:
295296
"""
296297
Normalizes a language-dialect string in to a standard form we can deal with.
297298
Converts any dash to underline, and makes sure that language is lowercase and dialect is upercase.

source/speech/speech.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def speakMessage(
182182
speak(seq, symbolLevel=None, priority=priority)
183183

184184

185-
def getCurrentLanguage():
185+
def getCurrentLanguage() -> str:
186186
synth = getSynth()
187187
language=None
188188
if synth:

source/synthDriverHandler.py

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# A part of NonVisual Desktop Access (NVDA)
22
# This file is covered by the GNU General Public License.
33
# See the file COPYING for more details.
4-
# Copyright (C) 2006-2019 NV Access Limited, Peter Vágner, Aleksey Sadovoy,
4+
# Copyright (C) 2006-2021 NV Access Limited, Peter Vágner, Aleksey Sadovoy,
55
# Joseph Lee, Arnold Loubriat, Leonard de Ruijter
66

77
import pkgutil
88
import importlib
9-
from typing import Optional
9+
from typing import Optional, OrderedDict, Set
1010
from locale import strxfrm
1111

1212
import config
@@ -38,11 +38,10 @@ class VoiceInfo(StringParameterInfo):
3838
"""Provides information about a single synthesizer voice.
3939
"""
4040

41-
def __init__(self, id, displayName, language=None):
41+
def __init__(self, id, displayName, language: Optional[str] = None):
4242
"""
4343
@param language: The ID of the language this voice speaks,
4444
C{None} if not known or the synth implements language separate from voices.
45-
@type language: str
4645
"""
4746
self.language = language
4847
super(VoiceInfo, self).__init__(id, displayName)
@@ -71,10 +70,6 @@ class SynthDriver(driverHandler.Driver):
7170
L{supportedNotifications} should specify what notifications the synthesizer provides.
7271
Currently, the available notifications are L{synthIndexReached} and L{synthDoneSpeaking}.
7372
Both of these must be supported.
74-
@ivar voice: Unique string identifying the current voice.
75-
@type voice: str
76-
@ivar availableVoices: The available voices.
77-
@type availableVoices: OrderedDict of L{VoiceInfo} keyed by VoiceInfo's ID
7873
@ivar pitch: The current pitch; ranges between 0 and 100.
7974
@type pitch: int
8075
@ivar rate: The current rate; ranges between 0 and 100.
@@ -102,6 +97,18 @@ class SynthDriver(driverHandler.Driver):
10297
#: @type: set of L{extensionPoints.Action} instances
10398
supportedNotifications = frozenset()
10499
_configSection = "speech"
100+
# type information for auto property _get_voice
101+
# Unique string identifying the current voice.
102+
voice: str
103+
# type information for auto property _get_availableVoices
104+
# OrderedDict of L{VoiceInfo} keyed by VoiceInfo's ID
105+
availableVoices: OrderedDict[str, VoiceInfo]
106+
# type information for auto property _get_language
107+
# the current voice's language
108+
language: Optional[str]
109+
# type information for auto property _get_availableLanguages
110+
# the set of languages available in the availableVoices
111+
availableLanguages: Set[Optional[str]]
105112

106113
@classmethod
107114
def LanguageSetting(cls):
@@ -218,29 +225,28 @@ def cancel(self):
218225
"""Silence speech immediately.
219226
"""
220227

221-
def _get_language(self):
228+
def _get_language(self) -> Optional[str]:
222229
return self.availableVoices[self.voice].language
223230

224231
def _set_language(self, language):
225232
raise NotImplementedError
226233

227-
def _get_availableLanguages(self):
228-
raise NotImplementedError
234+
def _get_availableLanguages(self) -> Set[Optional[str]]:
235+
return {self.availableVoices[v].language for v in self.availableVoices}
229236

230237
def _get_voice(self):
231238
raise NotImplementedError
232239

233240
def _set_voice(self, value):
234241
pass
235242

236-
def _getAvailableVoices(self):
243+
def _getAvailableVoices(self) -> OrderedDict[str, VoiceInfo]:
237244
"""fetches an ordered dictionary of voices that the synth supports.
238245
@returns: an OrderedDict of L{VoiceInfo} instances representing the available voices, keyed by ID
239-
@rtype: OrderedDict
240246
"""
241247
raise NotImplementedError
242248

243-
def _get_availableVoices(self):
249+
def _get_availableVoices(self) -> OrderedDict[str, VoiceInfo]:
244250
if not hasattr(self, '_availableVoices'):
245251
self._availableVoices = self._getAvailableVoices()
246252
return self._availableVoices
@@ -449,28 +455,37 @@ def setSynth(name, isFallback=False):
449455
prevSynthName = None
450456
try:
451457
_curSynth = getSynthInstance(name)
458+
except: # noqa: E722 # Legacy bare except
459+
log.error(f"setSynth failed for {name}", exc_info=True)
460+
461+
if _curSynth and not _curSynth.availableVoices:
462+
# This synth cannot be used without available voices
463+
log.info(f"No available voices for synthDriver {name}")
464+
_curSynth = None
465+
466+
if _curSynth is not None:
452467
_audioOutputDevice = config.conf["speech"]["outputDevice"]
453468
if not isFallback:
454469
config.conf["speech"]["synth"] = name
455-
log.info("Loaded synthDriver %s" % name)
470+
log.info(f"Loaded synthDriver {name}")
456471
return True
457-
except: # noqa: E722 # Legacy bare except
458-
log.error("setSynth", exc_info=True)
459-
# As there was an error loading this synth:
460-
if prevSynthName:
461-
# There was a previous synthesizer, so switch back to that one.
462-
setSynth(prevSynthName, isFallback=True)
463-
else:
464-
# There was no previous synth, so fallback to the next available default synthesizer
465-
# that has not been tried yet.
466-
try:
467-
nextIndex = defaultSynthPriorityList.index(name) + 1
468-
except ValueError:
469-
nextIndex = 0
470-
if nextIndex < len(defaultSynthPriorityList):
471-
newName = defaultSynthPriorityList[nextIndex]
472-
setSynth(newName, isFallback=True)
473-
return False
472+
# As there was an error loading this synth:
473+
elif prevSynthName:
474+
log.info(f"Falling back to previous synthDriver {prevSynthName}")
475+
# There was a previous synthesizer, so switch back to that one.
476+
setSynth(prevSynthName, isFallback=True)
477+
else:
478+
# There was no previous synth, so fallback to the next available default synthesizer
479+
# that has not been tried yet.
480+
try:
481+
nextIndex = defaultSynthPriorityList.index(name) + 1
482+
except ValueError:
483+
nextIndex = 0
484+
if nextIndex < len(defaultSynthPriorityList):
485+
newName = defaultSynthPriorityList[nextIndex]
486+
log.info(f"Falling back to next synthDriver {newName}")
487+
setSynth(newName, isFallback=True)
488+
return False
474489

475490

476491
def handlePostConfigProfileSwitch(resetSpeechIfNeeded=True):

source/synthDrivers/silence.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#This file is covered by the GNU General Public License.
55
#See the file COPYING for more details.
66

7+
from collections import OrderedDict
78
import synthDriverHandler
89
from speech.commands import IndexCommand
910

@@ -18,7 +19,8 @@ class SynthDriver(synthDriverHandler.SynthDriver):
1819
def check(cls):
1920
return True
2021

21-
supportedSettings=[]
22+
supportedSettings = frozenset()
23+
_availableVoices = OrderedDict({name: synthDriverHandler.VoiceInfo(name, description)})
2224

2325
def speak(self, speechSequence):
2426
self.lastIndex = None
@@ -28,3 +30,6 @@ def speak(self, speechSequence):
2830

2931
def cancel(self):
3032
self.lastIndex = None
33+
34+
def _get_voice(self):
35+
return self.name

0 commit comments

Comments
 (0)