Síntesis y reconocimiento del habla

En esta sesión continuamos examinando las capacidades multimedia de Android presentando el sintetizador de voz Text to Speech, el cual permitirá que una actividad reproduzca por los altavoces la lectura de un determinado texto. Se trata de un componente relativamente sencillo de utilizar que puede mejorar la accesibilidad de nuestras aplicaciones en gran medida.

Sintetizador de voz de Android

Android incorpora desde la versión 1.6 un motor de síntesis de voz conocido como Text to Speech. Mediante su API podremos hacer que nuestros programas "lean" un texto al usuario. Es necesario tener en cuenta que por motivos de espacio en disco los paquetes de lenguaje pueden no estar instalados en el dispositivo. Por lo tanto, antes de que nuestra aplicación utilice Text to Speech se podría considerar una buena práctica de programación el comprobar si dichos paquetes están instalados. Para ello podemos hacer uso de un Intent como el que se muestra a continuación:

Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(intent, TTS_DATA_CHECK);

El método onActivityResult() recibirá un CHECK_VOICE_DATA_PASS si todo está correctamente instalado. En caso contrario deberemos iniciar una nueva actividad por medio de un nuevo Intent implícito que haga uso de la acción ACTION_INSTALL_TTS_DATA del motor Text to Speech.

Una vez comprobemos que todo está instalado deberemos crear e inicializar una instancia de la clase TextToSpeech. Como no podemos utilizar dicha instancia hasta que esté inicializada (la inicialización se hace de forma asíncrona), la mejor opción es pasar como parámetro al constructor un manejador onInitListener de tal forma que en dicho método se especifiquen las tareas a llevar a cabo por el sintetizador de voz una vez esté inicializado.

boolean ttsIsInit = false;
TextToSpeech tts = null;

tts = new TextToSpeech(this, new OnInitListener() {
    public void onInit(int status) {
        if (status == TextToSpeech.SUCCESS) {
            ttsIsInit = true;
            // Hablar
        }
    }
});

Una vez que la instancia esté inicializada se puede utilizar el método speak para sintetizar voz por medio del dispositivo de salida por defecto. El primer parámetro será el texto a sintetizar y el segundo podrá ser o bien QUEUE_ADD, que añade una nueva salida de voz a la cola, o bien QUEUE_FLUSH, que elimina todo lo que hubiera en la cola y lo sustituye por el nuevo texto.

tts.speak("Hello, Android", TextToSpeech.QUEUE_ADD, null);

Otros métodos de interés de la clase TextToSpeech son:

  • setPitch y setSpeechRate permiten modificar el tono de voz y la velocidad. Ambos métodos aceptan un parámetro real.

  • setLanguage permite modificar la pronunciación. Se le debe pasar como parámetro una instancia de la clase Locale para indicar el país y la lengua a

    utilizar.

  • El método stop se debe utilizar al terminar de hablar; este método detiene la síntesis de voz.

  • El método shutdown permite liberar los recursos reservados por el motor de Text to Speech.

El siguiente código muestra un ejemplo en el que se comprueba si todo está correctamente instalado, se inicializa una nueva instancia de la clase TextToSpeech, y se utiliza dicha clase para decir una frase en español. Al llamar al método initTextToSpeech se desencadenará todo el proceso.

private static int TTS_DATA_CHECK = 1;
private TextToSpeech tts = null;
private boolean ttsIsInit = false;

private void initTextToSpeech() {
    Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
    startActivityForResult(intent, TTS_DATA_CHECK);
}

protected void onActivityResult(int requestCode, int resultCode,
                                Intent data) {
    if (requestCode == TTS_DATA_CHECK) {
        if (resultCode == Engine.CHECK_VOICE_DATA_PASS) {
            tts = new TextToSpeech(this, new OnInitListener() {
                public void onInit(int status) {
                    if (status == TextToSpeech.SUCCESS) {
                        ttsIsInit = true;
                        Locale loc = new Locale("es","","");
                        if (tts.isLanguageAvailable(loc) >=
                            TextToSpeech.LANG_AVAILABLE)
                                tts.setLanguage(loc);
                        tts.setPitch(0.8f);
                        tts.setSpeechRate(1.1f);
                        speak();
                    }
                }
            });
        } else {
            Intent installVoice =
                new Intent(Engine.ACTION_INSTALL_TTS_DATA);
            startActivity(installIntent);
        }
    }
}

private void speak() {
    if (tts != null && ttsIsInit) {
        tts.speak("Hola Android", TextToSpeech.QUEUE_ADD, null);
    }
}

@Override
public void onDestroy() {
    super.onDestroy();
    if (tts != null) {
        tts.stop();
        tts.shutdown();
    }

    super.onDestroy();
}

Reconocimiento del habla en Android

Otro sensor que podemos utilizar para introducir información en nuestras aplicaciones es el micrófono que incorpora el dispositivo. Tanto el micrófono como la cámara se pueden utilizar para capturar audio y video. Una característica altamente interesante de los dispositivos Android es que nos permiten realizar reconocimiento del habla de forma sencilla para introducir texto en nuestras aplicaciones.

Para realizar este reconocimiento deberemos utilizar intents. Concretamente, crearemos un Intent mediante las constantes definidas en la clase RecognizerIntent, que es la clase principal que deberemos utilizar para utilizar esta característica.

Lo primer que deberemos hacer es crear un Intent para inicial el reconocimiento:

Intent intent = new Intent(
    RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

Una vez creado, podemos añadir una serie de parámetros para especificar la forma en la que se realizará el reconocimiento. Estos parámetros se introducen llamando a:

intent.putExtra(parametro, valor);

Los parámetros se definen como constantes de la clase RecognizerIntent, todas ellas tienen el prefijo EXTRA_. Algunos de estos parámetros son:

Parámetro

Valor

EXTRA_LANGUAGE_MODEL

Obligatorio. Debemos especificar el tipo de lenguaje utilizado. Puede ser lenguaje orientado a realizar una búsqueda web (LANGUAGE_MODEL_WEB_SEARCH), o lenguaje de tipo general (LANGUAGE_MODEL_FREE_FORM).

EXTRA_LANGUAGE

Opcional. Se especifica para hacer el reconocimiento en un idioma diferente al idioma por defecto del dispositivo. Indicaremos el idioma mediante la etiqueta IETF correspondiente, como por ejemplo "es-ES" o "en-US"

EXTRA_PROMPT

Opcional. Nos permite indicar el texto a mostrar en la pantalla mientras se realiza el reconocimiento. Se especifica mediante una cadena de texto.

EXTRA_MAX_RESULTS |Opcional. Nos permite especificar el número máximo de posibles resultados que queremos que nos devuelva. Se especifica mediante un número entero.|

Una vez creado el intent y especificados los parámetros, podemos lanzar el reconocimiento llamando, desde nuestra actividad, a:

startActivityForResult(intent, codigo);

Como código deberemos especifica un entero que nos permita identificar la petición que estamos realizado. En la actividad deberemos definir el callback onActivityResult, que será llamado cuando el reconocimiento haya finalizado. Aquí deberemos comprobar en primer lugar que el código de petición al que corresponde el callback es el que pusimos al lanzar la actividad. Una vez comprobado esto, obtendremos una lista con los resultados obtenidos de la siguiente forma:

@Override
protected void onActivityResult(int requestCode,
                int resultCode, Intent data) {
    if (requestCode == codigo && resultCode == RESULT_OK) {

        ArrayList<String> resultados =
            data.getStringArrayListExtra(
                RecognizerIntent.EXTRA_RESULTS);

        // Utilizar los resultados obtenidos
        ...
    }
    super.onActivityResult(requestCode, resultCode, data);
}

Síntesis de voz en iOS

La síntesis de voz aparece a partir de iOS 7. Hasta entonces la única forma de implementar síntesis de voz era utilizar la característica de accesibilidad Voice Over, lo cual forzaba a tener que tener activada esta característica.

A partir e iOS 7 aparece la clase AVSpeechSynthesizer que nos permite sintetizar voz en cualquier aplicación.

En primer lugar deberemos establecer el texto a vocalizar mediante un objeto de tipo AVSpeechUtterance:

AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString: @"Hola mundo"];

Este objeto nos permite también especificar otras propiedades de la locución, como el pitch o la voz a utilizar.

Una vez definido y configurado el utterance, podremos reproducirlo con:

AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];

[synthesizer speakUtterance: utterance];

A través de este objeto podremos pausar o reanudar la locución, o conocer si actualmente está reproduciéndose.

Reconocimiento del habla en iOS

Los dispositivos iOS cuentan con el asistente Siri que utiliza reconocimiento de voz para realizar diferentes operaciones. Estas operaciones se realizan a nivel del Sistema Operativo, y nos permiten utilizar diferentes servicios que proporciona la plataforma, como por ejemplo hacer una llamada, leer los mensajes, o consultar el tiempo que hace.

A partir de iOS 10 aparece SiriKit, que nos permite integrar las funcionalidades que ofrecen nuestras aplicaciones en este entorno. De esta forma, través del asistente Siri podremos utilizar comandos que lanzarán determinadas funcionalidades de nuestra aplicación.

Si queremos tener un mayor control sobre el reconocimiento de voz dentro de nuestra aplicación, podemos encontrar APIs de terceros que nos proporcionan dicha funcionalidad, como por ejemplo las siguientes:

  • SpeechKit

http://developer.nuance.com/

  • MindMeld

https://expectlabs.com/docs/sdks/ios/gettingStarted

  • OpenEars

http://www.politepix.com/openears/

Ejercicios

Síntesis de voz con Text to Speech

En este primer ejercicio vamos a utilizar el motor Text to Speech para crear una aplicación que lea el texto contenido en un EditText de la actividad principal. Para ello el primer paso será descargar de las plantillas la aplicación SintesisVoz. La aplicación contiene una única actividad. La idea es que al pulsar el botón Leer se lea el texto en el cuadro de edición. Existen dos botones de radio para escoger la pronunciación (inglés o español).

Deberemos seguir los siguientes pasos:

  • Inserta el código necesario en el método initTextToSpeech para que se lance un Intent implícito para comprobar si el motor Text to Speech está instalado en el sistema:

Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(intent, TTS_DATA_CHECK);
  • En el manejador onActivityResult incorporamos el código necesario para inicializar el motor Text to Speech en el caso en el que esté instalado, o para

    instalarlo en el caso en el que no lo estuviera.

if (requestCode == TTS_DATA_CHECK) {
    if (resultCode == Engine.CHECK_VOICE_DATA_PASS) {
        tts = new TextToSpeech(this, new OnInitListener() {
            public void onInit(int status) {
                if (status == TextToSpeech.SUCCESS) {
                    ttsIsInit = true;
                    Locale loc = new Locale("es","","");
                    if (tts.isLanguageAvailable(loc)
                                >= TextToSpeech.LANG_AVAILABLE)
                        tts.setLanguage(loc);
                    tts.setPitch(0.8f);
                    tts.setSpeechRate(1.1f);
                }
            }
        });
    } else {
        Intent installVoice = new Intent(Engine.ACTION_INSTALL_TTS_DATA);
        startActivity(installVoice);
    }
}

En el código anterior tts es un objeto de la clase TextToSpeech que ya está definido en la plantilla. La variable booleana ttsIsInit tendrá valor true en el caso en el que el motor de síntesis de voz se haya inicializado correctamente. La utilizaremos más adelante para comprobar si se puede leer o no un texto. Mediante el objeto loc inicializamos el idioma a español, ya que es el botón de radio seleccionado por defecto al iniciar la actividad.

  • Añade el código necesario en el método onDestroy para liberar los recursos asociados a la instancia de Text to Speech cuando la actividad vaya a ser

    destruida:

if (tts != null) {
    tts.stop();
    tts.shutdown();
}
  • El manejador del click del botón Leer simplemente llama al método speak, que será el encargado de utilizar el objeto TextToSpeech para leer

    el texto en la vista EditText. Introduce el código necesario para hacer esto; no olvides de comprobar si

    el motor Text to Speech está inicializado por medio de la variable booleana ttsIsInit.

  • Por último añade el código necesario a los manejadores del click de los botones de radio para que se cambie el idioma a español o inglés según corresponda. Observa cómo se usa

    la clase Locale en onActivityResult para hacer exactamente lo mismo.

Last updated