Fixed 2 speech-dispatcher bugs. First, multiword voice names were not available. Next, when the module changed the old voice name was carried in causing some modules to fail. Ported over fixes from Fenrir. This mostly affected the quick speech keys.

This commit is contained in:
Storm Dragon
2026-07-25 04:52:17 -04:00
parent 65c0e57aa8
commit 6ec512ea33
3 changed files with 120 additions and 0 deletions
@@ -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
+2
View File
@@ -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()
@@ -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()