Steven Matthews
Steven Matthews

Reputation: 11285

Chinese text plays once with Web Speech API, but not a second time

So I am using a modified script to try to play some text from the Web Speech API.

The code was originally here:

Chrome Speech Synthesis with longer texts

Here's my modified variant:

function googleSpeech(text, rate) {
    if (!reading) {
        speechSynthesis.cancel();
        if (timer) {
            clearInterval(timer);
        }
        let msg = new SpeechSynthesisUtterance();
        let voices = window.speechSynthesis.getVoices();
        msg.voice = voices[63];
        msg.voiceURI = 'native';
        msg.volume = 1; // 0 to 1
        msg.rate = rate; // 0.1 to 10
        msg.pitch = 1; //0 to 2
        msg.text = text;
        msg.lang = 'zh-CN';

        msg.onerror = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        msg.onpause = function (e) {
        };

        msg.onboundary = function (event) {
        };

        msg.onend = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        speechSynthesis.onerror = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        console.log(msg);
        speechSynthesis.speak(msg);

        timer = setInterval(function () {
            if (speechSynthesis.paused) {
                speechSynthesis.resume();
            }

        }, 100);

        reading = true;
    }

}

I am able to get this to play ONCE. Whenever I try to get it to play again, it doesn't work - this is despite running speechSynthesis.cancel();. When I reload the page, everything works fine again - for one playback.

Is there any consistent way to play the text again? It seems like this might be related to the many bugs in the Web Speech API. This is on Chrome 68.

Here's a sample of the text I am playing:

我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。

Upvotes: 5

Views: 943

Answers (3)

Steven Matthews
Steven Matthews

Reputation: 11285

The answer actually turned out to be related to the value of the rate property.

While the official documentation for the Web Speech API says that it supports a value of .1 to 10 for rate, it appears that Chrome (at least Chrome 68, the version I am running) does not fully support a rate outside of the range of .5 to 2.

Anything outside this range causes sound dispersion and breaks after one use of the API and the sound will not work anymore until page refresh.

Upvotes: 1

Kishor Pawar
Kishor Pawar

Reputation: 3526

Your code worked as it is, except I had to define reading = false and timer = false before the function.

My observation is when you pass rate value not between 0.1 to 1o, your function is called only once. You may have to check your rate values.

Additionally, if rate values not between specified, your onend event doesn't get a call.

My system is Mac and chrome is Version 67.0.3396.99 (Official Build) (64-bit)

<script type="text/javascript">
    

reading = false;
timer = false;
function googleSpeech(text, rate) {
    if (!reading) {
        speechSynthesis.cancel();
        if (timer) {
            clearInterval(timer);
        }
        let msg = new SpeechSynthesisUtterance();
        let voices = window.speechSynthesis.getVoices();
        msg.voice = voices[63];
        msg.voiceURI = 'native';
        msg.volume = 1; // 0 to 1
        msg.rate = rate; // 0.1 to 10
        msg.pitch = 1; //0 to 2
        msg.text = text;
        msg.lang = 'zh-CN';

        msg.onerror = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        msg.onpause = function (e) {
        };

        msg.onboundary = function (event) {
        };

        msg.onend = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        speechSynthesis.onerror = function (e) {
            speechSynthesis.cancel();
            reading = false;
            clearInterval(timer);
        };

        console.log(msg);
        speechSynthesis.speak(msg);

        timer = setInterval(function () {
            if (speechSynthesis.paused) {
                speechSynthesis.resume();
            }

        }, 100);

        reading = true;
    }

}
</script>

<button  onclick="googleSpeech('我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。',1)"> Play </button>

Upvotes: 4

Jeremy J Starcher
Jeremy J Starcher

Reputation: 23863

let reading = false;
let timer;
// let VV="ja-JP";
let VV = 'en-US';


function handleSaying(msg, text, rate) {
  let voices = window.speechSynthesis.getVoices();


  // console.log(voices);
  msg.voice = voices.filter(v => v.lang === VV)[0];

  // console.log("voice: ", msg.voice);
  msg.voiceURI = 'native';
  msg.volume = 1; // 0 to 1
  msg.rate = rate; // 0.1 to 10
  msg.pitch = 1; //0 to 2
  msg.text = text;
  msg.lang = VV;

  msg.onerror = function(e) {
    speechSynthesis.cancel();
    reading = false;
    clearInterval(timer);
  };

  msg.onpause = function(e) {};

  msg.onboundary = function(event) {};

  msg.onend = function(e) {
    console.log("On end...");
    // speechSynthesis.cancel();
    reading = false;
    clearInterval(timer);
  };

  speechSynthesis.onerror = function(e) {
    speechSynthesis.cancel();
    reading = false;
    clearInterval(timer);
  };

  speechSynthesis.speak(msg);

  timer = setInterval(function() {
    if (speechSynthesis.paused) {
      speechSynthesis.resume();
    }

  }, 100);

  reading = true;
}


function googleSpeech(text, rate) {

  if (!reading) {
    speechSynthesis.cancel();
    if (timer) {
      clearInterval(timer);
    }
    let msg = new SpeechSynthesisUtterance();

    // Here is the problem -- if the voices are ALREADY loaded from an earlier attempt
    // onvoiceschanged does not fire a second time

    if (window.speechSynthesis.getVoices().length > 0) {
      handleSaying(msg, text, rate);
    }


    // wait on voices to be loaded before fetching list
    window.speechSynthesis.onvoiceschanged = function() {
      handleSaying(msg, text, rate);
    }

  };

}
<button type="button" onclick="googleSpeech('The quick brown fox jumped over the lazy dogs', 5)">English</button>

I've modified your code to play the voice twice and I commented in the code what the issue was. I ran into this problem before -- its a puzzler.

I've also had the issue of the onvoicechanged firing MORE than once as it loaded voices then loaded a few more. I didn't put in the code to verify the voice that we want exists.

My system wouldn't play whatever language you were trying to use, so I changed to English text and voice, but that is easy enough to change back.

I also removed the hard-coded reference to a specific voice number, since those can change. Instead I look for the first voice matching the language ID.

You may want to reference this answer.

Getting the list of voices in speechSynthesis of Chrome (Web Speech API)

Upvotes: 1

Related Questions