Criar APK grátis

API da ponte JavaScript

Texto para fala

As vozes do dispositivo, faladas a partir da página.

SpeechRecognition padrão web#

webkitSpeechRecognition / SpeechRecognition — speech to text

Exemplo

A Web Speech API padrão para ditado: o usuário fala e a página recebe o texto. O Chrome no Android a tem e a WebView do Android não, então o app a fornece sobre o reconhecedor de fala do próprio Android. O código escrito para o Chrome funciona sem alterações.

Retorna: start() começa a ouvir; os eventos result trazem event.results[i][0].transcript (e .confidence, .isFinal); os eventos error trazem event.error (not-allowed, no-speech, audio-capture, network, aborted, language-not-supported, service-not-allowed); end sempre vem por último. Requer: o microfone. O build o ativa sozinho quando seu código menciona SpeechRecognition, e o Android pergunta ao usuário na primeira vez que start() roda.

<button id="mic">Dictate</button>
<textarea id="note"></textarea>
<script>
const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
const mic = document.getElementById('mic');
if (!Rec) {
  mic.hidden = true;                       // no speech recogniser on this phone
} else {
  mic.addEventListener('click', () => {
    const rec = new Rec();
    rec.lang = 'en-IN';                    // leave empty for the page / phone language
    rec.interimResults = true;             // live text while the user speaks
    rec.onresult = (e) => {
      let text = '';
      for (const r of e.results) text += r[0].transcript;
      document.getElementById('note').value = text;
    };
    rec.onerror = (e) => {
      if (e.error === 'not-allowed') alert('Allow the microphone to dictate.');
    };
    rec.onend = () => { mic.disabled = false; };
    mic.disabled = true;
    rec.start();
  });
}
</script>

Ditado contínuo que para quando solicitado e informa cada erro:

const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
let rec = null;
function toggleDictation() {
  if (!Rec) return showMessage('Voice typing is not available on this phone.');
  if (rec) { rec.stop(); return; }                   // stop() delivers what was heard
  rec = new Rec();
  rec.continuous = true;
  rec.onresult = (e) => {
    for (let i = e.resultIndex; i < e.results.length; i++) {
      if (e.results[i].isFinal) appendToNote(e.results[i][0].transcript);
    }
  };
  rec.onerror = (e) => {
    const msg = { 'not-allowed': 'Allow the microphone first.', 'no-speech': 'Nothing was heard.',
                  'audio-capture': 'No microphone found.', 'network': 'Speech service unreachable.' }[e.error];
    if (msg) showMessage(msg);
  };
  rec.onend = () => { rec = null; };
  rec.start();
}

Diga um comando e execute-o, no idioma do celular:

const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
function listenForCommand() {
  if (!Rec) return;
  const rec = new Rec();
  rec.lang = navigator.language;
  rec.onresult = (e) => {
    const said = e.results[0][0].transcript.toLowerCase();
    if (said.includes('next')) goNext();
    else if (said.includes('back')) goBack();
    else showMessage('Heard: ' + said);
  };
  rec.onerror = (e) => {
    if (e.error === 'language-not-supported') showMessage('This language is not supported here.');
  };
  rec.start();
}

Observações: continuous = true continua ouvindo entre frases até você chamar stop(); depois de três rodadas em silêncio, termina com no-speech, para que um reconhecedor esquecido não fique ouvindo para sempre. stop() entrega o que foi ouvido; abort() descarta. O reconhecimento para quando o app vai para segundo plano, como acontece quando uma aba do Chrome fica oculta. Somente páginas que pertencem ao app (seus próprios arquivos ou o site do criador) podem usar o microfone dessa forma; qualquer outro site recebe not-allowed. A API não existe quando o celular não tem serviço de reconhecimento de fala, então verifique como acima. Transportes internos: __srStart, __srStop, __srAbort, __srAvailable; os resultados chegam como appmint:sr. Apps criados com IA usam speech.start() de @/lib/appmintNative.

speechSynthesis padrão web#

window.speechSynthesis + SpeechSynthesisUtterance

Exemplo

Lê texto em voz alta com o mecanismo de texto para fala do próprio celular, usando a Web Speech API normal.

Retorna: speechSynthesis.speak(u) não retorna nada; os eventos start / boundary / end / error / pause / resume da fala (como propriedades onstart … ou com addEventListener) informam o que aconteceu. getVoices() retorna um array de { voiceURI, name, lang, localService, default } de forma síncrona. Requer: nada. A WebView do Android não tem uma API de fala funcional própria; o app instala esta no início do documento em todos os frames.

Fale uma frase e saiba quando terminou:

function say(text) {
  if (!('speechSynthesis' in window)) { alert('Speech is not available here'); return; }
  var u = new SpeechSynthesisUtterance(text);
  u.lang = 'en-US';   // BCP-47 tag: 'ta-IN', 'hi-IN', 'fr-FR' ...
  u.rate = 1.2;       // 0.1 .. 4 (1 = normal)
  u.pitch = 1;        // 0.1 .. 4 (1 = normal)
  u.onstart = function () { document.body.classList.add('speaking'); };
  u.onend = function () { document.body.classList.remove('speaking'); };
  u.onerror = function (e) {
    document.body.classList.remove('speaking');
    console.log('Speech failed:', e.error);   // 'synthesis-failed'
  };
  speechSynthesis.speak(u);   // queues after anything already speaking
}

document.getElementById('stop').onclick = function () {
  if ('speechSynthesis' in window) speechSynthesis.cancel();   // stops now, clears the queue
};

As vozes chegam um instante depois da inicialização. Leia-as de novo quando voiceschanged disparar:

var voices = [];
function loadVoices() {
  voices = speechSynthesis.getVoices();   // [] until the engine is ready
  var tamil = voices.filter(function (v) { return v.lang.indexOf('ta') === 0; });
  document.getElementById('tamil-btn').hidden = tamil.length === 0;
}
if ('speechSynthesis' in window) {
  loadVoices();
  speechSynthesis.addEventListener('voiceschanged', loadVoices);
}

function sayWithVoice(text, voice) {
  var u = new SpeechSynthesisUtterance(text);
  u.voice = voice;           // a voice from getVoices(): that exact voice is used
  u.volume = 0.8;            // 0 .. 1
  speechSynthesis.speak(u);
}

Um leitor de artigos com um único botão Pausar / Continuar que destaca o parágrafo que está sendo lido:

var toggle = document.getElementById('pause-toggle');

toggle.onclick = function () {
  if (!('speechSynthesis' in window) || !speechSynthesis.speaking) return;
  if (speechSynthesis.paused) speechSynthesis.resume();   // continues from the last word
  else speechSynthesis.pause();
};

function readArticle() {
  if (!('speechSynthesis' in window)) return;
  speechSynthesis.cancel();                               // start over
  document.querySelectorAll('article p').forEach(function (p) {
    var u = new SpeechSynthesisUtterance(p.textContent);
    u.onstart = function () { p.classList.add('reading'); };
    u.onend = u.onerror = function () { p.classList.remove('reading'); };
    u.onpause = function () { toggle.textContent = 'Resume'; };
    u.onresume = function () { toggle.textContent = 'Pause'; };
    speechSynthesis.speak(u);                             // paragraphs queue in order
  });
  toggle.textContent = 'Pause';
}

Observaçþes:

  • volume (0-1), voice (a voz exata de getVoices()), rate, pitch e lang chegam todos ao mecanismo do celular. Com um voice e um lang, a voz prevalece; sem nenhum dos dois, o mecanismo fala com a voz padrão.
  • boundary dispara antes de cada palavra com charIndex / charLength (Android 8+; mecanismos que não informam posições de palavras não enviam nada).
  • pause() / resume(): O mecanismo do Android não consegue pausar no meio de uma palavra, então pause() o interrompe e resume() fala o resto da frase a partir da última palavra informada pelo mecanismo (os eventos pause / resume disparam). Em um mecanismo que não informa posições de palavras, resume() inicia essa fala de novo desde o começo. speaking continua true enquanto pausado; pending é true enquanto há falas na fila aguardando.
  • cancel() dispara error com error: 'interrupted' na fala que está sendo dita e 'canceled' nas que estão na fila, como diz o padrão: não os mostre como falhas.
  • Se o celular não tiver dados de voz para o idioma, o mecanismo ainda pode falar no idioma padrão dele. Verifique getVoices() para ver o idioma antes.
  • Os eventos brutos por trás disso são appmint:tts (veja ttsSpeak). Você não precisa deles ao usar esta API.

__srAbort ponte#

window.WebToApk.__srAbort(id: String)

SpeechRecognition.abort(): para na hora e descarta.

Exemplo

Documentado junto com WebToApk.__srStart - o exemplo de lá mostra este também.

Transporte interno por trás de webkitSpeechRecognition / SpeechRecognition (iniciar, parar, abortar e saber se existe um reconhecedor no celular). As páginas usam a API padrão; nunca chame estes diretamente, porque os métodos __ podem mudar sem aviso.

Retorna: __srStart(id, options) retorna "" quando a escuta começa, ou um nome de erro da Web Speech (not-allowed, service-not-allowed). Tudo depois disso chega como eventos appmint:sr: {id, type} para start/audiostart/speechstart/speechend/audioend/end, {id, type:'result', final, alternatives:[{transcript, confidence}]} e {id, type:'error', error}. Requer: o microfone (armado a partir do formato da chamada).

Use a API pública:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

Ditado contínuo com texto ao vivo e um idioma:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

Informe erros como um microfone recusado:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

Observações: Implementado no shell do app (SpeechRecognitionBridge.kt) sobre o SpeechRecognizer do Android. __srStart é recusado para páginas que não pertencem ao app.

__srAvailable ponte#

window.WebToApk.__srAvailable(): Boolean

Se um serviço de reconhecimento de fala está instalado (webkitSpeechRecognition só existe nesse caso).

Exemplo

Documentado junto com WebToApk.__srStart - o exemplo de lá mostra este também.

Transporte interno por trás de webkitSpeechRecognition / SpeechRecognition (iniciar, parar, abortar e saber se existe um reconhecedor no celular). As páginas usam a API padrão; nunca chame estes diretamente, porque os métodos __ podem mudar sem aviso.

Retorna: __srStart(id, options) retorna "" quando a escuta começa, ou um nome de erro da Web Speech (not-allowed, service-not-allowed). Tudo depois disso chega como eventos appmint:sr: {id, type} para start/audiostart/speechstart/speechend/audioend/end, {id, type:'result', final, alternatives:[{transcript, confidence}]} e {id, type:'error', error}. Requer: o microfone (armado a partir do formato da chamada).

Use a API pública:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

Ditado contínuo com texto ao vivo e um idioma:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

Informe erros como um microfone recusado:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

Observações: Implementado no shell do app (SpeechRecognitionBridge.kt) sobre o SpeechRecognizer do Android. __srStart é recusado para páginas que não pertencem ao app.

__srStart ponte#

window.WebToApk.__srStart(id: String, optionsJson: String): String

SpeechRecognition.start(): "" ao iniciar (os eventos chegam como `appmint:sr`), ou o nome de erro da Web Speech. Com verificação de origem: o que o usuário diz são dados do usuário.

Exemplo

Transporte interno por trás de webkitSpeechRecognition / SpeechRecognition (iniciar, parar, abortar e saber se existe um reconhecedor no celular). As páginas usam a API padrão; nunca chame estes diretamente, porque os métodos __ podem mudar sem aviso.

Retorna: __srStart(id, options) retorna "" quando a escuta começa, ou um nome de erro da Web Speech (not-allowed, service-not-allowed). Tudo depois disso chega como eventos appmint:sr: {id, type} para start/audiostart/speechstart/speechend/audioend/end, {id, type:'result', final, alternatives:[{transcript, confidence}]} e {id, type:'error', error}. Requer: o microfone (armado a partir do formato da chamada).

Use a API pública:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

Ditado contínuo com texto ao vivo e um idioma:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

Informe erros como um microfone recusado:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

Observações: Implementado no shell do app (SpeechRecognitionBridge.kt) sobre o SpeechRecognizer do Android. __srStart é recusado para páginas que não pertencem ao app.

__srStop ponte#

window.WebToApk.__srStop(id: String)

SpeechRecognition.stop(): para de ouvir e entrega o que foi ouvido.

Exemplo

Documentado junto com WebToApk.__srStart - o exemplo de lá mostra este também.

Transporte interno por trás de webkitSpeechRecognition / SpeechRecognition (iniciar, parar, abortar e saber se existe um reconhecedor no celular). As páginas usam a API padrão; nunca chame estes diretamente, porque os métodos __ podem mudar sem aviso.

Retorna: __srStart(id, options) retorna "" quando a escuta começa, ou um nome de erro da Web Speech (not-allowed, service-not-allowed). Tudo depois disso chega como eventos appmint:sr: {id, type} para start/audiostart/speechstart/speechend/audioend/end, {id, type:'result', final, alternatives:[{transcript, confidence}]} e {id, type:'error', error}. Requer: o microfone (armado a partir do formato da chamada).

Use a API pública:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

Ditado contínuo com texto ao vivo e um idioma:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

Informe erros como um microfone recusado:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

Observações: Implementado no shell do app (SpeechRecognitionBridge.kt) sobre o SpeechRecognizer do Android. __srStart é recusado para páginas que não pertencem ao app.

__ttsSpeak ponte#

window.WebToApk.__ttsSpeak(json: String)

O speak completo do polyfill de speechSynthesis: `{text, lang, rate, pitch, volume (0-1), voice (um nome de getVoices(), ou ''), id}`. [ttsSpeak] continua existindo para páginas que chamam a ponte diretamente; não tem volume nem voz.

Exemplo

Transporte interno por trás de speechSynthesis.speak(utterance): a fala completa (texto, idioma, velocidade, tom, volume e a voz escolhida) como uma única string JSON. As páginas usam a API padrão speechSynthesis; nunca chame isto diretamente.

Retorna: nada. O progresso chega como eventos appmint:tts, que a camada speechSynthesis transforma nos eventos start / boundary / end / error da fala. Requer: nada.

A API padrão que isto oferece:

function readAloud(text) {
  if (!('speechSynthesis' in window)) return;
  var voice = speechSynthesis.getVoices().filter(function (v) { return v.lang === 'en-GB'; })[0];
  var u = new SpeechSynthesisUtterance(text);
  if (voice) u.voice = voice;   // that exact voice
  u.volume = 0.6;               // 0 .. 1
  u.addEventListener('boundary', function (e) { highlightWordAt(e.charIndex, e.charLength); });
  u.addEventListener('end', function () { clearHighlight(); });
  speechSynthesis.speak(u);
}

Um seletor de voz que o usuário escolhe uma vez; toda fala seguinte leva exatamente essa voz:

var picker = document.getElementById('voice-picker');

function listVoices() {
  var saved = localStorage.getItem('voiceName') || '';
  picker.innerHTML = '';
  speechSynthesis.getVoices().forEach(function (v) {
    picker.add(new Option(v.name + ' (' + v.lang + ')', v.name, false, v.name === saved));
  });
}
if ('speechSynthesis' in window) {
  listVoices();                                   // [] until the engine is ready
  speechSynthesis.onvoiceschanged = listVoices;
}
picker.onchange = function () { localStorage.setItem('voiceName', picker.value); };

function speakWithSavedVoice(text) {
  var name = localStorage.getItem('voiceName');
  var voice = speechSynthesis.getVoices().filter(function (v) { return v.name === name; })[0];
  var u = new SpeechSynthesisUtterance(text);
  if (voice) u.voice = voice;                     // its language is used too when u.lang is empty
  speechSynthesis.speak(u);
}

Avisos de treino: baixos e rápidos para contagens regressivas, em volume total e mais agudos para a troca:

function cue(text, loud) {
  if (!('speechSynthesis' in window)) return;
  var u = new SpeechSynthesisUtterance(text);
  u.lang = 'en-US';
  u.rate = 1.4;                  // 0.1 .. 4
  u.pitch = loud ? 1.3 : 1;      // 0.1 .. 4
  u.volume = loud ? 1 : 0.4;     // 0 .. 1
  u.onerror = function (e) {
    if (e.error === 'synthesis-failed') console.log('Cue not spoken:', text);
  };
  speechSynthesis.speak(u);      // queues after the previous cue
}

cue('Ten seconds left', false);
cue('Switch sides!', true);

Observações: O JSON é {text, lang, rate, pitch, volume, voice, id} (voice é um nome de getVoices() ou ''). O ttsSpeak(text, lang, rate, pitch, id) mais antigo continua existindo para páginas que chamam a ponte diretamente; não tem volume nem voz.

ttsCancel ponte#

window.WebToApk.ttsCancel()

Dá suporte ao polyfill window.speechSynthesis, que é instalado no início do documento em todos os frames: as páginas devem usar a API PADRÃO speechSynthesis, e estes serão chamados por elas. A WebView do Android não traz um speechSynthesis funcional próprio, e é por isso que existe esse redirecionamento.

Descrito por seu grupo, Texto para fala, e não separadamente.

Exemplo

Interrompe a fala agora e descarta tudo o que ainda aguardava para ser falado. As páginas normalmente chamam o speechSynthesis.cancel() padrão, que chama isto por você.

Retorna: nada. Requer: nada.

A forma pública (use esta):

document.getElementById('stop-reading').addEventListener('click', function () {
  if ('speechSynthesis' in window) speechSynthesis.cancel();
});

A chamada bruta:

if (window.WebToApk && typeof window.WebToApk.ttsCancel === 'function') {
  window.WebToApk.ttsCancel();
}

Diga apenas o status mais recente e descarte qualquer coisa mais antiga ainda na fila (um rastreador de entrega, um placar ao vivo):

function announceLatest(text) {
  if (!('speechSynthesis' in window)) return;
  speechSynthesis.cancel();   // the old line gets error 'interrupted', queued ones 'canceled'
  var u = new SpeechSynthesisUtterance(text);
  u.onerror = function (e) {
    if (e.error === 'interrupted' || e.error === 'canceled') return;   // replaced, not a failure
    showToast('Could not read that aloud');
  };
  speechSynthesis.speak(u);
}

announceLatest('Your driver is 5 minutes away');
announceLatest('Your driver has arrived');   // cuts the first one off

Observações: Uma fala interrompida ou descartada não envia o evento appmint:tts end nem error, então o onend de uma fala não roda depois de um cancelamento. Se você acompanha ids brutos de ttsSpeak por conta própria, esqueça-os ao chamar ttsCancel().

ttsGetVoices ponte#

window.WebToApk.ttsGetVoices(): String

Array JSON das vozes instaladas: [{name, lang, default}]. Vazio até o mecanismo terminar de iniciar: a página recebe 'voiceschanged' nesse momento e consulta de novo, exatamente como no contrato assíncrono de getVoices() do Chrome.

Exemplo

Lista as vozes de texto para fala instaladas no celular. As páginas normalmente usam o speechSynthesis.getVoices() padrão, que chama isto por você.

Retorna: uma string JSON, de forma síncrona: [{ "name": "en-us-x-sfg-local", "lang": "en-US", "default": false }, ...]. É "[]" até o mecanismo iniciar. Requer: nada.

A forma pública (use esta):

function fillLanguages() {
  var langs = {};
  speechSynthesis.getVoices().forEach(function (v) { langs[v.lang] = true; });
  document.getElementById('langs').textContent = Object.keys(langs).join(', ');
}
if ('speechSynthesis' in window) {
  fillLanguages();
  speechSynthesis.onvoiceschanged = fillLanguages;
}

A chamada direta (lembre-se JSON.parse):

function nativeVoices() {
  if (!(window.WebToApk && typeof window.WebToApk.ttsGetVoices === 'function')) return [];
  try { return JSON.parse(window.WebToApk.ttsGetVoices()); } catch (e) { return []; }
}

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') {
    var hindi = nativeVoices().filter(function (v) { return v.lang.indexOf('hi') === 0; });
    document.getElementById('hindi-btn').hidden = hindi.length === 0;
  }
});

Fale no idioma do celular quando houver uma voz instalada para ele; caso contrário, deixe o mecanismo usar a voz padrão:

function bestSpeechLang() {
  if (!(window.WebToApk && typeof window.WebToApk.ttsGetVoices === 'function')) return '';
  var voices;
  try { voices = JSON.parse(window.WebToApk.ttsGetVoices()); } catch (e) { return ''; }
  var want = navigator.language || 'en-US';            // e.g. 'ta-IN'
  var exact = voices.filter(function (v) { return v.lang === want; })[0];
  if (exact) return exact.lang;
  var base = want.split('-')[0];
  var near = voices.filter(function (v) { return v.lang.split('-')[0] === base; })[0];
  return near ? near.lang : '';                        // '' = the engine's default voice
}

if (window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function') {
  window.WebToApk.ttsSpeak(greetingText(), bestSpeechLang(), 1.0, 1.0, 'greeting');
}

Observações: lang é uma tag BCP-47 como en-US ou ta-IN. O name de uma voz é o id do próprio mecanismo e não pode ser escolhido pelo nome ao falar; só o idioma dela é usado.

ttsIsSpeaking ponte#

window.WebToApk.ttsIsSpeaking(): Boolean

Dá suporte ao polyfill window.speechSynthesis, que é instalado no início do documento em todos os frames: as páginas devem usar a API PADRÃO speechSynthesis, e estes serão chamados por elas. A WebView do Android não traz um speechSynthesis funcional próprio, e é por isso que existe esse redirecionamento.

Descrito por seu grupo, Texto para fala, e não separadamente.

Exemplo

Informa se o mecanismo de texto para fala do celular está falando neste momento. As páginas normalmente usam speechSynthesis.speaking ou o onend da fala em vez disso.

Retorna: true / false, de forma síncrona. false antes de o mecanismo iniciar. Requer: nada.

A forma pública (use esta):

if ('speechSynthesis' in window && speechSynthesis.speaking) {
  speechSynthesis.cancel();
}

A chamada direta consulta o próprio mecanismo, então é exata mesmo que a fala tenha sido iniciada com a chamada direta ttsSpeak:

function isSpeaking() {
  if (window.WebToApk && typeof window.WebToApk.ttsIsSpeaking === 'function') {
    return window.WebToApk.ttsIsSpeaking() === true;
  }
  return false;
}

var btn = document.getElementById('read-btn');
setInterval(function () {
  btn.textContent = isSpeaking() ? 'Stop' : 'Read aloud';
}, 500);

Espere o app parar de falar antes de começar a gravar, para que o microfone não capte a própria voz dele:

function whenQuiet(fn) {
  if (!(window.WebToApk && typeof window.WebToApk.ttsIsSpeaking === 'function')) { fn(); return; }
  (function check() {
    if (window.WebToApk.ttsIsSpeaking()) setTimeout(check, 300);
    else fn();
  })();
}

document.getElementById('answer-btn').addEventListener('click', function () {
  whenQuiet(startRecordingAnswer);   // e.g. after the question was read aloud
});

Observações: Não consulte rápido demais; um evento (onend, ou appmint:tts com event: 'end') é melhor quando você mesmo iniciou a fala.

ttsSpeak ponte#

window.WebToApk.ttsSpeak(text: String, lang: String, rate: Double, pitch: Double, utteranceId: String)

Dá suporte ao polyfill window.speechSynthesis, que é instalado no início do documento em todos os frames: as páginas devem usar a API PADRÃO speechSynthesis, e estes serão chamados por elas. A WebView do Android não traz um speechSynthesis funcional próprio, e é por isso que existe esse redirecionamento.

Descrito por seu grupo, Texto para fala, e não separadamente.

Exemplo

Começa a falar o texto com o mecanismo de texto para fala do celular. As páginas normalmente usam a API speechSynthesis padrão, que chama isto por você.

Retorna: nada. O progresso chega depois como o evento appmint:tts (e o hook window.onAppMintTts(detail)) com detail = { id, event }, em que event é 'start', 'end' ou 'error', e id é o utteranceId que você passou. Requer: nada.

A forma pública (use esta):

if ('speechSynthesis' in window) {
  var u = new SpeechSynthesisUtterance('Your order is ready');
  u.lang = 'en-GB';
  u.onend = function () { console.log('done'); };
  speechSynthesis.speak(u);
}

A chamada direta, se você quiser seus próprios ids. Argumentos: text, lang, rate, pitch, utteranceId:

var ttsWaiting = {};

window.addEventListener('appmint:tts', function (e) {
  var d = e.detail || {};
  if (d.event === 'voiceschanged') return;        // engine ready, id is ''
  var done = ttsWaiting[d.id];
  if (!done || d.event === 'start') return;
  delete ttsWaiting[d.id];
  done(d.event === 'end');                         // 'end' = spoke it, 'error' = failed
});

function speakRaw(text) {
  return new Promise(function (resolve) {
    if (!(window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function')) { resolve(false); return; }
    var id = 'mine_' + Date.now();
    ttsWaiting[id] = resolve;
    window.WebToApk.ttsSpeak(String(text), 'en-US', 1.0, 1.0, id);
  });
}

speakRaw('Hello').then(function (ok) { console.log(ok ? 'spoken' : 'could not speak'); });

Leia uma lista de etapas em ordem com seus próprios ids e destaque a etapa que está sendo falada:

var steps = Array.prototype.slice.call(document.querySelectorAll('.recipe-step'));

window.addEventListener('appmint:tts', function (e) {
  var d = e.detail || {};
  var m = /^recipe_(\d+)$/.exec(d.id || '');
  if (!m) return;                                  // not one of ours
  var el = steps[Number(m[1])];
  if (d.event === 'start') el.classList.add('reading');
  if (d.event === 'end' || d.event === 'error') el.classList.remove('reading');
});

document.getElementById('read-recipe').addEventListener('click', function () {
  if (!(window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function')) return;
  steps.forEach(function (el, i) {
    // '' = the engine's default voice; 0.9 = a little slower for cooking
    window.WebToApk.ttsSpeak(el.textContent, '', 0.9, 1.0, 'recipe_' + i);
  });
});

Observações: As falas entram em fila, uma após a outra. lang pode ser '' (a voz padrão do mecanismo); rate e pitch são limitados a 0,1-4. Se o mecanismo ainda está iniciando, o texto espera e é falado quando ele estiver pronto; se o mecanismo não conseguir iniciar, cada fala em espera recebe 'error'. Use seu próprio prefixo de id; ids que começam com wta_u pertencem à camada speechSynthesis. No Android 8+, appmint:tts também traz {id, event: 'boundary', charIndex, charLength} antes de cada palavra. Esta chamada direta não tem volume nem voz: speechSynthesis tem ambos.

ttsWarmUp ponte#

window.WebToApk.ttsWarmUp()

Dá suporte ao polyfill window.speechSynthesis, que é instalado no início do documento em todos os frames: as páginas devem usar a API PADRÃO speechSynthesis, e estes serão chamados por elas. A WebView do Android não traz um speechSynthesis funcional próprio, e é por isso que existe esse redirecionamento.

Descrito por seu grupo, Texto para fala, e não separadamente.

Exemplo

Inicia cedo o mecanismo de texto para fala do celular, para que o primeiro speak() não tenha atraso e getVoices() seja preenchido. A camada speechSynthesis do app já chama isto no início da página, então as páginas quase nunca precisam dele.

Retorna: nada. Quando o mecanismo está pronto, o evento appmint:tts dispara com detail = { id: '', event: 'voiceschanged' } (e speechSynthesis.onvoiceschanged roda). Requer: nada.

O jeito público (use este): basta ler as vozes quando estiverem prontas.

if ('speechSynthesis' in window) {
  speechSynthesis.onvoiceschanged = function () {
    console.log('voices:', speechSynthesis.getVoices().length);
  };
}

A chamada direta e depois espere o evento de pronto:

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') {
    var list = JSON.parse(window.WebToApk.ttsGetVoices());
    console.log('engine ready,', list.length, 'voices');
  }
});

if (window.WebToApk && typeof window.WebToApk.ttsWarmUp === 'function') {
  window.WebToApk.ttsWarmUp();   // safe to call many times
}

Inicie o mecanismo quando uma tela "Ler em voz alta" abrir e ative o botão dela quando o mecanismo estiver pronto:

var readBtn = document.getElementById('read-aloud');

function engineReady() {
  try { return JSON.parse(window.WebToApk.ttsGetVoices()).length > 0; } catch (e) { return false; }
}

function openReaderScreen() {
  showReaderScreen();
  if (!(window.WebToApk && typeof window.WebToApk.ttsWarmUp === 'function')) return;
  readBtn.disabled = !engineReady();       // already started earlier? enable right away
  window.WebToApk.ttsWarmUp();             // no-op when the engine is already running
}

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') readBtn.disabled = false;
});

Observações: voiceschanged dispara uma vez, quando o mecanismo termina de iniciar. Se ele já iniciou (por exemplo, porque a camada speechSynthesis o aqueceu), você não o receberá de novo; chame ttsGetVoices() diretamente.

Gerado a partir do runtime do app e de seus arquivos de exemplo a cada build da documentação. Leia em Markdown · Todas as famílias.

Conferido com a ponte atual em 22/10/2026.