domingo, 10 de marzo de 2013

Tutorial: Simple Reconocimiento de voz en Android (speech recognition)

Buenos días, hoy vamos a aprender de una manera muy sencilla a utilizar la famosa herramienta de Google en Android, el Speech Recognition. El tutorial que he hecho se basa básicamente en decir la frase "Llamar a ..." y un nombre de nuestra agenda y nuestro mobil se pondrá inmediatamente a llamar a esta persona. Al ser un ejemplo y no hacerlo muy extenso solo funcionara al decir esa frase, por ejemplo no funcionará si decimos "Llama a ..."

El tutorial consta de dos partes, una de ellas de como utilizar la Api del reconocimiento de voz (Speech Recognition) para obtener un String (adena de texto) apartir de nuestro mensaje oral. y la segunda parte es como utilizar ese String para poder localizar el nombre en la SIM y llamar al contacto.



Para entender bien este tutorial recomiento antes la lectura de estos dos tutoriales:
-http://www.tutorialeshtml5.com/2012/02/tutorial-basico-android-agregar-boton.html
-http://www.tutorialeshtml5.com/2012/03/android-introduccion-tutorial-y-tipos.html

A continuación os presentaré el código totalmente comentado para su perfecto entendimiento. Entiendo que hay mil maneras de hacerlo y mucho mejor. He obtado por esta manera ya que es la más entendible para aficionados y obtener una idea más clara de la herramienta. No olvidemos que este blog es para aprender e intentar buscarnos un poco la vida para mejorar nuestra propia técnica.

Ante todo no olvidarnos que para que este tutorial funcione tenemos que poner estos permisos en el manifest de nuestra aplicación:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
Y este sería el código java:
public class Reconocimiento extends Activity {
 private static final int VOICE_RECOGNITION_REQUEST_CODE = 1;
 private Button bt_start;
 private Vector<String> nombres;
 private Vector<String> telefonos;

 @Override 
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_reconocimiento);
  //Relacionamos el boton con el XML 
  bt_start = (Button)findViewById(R.id.button1); 
  bt_start.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    //Lanzamos el reconoimiento de voz
    startVoiceRecognitionActivity();
   }
  });
  //Recogemos todos los telefonos y nombre en los
  //vetores: nombres y telefonos
  getNameNumber();
 }
 
 
 private void startVoiceRecognitionActivity() {
  // Definición del intent para realizar en análisis del mensaje
  Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
  // Indicamos el modelo de lenguaje para el intent
  intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
    RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
  // Definimos el mensaje que aparecerá 
  intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"Diga, Llamar a ...");
  // Lanzamos la actividad esperando resultados
  startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
 }
 
 @Override
 //Recogemos los resultados del reconocimiento de voz
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  //Si el reconocimiento a sido bueno
  if(requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK){
  //El intent nos envia un ArrayList aunque en este caso solo 
   //utilizaremos la pos.0
    ArrayList<String> matches = data.getStringArrayListExtra
      (RecognizerIntent.EXTRA_RESULTS);
    //Separo el texto en palabras.
    String [ ] palabras = matches.get(0).toString().split(" ");
    //Si la primera palabra es LLAMAR
    if(palabras[0].equals("llamar")){
    for(int a=0;a<nombres.size();a++){
    //Busco el nombre que es la tercera posicion (LLAMAR A LORENA)
    if(nombres.get(a).equals(palabras[2])){
    //Si la encuentra recojo el numero telf en el otro
    //vector que coincidira con la posicion ya que
    //los hemos rellenado a la vez.
    Intent callIntent = new Intent(Intent.ACTION_CALL);
    callIntent.setData(Uri.parse("tel:"+telefonos.get(a)));
    //Realizo la llamada
    startActivity(callIntent);
    break;
    }
    }
    }
  }
 }
 //Con el getNameNumber lo que hago es recoger los nombres 
 //de la SIM en un vector
 //Y los numeros de telefonos en otro vector, eso sí tienen que coincidir
 //las posiciones de uno y de otro, por eso los relleno a la vez.
 private void getNameNumber(){ 
 nombres = new Vector<String>();
 telefonos = new Vector<String>(); 
        Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
        ContentResolver cr = getContentResolver();
        Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
       null, null, null, null);
        String[] projection = new String[] {
       ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
              ContactsContract.CommonDataKinds.Phone.NUMBER };
        Cursor names = getContentResolver().query(
       uri, projection, null, null, null);
        int indexName = names.getColumnIndex(
       ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
        int indexNumber = names.getColumnIndex(
       ContactsContract.CommonDataKinds.Phone.NUMBER);
        names.moveToFirst();
        do {
           //Aquí relleno los dos
           String name   = names.getString(indexName);
           nombres.add(name);
           String number = names.getString(indexNumber);
           telefonos.add(number);
        } while (names.moveToNext());
    }
}
Espero vuestras dudas y opiniones.


Nuevo! Haz click en el siguiente Link para descargar el Proyecto



Muchas gracias! y no olviden escribir sus comentarios! 

69 comentarios:

  1. Hola Quisiera saber si hay alguna manera de obtener el resultado del onActivityResult(); ya que en mi programa tengo uso dos botones, en los cuales se activa el reconocimiento de voz, pero cada boton usa diferente Textview para mostrar el resultado. Gracias

    ResponderEliminar
    Respuestas
    1. Buenos días!

      Sí, de hecho si sigues el ejemplo donde dice:

      ArrayList matches = data.getStringArrayListExtra
      (RecognizerIntent.EXTRA_RESULTS);

      debajo de este puedes hacer un textview.settext(matches.get(0).toString());
      siendo matches.get(0).toString() la respuesta de texto a voz. Muchas gracias!
      No olvidéis el +1 ^^

      Eliminar
  2. Q tal amigo, hice la aplicaion tal cual como estaba aqui,el ide no me mostraba nigun error , compilo todo muy bien, pero a la hora de ejecutar el programa en mi celular, al aplastar el boton en el programa, me sale q hay un error y se debe cerrar :S ... q puede ser?

    ResponderEliminar
    Respuestas
    1. Mira a ver que te dice el Logcat y cópiamelo aquí y le hecho un ojo a ver. Muchas Gracias!

      Eliminar
    2. Este comentario ha sido eliminado por el autor.

      Eliminar
    3. A mi también me tira error pero cuando lo inicio me fije el problema y parece estar en el método getNameNumber(). Si comentarizo la llamada a ese método la aplicación se abre pero al hacer click en el boton tambien tira error, me fije de vuelta y este error parece que está en la llamada a startVoiceRecognitionActivity().

      Eliminar
    4. Que raro, ami me funcionó perfectamente en Samsung S3 android 4.1

      Eliminar
    5. Lo estuve probando en un emulador de Android 4.2.2, no se si puede ser este el problema. Lo voy a probar emulando el Android 4.1.2

      Eliminar
    6. Mmm... no tienes un terminal físico para probar? nose si el reconocimiento de voz funciona através del emulador. Te reconoce el microfono del PC?

      Eliminar
    7. No estoy seguro si reconoce el micrófono, pero si consigo un smartphone seguro lo pruebo

      Eliminar
    8. Hola. Conseguisteis probarlo en un emulador???

      Eliminar
  3. Sauludos, muy bien explicado, lo que deseo hacer es una aplicacion, para mi mama que es recientemente ciega o invidente, para que pueda encontrar cosas en su casa, necesito que la persona invidente por comandos de voz, se consulte una pequeña base de datos, y regrese las coordenadas del lugar donde se encuentra el objeto, se programar PIC, y solo necesito que el movil me identifique las coordenadas del comando de voz, para k el pic con sonidos distribuidos en la casa de mi mama en cuentre lo que necesita sin perderse tanto, espero poder aportar un buen tutorial del proyecto terminado, y ayudar a tanta gente con este problema similar, si ud, puede ayudarme a avanzar mas rapido se lo agradeceremos infinitamente, buen dia...

    ResponderEliminar
    Respuestas
    1. Me parece muy interesante el proyecto, actualmente dispongo de poco tiempo... pero para cualquier cosa no dudes en preguntar!

      Eliminar
    2. saludos, podrias facilitarme tu proyecto?? (lpandof3.1284@yahoo.com), lo k pasa es k se me esta complicando un poco soy nuevo en java, gracias

      Eliminar
    3. Disculpa pero no suelo guardar los proyectos.. :S
      Pero al ser nuevo lo que te iría mejor para empezar es saber crearlo. Busca algún tutorial en google de: crear proyecto nuevo android en eclipse.

      ;)

      Eliminar
  4. Hola, soy completamente nuevo, debo hacer un proyecto en el cual, debo conectar una aplicacion android con un circuito para el control de algunas luces, el ventilador y el sistema de seguridad de una maqueta de una casa mediante la voz. Podrias hecharme una mano, o dandome una idea por favor porque estoy un poco confundido.

    ResponderEliminar
    Respuestas
    1. Buenos días, Yo buscaría algo como Arduino busca información sobre eso. Con arduino podrás encender las luces, ventilador, etc.. Después tendrás que mirar como conectar el arduino al Android ya sea por cable o bluetooth,etc...

      Espero a verte encaminado un poco. gracias!

      Eliminar
  5. Buenas,

    lo primero decir que muchas gracias por este tuto y por otros muchos que me han servido de ayuda. Lo segundo, me gustaría saber si hay una forma de acceder al audio dentro del Intent y poder reproducirlo antes o después de realizar la transcripción al String.

    De nuevo muchas gracias y un saludo!

    ResponderEliminar
    Respuestas
    1. He estado buscando y no he encontrado nada al respecto...
      Si encuentro algo te lo aré llegar ;)

      Eliminar
    2. usa un TextToSpeech para reproducir el resultado es como yo lo hago.

      Eliminar
  6. se podrá hacer que funcione sin conexión a internet??

    ResponderEliminar
    Respuestas
    1. Con esta API de google no... desconozco ahora mismo si existen otras descargables que no utilicen internet.

      Eliminar
  7. hola saben si funciona en modo offline con la nueva actualización Android 4.1 Jelly Bean que tiene esta opción ya actualizada.

    ResponderEliminar
  8. Hola Saludos y buen tutorial. Quiero saber si hay alguna manera de que el reconocimiento de voz sea en español ya que me genera muchos errores al hablar, reconoce pero palabras en inglés. Muchas gracias.

    ResponderEliminar
    Respuestas
    1. En que idioma tienes el terminal? Yo lo tengo en Español-España y el reconocimiento en español es excelente. No se si a lo mejor el Español-Mexico (ejemplo) puede fallar..

      Eliminar
    2. victo yo cambio el idioma del telefono con Locale.setDefault(Locale.FRANCE); y me funciona bien pero el idioma del ACTION_RECOGNIZE_SPEECH no es el mismo del telefono segun la documentacion de android developer se puede hacer con intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
      intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE,"en-US");
      intent.putExtra(RecognizerIntent.EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE,"en-US"); pero no me funciona sabes como lo puedo cambiar a nivel de código y que funcione.

      Eliminar
  9. Amigo muchas gracias por el tuto, esta muy bueno.
    Una pregunta esq necesito es leer texto, también estoy haciendo una aplicación para invidentes y necesito leer un string para qué el invidentelo escuche, muchas gracias y que pena la molestia

    ResponderEliminar
    Respuestas
    1. Muchas gracias por tu comentario. Tengo algo que seguramente te ayude. Es un link de google en el cual tu le pasas un String por GET en el parametro 'q' y te devuelve un archivo .mp3 con la voz leyendo el texto. Pruebalo en el navegador primero y después lo tendrás que adaptar para que android descarge el archivo de audio, ejemplo:

      http://translate.google.com/translate_tts?tl=es&q=Link%20patrocinado%20por%20Tutoriales%20Html%205

      Cuentame qeu te parece, el +1 siempre se agradece :D

      Eliminar
    2. yo lo hago con un TextToSpeech:

      declaramos la variable

      TextToSpeech hablar;

      @Override
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      hablar = new TextToSpeech(this, this);
      }

      public void say(String text2say){
      hablar.speak(text2say, TextToSpeech.QUEUE_FLUSH, null);
      }

      @Override
      public void onInit(int status) {

      say("Hello World");

      }


      si quieres que diga lo que esta en el texview solo crea una variable y cambia el string en el say ejemplo say (variable) la variable debe ser de tipo string

      Eliminar
  10. Vitor el ejemplo no funciona se cierra cuando digo llamar a creo que es el en getNameNumber(); pude notar que en

    Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,

    cur nunca es utilizada

    podrías verificar el código y corregirlo te lo agradecería.

    ResponderEliminar
    Respuestas
    1. el 'cur' lo que hace es lanzar la 'query'

      He verificado el código, a mí me funciona bien. He subido un link para que descarguéis el proyecto. Muchas gracias!

      Eliminar
  11. Hola excelente codigo ahora si entiendo como funciona pero tengo una duda yo no quiero que lo llame quiero que con voz busque el contacto y de inmediato me envie a otra ventana donde pueda enviar un mensaje de auido y ademas tengo problemas, quiero que cada paso donde este con una voz me diga donde esta y que puede hacer... gracias

    ResponderEliminar
    Respuestas
    1. El proyecto que quieres hacer es bastante amplio, no se puede contestar en una simple pregunta ejejej
      Lo quee tienes que hacer es ir paso a pasa y para cada paso necesitaras buscar un tutorial para lo que estés realizando ese momento.

      Espero que te vaya bien! seguramente algún tutorial de esta web te ayudará en tus pasos.

      Eliminar
  12. Hola, muy bueno el proyecto, solamente que tengo una pregunta, ya pude correr la aplicacion en mi dispositivo(android 4.1) no marca errores, el error que tengo es al momento de cuando acepta lo que digo, me cierra la aplicacion, y revise el logcat y me aparece FATAL EXCEPTION:main, java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1,data=Intent, entre otros, muchas gracias por tu tiempo, espero tu respuesta

    ResponderEliminar
    Respuestas
    1. Repasare el código y comentaré los resultados. Muchas gracias!

      Eliminar
    2. Hola, gracias por subir tu proyecto, encontre que mi error eran unas librerias que me faltaban, y ya me funciona correctamente, gracias por darte el tiempo de subirlo. Saludos

      Eliminar
  13. hola, tengo problemas a correr el programa en cuando corro tu programa al pasar por

    startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);

    ya no me muestra nada!!!! no se si hay otra actividad que tengo q generar???


    gracias

    ResponderEliminar
    Respuestas
    1. Me di cuenta de un error, si a la primera no te reconoce ya no funciona el reintento. Puede ser que sea eso lo que te pasa.

      He verificado el código, a mí me funciona bien. He subido un link para que descarguéis el proyecto. Muchas gracias!

      Eliminar
  14. Hola Victor:
    Excelente tu tutorial!; una consulta, deseo implemente una app parecida al del tutorial, pero que me permita activar o llamar musica que tenga almacenado en mi ipod, por ejemplo, si digo; antistrees, a mi mobile, me suene musica relajante, si digo, romantica, se escuche musica del tipo que yo menciono. Es posible hacer esto?, agradeceré tus comentarios.
    Gracias!!

    ResponderEliminar
    Respuestas
    1. Como digo muchas veces hay que hacerlo paso a paso, primero deberías grabar en un Vector los tipos de música y después en la línea de código:
      if(palabras[0].equals("llamar")){
      A partir de hay la tendrías que sustituir el código para en vez de llamar al intent de llamara tendrías que hacer que llamara al reproductor indicándole la canción dependiendo del String recogido en la grabación de voz en el ejemplo és algo parecido a:
      "tel:"+telefonos.get(a)

      Muchas gracias por vuestros comentarios! :D

      Eliminar
    2. Gracias por la respuesta, otra consulta, como sabemos, los files de musica o audio, los guardamos en res/raw, pero en mi caso, los voy a guardar por tipo de musica, . mi idea seria: res/raw/relax, res/raw/rock, res/raw/pop. Mi pregunta seria, como resolveria esto en el código de mi reproductor de musica: reproductor = MediaPlayer.create(this,R.raw.audio)?

      Saludos

      Eliminar
    3. He estado buscando y probando.. parece que no es posible crear subcarpetas dentro del resource raw. Lo siento igualmente si encuentro algo te lo aré saber.

      Muchas gracias!

      Un +1 nos ayuda a todos! :D

      Eliminar
  15. Hola de nuevo Victor al momento de correr tu programa tal cual, me sale

    ¡Lo sentimos!

    La aplicacion Reconocimiento (com.---.reconocimeinto) se ha detenido inesperadamente. Intentelo de nuevo

    y en el logcat sale esto...

    mi correo es armando_27_01@hotmail.com para ver si te puedo mandar lo q dice mi logcat


    ResponderEliminar
  16. Hola. Tengo el mismo problema que comentan en el mensaje anterior, cuando pulso el botón se detiene la aplicación. Parece que el problema esta en la llamada a startActivityForResult(..). Lo estoy probando con un Emulador de eclipse, no se si sera ese el problema.
    Agradezco cualquier ayuda. david.rodriguezas@gmail.com

    ResponderEliminar
    Respuestas
    1. Sin duda creo que será por el emulador ya que no obtiene ningún sonido y no recibe nada en el ForResult.

      Mira a ver si puedes probarlo en un terminal. muchas gracias

      Un +1 nos ayuda a todos :D

      Eliminar
  17. Ya lo he podido probar en un terminal y funciona bien! Es problema del emulador...
    Otra pregunta, por lo que veo es necesario tener conexión a Internet no?

    ResponderEliminar
    Respuestas
    1. Muy bien! Gracias por aclarar lo del emulador.
      Si... google por reconocimiento de voz necesita internet. Al menos de momento.

      Un +1 nos ayuda a todos! :D

      Gracias!

      Eliminar
  18. salu2.... una pregunta el reconocimento de voz solo puede funcionar con las apis de google con conexion a internet???? puede funcionar con una api de android i sin conexion..????

    ResponderEliminar
    Respuestas
    1. Buenas tardes, la verdad que no he buscado otras apis, ya que la de android que es la de google es la nativa del terminal y se suele usar esa. Miraré a ver que otras posibilidades hay. Gracias por tu comentario.
      Recuerda un +1 nos ayuda a todos :D

      Eliminar
  19. Hola necesito mostrar en una caja de texto lo que se diga con la voz? como hago eso? porfavor

    ResponderEliminar
    Respuestas
    1. Eso no viene integrado en Android. Tendrás que buscar alguna solución alternativa, yo por ejemplo adapte una api web que tiene google para enviarlo los parámetros y me respondía el .mp3

      Eliminar
  20. Como se puede hacer par que también reconozca con contacto con apellidos?

    ResponderEliminar
    Respuestas
    1. en la linea del if quedaria asi
      if (nombres.get(a).equals(palabras[2])&& nombres.get(a).equals(palabras[3]))

      Eliminar
  21. Buenas amigo, muy buen post, esta genial pero tengo una duda. Es posible guardar el contacto que has dicho en un archivo de texto??

    ResponderEliminar
  22. hola amigo, excelente tuto, me podrias explicar como hacer para que en vez de llamar al contacto se le envie un sms con un texto ya definido? Muchas gracias

    ResponderEliminar
  23. No puedo descargar el proyecto, alguien seria tan amable de pasarmelo

    ResponderEliminar
  24. Buen dia quisiera saber si habria alguien quien me pudiera asesorar si esta libreria de voz o alguna otra me sirviera para hacer la comparacion con sonidos por ejemplo la sirena de una ambulancia o una patrulla, si alguien tuviera alguna informacion que resultado me devuelve esta libreria me gustaria saberlo gracias

    ResponderEliminar
  25. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  26. Buenas tengo un problema me compila sin errores y abre la app pero no me realiza la llama estoy probando en una terminal android 5.0.2 samsung A300H

    ResponderEliminar
  27. Hola un saludo...No se puede descargar el proyecto...agradecería que me mandes mi correo es hadesmaicol666@gmail.com
    Gracias de antemano

    ResponderEliminar
  28. Hola. No se puede descargar el proyecto. Por favor actualiza el link. Dea antemano mil gracias

    ResponderEliminar
  29. El link para descargar el proyecto no funciona, por favor podrías actualizarlo. Gracias

    ResponderEliminar
  30. HOLA EXCELENTE EXPLICACION, DISCULPA EL LINK YA NO FUNCIONA PODRIAS COMPARTIRLO NUEVAMENTE, O ALGUIEN QUE LO HAYA PODIDO DESCARGAR PODRIA COMPARTIRLO

    GRACIAS POR EL APORTE.
    SALUDOS.

    ResponderEliminar
  31. hola amigo tengo un problema dentro de tu codigo hay un objeto cur que no esta siendo utilizado,podrias colgar un link con el proyecto si fueras muy amable.

    ResponderEliminar
  32. hola como haria en vez de llamar al ejecutar el comando abra Gmail, Whatsapp alguna aplicación que quiera y este instalada

    ResponderEliminar
  33. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  34. Hola Victor, muy buen tutorial.
    Te hago una consulta, hice un Service que es un Teclado (para remplazar el que viene por defecto en Android) y que una tecla funcione como el boton de Whatsapp, que te escuche lo que decis, pero que lo convierta a texto.. logré hacer el teclado, el servicio.. cuando quiero ejecutar la clase de tu tutorial, lo qu estuve leyendo es que no puedo instanciar una clase Activity desde un service.
    Tenes idea como puedo ejecutarlo? (Encima, al ser un servicio y no una app comun, no puedo debugearlo y ver el tipo de error que sale)
    Gracias !!

    ResponderEliminar
  35. Hola Victor,quería consultarte como haría si yo quisiera dictar y al mismo tiempo quiero que se guarde el audio que dicte.¿se puede ?¿como lo haría ?.
    Te agradecería un montón si se pudiera.

    ResponderEliminar
  36. Hola Victor,quería consultarte como haría si yo quisiera dictar y al mismo tiempo quiero que se guarde el audio que dicte.¿se puede ?¿como lo haría ?.
    Te agradecería un montón si se pudiera.

    ResponderEliminar