web-dev-qa-db-ja.com

speechSynthesis of Chrome(Web Speech 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>
_

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);
_
45
Mehdi

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();
    ...
};
90
craveytrain

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);
5
Salman Oskooi

答えはここにあります

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);
  });
}
3
Juste Guipi

最初はonvoiceschangedを使用しましたが、音声が読み込まれた後も発火し続けたので、私の目標はすべての費用で変更されたonvoicesを避けることでした。

これは私が思いついたものです。これは今のところ機能しているようで、壊れると更新されます。

loadVoicesWhenAvailable();

function loadVoicesWhenAvailable() {
         voices = synth.getVoices();

         if (voices.length !== 0) {
                console.log("start loading voices");
                LoadVoices();
            }
            else {
                setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
            }
    }
3
Ar-51

まず、この答えをありがとう。第二に、誰かがこの質問/回答に再び出くわした場合に役立つJSBinです: http://jsbin.com/gosaqihi/9/edit?js,console

2
Brandon Aaskov

ボイスが必要になる前に確実にロードされるようにする別の方法は、ロード状態をプロミスにバインドしてから、thenから音声コマンドをディスパッチすることです。

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

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

listVoicesを呼び出すと、音声が最初にロードされるのを待つか、次のティックでオペレーションをディスパッチします。

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に加えて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ヒンディー語インド

2
Jayanta

このコードを使用して、音声を正常にロードしました。

<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のみ)で動作します。

1
Dagmar

これを適切に理解するには、独自の調査を行わなければならず、共有するだけです(自由に編集してください)。

私の目標は:

  • デバイスで利用可能な音声のリストを取得する
  • (特定のページがロードされた後)選択要素にそれらの音声を入力します
  • わかりやすいコードを使用する

基本的な機能は、MDNの 公式ライブデモ で実証されています:

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

しかし、私はそれをよりよく理解したかった。

トピックを分類するには...

SpeechSynthesis

Web Speech APISpeechSynthesisインターフェイスは、音声サービスのコントローラーインターフェイスです。これは、デバイスで使用可能な合成音声に関する情報の取得、音声の開始と一時停止、およびその他のコマンドに使用できます。

出所

onvoiceschanged

onvoiceschangedインターフェースのSpeechSynthesisプロパティは、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
user1063287

Android Chrome-データセーバーをオフにします。それは私にとって役に立ちました。(Chrome 71.0.3578.99)

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

};
0
Eugeny Aladov