Obtenir la liste des voix dans la synthèse des discours de Chrome (API de discours Web))

le HTML suivant affiche le 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 second clic vous obtiendrez la liste attendue.

Si vous ajoutez onload événement pour appeler cette fonction (<body onload="test()">), alors vous pouvez obtenir le résultat correct sur le premier clic. Notez que le premier appel sur onload ne fonctionne toujours pas correctement. Il retourne vide sur la page de chargement mais fonctionne après.

Questions:

Puisqu'il pourrait être un bug en version beta, j'ai abandonné les questions "Pourquoi".

maintenant, la question Est si vous voulez accéder à window.speechSynthesis au chargement de la page:

  • Quel est le meilleur hack pour cette question?
  • Comment pouvez-vous vous assurer qu'il va charger speechSynthesis, au chargement de la page?

arrière-plan et tests:

j'ai testé les nouvelles fonctionnalités de L'API de discours Web, 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>

mon la question était: pourquoi ne window.speechSynthesis.getVoices() retourner tableau vide, après la page est chargée et onready la fonction est activée? Comme vous pouvez le voir si vous cliquez sur le lien, la même fonction renvoie un tableau des voix de Chrome disponibles par onclick triger?

Il semble Chrome charges window.speechSynthesis après le chargement de la page!

Le problème n'est pas dans ready événement. Si je supprime la ligne var voice=...ready fonction, pour le premier clic il affiche la liste vide dans la console. Mais le deuxième clic fonctionne fin.

il semble window.speechSynthesis besoin de plus de temps pour charger après le premier appel. Vous avez besoin de l'appeler deux fois! Mais aussi, vous devez attendre et le charger avant le deuxième appel sur window.speechSynthesis. Par exemple, le code suivant affiche 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);
37
demandé sur Mehdi 2014-02-02 21:25:55

8 réponses

Selon Web Speech API Errata (E11 2013-10-17), la liste vocale est chargée async à la page. onvoiceschanged événement est déclenché lorsqu'ils sont chargés.

voiceschanged: Fired quand le contenu de la SpeechSynthesisVoiceList, que la méthode getVoices va revenir, ont changé. Exemples: synthèse côté serveur où la liste est déterminée de manière asynchrone, ou lorsque les voix côté client sont installées/désinstallées.

Donc, la truc est de mettre votre voix à partir de la fonction de rappel pour que l'écouteur d'événement:

// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
    window.speechSynthesis.getVoices();
    ...
};
78
répondu craveytrain 2014-04-10 04:42:22

Vous pouvez utiliser un setInterval pour attendre que les voix soient chargées avant de les utiliser comme vous en avez besoin et ensuite effacer le 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
répondu Salman Oskooi 2016-02-10 20:42:51

Au début j'ai utilisé onvoiceschanged, mais il a continué à tirer même après que les voix aient été chargées, donc mon but était d'éviter onvoiceschanged à tout prix.

c'est Ce que je suis venu avec. Il semble fonctionner jusqu'à présent, sera mis à jour si elle casse.

loadVoicesWhenAvailable();

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

         if (voices.length !== 0) {
                console.log("start loading voices");
                LoadVoices();
            }
            else {
                setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
            }
    }
3
répondu Ar-51 2017-10-06 16:36:18

tout d'Abord, merci beaucoup pour cette réponse. Deuxièmement, voici un JSBin utile si quelqu'un tombe sur cette question/réponse à nouveau: http://jsbin.com/gosaqihi/9/edit?js, console

2
répondu Brandon Aaskov 2014-04-26 02:51:22

une autre façon de s'assurer que les voix sont chargées avant que vous en ayez besoin est de lier leur état de chargement à une promesse, puis d'envoyer vos commandes vocales à partir d'un then:

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

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

quand vous appelez listVoices, il attendra que les voix se chargent en premier, ou envoyer votre opération sur le prochain TIC.

2
répondu Jimmy Breck-McKye 2017-05-16 09:51:11

consultez 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 en-GB Google UK Anglais féminin et n-GB Google anglais (royaume-UNI Hommes

FR-GB Daniel (Mac et iOS) en-GB Google UK Anglais Féminin en-GB Google UK Anglais masculin en_GB Anglais Royaume-Uni hi-in Google en Chine hi-IN Lekha (Mac et iOS)) hi_IN Hindi en Inde

2
répondu Jayanta 2018-01-02 07:15:33

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);
  });
}
2
répondu Juste Guipi 2018-05-29 09:30:53

j'ai dû faire ma propre recherche pour cela afin de m'assurer que je l'ai bien compris, donc juste partager (n'hésitez pas à éditer).

Mon but est de:

  • Obtenir une liste des voix disponibles sur mon appareil
  • peupler un élément select avec ces voix (après qu'une page particulière se charge)
  • utiliser un code facile à comprendre

la fonctionnalité de base est démontrée danslive officiel démo de:

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

mais je voulais mieux le comprendre.

pour décomposer le sujet...

SpeechSynthesis

SpeechSynthesis interface API de discours Web est le contrôleur interface pour le service de la parole; cela peut être utilisé pour récupérer informations sur les voix de synthèse disponible sur l'appareil, démarrer et pause discours, et d'autres commandes en plus.

Source

onvoiceschanged

onvoiceschanged propriété de l' SpeechSynthesis interface représente un gestionnaire d'événements qui s'exécute lorsque la liste SpeechSynthesisVoice objets qui seraient renvoyées par le SpeechSynthesis.getVoices() la méthode a changé (lorsque la voiceschanged événement feu.)

Source

Exemple

Si ma demande a simplement:

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

outils de développement Chrome console 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 avant et après sont les mêmes, et voices est un tableau vide.

enter image description here

Solution

Bien que je ne suis pas confiant la mise en œuvre de Promesses, le suivant a 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)

    });

}

Appel de la fonction

// in your handler, populate the select element    
if (page_title === "something") {
set_up_speech()
}
0
répondu user1063287 2018-08-24 13:01:25