Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

我是一个兵。来自老百姓。 打败了日本狗强盗。 消灭了蒋匪军。
like image 909
Steven Matthews Avatar asked Aug 02 '18 22:08

Steven Matthews


People also ask

How good is Web Speech API?

The Web Speech API is powerful and somewhat underused. However, there are a few annoying bugs and the SpeechRecognition interface is poorly supported. speechSynthesis works surprisingly well once you iron out all of its quirks and issues.

What is SpeechSynthesisUtterance in JS?

The SpeechSynthesisUtterance interface of the Web Speech API represents a speech request. It contains the content the speech service should read and information about how to read it (e.g. language, pitch and volume.)

What is a text to speech API?

The Text-to-Speech API enables developers to generate human-like speech. The API converts text into audio formats such as WAV, MP3, or Ogg Opus. It also supports Speech Synthesis Markup Language (SSML) inputs to specify pauses, numbers, date and time formatting, and other pronunciation instructions.


2 Answers

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>
like image 175
Kishor Pawar Avatar answered Sep 30 '22 06:09

Kishor Pawar


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)

like image 29
Jeremy J Starcher Avatar answered Sep 30 '22 07:09

Jeremy J Starcher