Convierte voz a texto con Javascript. La otra API speechSynthesis

En este documento vamos a conocer la otra cara de speechSynthesis, SpeechRecognition, que nos va a permitir convertir la voz de nuestros visitantes en texto a la hora de usar formularios, evitando que el usuario tenga que escribir.
Convierte voz a texto con Javascript. La otra API speechSynthesis

Hace poco hablábamos de cómo convertir un texto a viva voz usando tan solo un navegador moderno y algo de Javascript. En este artículo vamos a hacer justo lo contrario: convertir voz a texto utilizando únicamente Javascript. Esto es realmente útil para ofrecer accesibilidad al usuario o evitar que el usuario tenga que escribir textos largos.

Los navegadores modernos implementan la API Web Speech, pero esta está dividida en dos interfaces, independiente la una de la otra. La primera es la SpeechSynthesis de la que ya hablamos en otro artículo y que permite convertir texto a voz. La que vamos a ver hoy es la interfaz SpeechRecognition que permite capturar, a través del micrófono, voz humana y convertirla a una cadena de texto.

Suena bastante bien, pero tiene una parte mala: no es una API soportada por todas las familias de navegadores. De hecho, según CanIUse, esta API solo está disponible principalmente para los navegadores Firefox, Chrome y Safari y con un porcentaje de uso de poco más del 89%. Si esto no es un impedimento, ¡vamos allá !

En algunos navegadores como Chrome, el uso de Speech Recognition implica que el audio se procesa en servidores externos, por lo que no funcionará en modo offline o sin conexión.

Configurar la gramática de nuestro reconocimiento de voz

Como se ha mencionado anteriormente, esta API no está realmente extendida, por lo que cada navegador la implementa como quiere. Este es el caso de Chrome, que lo implementa usando el prefijo webkit-. Por ello, antes de nada, vamos a crear un código que permita utilizar la API en su versión sin prefijo o, si no se encuentra, con prefijo. Esto hará que funcione en un futuro, cuando se le retire el prefijo.

var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
var SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList
var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent

var recognition = new SpeechRecognition(); //<-- Reconocimiento de voz

Hecho esto, vamos a comenzar con la gramática. Tenemos que decirle al navegador en qué palabras se tiene que centrar. Podemos tomar dos opciones:

  • Si es un cuadro de texto genérico, donde el usuario puede escribir (o dictar) literalmente cualquier cosa, utilizaremos la gramática que viene por defecto, que generalmente comprende la mayoría de palabras de un idioma.

  • Si, por el contrario, el usuario tiene que limitarse a una serie de palabras, como por ejemplo colores, debemos limitar la lista de palabras que el navegador debería "admitir". Es aquí cuando establecemos nuestra propia gramática.

Si has decidido que el navegador puede escuchar cualquier cosa, pasa al punto siguiente. Si, por el contrario, necesitas especificar la lista de palabras, sigue leyendo.

Crearemos una gramática concreta que incluye colores. De esta forma, el navegador se centrará en dicha lista de palabras, dando de lado al resto y ofreciendo resultados más precisos.

var colors = [ 'aqua' , 'azure' , 'beige', 'bisque', 'black', 'blue', 'brown', 'chocolate', 'coral' ... ];
var grammar = '#JSGF V1.0; grammar colors; public <color> = ' + colors.join(' | ') + ' ;'

El array de colors contiene las palabras admitidas y la variable grammar contiene la definición de la gramática, que está en formato JSpeech Grammar Format. Una vez definida, tenemos que conectarla a nuestro reconocimiento de voz en Javascript ofrecido por SpeechRecognition.

Lo haremos con la función addFromString(gramática, importancia). El primer parámetro define la gramática que queremos utilizar; el segundo especifica un número decimal entre 0 y 1 (inclusive), que determina la importancia de esta gramática frente al resto de gramáticas disponibles.

var speechRecognitionList = new SpeechGrammarList();
speechRecognitionList.addFromString(grammar, 1);
recognition.grammars = speechRecognitionList;

Establezcamos las opciones del reconocimiento

Establecer la gramática es solo una de las opciones de personalización que nos ofrece la API de SpeechRecognition, pero no es la única. Gracias al resto de propiedades podemos moldear el funcionamiento en base a nuestras necesidades. A continuación se definen qué otras propiedades son realmente útiles a la hora de personalizar nuestro reconocimiento de voz.

  • SpeechRecognition.continuous: Determina si solo se debe capturar un solo resultado (false) o, por el contrario, si se deben capturar resultados de forma continua (true). Si se establece a true, el reconocimiento seguirá escuchando de forma infinita entregando un solo resultado largo.
  • SpeechRecognition.lang: Determina el idioma en del reconociminiento. Deberías establecerlo para unos mejores resultados.
  • SpeechRecognition.interimResults: Mientras se está procesando el audio, se producen resultados temporales. Gracias a esta propiedad, determinamos si el reconocimiento debe devolver resultados provisionales o no.
  • SpeechRecognition.maxAlternatives: Determina el número de posibles coincidencias que se deben devolver. Es útil si un resultado no está claro y desea mostrar alternativas para que el usuario elija la correcta.
recognition.continuous = false;
recognition.lang = 'es-ES';
recognition.interimResults = false;
recognition.maxAlternatives = 1;

Comenzando a escuchar con el reconocimiento de voz

Ya tenemos el reconocimiento de voz preparado, configurado y personalizado; solo falta indicarle que empiece a escuchar y eso lo conseguimos con el siguiente código

recognition.start();

Sin embargo, ten en cuenta que antes de empezar a escuchar realmente, y por política de privacidad, el navegador preguntará al usuario si autoriza a que la web use el micrófono. En cualquier caso, es un proceso transparente para el código: si el usuario no concede permiso se tirará una excepción que deberíamos manejar.

Recogiendo resultados

Existen un montón de eventos asociados a la API de SpeechRecognition, pero sin duda, los más útiles son onspeechend y onresult. El primero se detona cuando se ha reconocido un texto y se ha finalizado de hablar. El segundo se detona cuando el reconocimiento de voz tiene algún resultado.

recognition.onspeechend = function() {
  recognition.stop();
}

recognition.onresult = function(event) {
  var color = event.results[0][0].transcript;
  var confianza = event.results[0][0].confidence;
  console.log('Estoy seguro al ' + confianza + ' % que dijo ' + color)
}

Generalmente, el reconocimiento de voz devuelve lo que cree que ha escuchado (transcript) y el porcentaje de confiaza; o dicho de otra forma, cuánto de seguro está de lo que ha escuchado (confidence). En este caso y como especificamos colores en la gramática, estamos seguros de que el reconocimiento de voz devolverá un color (o un error si no entendió nada, pero eso lo veremos después).

Otros eventos y manejando errores del reconocimiento

Existen otros tres eventos que requieren una mención especial: onstart, onnomatch y onerror. Vamos a verlos en detenimiento utilizando el siguiente código (pero como te imaginas, el primero es cuando comienza el reconocimiento, el segundo cuando no hay coincidencia y el tercero es cuando hay un error)

recognition.onstart = function() {
    // Se activó el reconocimiento. Añadir algún elemento a la UI
    //  para que el usuario sepa que le está oyendo.
}

recognition.onnomatch = function(event) {
    // No hay coincidencia entre lo escuchado y la gramática
    //  Deberá hablar más claro o deberemos añadir palabras a la gramática.
}

recognition.onerror = function(event) {
    if(event.error == 'no-speech') {
        // No he escuchado nada. Mejora la calidad de la grabación.
    } else {
        // Error gen
    }
}

Conclusión

Si bien el uso de este sistema suele ofrecer buenos resultados, depende mucho del navegador del usuario, pues se trata de una API no muy extendida. Por lo general, se suelen utilizar servicios en la nube que tratan de identificar lo que ha dicho el usuario. Pero si la compatibilidad no es problema o no quieres invertir mucho dinero, esta API merece una mención especial sobre todo para la accesibilidad y para la comodidad del usuario.

Hace años, esta API habría sido un sueño inalcanzable. Hemos convertido voz humana a texto utilizando tan solo Javascript y un navegador, además de hacerlo sin pagar. Pero sí, el futuro ha llegado. Espero que te haya picado la curiosidad y que te animes a probar esta fantástica feature en tu próximo proyecto.

¡Qué tengas un feliz coding!