获取speechSynthesis中声音列表(Web Speech API)

62

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

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

第二次点击您将获得预期的列表。

如果您添加 onload 事件来调用此函数 (<body onload="test()">),那么您可以在第一次单击时获取正确的结果。请注意,onload 上的第一次调用仍然无法正常工作。它返回空值,但之后可以正常工作。

问题:

由于这可能是 beta 版本中的一个错误,我放弃了“为什么”的问题。

现在,问题是如果您想在页面加载时访问 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>

我的问题是:为什么在页面加载完成并触发“onready”函数后,window.speechSynthesis.getVoices()返回空数组?如果您点击链接,可以看到相同的函数通过“onclick”触发返回Chrome可用语音的数组?
似乎Chrome在页面加载后加载window.speechSynthesis
问题不在于“ready”事件。如果我从“ready”函数中删除var voice=...这一行,第一次单击时它会在控制台中显示空列表。但第二次单击正常工作。
似乎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);

嗯,我猜测一下,你的页面默认是否有与语音合成相关的HTML属性标签?如果没有,Chrome可能需要一些时间来自行解决这个问题。 - Rooster
我有一个关于我的问题的更新。也许问题不在于 ready - Mehdi
在Dart中,我使用Timer在5秒延迟后调用了getVoices(),并获得了一个可用语音列表。 - Nawaf Alsulami
@Mehdi。你应该在上面进行一些编辑并写出答案。调用getVoices()两次,间隔一秒,可以解决这个问题。 - Nawaf Alsulami
13个回答

1

确保在需要使用语音之前加载语音的另一种方法是将它们的加载状态绑定到一个 Promise,并从 then 中分派语音命令:

const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);

function listVoices() {
    awaitVoices.then(()=> {
        let voices = speechSynthesis.getVoices();
        console.log(voices);
    });
}

当您调用listVoices时,它将等待声音加载完毕,或在下一个时钟周期中执行您的操作。

1
这对我不起作用,console.log(voices)从未被执行。 - Eugenio

0

我不得不自己做研究以确保我正确理解它,所以只是分享(请随意编辑)。

我的目标是:

  • 获取设备上可用的语音列表
  • 在特定页面加载后,使用这些语音填充选择元素
  • 使用易于理解的代码

在MDN的官方演示中展示了基本功能。

https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis

但我想更好地理解它。

将主题分解为以下几个部分...

SpeechSynthesis

Web Speech APISpeechSynthesis接口是语音服务的控制器接口;可以用于检索设备上可用的合成语音信息,启动和暂停语音以及执行其他命令。

来源

onvoiceschanged

SpeechSynthesis接口的onvoiceschanged属性表示一个事件处理程序,当SpeechSynthesis.getVoices()方法返回的SpeechSynthesisVoice对象列表发生更改(即voiceschanged事件触发)时,该处理程序将运行。

源代码

示例A

如果我的应用程序仅具有:

var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);

Chrome 开发者工具控制台将显示:

enter image description here

示例B

如果我将代码更改为:

var synth = window.speechSynthesis;

console.log("BEFORE");
console.log(synth);
console.log(synth.onvoiceschanged);

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

console.log(voices);
console.log(synth);
console.log(synth.onvoiceschanged);

前后状态相同,voices 是一个空数组。

enter image description here

解决方案

虽然我不太自信实现Promises,但以下方法对我有效:

定义函数

var synth = window.speechSynthesis;
// declare so that values are accessible globally
var voices = [];


function set_up_speech() {

    return new Promise(function(resolve, reject) {

        // get the voices
        var voices = synth.getVoices();

        // get reference to select element
        var $select_topic_speaking_voice = $("#select_topic_speaking_voice");

        // for each voice, generate select option html and append to select
        for (var i = 0; i < voices.length; i++) {

            var option = $("<option></option>");

            var suffix = "";

            // if it is the default voice, add suffix text  
            if (voices[i].default) {
                suffix = " -- DEFAULT";
            }

            // create the option text
            var option_text = voices[i].name + " (" + voices[i].lang + suffix + ")";

            // add the option text
            option.text(option_text);

            // add option attributes
            option.attr("data-lang", voices[i].lang);
            option.attr("data-name", voices[i].name);

            // append option to select element
            $select_topic_speaking_voice.append(option);
        }

        // resolve the voices value
        resolve(voices)

    });

}

调用函数

// in your handler, populate the select element    
if (page_title === "something") {
set_up_speech()
}

0

Android Chrome - 关闭数据节省器。它对我很有帮助。(Chrome 71.0.3578.99)

// wait until the voices load
   window.speechSynthesis.onvoiceschanged = function() {
    window.speechSynthesis.getVoices();

};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接