Skip to content

Commit

Permalink
* 數字功能分為 2 個設定選項「數字語言」與「數字模式」,將選項分為 2 維度以利選擇
Browse files Browse the repository at this point in the history
*	忽略數字間逗點選項使數值報讀更正確
*	自動偵測語言功能當信任語音語言勾選時才使用不同解釋檔
*	語音設定中的值調整按確認才生效按取消會回到設定前的值
*	修正語音設定中當語音為預設語音時調整設定值後重啟 NVDA 後回到設定前的值
  • Loading branch information
tsengwoody committed Dec 16, 2020
1 parent 655f23b commit 5c9c329
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 41 deletions.
24 changes: 21 additions & 3 deletions addon/globalPlugins/WorldVoiceXVED2/speechSettingsDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import wx
from autoSettingsUtils.utils import paramToPercent, percentToParam
import addonHandler
import config
import gui
from gui import guiHelper
import speech
Expand Down Expand Up @@ -73,14 +74,16 @@ def _updateVoicesSelection(self):
self._voicesChoice.SetItems([])
else:
locale = self._locales[localeIndex]
voices = sorted(self._localeToVoices[locale])
voices = ["default"] + sorted(self._localeToVoices[locale])
self._voicesChoice.SetItems(voices)
if locale in _config.vocalizerConfig["autoLanguageSwitching"]:
voice = _config.vocalizerConfig["autoLanguageSwitching"][locale]["voice"]
if voice:
self.sliderEnable()
self._voicesChoice.Select(voices.index(voice))
self.onVoiceChange(None)
else:
self._voicesChoice.Select(0)
self.onVoiceChange(None)

def sliderEnable(self):
self._rateSlider.Enable()
Expand All @@ -97,7 +100,7 @@ def onLocaleChanged(self, event):

def onVoiceChange(self, event):
voiceName = self._voicesChoice.GetStringSelection()
if voiceName == '':
if voiceName == "default":
self.sliderDisable()
return
self.sliderEnable()
Expand Down Expand Up @@ -128,7 +131,22 @@ def onVolumeSliderScroll(self, event):
voiceInstance = self._manager.getVoiceInstance(voiceName)
voiceInstance.volume = self._volumeSlider.GetValue()

def onCancel(self, event):
for instance in self._manager._instanceCache.values():
instance.rollback()
return super(Dialog, self).onCancel(event)

def onOk(self, event):
for instance in self._manager._instanceCache.values():
instance.commit()
try:
if instance.name == config.conf["speech"][self._synthInstance.name]["voice"]:
config.conf["speech"][self._synthInstance.name]["rate"] = instance.rate
config.conf["speech"][self._synthInstance.name]["pitch"] = instance.pitch
config.conf["speech"][self._synthInstance.name]["volume"] = instance.volume
except:
pass

if not self._synthInstance.name == 'WorldVoiceXVED2':
self._manager.close()
return super(Dialog, self).onOk(event)
Expand Down
98 changes: 69 additions & 29 deletions addon/synthDrivers/WorldVoiceXVED2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import driverHandler

number_pattern = re.compile(r"[0-9]+[0-9.:]*[0-9]+|[0-9]")
comma_number_pattern = re.compile(r"(?<=[0-9]),(?=[0-9])")
chinese_space_pattern = re.compile(r"(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])")

english_number = {
Expand Down Expand Up @@ -65,23 +66,34 @@ class SynthDriver(SynthDriver):
SynthDriver.PitchSetting(),
SynthDriver.VolumeSetting(),
driverHandler.DriverSetting(
"num",
"numlan",
# Translators: Label for a setting in voice settings dialog.
_("&Number Mode"),
_("Number &Language"),
availableInSettingsRing=True,
defaultVal="default",
# Translators: Label for a setting in synth settings ring.
displayName=_("Number Mode"),
),
driverHandler.DriverSetting(
"nummod",
# Translators: Label for a setting in voice settings dialog.
_("Number &Mode"),
availableInSettingsRing=True,
defaultVal="value",
# Translators: Label for a setting in synth settings ring.
displayName=_("Number Mode"),
),
driverHandler.NumericDriverSetting(
"chinesespace",
# Translators: Label for a setting in voice settings dialog.
_("&Chinese Space Break"),
availableInSettingsRing=True,
_("Pause time when encountering spaces between Chinese"),
defaultVal=0,
minStep=1,
# Translators: Label for a setting in synth settings ring.
displayName=_("Chinese Space Break"),
),
driverHandler.BooleanDriverSetting(
"cni",
_("Ignore comma between number"),
defaultVal=False,
),
driverHandler.BooleanDriverSetting(
"dli",
Expand Down Expand Up @@ -233,6 +245,14 @@ def _speak(self, voiceInstance, chunks):
_vocalizer.processText2Speech(voiceInstance, text)

def patchedSpeak(self, speechSequence, symbolLevel=None, priority=None):
if self._cni:
temp = []
for command in speechSequence:
if isinstance(command, str):
temp.append(comma_number_pattern.sub(lambda m:'', command))
else:
temp.append(command)
speechSequence = temp
if self._dli:
speechSequence = self.removeLangChangeCommand(speechSequence)
if config.conf["speech"]["autoLanguageSwitching"] \
Expand All @@ -244,7 +264,8 @@ def patchedSpeak(self, speechSequence, symbolLevel=None, priority=None):

def patchedSpeakSpelling(self, text, locale=None, useCharacterDescriptions=False, priority=None):
if config.conf["speech"]["autoLanguageSwitching"] \
and _config.vocalizerConfig['autoLanguageSwitching']['useUnicodeLanguageDetection']:
and _config.vocalizerConfig['autoLanguageSwitching']['useUnicodeLanguageDetection'] \
and config.conf["speech"]["trustVoiceLanguage"]:
for text, loc in self._languageDetector.process_for_spelling(text, locale):
self._realSpellingFunc(text, loc, useCharacterDescriptions, priority=priority)
else:
Expand All @@ -264,30 +285,35 @@ def _get_volume(self):

def _set_volume(self, value):
self._voiceManager.defaultVoiceInstance.volume = value
self._voiceManager.defaultVoiceInstance.commit()

def _get_rate(self):
return self._voiceManager.defaultVoiceInstance.rate

def _set_rate(self, value):
self._voiceManager.defaultVoiceInstance.rate = value
self._voiceManager.defaultVoiceInstance.commit()

def _get_pitch(self):
return self._voiceManager.defaultVoiceInstance.pitch

def _set_pitch(self, value):
self._voiceManager.defaultVoiceInstance.pitch = value
self._voiceManager.defaultVoiceInstance.commit()

def _getAvailableVoices(self):
return self._voiceManager.voiceInfos

def _get_voice(self):
if self._voice is None:
self._voice = self._voiceManager.getVoiceNameForLanguage(languageHandler.getLanguage())
if self._voice is None:
self._voice = list(self.availableVoices.keys())[0]
return self._voice
voice = self._voiceManager.getVoiceNameForLanguage(languageHandler.getLanguage())
if voice is None:
voice = list(self.availableVoices.keys())[0]
return voice
return self._voiceManager.defaultVoiceName

def _set_voice(self, voiceName):
self._voice = voiceName
if voiceName == self._voiceManager.defaultVoiceName:
return
# Stop speech before setting a new voice to avoid voice instances
Expand All @@ -296,8 +322,8 @@ def _set_voice(self, voiceName):
_vocalizer.stop()
self._voiceManager.setDefaultVoice(voiceName)
# Available variants are cached by default. As variants maybe different for each voice remove the cached value
if hasattr(self, '_availableVariants'):
del self._availableVariants
# if hasattr(self, '_availableVariants'):
# del self._availableVariants
# Synchronize with the synth so the parameters
# we report are not from the previous voice.
# _vocalizer.sync()
Expand All @@ -323,22 +349,32 @@ def _info(self):
s = [self.description]
return ", ".join(s)

def _get_availableNums(self):
def _get_availableNumlans(self):
return dict({
"default": driverHandler.StringParameterInfo("default", _("default")),
"automatic_number": driverHandler.StringParameterInfo("automatic_number", _("automatic number")),
"chinese_number": driverHandler.StringParameterInfo("chinese_number", _("chinese number")),
"english_number": driverHandler.StringParameterInfo("english_number", _("english number")),
}, **{
locale + "_number": driverHandler.StringParameterInfo(locale + "_number", _("number ") + name) for locale, name in zip(self._locales, self._localeNames)
locale: driverHandler.StringParameterInfo(locale, name) for locale, name in zip(self._locales, self._localeNames)
}, **{
locale + "_value": driverHandler.StringParameterInfo(locale + "_value", _("value ") + name) for locale, name in zip(self._locales, self._localeNames)
"chinese_number": driverHandler.StringParameterInfo("chinese_number", _("chinese number")),
"english_number": driverHandler.StringParameterInfo("english_number", _("english number")),
})

def _get_num(self):
def _get_numlan(self):
return self._numlan

def _set_numlan(self,value):
self._numlan = value

def _get_availableNummods(self):
return dict({
"value": driverHandler.StringParameterInfo("value", _("value")),
"number": driverHandler.StringParameterInfo("number", _("number")),
})

def _get_nummod(self):
return self._nummod

def _set_num(self,value):
def _set_nummod(self,value):
self._nummod = value

def _get_chinesespace(self):
Expand All @@ -347,23 +383,27 @@ def _get_chinesespace(self):
def _set_chinesespace(self,value):
self._chinesespace = value

def _get_cni(self):
return self._cni

def _set_cni(self,value):
self._cni = value

def _get_dli(self):
return self._dli

def _set_dli(self,value):
self._dli = value

def patchedNumSpeechSequence(self, speechSequence):
if self._nummod == "automatic_number":
speechSequence = [number_pattern.sub(lambda m: ' '.join(m.group(0)), command) if isinstance(command, str) else command for command in speechSequence]
elif self._nummod == "chinese_number":
if False:
pass
elif self._numlan == "chinese_number":
speechSequence = [command.translate(chinese_number) if isinstance(command, str) else command for command in speechSequence]
elif self._nummod == "english_number":
elif self._numlan == "english_number":
speechSequence = [command.translate(english_number) if isinstance(command, str) else command for command in speechSequence]
elif self._nummod.endswith("_number"):
speechSequence = self.coercionNumberLangChange(speechSequence, self._nummod[:-7], 'number')
elif self._nummod.endswith("_value"):
speechSequence = self.coercionNumberLangChange(speechSequence, self._nummod[:-6], 'value')
else:
speechSequence = self.coercionNumberLangChange(speechSequence, self._numlan, self._nummod)
return speechSequence

def patchedSpaceSpeechSequence(self, speechSequence):
Expand Down
33 changes: 25 additions & 8 deletions addon/synthDrivers/WorldVoiceXVED2/_voiceManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
]

VOICE_PARAMETERS2 = [
("rate", "rate", int),
("pitch", "pitch", int),
("volume", "volume", int),
("rate", int),
("pitch", int),
("volume", int),
]

# PARAMETERS_TO_UPDATE = [_vocalizer.VE_PARAM_VOLUME, _vocalizer.VE_PARAM_SPEECHRATE, _vocalizer.VE_PARAM_PITCH]
Expand Down Expand Up @@ -66,10 +66,24 @@ def __init__(self, token, name):
except AttributeError:
self.language = None

self.pitch = 50 # 33
self.rate = 14
self.pitch = 50
self.volume = 50

self.commitRate = 14
self.commitPitch = 50
self.commitVolume = 50

def commit(self):
self.commitRate = self.rate
self.commitPitch = self.pitch
self.commitVolume = self.volume

def rollback(self):
self.rate = self.commitRate
self.pitch = self.commitPitch
self.volume = self.commitVolume

@property
def rate(self):
self._rate = _vocalizer.getParameter(self.token, _vocalizer.VE_PARAM_SPEECHRATE, type_=int)
Expand Down Expand Up @@ -282,21 +296,24 @@ def onVoiceLoad(self, voiceName, instance):
""" Restores variant and other settings if available, when a voice is loaded."""

if voiceName in _config.vocalizerConfig['voices']:
for p, name, t in VOICE_PARAMETERS2:
value = _config.vocalizerConfig['voices'][voiceName].get(name, None)
for p, t in VOICE_PARAMETERS2:
value = _config.vocalizerConfig['voices'][voiceName].get(p, None)
if value is None:
continue
# p = "commit" + p.capitalize()
setattr(instance, p, t(value))
instance.commit()

def onVoiceUnload(self, voiceName, instance):
""" Saves variant to be restored for each voice."""

if voiceName not in _config.vocalizerConfig['voices']:
_config.vocalizerConfig['voices'][voiceName] = {}

for p, name, t in VOICE_PARAMETERS2:
for p, t in VOICE_PARAMETERS2:
# p = "commit" + p.capitalize()
value = t(getattr(instance, p))
_config.vocalizerConfig['voices'][voiceName][name] = value
_config.vocalizerConfig['voices'][voiceName][p] = value

def _localeGroupKey(self, localeName):
if '_' in localeName:
Expand Down
2 changes: 1 addition & 1 deletion buildVars.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# Translators: Long description to be shown for this add-on on add-on information from add-ons manager
"addon_description" : _("WorldVoice"),
# version
"addon_version" : "1.4",
"addon_version" : "1.5",
# Author(s)
"addon_author" : "Tseng Woody <[email protected]>",
# URL for the add-on documentation support
Expand Down
8 changes: 8 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,11 @@ WorldVoice 是依據 VE driver 為基礎開發而成的 addon。運行於 NVDA 2
* 修正 unicode rule 強制模式下後方文字偵測錯誤
* 修正初始值類型錯誤導致語音設定對話框無法顯示
* 初始語音改優先使用預設語言之語音

### v1.5

* 數字功能分為 2 個設定選項「數字語言」與「數字模式」,將選項分為 2 維度以利選擇
* 忽略數字間逗點選項使數值報讀更正確
* 自動偵測語言功能當信任語音語言勾選時才使用不同解釋檔
* 語音設定中的值調整按確認才生效按取消會回到設定前的值
* 修正語音設定中當語音為預設語音時調整設定值後重啟 NVDA 後回到設定前的值

0 comments on commit 5c9c329

Please sign in to comment.