Ejemplo n.º 1
0
    def getPreInitSettings(cls) -> SupportedSettingType:
        """Get settings that can be configured before the provider is initialized.
		This is a class method because it does not rely on any instance state in this class.
		"""
        return [
            BooleanDriverSetting(
                "shouldDoX",  # value stored in matching property name on class
                "Should Do X",
                defaultVal=True),
            BooleanDriverSetting(
                "shouldDoY",  # value stored in matching property name on class
                "Should Do Y",
                defaultVal=False),
            NumericDriverSetting(
                "amountOfZ",  # value stored in matching property name on class
                "Amount of Z",
                defaultVal=11),
            DriverSetting(
                # options for this come from a property with name generated by
                # f"available{settingID.capitalize()}s"
                # Note:
                #   First letter of Id becomes capital, the rest lowercase.
                #   the 's' character on the end.
                # result: 'availableNameofsomethings'
                "nameOfSomething",  # value stored in matching property name on class
                "Name of something",
            )
        ]
Ejemplo n.º 2
0
 def VariantSetting(cls):
     """Factory function for creating variant setting."""
     return DriverSetting(
         "variant",
         # Translators: Label for a setting in voice settings dialog.
         _("V&ariant"),
         availableInSettingsRing=True,
         # Translators: Label for a setting in synth settings ring.
         displayName=pgettext('synth setting', 'Variant'),
     )
Ejemplo n.º 3
0
 def LanguageSetting(cls):
     """Factory function for creating a language setting."""
     return DriverSetting(
         "language",
         # Translators: Label for a setting in voice settings dialog.
         _("&Language"),
         availableInSettingsRing=True,
         # Translators: Label for a setting in synth settings ring.
         displayName=pgettext('synth setting', 'Language'),
     )
Ejemplo n.º 4
0
class SynthDriver(WorldVoiceBaseSynthDriver, SynthDriver):
    name = "WorldVoiceXVED2"
    description = "WorldVoice(VE)"
    supportedSettings = [
        SynthDriver.VoiceSetting(),
        # SynthDriver.VariantSetting(),
        SynthDriver.RateSetting(),
        SynthDriver.PitchSetting(),
        SynthDriver.VolumeSetting(),
        DriverSetting(
            "numlan",
            # Translators: Label for a setting in voice settings dialog.
            _("Number &Language"),
            availableInSettingsRing=True,
            defaultVal="default",
            # Translators: Label for a setting in synth settings ring.
            displayName=_("Number Language"),
        ),
        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"),
        ),
        NumericDriverSetting(
            "chinesespace",
            # Translators: Label for a setting in voice settings dialog.
            _("Pause time when encountering spaces between Chinese"),
            defaultVal=0,
            minStep=1,
        ),
        BooleanDriverSetting(
            "cni",
            _("Ignore comma between number"),
            defaultVal=False,
        ),
        BooleanDriverSetting(
            "uwv",
            _("Enable WorldVoice setting rules to detect text language"),
            availableInSettingsRing=True,
            defaultVal=True,
            displayName=_("Enable WorldVoice rules"),
        ),
    ]
    supportedCommands = {
        IndexCommand,
        CharacterModeCommand,
        LangChangeCommand,
        BreakCommand,
        PitchCommand,
        RateCommand,
        VolumeCommand,
    }
    supportedNotifications = {synthIndexReached, synthDoneSpeaking}

    @classmethod
    def check(cls):
        with _vocalizer.preOpenVocalizer() as check:
            return check

    def __init__(self):
        _config.load()
        # Initialize the driver
        try:
            _vocalizer.initialize(self._onIndexReached)
            log.debug("Vocalizer info: %s" % self._info())
        except _vocalizer.VeError as e:
            if e.code == _vocalizer.VAUTONVDA_ERROR_INVALID:
                log.info("Vocalizer license for NVDA is Invalid")
            elif e.code == _vocalizer.VAUTONVDA_ERROR_DEMO_EXPIRED:
                log.info("Vocalizer demo license for NVDA as expired.")
            raise
        self._voiceManager = VoiceManager()

        self._realSpeakFunc = speech.speech.speak
        self._realSpellingFunc = speech.speech.speakSpelling
        speech.speech.speak = self.patchedSpeak
        speech.speech.speakSpelling = self.patchedSpeakSpelling

        self.speechSymbols = SpeechSymbols()
        self.speechSymbols.load('unicode.dic')
        self._languageDetector = languageDetection.LanguageDetector(
            list(self._voiceManager.languages), self.speechSymbols)

        self._localeToVoices = self._voiceManager.localeToVoicesMap
        self._locales = sorted([
            l for l in self._localeToVoices if len(self._localeToVoices[l]) > 0
        ])
        self._localeNames = list(
            map(self._getLocaleReadableName, self._locales))

        self._voice = None

    def _onIndexReached(self, index):
        if index is not None:
            synthIndexReached.notify(synth=self, index=index)
        else:
            synthDoneSpeaking.notify(synth=self)

    def terminate(self):
        speech.speech.speak = self._realSpeakFunc
        speech.speech.speakSpelling = self._realSpellingFunc

        try:
            self.cancel()
            self._voiceManager.close()
            _vocalizer.terminate()
        except RuntimeError:
            log.error("Vocalizer terminate", exc_info=True)

    def speak(self, speechSequence):
        if self.uwv \
         and _config.vocalizerConfig['autoLanguageSwitching']['useUnicodeLanguageDetection'] \
         and _config.vocalizerConfig['autoLanguageSwitching']['afterSymbolDetection']:
            speechSequence = self._languageDetector.add_detected_language_commands(
                speechSequence)
            speechSequence = list(speechSequence)
        speechSequence = self.patchedSpaceSpeechSequence(speechSequence)

        currentInstance = defaultInstance = self._voiceManager.defaultVoiceInstance.token
        currentLanguage = defaultLanguage = self.language
        chunks = []
        hasText = False
        charMode = False
        for command in speechSequence:
            if isinstance(command, str):
                command = command.strip()
                if not command:
                    continue
                # If character mode is on use lower case characters
                # Because the synth does not allow to turn off the caps reporting
                if charMode or len(command) == 1:
                    command = command.lower()
                # replace the excape character since it is used for parameter changing
                chunks.append(command.replace("\x1b", ""))
                hasText = True
            elif isinstance(command, IndexCommand):
                chunks.append("\x1b\\mrk=%d\\" % command.index)
            elif isinstance(command, BreakCommand):
                maxTime = 6553 if self.variant == "bet2" else 65535
                breakTime = max(1, min(command.time, maxTime))
                self._speak(currentInstance, chunks)
                chunks = []
                hasText = False
                _vocalizer.processBreak(currentInstance, breakTime)
            elif isinstance(command, CharacterModeCommand):
                charMode = command.state
                s = "\x1b\\tn=spell\\" if command.state else "\x1b\\tn=normal\\"
                chunks.append(s)
            elif isinstance(command, LangChangeCommand) or isinstance(
                    command, speechcommand.WVLangChangeCommand):
                if command.lang == currentLanguage:
                    # Keep on the same voice.
                    continue
                if command.lang is None:
                    # No language, use default.
                    currentInstance = defaultInstance
                    currentLanguage = defaultLanguage
                    continue
                # Changed language, lets see what we have.
                currentLanguage = command.lang
                newVoiceName = self._voiceManager.getVoiceNameForLanguage(
                    currentLanguage)
                if newVoiceName is None:
                    # No voice for this language, use default.
                    newInstance = defaultInstance
                else:
                    newInstance = self._voiceManager.getVoiceInstance(
                        newVoiceName).token
                if newInstance == currentInstance:
                    # Same voice, next command.
                    continue
                if hasText:  # We changed voice, send text we already have to vocalizer.
                    self._speak(currentInstance, chunks)
                    chunks = []
                    hasText = False
                currentInstance = newInstance
            elif isinstance(command, PitchCommand):
                pitch = self._voiceManager.getVoiceParameter(
                    currentInstance, _vocalizer.VE_PARAM_PITCH, type_=int)
                pitchOffset = self._percentToParam(
                    command.offset, _vocalizer.PITCH_MIN,
                    _vocalizer.PITCH_MAX) - _vocalizer.PITCH_MIN
                chunks.append("\x1b\\pitch=%d\\" % (pitch + pitchOffset))
            elif isinstance(command, speechcommand.SplitCommand):
                self._speak(currentInstance, chunks)
                chunks = []
                hasText = False
        if chunks:
            self._speak(currentInstance, chunks)

    def _speak(self, voiceInstance, chunks):
        text = speech.CHUNK_SEPARATOR.join(chunks).replace("  \x1b", "\x1b")
        _vocalizer.processText2Speech(voiceInstance, text)

    def patchedSpeak(self, speechSequence, symbolLevel=None, priority=None):
        if self._cni:
            speechSequence = [
                comma_number_pattern.sub(lambda m: '', command) if isinstance(
                    command, str) else command for command in speechSequence
            ]
        speechSequence = self.patchedNumSpeechSequence(speechSequence)
        if self.uwv \
         and _config.vocalizerConfig['autoLanguageSwitching']['useUnicodeLanguageDetection'] \
         and not _config.vocalizerConfig['autoLanguageSwitching']['afterSymbolDetection']:
            speechSequence = self._languageDetector.add_detected_language_commands(
                speechSequence)
            speechSequence = list(speechSequence)
        self._realSpeakFunc(speechSequence, symbolLevel, priority=priority)

    def patchedSpeakSpelling(self,
                             text,
                             locale=None,
                             useCharacterDescriptions=False,
                             priority=None):
        if config.conf["speech"]["autoLanguageSwitching"] \
         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:
            self._realSpellingFunc(text,
                                   locale,
                                   useCharacterDescriptions,
                                   priority=priority)

    def cancel(self):
        _vocalizer.stop()

    def pause(self, switch):
        if switch:
            _vocalizer.pause()
        else:
            _vocalizer.resume()

    def _get_volume(self):
        return self._voiceManager.defaultVoiceInstance.volume

    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:
            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
        # continuing speaking when changing voices for, e.g., say-all
        # See NVDA ticket #3540
        _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
        # Synchronize with the synth so the parameters
        # we report are not from the previous voice.
        # _vocalizer.sync()

    def _get_variant(self):
        return self._voiceManager.defaultVoiceInstance.variant

    def _set_variant(self, name):
        self.cancel()
        self._voiceManager.defaultVoiceInstance.variant = name

    def _getAvailableVariants(self):
        dbs = self._voiceManager.defaultVoiceInstance.variants
        return OrderedDict([(d, VoiceInfo(d, d)) for d in dbs])

    def _get_availableLanguages(self):
        return self._voiceManager.languages

    def _get_language(self):
        return self._voiceManager.getVoiceLanguage()

    def _info(self):
        s = [self.description]
        return ", ".join(s)

    def _get_availableNumlans(self):
        return dict({
            "default": StringParameterInfo("default", _("default")),
        }, **{
            locale: StringParameterInfo(locale, name)
            for locale, name in zip(self._locales, self._localeNames)
        })

    def _get_numlan(self):
        return self._numlan

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

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

    def _get_nummod(self):
        return self._nummod

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

    def _get_chinesespace(self):
        return self._chinesespace

    def _set_chinesespace(self, value):
        self._chinesespace = value

    def _get_cni(self):
        return self._cni

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

    def _getLocaleReadableName(self, locale):
        description = languageHandler.getLanguageDescription(locale)
        return "%s" % (description) if description else locale