Skip to content

Commit

Permalink
Refactor to use one announcer and utterance queue, add reference to o…
Browse files Browse the repository at this point in the history
…neTwoThreeStringProperty, see #55 and phetsims/number-compare#20
  • Loading branch information
chrisklus committed Mar 15, 2023
1 parent 1d57597 commit 09f8f7d
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 102 deletions.
1 change: 1 addition & 0 deletions js/NumberSuiteCommonStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type StringsType = {
'showOnesDescriptionStringProperty': LinkableProperty<string>;
'noVoiceFoundDescriptionStringProperty': LinkableProperty<string>;
'yourDeviceMaySupportDescriptionStringProperty': LinkableProperty<string>;
'oneTwoThreeStringProperty': LinkableProperty<string>;
};

const NumberSuiteCommonStrings = getStringModule( 'NUMBER_SUITE_COMMON' ) as StringsType;
Expand Down
12 changes: 1 addition & 11 deletions js/common/view/LanguageAndVoiceControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import CarouselItemNode from './CarouselItemNode.js';
import NumberSuiteCommonStrings from '../../NumberSuiteCommonStrings.js';
import PhetFont from '../../../../scenery-phet/js/PhetFont.js';
import StringUtils from '../../../../phetcommon/js/util/StringUtils.js';
import preferencesSpeechSynthesisAnnouncer from './preferencesSpeechSynthesisAnnouncer.js';
import PreferencesUtteranceQueue from './PreferencesUtteranceQueue.js';
import NumberSuiteCommonUtteranceQueue from './NumberSuiteCommonUtteranceQueue.js';
import Multilink from '../../../../axon/js/Multilink.js';

Expand Down Expand Up @@ -59,9 +57,6 @@ export default class LanguageAndVoiceControl extends HBox {
spacing: 10
}, providedOptions );

// An UtteranceQueue specifically for testing out voices when selecting a voice with this control.
const voiceSelectionUtteranceQueue = new PreferencesUtteranceQueue( preferencesSpeechSynthesisAnnouncer );

const languageCarouselLabel = new Text( NumberSuiteCommonStrings.languageStringProperty, LABEL_TEXT_OPTIONS );

// Carousel for choosing a language.
Expand Down Expand Up @@ -140,12 +135,7 @@ export default class LanguageAndVoiceControl extends HBox {
voice.name,
voice.lang, () => {
voiceProperty.value = voice;

// When changing the voiceProperty in this control, we don't want to hear the speech data being read
// out, only the test voice. So clear the general utteranceQueue while testing the voice for this button.
utteranceQueue.clear();

voiceSelectionUtteranceQueue.testVoiceBySpeaking( voice, locale );
utteranceQueue.testVoiceBySpeaking( voice, locale );
} )
};
} );
Expand Down
90 changes: 81 additions & 9 deletions js/common/view/NumberSuiteCommonUtteranceQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,23 @@
* @author Chris Klusendorf (PhET Interactive Simulations)
*/

import { Locale } from '../../../../joist/js/i18n/localeProperty.js';
import UtteranceQueue from '../../../../utterance-queue/js/UtteranceQueue.js';
import NumberSuiteCommonSpeechSynthesisAnnouncer from './NumberSuiteCommonSpeechSynthesisAnnouncer.js';
import numberSuiteCommon from '../../numberSuiteCommon.js';
import Utterance from '../../../../utterance-queue/js/Utterance.js';
import TReadOnlyProperty from '../../../../axon/js/TReadOnlyProperty.js';
import Multilink from '../../../../axon/js/Multilink.js';
import NumberSuiteCommonConstants from '../NumberSuiteCommonConstants.js';
import NumberSuiteCommonStrings from '../../NumberSuiteCommonStrings.js';

// constants
const ONE_TWO_THREE_STRING_KEY = `${NumberSuiteCommonConstants.NUMBER_SUITE_COMMON_REQUIREJS_NAMESPACE}/oneTwoThree`;

// This reference exists to ensure that the string from the string key above does not get stripped out of the sim
// during a build. Since we need to be able to get the string in ANY language requested (instead of the language that
// the sim is running in), we can't use this string Property like normal.
NumberSuiteCommonStrings.oneTwoThreeStringProperty;

export default abstract class NumberSuiteCommonUtteranceQueue extends UtteranceQueue {

Expand All @@ -28,8 +39,14 @@ export default abstract class NumberSuiteCommonUtteranceQueue extends UtteranceQ
// See doc in NumberSuiteCommonPreferences.
private readonly readAloudProperty: TReadOnlyProperty<boolean>;

// The Utterance that this UtteranceQueue uses for speaking.
private readonly speechUtterance: Utterance;
// The Utterance used for speaking speechData.
private readonly speechDataUtterance: Utterance;

// The Utterance used for speaking when the user is testing a voice in Preferences.
private readonly testVoiceUtterance: Utterance;

// Whether the test voice is currently speaking.
private isTestVoiceSpeaking: boolean;

protected constructor(
numberSuiteCommonAnnouncer: NumberSuiteCommonSpeechSynthesisAnnouncer,
Expand All @@ -42,22 +59,77 @@ export default abstract class NumberSuiteCommonUtteranceQueue extends UtteranceQ

this.numberSuiteCommonAnnouncer = numberSuiteCommonAnnouncer;
this.readAloudProperty = readAloudProperty;
this.speechUtterance = new Utterance();

this.speechDataUtterance = new Utterance( {
priority: Utterance.DEFAULT_PRIORITY
} );
this.testVoiceUtterance = new Utterance( {
priority: Utterance.HIGH_PRIORITY
} );

this.isTestVoiceSpeaking = false;
}

/**
* Cancels any ongoing speech and speaks the value of this.speechDataProperty.
* Speaks the value of this.speechDataProperty.
*/
public speakSpeechData(): void {
assert && assert( this.initialized && this.speechDataProperty, 'Cannot speak before initialization' );
const speechData = this.speechDataProperty!.value;

if ( speechData ) {
this.speechUtterance.alert = speechData;
speechData && this.speak( speechData, this.speechDataUtterance );
}

/**
* Speaks a 'test' string in the provided voice and locale. This temporarily sets the voice to read out the test
* string, and then resets the voice to what it was before starting the test read.
*/
public testVoiceBySpeaking( voiceToTest: SpeechSynthesisVoice, locale: Locale ): void {
const currentVoice = this.numberSuiteCommonAnnouncer.voiceProperty.value;
this.numberSuiteCommonAnnouncer.voiceProperty.value = voiceToTest;

// Indicate when we are speaking with the test voice so we know if we should set the voice back to the non-testing
// voice or not.
this.isTestVoiceSpeaking = true;
this.speak( this.getTestStringForLocale( locale ), this.testVoiceUtterance );
this.isTestVoiceSpeaking = false;

const resetVoiceListener = () => {
if ( !this.isTestVoiceSpeaking ) {
this.numberSuiteCommonAnnouncer.voiceProperty.value = currentVoice;
}
this.speechDataUtterance.reset();
this.numberSuiteCommonAnnouncer.announcementCompleteEmitter.removeListener( resetVoiceListener );
};

// When the test speech is complete, set the voice back to what it was before testing, unless we have already
// started speaking for a different test.
this.numberSuiteCommonAnnouncer.announcementCompleteEmitter.addListener( resetVoiceListener );
}

/**
* Speaks the provided string.
*/
private speak( string: string, utterance: Utterance ): void {
utterance.alert = string;
this.addToBack( utterance );
}

/**
* Returns a test string in the provided locale. If the string isn't found in the desired locale, the english version
* of the string is returned instead.
*/
private getTestStringForLocale( locale: Locale ): string {
const strings = phet.chipper.strings;
const translatedStrings = strings[ locale ];
const backupStrings = strings.en;

const testString = translatedStrings && translatedStrings[ ONE_TWO_THREE_STRING_KEY ] ?
translatedStrings[ ONE_TWO_THREE_STRING_KEY ] : backupStrings[ ONE_TWO_THREE_STRING_KEY ];

assert && assert( testString, `No test string found for locales ${locale} or en.` );

this.cancelUtterance( this.speechUtterance );
this.addToBack( this.speechUtterance );
}
return testString;
}

/**
Expand Down
64 changes: 0 additions & 64 deletions js/common/view/PreferencesUtteranceQueue.ts

This file was deleted.

18 changes: 0 additions & 18 deletions js/common/view/preferencesSpeechSynthesisAnnouncer.ts

This file was deleted.

0 comments on commit 09f8f7d

Please sign in to comment.