Obtendo a lista de vozes em speechSynthesis of Chrome (Web Speech API)

A seguir, o HTML mostra um array vazio no console no primeiro clique:

    function test(){ console.log(window.speechSynthesis.getVoices()) }    Test   

No segundo clique, você receberá a lista esperada.

Se você adicionar o evento onload para chamar essa function ( ), você poderá obter o resultado correto no primeiro clique. Observe que a primeira chamada no onload ainda não funciona corretamente. Ele retorna vazio no carregamento da página, mas funciona depois.

Questões:

Como pode ser um bug na versão beta, desisti das perguntas “Por quê”.

Agora, a questão é se você deseja acessar window.speechSynthesis no carregamento da página:

  • Qual é o melhor hack para esse problema?
  • Como você pode ter certeza de que irá carregar o speechSynthesis , no carregamento da página?

Antecedentes e testes:

Eu estava testando os novos resources do Web Speech API, então cheguei a esse problema no meu código:

  $(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, ...] }); });  click here if 'ready()' didn't work 

Minha pergunta era: por que window.speechSynthesis.getVoices() retorna matriz vazia, depois que a página é carregada e a function onready é acionada? Como você pode ver se você clicar no link, a mesma function retorna uma matriz de vozes disponíveis do Chrome by onclick triger?

Parece Chrome carrega window.speechSynthesis após o carregamento da página!

O problema não está no evento ready . Se eu remover a linha var voice=... da function ready , para o primeiro clique, mostra a lista vazia no console. Mas o segundo clique funciona bem.

Parece que window.speechSynthesis precisa de mais tempo para carregar após a primeira chamada. Você precisa ligar duas vezes! Mas também, você precisa esperar e deixar carregar antes da segunda chamada em window.speechSynthesis . Por exemplo, o código a seguir mostra duas matrizes vazias no console, se você executá-lo pela primeira vez:

 // First speechSynthesis call var voices = window.speechSynthesis.getVoices(); console.log(voices); // Second speechSynthesis call voices = window.speechSynthesis.getVoices(); console.log(voices); 

De acordo com a Errata da API do Web Speech (E11 2013-10-17), a lista de voz é carregada como assíncrona para a página. Um evento onvoiceschanged é triggersdo quando eles são carregados.

voiceschanged: Disparado quando o conteúdo do SpeechSynthesisVoiceList, que o método getVoices retornará, foi alterado. Os exemplos incluem: síntese do lado do servidor em que a lista é determinada de forma assíncrona ou quando as vozes do lado do cliente são instaladas / desinstaladas.

Então, o truque é definir sua voz a partir do retorno de chamada para o ouvinte de evento:

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

Você pode usar um setInterval para esperar até que as vozes sejam carregadas antes de usá-las da forma que você precisar e depois limpar o 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); 

No começo eu usei onvoiceschanged, mas continuou triggersndo mesmo depois que as vozes foram carregadas, então meu objective era evitar onvoiceschanged a todo custo.

Isso é o que eu inventei. Parece funcionar até agora, irá atualizar se quebrar.

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

Primeiramente, muito obrigado por esta resposta. Segundo, aqui está um JSBin útil se alguém se deparar com essa pergunta / resposta novamente: http://jsbin.com/gosaqihi/9/edit?js,console

Outra forma de garantir que as vozes sejam carregadas antes de você precisar delas é vincular seu estado de carregamento a uma promise e, em seguida, despachar seus comandos de fala a partir de then :

 const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done); function listVoices() { awaitVoices.then(()=> { let voices = speechSynthesis.getVoices(); console.log(voices); }); } 

Quando você chamar listVoices , ele aguardará o carregamento das vozes primeiro ou enviará sua operação no próximo tick.

A solução setInterval de Salman Oskooi foi perfeita

Por favor, veja 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 

Isso funciona bem no Chrome para MAC, Linux (Ubuntu), Windows e Android

O Android tem en_GB en-GB enquanto outros têm en-GB como código de idioma Também você verá que o mesmo idioma (lang) tem vários nomes

No Mac Chrome você recebe en-GB Daniel além de en-GB Google UK English Feminino e n-GB Google UK English Masculino

pt-BR Daniel (Mac e iOS) en-GB Google UK Inglês Feminino pt-GB Google UK English Masculino en_GB Inglês Reino Unido hi-IN Google हिन्दी oi-IN Lekha (Mac e iOS) hi_IN Hindi India

heres a resposta

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

Eu tive que fazer minha própria pesquisa para ter certeza que eu entendi corretamente, então apenas compartilhando (fique à vontade para editar).

Meu objective é:

  • Obter uma lista de vozes disponíveis no meu dispositivo
  • Preencher um elemento de seleção com essas vozes (depois que uma determinada página for carregada)
  • Use o código de fácil compreensão

A funcionalidade básica é demonstrada na demonstração ao vivo oficial do MDN de:

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

mas eu queria entender melhor.

Para quebrar o assunto …

Síntese de fala

A interface do SpeechSynthesis da Web Speech API é a interface do controlador para o serviço de fala; isso pode ser usado para recuperar informações sobre as vozes de síntese disponíveis no dispositivo, iniciar e pausar a fala e outros comandos.

Fonte

onvoiceschanged

A propriedade SpeechSynthesis interface do SpeechSynthesis representa um manipulador de events que será executado quando a lista de objects SpeechSynthesisVoice que seriam retornados pelo método SpeechSynthesis.getVoices() foi alterada (quando o evento voiceschanged acionado).

Fonte

Exemplo A

Se o meu pedido apenas tiver:

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

O console de ferramentas do desenvolvedor do Chrome mostrará:

insira a descrição da imagem aqui

Exemplo B

Se eu mudar o código para:

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

Os estados antes e depois são os mesmos e as voices são um array vazio.

insira a descrição da imagem aqui

Solução

Embora eu não esteja confiante em implementar promises , o seguinte funcionou para mim:

Definindo a function

 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 = $(""); 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) }); } 

Chamando a function

 // in your handler, populate the select element if (page_title === "something") { set_up_speech() } 
    Intereting Posts