diff --git a/src/cthulhu/speechdispatcherfactory.py b/src/cthulhu/speechdispatcherfactory.py index 4b233e6..6802cf1 100644 --- a/src/cthulhu/speechdispatcherfactory.py +++ b/src/cthulhu/speechdispatcherfactory.py @@ -614,15 +614,6 @@ class SpeechServer(speechserver.SpeechServer): if not text: return - # In order to re-enable this, a potentially non-trivial amount of work - # will be needed to ensure multiple utterances sent to speech.speak - # do not result in the intial utterances getting cut off before they - # can be heard by the user. Anyone needing to interrupt speech can - # do so via speech.stop -- or better yet, by using the default script - # method's presentationInterrupt. - #if interrupt: - # self._cancel() - # "We will not interrupt a key echo in progress." (Said the comment in # speech.py where these next two lines used to live. But the code here # suggests we haven't been doing anything with the lastKeyEchoTime in @@ -630,6 +621,9 @@ class SpeechServer(speechserver.SpeechServer): if self._lastKeyEchoTime: interrupt = interrupt and (time.time() - self._lastKeyEchoTime) > 0.5 + if interrupt: + self._cancel() + if len(text) == 1: tokens = ["SPEECH DISPATCHER: Speaking '", text.replace("\n", "\\n"), "' as char"] debug.printTokens(debug.LEVEL_INFO, tokens, True) diff --git a/tests/test_speechdispatcher_interrupt_regressions.py b/tests/test_speechdispatcher_interrupt_regressions.py new file mode 100644 index 0000000..674b966 --- /dev/null +++ b/tests/test_speechdispatcher_interrupt_regressions.py @@ -0,0 +1,43 @@ +import sys +import time +import unittest +from pathlib import Path +from unittest import mock + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) + +from cthulhu import speechdispatcherfactory + + +class SpeechDispatcherInterruptRegressionTests(unittest.TestCase): + def _make_server(self): + server = speechdispatcherfactory.SpeechServer.__new__(speechdispatcherfactory.SpeechServer) + server._lastKeyEchoTime = None + server._cancel = mock.Mock() + server._apply_acss = mock.Mock() + server._send_command = mock.Mock() + server._speak = mock.Mock() + server._client = mock.Mock() + server._client.char = mock.Mock() + return server + + def test_interrupting_string_speech_cancels_active_output_first(self): + server = self._make_server() + + server.speak("long utterance", interrupt=True) + + server._cancel.assert_called_once_with() + server._speak.assert_called_once_with("long utterance", None) + + def test_recent_key_echo_suppresses_backend_cancel(self): + server = self._make_server() + server._lastKeyEchoTime = time.time() + + server.speak("next", interrupt=True) + + server._cancel.assert_not_called() + server._speak.assert_called_once_with("next", None) + + +if __name__ == "__main__": + unittest.main()