From 45127a1bd3200fd6faf2cce88842b6d633ddc455 Mon Sep 17 00:00:00 2001 From: Jesse Date: Tue, 26 Oct 2021 17:50:26 -0400 Subject: [PATCH] keep the SpeechSynthesis engine active for Chromebooks, see https://github.com/phetsims/gravity-force-lab-basics/issues/303 --- js/accessibility/voicing/voicingManager.js | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/js/accessibility/voicing/voicingManager.js b/js/accessibility/voicing/voicingManager.js index e19bdf47b..9f1e7deb0 100644 --- a/js/accessibility/voicing/voicingManager.js +++ b/js/accessibility/voicing/voicingManager.js @@ -27,6 +27,11 @@ import KeyboardUtils from '../KeyboardUtils.js'; const DEFAULT_PRIORITY = 1; +// In seconds, how frequently we will use SpeechSynthesis to keep the feature active. After long intervals without +// using SpeechSynthesis Chromebooks will take a long time to produce the next speech. Presumably it is disabling +// the feature as an optimization. But this workaround gets around it and keeps speech fast. +const ENGINE_WAKE_INTERVAL = 10; + const UTTERANCE_OPTION_DEFAULTS = { // {boolean} - If true and this Utterance is currently being spoken by the speech synth, announcing it @@ -73,6 +78,10 @@ class VoicingManager extends Announcer { // first request for speech. this.hasSpoken = false; + // @private {number} - In seconds, how long to go before "waking the SpeechSynthesis" engine to keep speech + // fast on Chromebooks, see documentation around ENGINE_WAKE_INTERVAL. + this.timeSinceWakingEngine = 0; + // @public {Emitter} - emits events when the speaker starts/stops speaking, with the Utterance that is // either starting or stopping this.startSpeakingEmitter = new Emitter( { parameters: [ { valueType: 'string' }, { valueType: Utterance } ] } ); @@ -248,6 +257,9 @@ class VoicingManager extends Announcer { this.speakingProperty.set( true ); synth.speak( voicingQueueElement.speechSynthUtterance ); + // We have just used speech synthesis, wait until ENGINE_WAKE_INTERVAL to apply the workaround again + this.timeSinceWakingEngine = 0; + this.assertSpeakingPropertyInSync(); } @@ -298,6 +310,17 @@ class VoicingManager extends Announcer { if ( !this.getSynth().speaking && this.voicingQueue.length === 0 && this.safariWorkaroundUtterances.length > 0 ) { this.safariWorkaroundUtterances = []; } + + // A workaround to keep SpeechSynthesis responsive on Chromebooks. If there is a long enough interval between + // speech requests, the next time SpeechSynthesis is used it is very slow on Chromebook. We think the browser + // turns "off" the synthesis engine for performance. If it has been long enough since using speech synthesis and + // there is nothing to speak in the queue, requesting speech with empty content keeps the engine active. + // See https://github.com/phetsims/gravity-force-lab-basics/issues/303. + this.timeSinceWakingEngine += dt; + if ( !this.speakingProperty.value && this.voicingQueue.length === 0 && this.timeSinceWakingEngine > ENGINE_WAKE_INTERVAL ) { + this.timeSinceWakingEngine = 0; + this.getSynth().speak( new SpeechSynthesisUtterance( '' ) ); + } } }