Exemple #1
0
def _processMpSpeech(text, language):
    # MathPlayer's default rate is 180 wpm.
    # Assume that 0% is 80 wpm and 100% is 450 wpm and scale accordingly.
    synth = getSynth()
    wpm = synth._percentToParam(synth.rate, 80, 450)
    breakMulti = 180.0 / wpm
    out = []
    if language:
        out.append(LangChangeCommand(language))
    resetProsody = set()
    for m in RE_MP_SPEECH.finditer(text):
        if m.lastgroup == "break":
            out.append(BreakCommand(time=int(m.group("break")) * breakMulti))
        elif m.lastgroup == "char":
            out.extend((CharacterModeCommand(True), m.group("char"),
                        CharacterModeCommand(False)))
        elif m.lastgroup == "comma":
            out.append(BreakCommand(time=100))
        elif m.lastgroup in PROSODY_COMMANDS:
            command = PROSODY_COMMANDS[m.lastgroup]
            out.append(command(multiplier=int(m.group(m.lastgroup)) / 100.0))
            resetProsody.add(command)
        elif m.lastgroup == "prosodyReset":
            for command in resetProsody:
                out.append(command(multiplier=1))
            resetProsody.clear()
        elif m.lastgroup == "phonemeText":
            out.append(
                PhonemeCommand(m.group("ipa"), text=m.group("phonemeText")))
        elif m.lastgroup == "content":
            out.append(m.group(0))
    if language:
        out.append(LangChangeCommand(None))
    return out
def messageWithLangDetection(msg: Dict[str, str]) -> None:
    """Pronounce text in a given language if enabled the setting for auto-switching languages of the synthesizer.
	After the speech, switche to the previous synthesizer, if the corresponding option is enabled.
	@param msg: language code and text to be spoken in the specified language
	@type msg: Dict[str, str] -> {'lang': str, 'text': str}
	"""
    switchSynth = config.conf[addonName][services[
        config.conf[addonName]['active']].name]['switchsynth']
    profile = next(
        filter(lambda x: x.lang == msg['lang'], (p for s, p in profiles)),
        None)
    if switchSynth and profile:
        profiles.rememberCurrent()
        profile.set()
    speechSequence = []
    if config.conf['speech']['autoLanguageSwitching']:
        speechSequence.append(LangChangeCommand(msg['lang']))
    if switchSynth and profile:
        speechSequence.append(
            CallbackCommand(callback=Thread(
                target=restoreSynthIfSpeechBeenCanceled).start))
    speechSequence.append(msg['text'])
    if switchSynth and profile:
        speechSequence.append(CallbackCommand(callback=speech.cancelSpeech))
    speech.speak(speechSequence)
    braille.handler.message(msg['text'])
Exemple #3
0
    def test_convertComplex(self):
        """Test converting a complex speech sequence to SSML.
		XML generation is already tested by TestXmlBalancer.
		However, SSML is what callers expect at the end of the day,
		so test converting a complex speech sequence to SSML.
		Depends on behavior tested by TestXmlBalancer.
		"""
        converter = speechXml.SsmlConverter("en_US")
        xml = converter.convertToXml([
            "t1",
            PitchCommand(multiplier=2),
            VolumeCommand(multiplier=2), "t2",
            PitchCommand(),
            LangChangeCommand("de_DE"),
            CharacterModeCommand(True),
            IndexCommand(1), "c",
            CharacterModeCommand(False),
            PhonemeCommand("phIpa", text="phText")
        ])
        self.assertEqual(
            xml,
            '<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">'
            't1'
            '<prosody pitch="200%" volume="200%">t2</prosody>'
            '<prosody volume="200%"><voice xml:lang="de-DE">'
            '<mark name="1"/><say-as interpret-as="characters">c</say-as>'
            '<phoneme alphabet="ipa" ph="phIpa">phText</phoneme>'
            '</voice></prosody></speak>')
Exemple #4
0
 def test_languageDetection(self):
     config.conf['speech']['autoLanguageSwitching'] = True
     expected = repr([
         LangChangeCommand('fr_FR'),
         'a',
         EndUtteranceCommand(),
     ])
     output = _getSpellingSpeechWithoutCharMode(
         text='a',
         locale='fr_FR',
         useCharacterDescriptions=False,
         sayCapForCapitals=False,
         capPitchChange=0,
         beepForCapitals=False,
     )
     self.assertEqual(repr(list(output)), expected)
Exemple #5
0
    def _normalizeLangCommand(self,
                              command: LangChangeCommand) -> LangChangeCommand:
        """
		Checks if a LangChangeCommand language is compatible with eSpeak.
		If not, find a default mapping occurs in L{_defaultLangToLocale}.
		Otherwise, finds a language of a different dialect exists (e.g. ru-ru to ru).
		Returns an eSpeak compatible LangChangeCommand.
		"""
        lowerCaseAvailableLangs = set(lang.lower()
                                      for lang in self.availableLanguages)
        # Use default language if no command.lang is supplied
        langWithLocale = command.lang if command.lang else self._language
        langWithLocale = langWithLocale.lower().replace('_', '-')

        langWithoutLocale: Optional[str] = stripLocaleFromLangCode(
            langWithLocale)

        # Check for any language where the language code matches, regardless of dialect: e.g. ru-ru to ru
        matchingLangs = filter(
            lambda lang: stripLocaleFromLangCode(lang) == langWithoutLocale,
            lowerCaseAvailableLangs)
        anyLocaleMatchingLang = next(matchingLangs, None)

        # Check from a list of known default mapping locales: e.g. en to en-gb
        # Created due to eSpeak issue: https://github.com/espeak-ng/espeak-ng/issues/1200
        knownDefaultLang = self._defaultLangToLocale.get(
            langWithoutLocale, None)
        if knownDefaultLang is not None and knownDefaultLang not in self.availableLanguages:
            # This means eSpeak has changed and we need to update the mapping
            log.error(
                f"Default mapping unknown to eSpeak {knownDefaultLang} not in {self.availableLanguages}"
            )
            knownDefaultLang = None

        if langWithLocale in lowerCaseAvailableLangs:
            eSpeakLang = langWithLocale
        elif knownDefaultLang is not None:
            eSpeakLang = knownDefaultLang
        elif langWithoutLocale in lowerCaseAvailableLangs:
            eSpeakLang = langWithoutLocale
        elif anyLocaleMatchingLang is not None:
            eSpeakLang = anyLocaleMatchingLang
        else:
            log.debugWarning(
                f"Unable to find an eSpeak language for '{langWithLocale}'")
            eSpeakLang = None
        return LangChangeCommand(eSpeakLang)
    def test_stopsSpeakingCase(self):
        callbackCommand = CallbackCommand(name="dummy", callback=None)
        lang_en = LangChangeCommand('en')
        lang_default = LangChangeCommand(None)

        def createInputSequences():
            """Speech sequences that are input to 'speechWithoutPauses' when triggering the 'read-all' command
			on the wxPython wiki page.
			"""
            return [[
                callbackCommand, lang_en,
                'The purpose of the wxPyWiki is to provide documentation, examples, how-tos, etc. for helping people ',
                lang_default
            ],
                    [
                        callbackCommand, lang_en, 'learn, understand and use ',
                        lang_default
                    ],
                    [
                        callbackCommand, 'visited', 'link', '', lang_en,
                        'wxPython', lang_default, lang_en,
                        '. Anything that falls within those guidelines is fair game. ',
                        lang_default
                    ],
                    [
                        EndUtteranceCommand(), callbackCommand, lang_en,
                        'Note: To get to the main wxPython site click ',
                        lang_default
                    ]]

        expectedSpeech = repr([
            [
                callbackCommand,
                lang_en,
                'The purpose of the wxPyWiki is to provide documentation, examples, how-tos, etc. ',
            ],
            'spoke:True',
            'spoke:False',
            [
                lang_en, 'for helping people ', lang_default, callbackCommand,
                lang_en, 'learn, understand and use ', lang_default,
                callbackCommand, 'visited', 'link', '', lang_en, 'wxPython',
                lang_default, lang_en,
                '. Anything that falls within those guidelines is fair game. '
            ],
            'spoke:True',
            [
                # this sequence seems incorrect, however it persists the "old" behavior:
                # - it is missing a callback command
                # - it has no speech, just a meaningless pair of lang change commands
                lang_en,
                lang_default
            ],
            'spoke:False'
        ])

        oldSpeech = resetSpeakDest()
        for seq in createInputSequences():
            spoke = old_speakWithoutPauses(seq)
            oldSpeech.append(f"spoke:{spoke}")

        self.maxDiff = 5000  # text comparison is quite long, and it is handy to be able to see it in the output.
        self.assertMultiLineEqual(repr(oldSpeech), expectedSpeech,
                                  "generated old speech vs expected")

        newSpeech = resetSpeakDest()
        _speakWithoutPauses = SpeechWithoutPauses(speak)
        for inSeq in createInputSequences():
            spoke = _speakWithoutPauses.speakWithoutPauses(inSeq)
            newSpeech.append(f"spoke:{spoke}")

        self.assertMultiLineEqual(repr(newSpeech), expectedSpeech,
                                  "generated new speech vs expected")
Exemple #7
0
 def test_normalizeLangCommand(self):
     """Test cases for determining a supported eSpeak language from a LangChangeCommand."""
     self.assertEqual(
         LangChangeCommand("en-gb"),
         SynthDriver._normalizeLangCommand(FakeESpeakSynthDriver,
                                           LangChangeCommand(None)),
         msg="Default language used if language code not provided")
     self.assertEqual(LangChangeCommand("fr-fr"),
                      SynthDriver._normalizeLangCommand(
                          FakeESpeakSynthDriver,
                          LangChangeCommand("fr_FR")),
                      msg="Language with locale used when available")
     self.assertEqual(LangChangeCommand("en-gb"),
                      SynthDriver._normalizeLangCommand(
                          FakeESpeakSynthDriver,
                          LangChangeCommand("default")),
                      msg="Default eSpeak language mappings used")
     self.assertEqual(LangChangeCommand("fr"),
                      SynthDriver._normalizeLangCommand(
                          FakeESpeakSynthDriver,
                          LangChangeCommand("fr_FAKE")),
                      msg="Language without locale used when available")
     self.assertEqual(LangChangeCommand("ta-ta"),
                      SynthDriver._normalizeLangCommand(
                          FakeESpeakSynthDriver,
                          LangChangeCommand("ta-gb")),
                      msg="Language with any locale used when available")
     with self.assertLogs(logHandler.log,
                          level=logging.DEBUG) as logContext:
         self.assertEqual(LangChangeCommand(None),
                          SynthDriver._normalizeLangCommand(
                              FakeESpeakSynthDriver,
                              LangChangeCommand("fake")),
                          msg="No matching available language returns None")
     self.assertIn("Unable to find an eSpeak language for 'fake'",
                   logContext.output[0])