Speech Synthesis problem with long texts pause mid-speaking
Asked Answered
H

2

4

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'));
    }
}
Hypoxanthine answered 27/8, 2019 at 3:32 Comment(7)
Could you share the article you were testing on to produce this issue?Lavelle
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.Hypoxanthine
@SerraYara - Your question says 30 words and this comment, 30 characters. Am I correct in assuming it's 30 words?Holbert
Pardon me. It's actually 30 words.Hypoxanthine
What browser/OS are you using? Presumably Safari?Tenet
@Tenet im using Window for OS and Google Chrome for browserHypoxanthine
Still happening in Chrome in 2024... (Chrome Version 121.0.6167.184)Eduardo
T
12

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);
Tenet answered 27/8, 2019 at 9:57 Comment(5)
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?Hypoxanthine
If it works please mark it as the accepted answer and upvote the question linked as 'workaround'.Tenet
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.Tenet
UPDATE: This no longer works. You have to first issue the "pause" command right before the "resume".Deploy
Ideally, you'd want to run the setInterval inside a Web Worker so that the pause/resume gets called in cases where the window/tab is no longer active.Ousel
L
11

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);
Loveland answered 6/12, 2022 at 14:6 Comment(2)
Yes, this works, but Chrome on Android currently has a bug where you can't resume from a pause, so this fix doesn't actually fix it. :(Deploy
This worked on desktop (chrome).Cephalo

© 2022 - 2024 — McMap. All rights reserved.