2

Good day.

I'm having trouble with inconsistencies of my speech synthesis speaking long texts.

I'm trying to make text-to-speech in English and Mandarin. When I specify utterance.lang = 'en-US'; I found out that my article in English is read out until finished. However, when I'm using utterance.lang = 'zh-CN'; my text in English and Mandarin can only read out until 30 words only. I don't know if there's a problem with coding or anything.

The article:

E. Cyclocarpum

Enterolobium cyclocarpum, commonly known as guanacaste, caro caro, or elephant-ear tree, is a species of flowering tree in the pea family. Fabaceae, that is native to tropical regions of the Americas, from the central Mexico south to northern Brazil (Roraima) and Venezuela. It is known for its large proportions, its expansive, often spherical crown, and its curiously shaped seedpods. The abundance of this tree, especially in Guanacaste Province, Costa Rica, where it is prized for the shady relief it provides from the intense sun, coupled with its immensity, have made it a widely recognized species. It is the national tree of Costa Rica.

onload = function() {
    if ('speechSynthesis' in window) with(speechSynthesis) {

        var playEle = document.querySelector('#play');
        var pauseEle = document.querySelector('#pause');
        var stopEle = document.querySelector('#stop');
        var flag = false;

        playEle.addEventListener('click', onClickPlay);
        pauseEle.addEventListener('click', onClickPause);
        stopEle.addEventListener('click', onClickStop);

        function onClickPlay() {
            if(!flag){
                flag = true;
                utterance = new SpeechSynthesisUtterance(document.querySelector('article').textContent);
                utterance.lang = 'zh-CN';
                utterance.onend = function(){
                    flag = false; playEle.className = pauseEle.className = ''; stopEle.className = 'stopped';
                };
                playEle.className = 'played';
                stopEle.className = '';
                speak(utterance);
            }
             if (paused) { /* unpause/resume narration */
                playEle.className = 'played';
                pauseEle.className = '';
                resume();
            } 
        }

        function onClickPause() {
            if(speaking && !paused){ /* pause narration */
                pauseEle.className = 'paused';
                playEle.className = '';
                pause();
            }
        }

        function onClickStop() {
            if(speaking){ /* stop narration */
                /* for safari */
                stopEle.className = 'stopped';
                playEle.className = pauseEle.className = '';
                flag = false;
                cancel();

            }
        }
    }

    else { /* speech synthesis not supported */
        msg = document.createElement('h5');
        msg.textContent = "Detected no support for Speech Synthesis";
        msg.style.textAlign = 'center';
        msg.style.backgroundColor = 'red';
        msg.style.color = 'white';
        msg.style.marginTop = msg.style.marginBottom = 0;
        document.body.insertBefore(msg, document.querySelector('div'));
    }
}
enhzflep
  • 12,927
  • 2
  • 32
  • 51
Serra Yara
  • 33
  • 6
  • Could you share the article you were testing on to produce this issue? – lovelikelando Aug 27 '19 at 03:42
  • Sure. My article is in php. But this is the article "Enterolobium cyclocarpum, commonly known as guanacaste, caro caro, or elephant-ear tree, is a species of flowering tree in the pea family, Fabaceae, that is native to tropical regions of the Americas, from central Mexico south..." my article is a bit too long. Cannot paste everything here. I dont know why it can only read out until 30 characters only. – Serra Yara Aug 27 '19 at 03:43
  • @SerraYara - Your question says 30 words and this comment, 30 characters. Am I correct in assuming it's 30 words? – enhzflep Aug 27 '19 at 04:12
  • Pardon me. It's actually 30 words. – Serra Yara Aug 27 '19 at 04:52
  • What browser/OS are you using? Presumably Safari? – Frazer Aug 27 '19 at 08:36
  • @Frazer im using Window for OS and Google Chrome for browser – Serra Yara Aug 27 '19 at 08:43

2 Answers2

10

It's a known bug. The workaround is to issue a resume every 14 seconds.

For your code this means to add the following after 'speak(utterance)':

let r = setInterval(() => {
  console.log(speechSynthesis.speaking);
  if (!speechSynthesis.speaking) {
    clearInterval(r);
  } else {
    speechSynthesis.resume();
  }
}, 14000);
Frazer
  • 1,049
  • 1
  • 10
  • 16
  • Thank you! It works. I have another little question. As I set my language to Chinese "zn-CH" it read English very well but when there's like year. Why it still read in Chinese? – Serra Yara Aug 28 '19 at 03:19
  • If it works please mark it as the accepted answer and upvote the question linked as 'workaround'. – Frazer Aug 28 '19 at 09:12
  • A voice will try to read in its own language, so when it encounters a number nothing prevents it from doing so. You could fiddle it by changing the number to its word equivalent e.g. from 1 to one, or 'twenty nineteen' for this year. – Frazer Aug 28 '19 at 09:12
  • 2
    UPDATE: This no longer works. You have to first issue the "pause" command right before the "resume". – Magmatic May 15 '23 at 18:22
5

I've recently stumbled upon the issue where speech is cut off after a duration of 14(?) seconds as well. This causes for the synthesis to get stuck in 'speaking' mode, never marking it as done and thus never actually being able to check when it finishes talking. This also makes for all speech-synth options to be blocked until you restart your browser.

This bug only seems to happen when you use the promise options to fetch the voices and languages. If you don't need to option to select and just pre-set them, it seems to be working as should (at least for me).

The solution of Frazer didn't work for me until I also added a pause right before the resume to make it stop for a very short moment and then continue. This cancels the "max 14-second" bug.

With this small tweak added, the code from Frazer would look like:

let r = setInterval(() => {
  console.log(speechSynthesis.speaking);
  if (!speechSynthesis.speaking) {
    clearInterval(r);
  } else {
    speechSynthesis.pause();
    speechSynthesis.resume();
  }
}, 14000);
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
s00zn
  • 51
  • 1
  • 2