Le code HTML suivant montre un tableau vide dans la console au premier clic:
<!DOCTYPE html>
<html>
<head>
<script>
function test(){
console.log(window.speechSynthesis.getVoices())
}
</script>
</head>
<body>
<a href="#" onclick="test()">Test</a>
</body>
</html>
En deuxième clic, vous obtiendrez la liste attendue.
Si vous ajoutez un événement onload
pour appeler cette fonction (<body onload="test()">
), vous pouvez obtenir un résultat correct au premier clic. Notez que le premier appel sur onload
ne fonctionne toujours pas correctement. Il retourne vide au chargement de la page mais fonctionne ensuite.
Des questions:
Puisqu'il pourrait s'agir de un bogue en version bêta, j'ai abandonné les questions "Pourquoi".
Maintenant, la question est de savoir si vous souhaitez accéder à window.speechSynthesis
au chargement de la page:
speechSynthesis
, au chargement de la page? Contexte et tests:
Je testais les nouvelles fonctionnalités de l'API Web Speech, puis j'ai eu ce problème dans mon code:
<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>
Ma question était la suivante: pourquoi window.speechSynthesis.getVoices()
renvoie-t-il un tableau vide, une fois la page chargée et la fonction onready
déclenchée? Comme vous pouvez le voir si vous cliquez sur le lien, la même fonction renvoie un tableau des voix disponibles de Chrome par onclick
triger?
Il semble que Chrome charge window.speechSynthesis
après le chargement de la page!
Le problème n'est pas dans l'événement ready
. Si je supprime la ligne var voice=...
de la fonction ready
, pour la première fois, elle affiche une liste vide dans la console. Mais le deuxième clic fonctionne bien.
Il semble que window.speechSynthesis
ait besoin de plus de temps pour se charger après le premier appel. Vous devez l'appeler deux fois! Mais aussi, vous devez attendre et le laisser se charger avant le second appel sur window.speechSynthesis
. Par exemple, le code suivant montre deux tableaux vides dans la console si vous l'exécutez pour la première fois:
// First speechSynthesis call
var voices = window.speechSynthesis.getVoices();
console.log(voices);
// Second speechSynthesis call
voices = window.speechSynthesis.getVoices();
console.log(voices);
Selon Errata de l'API Web Speech (E11 2013-10-17), la liste de voix est chargée de manière asynchrone sur la page. Un événement onvoiceschanged
est déclenché quand ils sont chargés.
voiceschanged: déclenché lorsque le contenu de SpeechSynthesisVoiceList, que la méthode getVoices retournera, aura changé. Les exemples incluent: synthèse côté serveur où la liste est déterminée de manière asynchrone, ou quand des voix côté client sont installées/désinstallées.
L'astuce consiste donc à définir votre voix à partir du rappel pour cet écouteur d'événement:
// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
...
};
Vous pouvez utiliser setInterval pour attendre que les voix soient chargées avant de les utiliser. Toutefois, vous devez effacer 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);
heres la réponse
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);
});
}
Au début, j’utilisais des voix modifiées, mais le tir continuait même après le chargement des voix; mon objectif était donc d’éviter à tout prix les voix modifiées.
C'est ce que je suis venu avec. Cela semble fonctionner jusqu'à présent, sera mis à jour si ça casse.
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
Tout d'abord, merci beaucoup pour cette réponse. Deuxièmement, voici un JSBin utile si quelqu'un tombe sur cette question/répond à nouveau: http://jsbin.com/gosaqihi/9/edit?js,console
Une autre façon de vous assurer que les voix sont bien chargées avant d’en avoir besoin est de lier leur état de chargement à une promesse, puis d’envoyer vos commandes vocales à partir de then
:
const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);
function listVoices() {
awaitVoices.then(()=> {
let voices = speechSynthesis.getVoices();
console.log(voices);
});
}
Lorsque vous appelez listVoices
, il attendra que les voix soient chargées en premier ou enverra votre opération lors du tick suivant.
La solution setInterval de Salman Oskooi était parfaite
Veuillez consulter 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);
}
Cela fonctionne très bien sur Chrome pour MAC, Linux (Ubuntu), Windows et Android.
Android a un en_GB non standard tandis que d'autres ont un en-GB comme code de langue Vous verrez aussi que la même langue (lang) a plusieurs noms
Sur Mac Chrome, vous obtenez en-GB Daniel en plus de en-GB Google UK Anglais Femme et n-GB Google UK Anglais Homme
en-GB Daniel (Mac et iOS) en-GB Google Anglais Anglais Femmes en-GB Google Anglais Anglais en_GB Anglais Royaume-Uni hi-IN Google hi-IN Lekha (Mac et iOS) Hi_IN Inde hindi
J'ai utilisé ce code pour charger des voix avec succès:
<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;
}
}
J'ai trouvé le code 'onvoiceschanged' de cet article: https://hacks.mozilla.org/2016/01/firefox-and-the-web-speech-api/
Fonctionne dans Firefox/Safari et Chrome (et dans Google Apps Script également, mais uniquement en HTML).
Je devais faire mes propres recherches à ce sujet pour m'assurer de bien comprendre, donc il suffit de partager (n'hésitez pas à modifier).
Mon objectif est de:
La fonctionnalité de base est démontrée dans MDN/ démonstration officielle en direct de:
https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis
mais je voulais mieux comprendre.
Pour briser le sujet ...
Synthèse de discours
L’interface
SpeechSynthesis
de l’API Web Speech API est le contrôleur interface pour le service de parole; cela peut être utilisé pour récupérer informations sur les voix de synthèse disponibles sur le périphérique, démarrez et mettre en pause le discours, et d'autres commandes en plus.
onvoices changged
La propriété
onvoiceschanged
de l'interfaceSpeechSynthesis
représente un gestionnaire d'événements qui sera exécuté lorsque la liste deSpeechSynthesisVoice
objets qui seraient retournés par le La méthodeSpeechSynthesis.getVoices()
a changé (lorsque l'événementvoiceschanged
est déclenché.)
Exemple A
Si ma demande a simplement:
var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);
La console des outils de développement Chrome affiche:
Exemple B
Si je change le code en:
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);
Les états before et after sont les mêmes et voices
est un tableau vide.
Solution
Bien que je ne sois pas confiant de mettre en œuvre Promises , les éléments suivants ont fonctionné pour moi:
Définition de la fonction
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)
});
}
Appelant la fonction
// in your handler, populate the select element
if (page_title === "something") {
set_up_speech()
}
Android Chrome - désactiver l'économiseur de données. Cela m'a été utile. (Chrome 71.0.3578.99)
// wait until the voices load
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
};