Получение списка голосов в 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>

во втором клике вы получите ожидаемый список.

если добавить onload событие для вызова этой функции (<body onload="test()">), то вы можете получить правильный результат на первый клик. Обратите внимание, что первый вызов onload все еще не работает должным образом. Она возвращает пустой при загрузке страницы, но работает после этого.

вопросы:

так как это может быть a баг в бета-версии, я отказался от вопросов "Почему".

теперь вопрос в том, хотите ли вы получить доступ 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 функция срабатывает? Как вы можете видеть, если вы нажмете на ссылку, та же функция возвращает массив доступных голосов Chrome по onclick тригер?

кажется, 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);

8 ответов


по данным ошибки API веб-речи (E11 2013-10-17), голосовой список загружается асинхронно на страницу. Ан onvoiceschanged событие запускается при их загрузке.

voiceschanged: срабатывает, когда содержимое SpeechSynthesisVoiceList, что метод getVoices будет возвращен, изменились. Примеры включают: синтез на стороне сервера, где список определяется асинхронно или когда голоса на стороне клиента установлены/удалены.

Итак, трюк состоит в том, чтобы установить свой голос из обратного вызова для этого прослушивателя событий:

// 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);

вот что я придумал. Кажется, он работает до сих пор, будет обновляться, если он сломается.

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, консоль


другой способ убедиться, что голоса загружаются до того, как они вам понадобятся, - связать их состояние загрузки с обещанием, а затем отправить свои речевые команды из then:

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

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

когда вы называете listVoices, он будет либо ждать голоса, чтобы загрузить первый,или отправить вашу операцию на следующий ТИК.


setInterval решение Салмана Oskooi было идеальным

можно найти 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);

} 

это отлично работает на Chrome для MAC, Linux (Ubuntu), Windows и Android

Android имеет нестандартный en_GB wile другие имеют en-GB в качестве кода языка Также вы увидите, что один и тот же язык(lang) имеет несколько имен

на Mac Chrome вы получаете en-GB Daniel помимо en-GB Google UK английский женский и n-GB Google UK английский мужчина

en-GB Daniel (Mac и iOS) ru-GB Google UK английский женский ru-GB Google UK английский мужской en_GB английский Великобритания привет-в Google हिन्दी привет-в Лехе (Mac и iOS) hi_IN хинди Индия


вот ответ!--2-->

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);
  });
}

Я должен был сделать свое собственное исследование для этого, чтобы убедиться, что я понял его правильно, поэтому просто делюсь (не стесняйтесь редактировать).

моя цель:

  • получить список голосов, доступных на моем устройстве
  • заполните элемент select этими голосами (после загрузки определенной страницы)
  • использовать простой для понимания код

основная функциональность продемонстрирована в MDN официальный live demo из:

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

но я хотел понять это лучше.

ломать темы...

SpeechSynthesis

на SpeechSynthesis интерфейс API веб-речи контроллер интерфейс для службы речи; это может использоваться для получения информация о синтезе голосов доступный на устройстве, запустите и пауза речи, и другие команды.

источник

onvoiceschanged

на onvoiceschanged свойства SpeechSynthesis интерфейс представляет обработчик событий, который будет выполняться при SpeechSynthesisVoice объекты, которые будут возвращены SpeechSynthesis.getVoices() метод изменился (когда 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

решение

хотя я не уверен в реализации обещания, для меня сработало следующее:

определение функции

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()
}