huu
huu

Reputation: 7472

SpeechSynthesis API onend callback not working

I'm using the Speech Synthesis API on Google Chrome v34.0.1847.131. The API is implemented in Chrome starting in v33.

The text-to-speech works for the most part, except when assigning a callback to onend. For instance, the following code:

var message = window.SpeechSynthesisUtterance("Hello world!");
message.onend = function(event) {
    console.log('Finished in ' + event.elapsedTime + ' seconds.');
};
window.speechSynthesis.speak(message);

will sometimes call onend and sometimes not call it. The timing appears to be completely off. When it does get called, the printed elapsedTime is always some epoch time like 1399237888.

Upvotes: 34

Views: 19775

Answers (9)

Guillaume
Guillaume

Reputation: 21736

Also, on Chrome (but not Safari), the callback is never called if you try to speak an empty string.

Upvotes: 0

ubershmekel
ubershmekel

Reputation: 12818

The only thing that worked for me was to avoid voices with localService being true. Those voices never fired onend while the other voices (localService being false) did fire onend.

Upvotes: 0

Muhammad Umer
Muhammad Umer

Reputation: 18097

While this is how I found it to make it work, I am not sure if this is the right behavior....

First don't call the speak function it right away, use callback.

2nd, to get time use timeStamp instead of elapsedTime. You could have just used performance.now() as well.

var btn = document.getElementById('btn');
speechSynthesis.cancel()
var u = new SpeechSynthesisUtterance();
u.text = "This text was changed from the original demo.";

var t;
u.onstart = function (event) {
    t = event.timeStamp;
    console.log(t);
};

u.onend = function (event) {
    t = event.timeStamp - t;
    console.log(event.timeStamp);
    console.log((t / 1000) + " seconds");
};

btn.onclick = function () {speechSynthesis.speak(u);};

Demo: http://jsfiddle.net/QYw6b/2/

you get time passed and both events fired for sure.

Upvotes: 12

yun
yun

Reputation: 125

print the utterance before speak seems working... If I remove the console, this issue will happen, don't know why

console.log("utterance", utterThis);
synth.speak(utterThis);

Upvotes: 1

techpeace
techpeace

Reputation: 2644

According to this comment on the bug mentioned in the answer from Kevin Hakanson, it might be a problem with garbage collection. Storing the utterance in a variable before calling speak seems to do the trick:

window.utterances = [];
var utterance = new SpeechSynthesisUtterance( 'hello' );
utterances.push( utterance );
speechSynthesis.speak( utterance );

Upvotes: 25

Kevin Hakanson
Kevin Hakanson

Reputation: 42210

This looks similar to a Chromium bug reported on Jul 12, 2015.

Issue 509488: Web Speech API: 'end' event of SpeechSynthesisUtterance object is not dispatched sometimes

Upvotes: 3

Bruce Allen
Bruce Allen

Reputation: 91

I also found the only way to make this work reliably is to use .cance. I use a 17 second timeout. All of my recordings are under 20 seconds so this works for me.

utterance.onstart = function (event) {
setTimeout(function(){window.speechSynthesis.cancel();},17000);
};

Before I would run into this problem once every 8-10 messages it attempted. Once I added .cancel it seems to always work. I also call set timeout when invoking.

setTimeout(function(){window.speechSynthesis.speak(utterance);},100);

Upvotes: 0

user109839
user109839

Reputation:

I found both solutions suggested here not working in an app I just wrote. The only solution I could come up with is a (sort of) busy waiting:

function speak( text, onend ) {
  window.speechSynthesis.cancel();
  var ssu = new SpeechSynthesisUtterance( text );
  window.speechSynthesis.speak( ssu );
  function _wait() {
    if ( ! window.speechSynthesis.speaking ) {
      onend();
      return;
    }
    window.setTimeout( _wait, 200 );
  }
  _wait();
}

you can find a complete example in this codepen

Upvotes: 5

fimbim
fimbim

Reputation: 113

You could use the EventListener for start and end like I did for Speakerbot (http://www.speakerbot.de/).

Here my face changes while words are spoken.

newUtt = new SpeechSynthesisUtterance();

newUtt.addEventListener('start', function () {
     console.log('started');
})

newUtt.addEventListener('end', function () {
     console.log('stopped');
})

Upvotes: 6

Related Questions