以下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
上的第一个呼叫仍然无法正常工作。它在页面加载时返回空,但在之后工作。
问题:
由于测试版可能是a bug,我放弃了“为什么”的问题。
现在,问题是您是否要在页面加载时访问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>
我的问题是:为什么window.speechSynthesis.getVoices()
在页面加载并触发onready
函数后返回空数组?正如您所看到的,如果单击该链接,同一函数会通过onclick
triger返回Chrome的可用语音数组?
在页面加载后,Chrome似乎加载了window.speechSynthesis
!
问题不在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);
答案 0 :(得分:86)
根据Web Speech API Errata(E11 2013-10-17),语音列表被异步加载到页面。加载时会触发onvoiceschanged
事件。
voiceschanged:当getVoices方法将返回的SpeechSynthesisVoiceList的内容发生更改时触发。示例包括:服务器端综合,其中列表是异步确定的,或者是在安装/卸载客户端语音时。
所以,诀窍是从该事件监听器的回调设置你的声音:
// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
...
};
答案 1 :(得分: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);
答案 2 :(得分:3)
起初我使用的是onvoices,但是即使在加载声音之后它也会继续发射,所以我的目标是不惜一切代价避免发票。
这就是我想出的。它似乎工作到目前为止,如果它破坏将更新。
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
答案 3 :(得分:3)
继承人的答案
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);
});
}
答案 4 :(得分:2)
首先,非常感谢你的回答。其次,如果有人再次遇到这个问题/答案,这是一个有用的JSBin:http://jsbin.com/gosaqihi/9/edit?js,console
答案 5 :(得分:2)
确保在需要之前加载语音的另一种方法是将其加载状态绑定到promise,然后从then
发送语音命令:
const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);
function listVoices() {
awaitVoices.then(()=> {
let voices = speechSynthesis.getVoices();
console.log(voices);
});
}
当你拨打listVoices
时,它会先等待声音加载,或者在下一个时刻发出你的行动。
答案 6 :(得分:2)
Salman Oskooi的setInterval解决方案非常完美
请参阅https://jsfiddle.net/exrx8e1y/
function myFunction() {
dtlarea=document.getElementById("details");
//dtlarea.style.display="none";
dtltxt="";
var mytimer = setInterval(function() {
var voices = speechSynthesis.getVoices();
//console.log(voices);
if (voices.length !== 0) {
var msg = new SpeechSynthesisUtterance();
msg.rate = document.getElementById("rate").value; // 0.1 to 10
msg.pitch = document.getElementById("pitch").value; //0 to 2
msg.volume = document.getElementById("volume").value; // 0 to 1
msg.text = document.getElementById("sampletext").value;
msg.lang = document.getElementById("lang").value; //'hi-IN';
for(var i=0;i<voices.length;i++){
dtltxt+=voices[i].lang+' '+voices[i].name+'\n';
if(voices[i].lang==msg.lang) {
msg.voice = voices[i]; // Note: some voices don't support altering params
msg.voiceURI = voices[i].voiceURI;
// break;
}
}
msg.onend = function(e) {
console.log('Finished in ' + event.elapsedTime + ' seconds.');
dtlarea.value=dtltxt;
};
speechSynthesis.speak(msg);
clearInterval(mytimer);
}
}, 1000);
}
这适用于适用于MAC,Linux(Ubuntu),Windows和Android的Chrome
Android有非标准en_GB,其他人使用en-GB作为语言代码 你也会看到同一种语言(lang)有多个名字
在Mac Chrome上,除了en-GB谷歌英国英语女性和n-GB谷歌英国英语男性之外你还会得到en-GB Daniel
en-GB Daniel(Mac和iOS) en-GB谷歌英国英语女性 en-GB谷歌英国英语男 en_GB英语英国 喜欢谷歌हिन्दी hi-IN Lekha(Mac和iOS) hi_IN印地语印度
答案 7 :(得分:2)
研究了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));
注意:
voiceschanged
触发时,我们需要再次致电.getVoices()
。原始数组将不会填充内容。getVoices()
。我们只需要听事件,然后事件就会发生。在Firefox上,侦听还不够,您必须调用getVoices()
,然后侦听事件voiceschanged
,并在收到通知后使用getVoices()
设置数组。 voiceObtained
的诺言来解决您想要的声音,然后您的函数可以说出一些话:voiceObtained.then(voice => { })
,并在该处理程序中,调用window.speechSynthesis.speak()
说点什么。或者,您甚至可以写一个承诺speechReady("hello world").then(speech => { window.speechSynthesis.speak(speech) })
说些什么。答案 8 :(得分:1)
我使用此代码成功加载了声音:
<select id="voices"></select>
...
function loadVoices() {
populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
}
function populateVoiceList() {
var allVoices = speechSynthesis.getVoices();
allVoices.forEach(function(voice, index) {
var option = $('<option>').val(index).html(voice.name).prop("selected", voice.default);
$('#voices').append(option);
});
if (allVoices.length > 0 && speechSynthesis.onvoiceschanged !== undefined) {
// unregister event listener (it is fired multiple times)
speechSynthesis.onvoiceschanged = null;
}
}
我从这篇文章中找到了“ onvoiceschanged”代码:https://hacks.mozilla.org/2016/01/firefox-and-the-web-speech-api/
在Firefox / Safari和Chrome(也可以在Google Apps脚本中工作,但只能在HTML中工作)。
答案 9 :(得分:1)
async function speak(txt) {
await initVoices();
const u = new SpeechSynthesisUtterance(txt);
u.voice = speechSynthesis.getVoices()[3];
speechSynthesis.speak(u);
}
function initVoices() {
return new Promise(function (res, rej){
speechSynthesis.getVoices();
if (window.speechSynthesis.onvoiceschanged) {
res();
} else {
window.speechSynthesis.onvoiceschanged = () => res();
}
});
}
答案 10 :(得分:0)
为此,我必须进行自己的研究,以确保自己理解正确,因此可以共享(随意编辑)。
我的目标是:
基本功能在MDN的official live demo中得到了展示:
https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis
但是我想更好地理解它。
要分解话题...
语音合成
Web Speech API的
SpeechSynthesis
接口是控制器 语音服务接口;这可以用来找回 关于设备上可用的合成音色的信息,开始 并暂停语音和其他命令。
发票已更改
onvoiceschanged
界面的SpeechSynthesis
属性 代表事件处理程序,当SpeechSynthesisVoice
个对象将由SpeechSynthesis.getVoices()
方法已更改(当voiceschanged
事件触发。)
示例A
如果我的应用程序仅具有:
var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);
Chrome开发者工具控制台将显示:
示例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
是一个空数组。
解决方案
尽管我不确定实施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()
}
答案 11 :(得分:0)
Android Chrome浏览器-关闭数据保护程序。对我有帮助。(Chrome 71.0.3578.99)
// wait until the voices load
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
};