Подтвердить что ты не робот

Получение списка голосов в речи Синтез 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 по-прежнему не работает должным образом. Он возвращает пустое значение на странице, но потом работает.

Вопросы:

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

Теперь возникает вопрос, хотите ли вы получить доступ к 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 triger?

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

Ответ 1

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

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

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

// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
    window.speechSynthesis.getVoices();
    ...
};

Ответ 2

Вы можете использовать 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);

Ответ 3

Сначала я использовал onvoiceschanged, но он продолжал стрелять даже после загрузки голосов, поэтому моя цель состояла в том, чтобы избежать onvoiceschanged любой ценой.

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

loadVoicesWhenAvailable();

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

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

Ответ 4

вот ответ

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

Ответ 5

Во-первых, большое спасибо за этот ответ. Во-вторых, здесь полезный JSBin, если кто-нибудь снова встретит этот вопрос/ответ: http://jsbin.com/gosaqihi/9/edit?js,console

Ответ 6

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

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

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

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

Ответ 7

setInterval решение Salman 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 English Female и n-GB Google UK English Male

ru-RU Даниэль (Mac и iOS) ru-RU Google Великобритания Английский Женский ru-RU Google UK Английский Мужской en_GB English Великобритания hi-IN Google हिन्दी hi-IN Lekha (Mac и iOS) hi_IN Хинди Индия

Ответ 8

Я использовал этот код для успешной загрузки голосов:

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

Я нашел код "onvoiceschange" из этой статьи: https://hacks.mozilla.org/2016/01/firefox-and-the-web-speech-api/

Работает в Firefox/Safari и Chrome (и в Google Apps Script тоже - но только в HTML).

Ответ 9

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

Моя цель:

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

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

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

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

Чтобы сломать тему вниз...

SpeechSynthesis

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

Источник

onvoiceschanged

Свойство onvoiceschanged интерфейса SpeechSynthesis представляет обработчик события, который будет запущен, когда список SpeechSynthesisVoice объекты, которые будут возвращены Метод SpeechSynthesis.getVoices() изменился (когда voiceschanged событие происходит.)

Источник

Пример А

Если мое приложение просто имеет:

var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);

Консоль инструментов разработчика Chrome покажет:

enter image description here

Пример Б

Если я изменю код на:

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

Ответ 10

Android Chrome - отключить сохранение данных. Это было полезно для меня. (Chrome 71.0.3578.99)

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

};