获取语​​音列表语音合成Chrome(Web Speech API)

Meh*_*hdi 48 voice speech-synthesis dom-events webspeech-api

以下HTML在第一次单击时在控制台中显示空数组:

<!DOCTYPE html>
<html>
    <head>
        <script>
            function test(){
                console.log(window.speechSynthesis.getVoices())
            }
        </script>
    </head>
    <body>
        <a href="#" onclick="test()">Test</a>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

在第二次单击中,您将获得预期的列表.

如果添加onload事件来调用此函数(<body onload="test()">),则可以在第一次单击时获得正确的结果.请注意,第一次调用onload仍然无法正常工作.它在页面加载时返回空,但后续工作.

问题:

由于它可能是测试版中的错误,我放弃了"为什么"的问题.

现在,问题是您是否要window.speechSynthesis在页面加载时访问:

  • 这个问题最好的黑客是什么?
  • 如何speechSynthesis在页面加载时确保它会加载?

背景和测试:

我正在测试Web Speech API中的新功能,然后我在我的代码中遇到了这个问题:

<script type="text/javascript">
$(document).ready(function(){
    // Browser support messages. (You might need Chrome 33.0 Beta)
    if (!('speechSynthesis' in window)) {
      alert("You don't have speechSynthesis");
    }

    var voices = window.speechSynthesis.getVoices();
    console.log(voices) // []

    $("#test").on('click', function(){
        var voices = window.speechSynthesis.getVoices();
        console.log(voices); // [SpeechSynthesisVoice, ...]
    });
});
</script>
<a id="test" href="#">click here if 'ready()' didn't work</a>
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么window.speechSynthesis.getVoices()在页面加载并onready触发函数后返回空数组?正如您所看到的,如果单击该链接,同样的函数会返回一个由onclicktriger 提供的Chrome可用语音数组?

window.speechSynthesis在页面加载后,Chrome似乎加载了!

问题不在于ready事件.如果我var voice=...ready函数中删除该行,首先单击它会在控制台中显示空列表.但第二次点击工作正常.

window.speechSynthesis首次呼叫后似乎需要更多时间加载.你需要打两次电话!但是,你需要等待并在第二次调用之前加载它window.speechSynthesis.例如,以下代码在第一次运行时在控制台中显示两个空数组:

// First speechSynthesis call
var voices = window.speechSynthesis.getVoices();
console.log(voices);

// Second speechSynthesis call
voices = window.speechSynthesis.getVoices();
console.log(voices);
Run Code Online (Sandbox Code Playgroud)

cra*_*ain 94

根据Web Speech API勘误表(E11 2013-10-17),语音列表被异步加载到页面.一个onvoiceschanged在加载时触发事件.

voiceschanged:当getVoices方法将返回的SpeechSynthesisVoiceList的内容发生更改时触发.示例包括:服务器端综合,其中列表是异步确定的,或者是在安装/卸载客户端语音时.

所以,诀窍是从该事件监听器的回调中设置你的声音:

// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
    window.speechSynthesis.getVoices();
    ...
};
Run Code Online (Sandbox Code Playgroud)

  • 声音是异步加载的,但是它们是同时加载的还是`onvoiceschanged`事件被多次触发? (6认同)
  • 这是一个糟糕的决定.在加载(法语)之前,声音会很奇怪. (4认同)
  • 此外,你是否必须调用`speechSynthesis.getVoices()`然后等待`onvoiceschanged`触发或在页面加载期间或之后的某个时间触发事件? (3认同)
  • @DouglasDeRizzoMeneghetti - 针对 Chrome 进行测试,在我执行一些语音工作(例如首先调用 getVoices())之前,我没有发现 onvoiceschanged 回调被调用。 (2认同)
  • 不完整的答案。 (2认同)

nop*_*ole 10

在研究了 Google Chrome 和 Firefox 上的行为之后,这是可以得到所有声音的:

由于它涉及异步的事情,所以最好用一个承诺来完成:

const allVoicesObtained = new Promise(function(resolve, reject) {
  let voices = window.speechSynthesis.getVoices();
  if (voices.length !== 0) {
    resolve(voices);
  } else {
    window.speechSynthesis.addEventListener("voiceschanged", function() {
      voices = window.speechSynthesis.getVoices();
      resolve(voices);
    });
  }
});

allVoicesObtained.then(voices => console.log("All voices:", voices));
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 当事件voiceschanged触发时,我们需要.getVoices()再次调用。原始数组不会填充内容。
  2. 在 Google Chrome 上,我们不必getVoices()首先调用。我们只需要监听该事件,然后它就会发生。在 Firefox 上,仅侦听是不够的,您必须调用getVoices()并侦听事件,并在收到通知后voiceschanged使用设置数组。getVoices()
  3. 使用 Promise 可以使代码更加简洁。与获取声音相关的所有内容都在这个承诺代码中。如果你不使用 Promise 而是将这段代码放在你的演讲例程中,那就很混乱了。
  4. 您可以编写一个voiceObtained承诺来解决您想要的声音,然后您的函数可以说一些话: voiceObtained.then(voice => { })并在该处理程序中,调用 来说window.speechSynthesis.speak()出一些话。或者你甚至可以写下承诺speechReady("hello world").then(speech => { window.speechSynthesis.speak(speech) })说些什么。


小智 5

你可以使用setInterval等到语音加载后再使用它们,然后你需要然后清除setInterval:

var timer = setInterval(function() {
    var voices = speechSynthesis.getVoices();
    console.log(voices);
    if (voices.length !== 0) {
      var msg = new SpeechSynthesisUtterance(/*some string here*/);
      msg.voice = voices[/*some number here to choose from array*/];
      speechSynthesis.speak(msg);
      clearInterval(timer);
    }
}, 200);

$("#test").on('click', timer);
Run Code Online (Sandbox Code Playgroud)


小智 5

这是答案

function synthVoice(text) {

  const awaitVoices = new Promise(resolve=> 
    window.speechSynthesis.onvoiceschanged = resolve)  
  .then(()=> {
    const synth = window.speechSynthesis;

    var voices = synth.getVoices();
    console.log(voices)

    const utterance = new SpeechSynthesisUtterance();
    utterance.voice = voices[3];        
    utterance.text = text;

    synth.speak(utterance);
  });
}
Run Code Online (Sandbox Code Playgroud)