次のHTMLは、最初のクリックでコンソールに空の配列を表示します。
_<!DOCTYPE html>
<html>
<head>
<script>
function test(){
console.log(window.speechSynthesis.getVoices())
}
</script>
</head>
<body>
<a href="#" onclick="test()">Test</a>
</body>
</html>
_
2回目のクリックで、予想されるリストを取得します。
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>
_
私の質問は、ページがロードされonready
関数がトリガーされた後、なぜwindow.speechSynthesis.getVoices()
は空の配列を返すのですか?リンクをクリックするとわかるように、同じ関数はChrome by onclick
triger?の利用可能な音声の配列を返しますか?
Chromeページの読み込み後に_window.speechSynthesis
_を読み込みます!
問題はready
イベントにありません。 ready
関数から_var voice=...
_行を削除すると、最初のクリックでコンソールに空のリストが表示されます。ただし、2回目のクリックは正常に機能します。
_window.speechSynthesis
_は、最初の呼び出しの後、ロードするのにより多くの時間を必要とするようです。 2回呼び出す必要があります!しかし、また、_window.speechSynthesis
_の2番目の呼び出しの前に待機してロードする必要があります。たとえば、次のコードは、初めて実行した場合にコンソールに2つの空の配列を示します。
_// 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();
...
};
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);
答えはここにあります
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);
});
}
最初はonvoiceschangedを使用しましたが、音声が読み込まれた後も発火し続けたので、私の目標はすべての費用で変更されたonvoicesを避けることでした。
これは私が思いついたものです。これは今のところ機能しているようで、壊れると更新されます。
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
まず、この答えをありがとう。第二に、誰かがこの質問/回答に再び出くわした場合に役立つJSBinです: http://jsbin.com/gosaqihi/9/edit?js,console
ボイスが必要になる前に確実にロードされるようにする別の方法は、ロード状態をプロミスにバインドしてから、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に加えてen-GBダニエルを取得しますGoogle UK English Femaleおよびn-GB Google UK English Male
en-GBダニエル(MacおよびiOS)en-GB Google UK英語女性en-GB Google UK英語男性en_GB英語イギリスhi-IN Googleサービスhi-IN Lekha(MacおよびiOS)hi_INヒンディー語インド
このコードを使用して、音声を正常にロードしました。
<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 Scriptでも-HTMLのみ)で動作します。
これを適切に理解するには、独自の調査を行わなければならず、共有するだけです(自由に編集してください)。
私の目標は:
基本的な機能は、MDNの 公式ライブデモ で実証されています:
https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis
しかし、私はそれをよりよく理解したかった。
トピックを分類するには...
SpeechSynthesis
Web Speech API の
SpeechSynthesis
インターフェイスは、音声サービスのコントローラーインターフェイスです。これは、デバイスで使用可能な合成音声に関する情報の取得、音声の開始と一時停止、およびその他のコマンドに使用できます。
onvoiceschanged
onvoiceschanged
インターフェースのSpeechSynthesis
プロパティは、SpeechSynthesis.getVoices()
メソッドによって返されるSpeechSynthesisVoice
オブジェクトのリストが持つときに実行されるイベントハンドラーを表します変更(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()
}
Android Chrome-データセーバーをオフにします。それは私にとって役に立ちました。(Chrome 71.0.3578.99)
// wait until the voices load
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
};