web-dev-qa-db-fra.com

Obtenir la liste des voix dans speechSynthesis of Chrome (Web Speech API)

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:

  • Quel est le meilleur hack pour ce problème?
  • Comment pouvez-vous vous assurer qu'il va charger 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);
40
Mehdi

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

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

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

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)
            }
    }
3
Ar-51

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

2
Brandon Aaskov

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.

2

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

2
Jayanta

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).

0
Dagmar

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:

  • Obtenir une liste des voix disponibles sur mon appareil 
  • Remplir un élément sélectionné avec ces voix (après le chargement d'une page particulière) 
  • Utilisez un code facile à comprendre 

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. 

La source

onvoices changged

La propriété onvoiceschanged de l'interface SpeechSynthesis représente un gestionnaire d'événements qui sera exécuté lorsque la liste de SpeechSynthesisVoice objets qui seraient retournés par le La méthode SpeechSynthesis.getVoices() a changé (lorsque l'événement voiceschanged est déclenché.)

La source

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:

 enter image description here

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. 

 enter image description here

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

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

};
0
Eugeny Aladov