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:
@@ -36,6 +36,7 @@ from . import cmdnames
|
|||||||
from . import cthulhu
|
from . import cthulhu
|
||||||
from . import dbus_service
|
from . import dbus_service
|
||||||
from . import debug
|
from . import debug
|
||||||
|
from . import guilabels
|
||||||
from . import input_event
|
from . import input_event
|
||||||
from . import keybindings
|
from . import keybindings
|
||||||
from . import messages
|
from . import messages
|
||||||
@@ -479,6 +480,20 @@ class SpeechAndVerbosityManager:
|
|||||||
if not filtered:
|
if not filtered:
|
||||||
return []
|
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):
|
if not self._should_filter_voices_by_locale(server):
|
||||||
return filtered
|
return filtered
|
||||||
|
|
||||||
|
|||||||
@@ -823,6 +823,8 @@ class SpeechServer(speechserver.SpeechServer):
|
|||||||
|
|
||||||
def setOutputModule(self, module):
|
def setOutputModule(self, module):
|
||||||
self._client.set_output_module(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):
|
def stop(self):
|
||||||
self._cancel()
|
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()
|
||||||
Reference in New Issue
Block a user