discussion
Version:
Basic implementating to help altering between ongoing SpeechRecognition and intermittant SpeechSynthesis
144 lines (143 loc) • 5.53 kB
JavaScript
/// <reference types="dom-speech-recognition" />
export class Discussion {
/**
* Constructs the class. Make sure to call initialize() as well afterwards when your page is ready to go.
* @param recognitionLanguageCode
* @param synthesisLanguageCode
* @param recognitionMaxAlternatives how many guesses for the recognition should be returned? (See https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/maxAlternatives)
*/
constructor(factory, recognitionLanguageCode = "en-GB", synthesisLanguageCode = "en-GB", recognitionMaxAlternatives = 10) {
this.factory = factory;
this.recognitionLanguageCode = recognitionLanguageCode;
this.synthesisLanguageCode = synthesisLanguageCode;
this.recognitionMaxAlternatives = recognitionMaxAlternatives;
this.isSynthesizing = false;
this.isRecognitionEnabled = true;
this.isGoodVoice = false;
}
/**
* Lists all available synthesis voices. This list will only be filled after initialize() was called
* */
getSynthesisVoices() {
return this.voices;
}
/**
* Indicates whether the current synthesis language is installed locally (and thus faster in ending)
* */
hasGoodVoice() {
return this.isGoodVoice;
}
/**
* Disable the recognition
* */
disableRecognition() {
this.isRecognitionEnabled = false;
this.toggleRecognition();
}
/**
* Re-enable the recognition
* */
enableRecognition() {
this.isRecognitionEnabled = true;
this.toggleRecognition();
}
toggleRecognition() {
if (this.isRecognitionEnabled) {
this.initializeRecognition();
this.initializeSynthesis();
}
else {
this.recognition.stop();
}
}
/**
* Setup the recognition and synthesis
* */
initialize() {
this.initializeSynthesis();
this.initializeRecognition();
}
initializeSynthesis() {
window.speechSynthesis.speak(new SpeechSynthesisUtterance("")); // say nothing to load all voices
this.voices = window.speechSynthesis.getVoices();
this.preferedVoice = this.voices.filter(x => x.localService && x.lang.startsWith(this.synthesisLanguageCode.split("-")[0]))[0];
this.isGoodVoice = !!this.preferedVoice;
}
initializeRecognition() {
if (this.recognition) {
try {
this.recognition.stop();
}
catch (_a) { }
}
this.recognition = this.factory();
this.recognition.continuous = false;
this.recognition.interimResults = true;
this.recognition.lang = this.recognitionLanguageCode;
this.recognition.maxAlternatives = this.recognitionMaxAlternatives;
this.recognition.start();
this.keepSpeechRecognitionAlive();
this.recognition.onresult = (x) => {
if (!this.onRecognitionResult) {
throw "No event handler registered for a recognition result";
}
else {
this.onRecognitionResult(x);
}
};
}
/**
* Synthesize something to the user
* @param text what to say
* @param pitch at which pitch to say it (from 0 to 2)
* @param rate how fast to say it (from 0.1 to 10)
* @param volume how loud to say it (from 0 to 1)
* @recognitionWaitPeriod While synthesizing, the recognition has to be stopped. Set this parameter to start listening again after N milliseconds. Set this parameter to 0 to wait until the synthesis is completely over (may take +- 1 second AFTER the voice is finished).
*/
synthesize(text, pitch = 1, rate = 1, volume = 1, recognitionWaitPeriod = 500) {
this.isSynthesizing = true;
this.recognition.stop();
this.utterance = new SpeechSynthesisUtterance(text); // has to be global because of https://stackoverflow.com/questions/51645956/speechsynthesisutterance-onboundary-event-not-firing-properly
this.utterance.volume = volume;
this.utterance.rate = rate;
this.utterance.pitch = pitch;
this.utterance.lang = this.synthesisLanguageCode;
this.utterance.voice = this.preferedVoice;
window.speechSynthesis.cancel();
window.speechSynthesis.speak(this.utterance);
if (recognitionWaitPeriod !== 0) {
setTimeout(() => {
this.isSynthesizing = false;
try {
this.recognition.start();
}
catch (_a) { }
}, recognitionWaitPeriod);
}
this.utterance.onend = () => {
this.isSynthesizing = false;
try {
this.recognition.start();
}
catch (_a) { }
};
this.utterance.addEventListener("boundary", () => {
this.isSynthesizing = false;
try {
this.recognition.start();
}
catch (_a) { }
});
}
keepSpeechRecognitionAlive() {
this.recognition.onend = (_) => {
if (this.isSynthesizing || !this.isRecognitionEnabled) {
return;
}
try {
this.recognition.start();
}
catch (_a) { }
};
}
}