以下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版的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
触发返回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);
根据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();
...
};
Android Chrome-关闭数据保护程序。对我有帮助。(Chrome 71.0.3578.99)
您可以使用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);
起初,我使用onvoiceschanged,但是即使在加载声音之后它仍会触发,因此我的目标是不惜一切代价避免onvoiceschanged。
这是我想出的。到目前为止,它似乎仍然有效,如果出现问题,它将进行更新。
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
这里是答案
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);
});
}
首先,非常感谢您的回答。其次,如果有人再次遇到此问题/答案,这里有一个有用的JSBin:http://jsbin.com/gosaqihi/9/edit?js,console
确保语音在需要之前已加载的另一种方法是将其加载状态绑定到Promise,然后从then
分发语音命令:
const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);
function listVoices() {
awaitVoices.then(()=> {
let voices = speechSynthesis.getVoices();
console.log(voices);
});
}
当您呼叫listVoices
时,它将等待声音首先加载,或在下一个滴答声中调度您的操作。
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 Google UK English Female和n-GB Google UK English Male。>
en-GB Daniel(适用于Mac和iOS)zh-CN Google UK英文女zh-CN Google UK英语男性en_GB英文英国hi-IN Googleहिन्दीhi-IN Lekha(Mac和iOS)hi_IN印度北印度语
我使用此代码成功加载了声音:
我必须为此做我自己的研究,以确保我正确地理解它,因此只需共享(可以自由编辑)。