Получение списка голосов в речи Синтез 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);
Ответы
Ответ 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 покажет:
Пример Б
Если я изменю код на:
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();
};