diff --git a/src/cthulhu/speech_and_verbosity_manager.py b/src/cthulhu/speech_and_verbosity_manager.py index 5ff8a6d..9864731 100644 --- a/src/cthulhu/speech_and_verbosity_manager.py +++ b/src/cthulhu/speech_and_verbosity_manager.py @@ -36,6 +36,7 @@ from . import cmdnames from . import cthulhu from . import dbus_service from . import debug +from . import guilabels from . import input_event from . import keybindings from . import messages @@ -479,6 +480,20 @@ class SpeechAndVerbosityManager: if not filtered: return [] + if hasattr(server, "getOutputModule"): + try: + module = server.getOutputModule() or "" + except Exception: + module = "" + if module: + default_name = guilabels.SPEECH_DEFAULT_VOICE % module + real_voices = [ + voice for voice in filtered + if voice.get(speechserver.VoiceFamily.NAME) != default_name + ] + if real_voices: + filtered = real_voices + if not self._should_filter_voices_by_locale(server): return filtered diff --git a/src/cthulhu/speechdispatcherfactory.py b/src/cthulhu/speechdispatcherfactory.py index a40b4e3..355611c 100644 --- a/src/cthulhu/speechdispatcherfactory.py +++ b/src/cthulhu/speechdispatcherfactory.py @@ -823,6 +823,8 @@ class SpeechServer(speechserver.SpeechServer): def setOutputModule(self, module): self._client.set_output_module(module) + self._default_voice_name = guilabels.SPEECH_DEFAULT_VOICE % module + self._current_voice_properties.pop(ACSS.FAMILY, None) def stop(self): self._cancel() diff --git a/tests/test_speechdispatcher_voice_selection_regressions.py b/tests/test_speechdispatcher_voice_selection_regressions.py new file mode 100644 index 0000000..e48697c --- /dev/null +++ b/tests/test_speechdispatcher_voice_selection_regressions.py @@ -0,0 +1,103 @@ +import sys +import unittest +from pathlib import Path +from unittest import mock + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) + +from cthulhu import guilabels +from cthulhu import speech_and_verbosity_manager +from cthulhu import speechdispatcherfactory +from cthulhu import speechserver + + +class SpeechDispatcherVoiceSelectionRegressionTests(unittest.TestCase): + def test_output_module_updates_the_synthetic_default_voice_name(self): + server = speechdispatcherfactory.SpeechServer.__new__( + speechdispatcherfactory.SpeechServer + ) + server._client = mock.Mock() + server._default_voice_name = guilabels.SPEECH_DEFAULT_VOICE % "default" + server._current_voice_properties = { + speechdispatcherfactory.ACSS.FAMILY: {"name": "Previous Voice"} + } + + server.setOutputModule("doubletalk") + + server._client.set_output_module.assert_called_once_with("doubletalk") + self.assertEqual( + guilabels.SPEECH_DEFAULT_VOICE % "doubletalk", + server._default_voice_name, + ) + self.assertNotIn( + speechdispatcherfactory.ACSS.FAMILY, + server._current_voice_properties, + ) + + def test_quick_voice_list_prefers_real_doubletalk_voices(self): + default_name = guilabels.SPEECH_DEFAULT_VOICE % "doubletalk" + voice_names = [ + "Perfect Paul", + "Vader", + "Big Bob", + "Precise Pete", + "Ricochet", + "Biff", + "Skip", + "Robo Robert", + ] + voices = [ + speechserver.VoiceFamily( + { + speechserver.VoiceFamily.NAME: default_name, + speechserver.VoiceFamily.LANG: "en", + speechserver.VoiceFamily.DIALECT: "US", + speechserver.VoiceFamily.VARIANT: None, + } + ), + ] + voices.extend( + speechserver.VoiceFamily( + { + speechserver.VoiceFamily.NAME: voice_name, + speechserver.VoiceFamily.LANG: "en", + speechserver.VoiceFamily.DIALECT: "US", + speechserver.VoiceFamily.VARIANT: "none", + } + ) + for voice_name in voice_names + ) + server = mock.Mock() + server.getOutputModule.return_value = "doubletalk" + server.getVoiceFamilies.return_value = voices + manager = speech_and_verbosity_manager.SpeechAndVerbosityManager.__new__( + speech_and_verbosity_manager.SpeechAndVerbosityManager + ) + + available = manager._get_available_voices(server) + + self.assertEqual( + voice_names, + [voice[speechserver.VoiceFamily.NAME] for voice in available], + ) + + def test_quick_voice_list_keeps_default_when_no_real_voice_exists(self): + default_name = guilabels.SPEECH_DEFAULT_VOICE % "generic" + default_voice = speechserver.VoiceFamily( + { + speechserver.VoiceFamily.NAME: default_name, + speechserver.VoiceFamily.LANG: "en", + } + ) + server = mock.Mock() + server.getOutputModule.return_value = "generic" + server.getVoiceFamilies.return_value = [default_voice] + manager = speech_and_verbosity_manager.SpeechAndVerbosityManager.__new__( + speech_and_verbosity_manager.SpeechAndVerbosityManager + ) + + self.assertEqual([default_voice], manager._get_available_voices(server)) + + +if __name__ == "__main__": + unittest.main()