codingsplash
codingsplash

Reputation: 5045

How to wait for SpeechSynthesis to end before starting SpeechRecognition and vice versa?

I am trying to use SpeechSynthesis and SpeechRecognition to create a mini version of a chatbot. Basically what I want is to start text to speech. Once that is done, I want to listen to what the user says ( speech to text ) and then speak the users text back. This is the code I have:

speak("Say something");
var spokenWord=hear();
speak(spokenWord);

function speak(message) {
    var synth = window.speechSynthesis;
    var utterThis = new SpeechSynthesisUtterance(message);
    synth.speak(utterThis);
    utterThis.onend = function (event) {
        console.log('Utterance has finished being spoken after ' + event.elapsedTime + ' milliseconds.');
    }
}

function hear() {
    var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    var recognition = new SpeechRecognition();
    recognition.start();

    recognition.onresult = function (event) {
        var current = event.resultIndex;
        var transcript = event.results[current][0].transcript;
        console.log(transcript);
        recognition.stop();
        return transcript;
    }
}

Since these methods are asynchronous, it is not working the way I expect it to work. The second speak runs before hear completes. Any suggestions how to fix this?

Upvotes: 3

Views: 2339

Answers (3)

Wim den Herder
Wim den Herder

Reputation: 1305

The trick is wrapping it up in promises and using await and async.

main();

async function main() {
  await speak("Say something");
  var spokenWord = await hear();
  await speak(spokenWord);
}

async function speak(message) {
  return new Promise((resolve, reject) => {
    var synth = window.speechSynthesis;
    var utterThis = new SpeechSynthesisUtterance(message);
    synth.speak(utterThis);
    utterThis.onend = resolve;
  });
}

async function hear() {
  return new Promise((resolve, reject) => {
    var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    var recognition = new SpeechRecognition();
    recognition.start();

    recognition.onresult = function (event) {
        var current = event.resultIndex;
        var transcript = event.results[current][0].transcript;
        console.log(transcript);
        recognition.stop();
        resolve(transcript);
    }
  });
}

Upvotes: 2

ubershmekel
ubershmekel

Reputation: 12808

In my case, window.speechSynthesis.speaking stayed true long after the speech was done. I noticed this is only a problem with voices that have localService: true. So if you can avoid those - your onend will be called.

Upvotes: 0

Justin D. Harris
Justin D. Harris

Reputation: 2285

async function waitUntil(check: () => boolean, intervalPeriodMs = 300): Promise<void> {
    if (check()) {
        return
    }
    return new Promise((resolve, reject) => {
        const wait = setInterval(() => {
            try {
                if (check()) {
                    clearInterval(wait)
                    resolve()
                }
            } catch (err) {
                clearInterval(wait)
                reject(err)
            }
        }, intervalPeriodMs)
    })
}

await waitUntil(() => !window.speechSynthesis.speaking, 300)

Upvotes: 0

Related Questions