SpeechSynthesisUtterance 脚本,带有播放、暂停、停止按钮以及语言和语音选择

SpeechSynthesisUtterance script with play, pause, stop buttons and selection of language and voice

我想用 SpeechSynthesisUtterance 阅读我的页面文本。

我找到了这个脚本:https://www.hongkiat.com/blog/text-to-speech/

几乎完美,但暂停按钮似乎没什么用,我希望我有设置语言和选择语音的功能。

我在这里找到了参考资料:https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance,但我对 JavaScript 不是很了解。

对于语言,据我了解,应该使用html标签中设置的lang参数。

对于语音,我完全不知道如何在代码中实现它。

这很重要,因为我有英语、西班牙语、法语和意大利语的文本,没有语音和语言设置的结果有时听起来很奇怪。

更新

这些天我摆弄了一点,我设法(或多或少)结合了两个不同的scripts/examples。

这个:https://www.hongkiat.com/blog/text-to-speech/

还有这个:https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis#Examples

出来的代码是这样的:

HTML

<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="text-to-speech.js"></script>
</head>
<body>
<div class=buttons>
    <button id=play></button> &nbsp;
    <button id=pause></button> &nbsp;
    <button id=stop></button>
</div>
    <select id="voices">

    </select>
<div id="description">
The SpeechSynthesis interface of the Web Speech API is the controller interface for the speech service; this can be used to retrieve information about the synthesis voices available on the device, start and pause speech, and other commands besides. 
Questo è in italiano come viene?
</div>

</body>
</html>

CSS

@import url('https://fonts.googleapis.com/css?family=Crimson+Text');

.buttons {
    margin-top: 25px;
}

button {
    background: none;
    border: none;
    cursor: pointer;
    height: 48px;
    outline: none;
    padding: 0;
    width: 48px;
}

#play {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/play.svg);
}

#play.played {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/play1.svg);
}

#pause {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/pause.svg);
}

#pause.paused {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/pause1.svg);
}

#stop {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/stop.svg);
}

#stop.stopped {
    background-image: url(https://rpsthecoder.github.io/js-speech-synthesis/stop1.svg);
}

JAVASCRIPT

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

// select voices////
var synth = window.speechSynthesis;

var voiceSelect = document.querySelector('#voices');

var voices = [];

function populateVoiceList() {
  voices = synth.getVoices().sort(function (a, b) {
      const aname = a.name.toUpperCase(), bname = b.name.toUpperCase();
      if ( aname < bname ) return -1;
      else if ( aname == bname ) return 0;
      else return +1;
  });
  var selectedIndex = voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
  voiceSelect.innerHTML = '';
  for(i = 0; i < voices.length ; i++) {
    var option = document.createElement('option');
    option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

    if(voices[i].default) {
      option.textContent += ' -- DEFAULT';
    }

    option.setAttribute('data-lang', voices[i].lang);
    option.setAttribute('data-name', voices[i].name);
    voiceSelect.appendChild(option);
  }
  voiceSelect.selectedIndex = selectedIndex;
}

populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
  speechSynthesis.onvoiceschanged = populateVoiceList;
}
//end select voices

        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('#description').textContent);
                //utterance.voice = getVoices()[0];

                //add voice//
                var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
                    for(i = 0; i < voices.length ; i++) {
                      //if(voices[i].name === 'Google UK English Female') {
                      if(voices[i].name === selectedOption) {
                        utterance.voice = voices[i];
                        break;
                      }
                    }


                voiceSelect.onchange = function(){
                    onClickStop();
                    stopEle.className = '';
                    onClickPlay();
                    playEle.className = 'played';
                }
                //and add voice

                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'));
    }

}

现在我有了播放、停止和暂停按钮(暂停仍然不起作用),我可以 select 设备中可用的声音之一。

似乎在 chrome 上工作得很好,在 Firefox 上可能工作得少一些,(但我正在使用 Linux LMDE,也许这是我的错)。过了一会儿 Chrome 停止说话。我不知道为什么,但在我看来,我看到有人可能理解为什么在我这些天打开的数千个网页中的某些网页中,我必须重新打开它们。

如果 selected 的声音保存在 cookie 中就好了,所以如果我打开另一个页面,脚本会从我 selected 的最后一个声音开始(我不知道如何在 JavaScript)

中完成

更新 2

我向前迈出了一些小步骤并进行了简化。

它似乎几乎可以工作,但不总是暂停按钮,我现在最大的疑问是使用 chrome 它似乎在我更新或更改页面时不会停止,这真的很糟糕当您换页时,他会继续阅读上一页。

HTML

<div id="SpeechSynthesis">
    <div>
        <button id=play>play</button>
        <button id=pause>pause</button>
        <button id=stop>stop</button>
    </div>
        <select id="voices">

        </select>
    </div>
<p id="texttospeech">
The SpeechSynthesis interface of the Web Speech API is the controller interface for the speech service; this can be used to retrieve information about the synthesis voices available on the device, start and pause speech, and other commands besides. 
Questo è in italiano come viene?
</p>

JAVASCRIPT

onload = function() {
    if ('speechSynthesis' in window){
        var synth = speechSynthesis;
        var flag = false;

        //stop when change page ???(not sure)
            if(synth.speaking){ /* stop narration */
                /* for safari */
                flag = false;
                synth.cancel();
            }

        /* references to the buttons */
        var playEle = document.querySelector('#play');
        var pauseEle = document.querySelector('#pause');
        var stopEle = document.querySelector('#stop');

        /* click event handlers for the buttons */
        playEle.addEventListener('click', onClickPlay);
        pauseEle.addEventListener('click', onClickPause);
        stopEle.addEventListener('click', onClickStop);

// select voices////
//var synth = window.speechSynthesis;

var voiceSelect = document.querySelector('#voices');

var voices = [];

function populateVoiceList() {
  voices = synth.getVoices().sort(function (a, b) {
      const aname = a.name.toUpperCase(), bname = b.name.toUpperCase();
      if ( aname < bname ) return -1;
      else if ( aname == bname ) return 0;
      else return +1;
  });
  var selectedIndex = voiceSelect.selectedIndex < 0 ? 0 : voiceSelect.selectedIndex;
  voiceSelect.innerHTML = '';
  for(i = 0; i < voices.length ; i++) {
    var option = document.createElement('option');
    option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

    if(voices[i].default) {
      option.textContent += ' -- DEFAULT';
    }

    option.setAttribute('data-lang', voices[i].lang);
    option.setAttribute('data-name', voices[i].name);
    voiceSelect.appendChild(option);
  }
  voiceSelect.selectedIndex = selectedIndex;
}

populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
  speechSynthesis.onvoiceschanged = populateVoiceList;
}
//end select voices

        function onClickPlay() {
            if(!flag){
                flag = true;
                utterance = new SpeechSynthesisUtterance(document.querySelector('#texttospeech').textContent);
                //utterance.voice = synth.getVoices()[0];

                //add voice//
                var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
                    for(i = 0; i < voices.length ; i++) {
                      //if(voices[i].name === 'Google UK English Female') {
                      if(voices[i].name === selectedOption) {
                        utterance.voice = voices[i];
                        break;
                      }
                    }

                voiceSelect.onchange = function(){
                    onClickStop();
                    onClickPlay();
                }
                //and add voice

                utterance.onend = function(){
                    flag = false;
                };
                synth.speak(utterance);

                //fix stop after a while bug
                let r = setInterval(() => {
                  console.log(speechSynthesis.speaking);
                  if (!speechSynthesis.speaking) {
                    clearInterval(r);
                  } else {
                    speechSynthesis.resume();
                  }
                }, 14000);
                //end fix stop after a while bug
            }
            if(synth.paused) { /* unpause/resume narration */
                synth.resume();
            }
        }
        function onClickPause() {
            if(synth.speaking && !synth.paused){ /* pause narration */
                synth.pause();
            }
        }
        function onClickStop() {
           if(synth.speaking){ /* stop narration */
                /* for safari */
                flag = false;
                synth.cancel();
            }
        }
    }
  else {
        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('#SpeechSynthesis'));
  }
}

更新 3

我试图用最后 selected 的声音添加一个 cookie。我添加了几个函数来管理 cookie,并在 onClickPlay() 函数中设置了 cookie。

//add voice//
                var selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name');
                    for(i = 0; i < voices.length ; i++) {
                      //if(voices[i].name === 'Google UK English Female') {
                      if(voices[i].name === selectedOption) {
                        utterance.voice = voices[i];
                        setCookie('SpeechSynthesisVoice',voices[i].name,30);
                        break;
                      }
                    }

Firefox 设置 cookie 没有问题,chrome 没有(即使文件在在线服务器上)。

然后我尝试在populateVoiceList()函数中将保存在cookie中的语音设置为"selected":

//get cookie voice
    var cookievoice = getCookie('SpeechSynthesisVoice');

//add selcted to option if if cookievoice
    if (cookievoice === voices[i].name) {
        option.setAttribute('selected', 'selected');
    }

有效,我在代码中找到了"selected",但似乎没有考虑,它总是从列表中的第一个语音开始说话,或者默认语音,我是不确定,而不是带有 "selected".

的那个

我使用的cookie函数:

//cookie functions
function setCookie(name, value, days) {
    var expires = '',
        date = new Date();
    if (days) {
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        expires = '; expires=' + date.toGMTString();
    }
    document.cookie = name + '=' + value + expires + '; path=/';
}

function getCookie(name) {
    var cookies = document.cookie.split(';'),
        length = cookies.length,
        i,
        cookie,
        nameEQ = name + '=';
    for (i = 0; i < length; i += 1) {
        cookie = cookies[i];
        while (cookie.charAt(0) === ' ') {
            cookie = cookie.substring(1, cookie.length);
        }
        if (cookie.indexOf(nameEQ) === 0) {
            return cookie.substring(nameEQ.length, cookie.length);
        }
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name, '', -1);
}

"for" 循环后的这一行怎么样?

voiceSelect.selectedIndex = selectedIndex;

如果它仍然存在,则说明您正在覆盖您的选择。 试试

if(cookievoice === voices[i].name) {
    option.setAttribute('selected', 'selected');
   selectedIndex = i;
}

我没有得到太多帮助,但我设法设置了一个读取网页文本的小播放器。

我做了一个带有演示的小教程,解释了我做了什么以及它是如何工作的。

https://www.alebalweb-blog.com/85-text-to-speech-player-with-buttons-play-pause-stop-and-voice-choice.html