2007-08-28

Cool & Useless: Creando nuestra propia aplicación para manejar la PC por medio de la voz, (casi) todo desde código manejado (Parte 1)

Después de gastar horas y horas jugando al Star Trek: Bridge Commander, empecé a pensar que al juego le faltaba algo. En un perfecto simulador de una nave de Star Trek el capitán debería estar hablando con la tripulación de puente, y no picándole la cabeza (traducción al mundo real de hacer click al mirar hacia un tripulante) para que hagan lo que dice, por lo que me propuse lo siguiente: Hacer íntegramente en .Net una aplicación en la cual pueda reconocer comandos de voz, los mapee a hotkeys del juego y simule su uso, para que entonces yo pueda gritar: Red alert! Shields up! Y mi primer oficial levante los escudos y las armas, sin tener que memorizarme los 381273981 hotkeys que trae el juego.

Para lograr mi objetivo, empecé por donde suponía que iba a tener mayores dolores de cabeza: el Reconocimiento de Voz.

Desde hace unos días ya había estado entrenando al motor de reconocimiento de voz en ingles que trae Windows Vista, usando el (pésimo) micrófono incorporado a la laptop. A pesar de todo, el resultado había sido bastante satisfactorio, pudiendo usar con relativa comodidad la maquina mediante órdenes de voz, siempre y cuando este en un ambiente silencioso. La primera vez que instalé Windows Vista había bajado el MUI Pack en español, para poder tener reconocimiento de voz en ese idioma. El problema era que para poder usarlo desde Windows tenía que cambiar el idioma de toda la interfaz de usuario, cosa que prefería evitar, por lo que decidí no bajarlo esta vez.

Investigando en MSDN, encontré que con el Framework 3.0 viene un assembly llamado System.Speech, el cual tiene clases que sirven tanto para el reconocimiento como la síntesis de voz.

Si quieren usar este assembly en un proyecto de Visual Studio 2005 y no aparece en el listado de referencias para agregar, vayan a C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\ (o el equivalente en su máquina) y lo van a encontrar.

Dentro del namespace System.Speech.Recognition encontré las clases SpeechRecognitionEngine y SpeechRecognition, las cuales use para poder empezar a "escuchar" lo que estaba diciendo, dentro del programa (más información en http://msdn2.microsoft.com/en-us/library/system.speech.recognition.aspx).

A grandes rasgos, noté como diferencia de SpeechRecognition y SpeechRecognitionEngine que el primero levanta la aplicación que Windows Vista usa para reconocimiento de voz, por lo que todo lo que yo diga iba a ser "escuchado" por esta aplicación también; entonces si yo decía entre otras cosas "Open Internet Explorer" Windows iba a abrir el Internet Explorer, mientras que usando la clase SpeechRecognitionEngine, solo el motor de reconocimiento es activado, pero todo el control queda a manos del desarrollador.

A continuación, un pequeño ejemplo de cómo usé esta clase:



using System;
using System.Collections.Generic;
using System.Text;
using System.Speech.Recognition;
using System.Threading;

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
SpeechRecognitionEngine sre = new SpeechRecognitionEngine();
Choices choises = new Choices(new string[] { "Hello", "Good bye" });

GrammarBuilder grammarBuilder = choises.ToGrammarBuilder();

sre.LoadGrammar(new Grammar(grammarBuilder));

sre.SpeechRecognized += new EventHandler(sre_SpeechRecognized);
sre.SetInputToDefaultAudioDevice();
sre.RecognizeAsync(RecognizeMode.Multiple);

Console.ReadKey();
}

static void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
if (e.Result.Confidence > 0.9)
{
Console.WriteLine("Escuché '{0}' con una certeza del {1}%",
e.Result.Text,
e.Result.Confidence * 100);
}
}
}
}

En el ejemplo se ve como creamos una nueva instancia de la clase Grammar, mediante el uso de la clase Choises, con la cual indicamos que es lo que vamos a querer escuchar desde el programa. Una vez creada la nueva gramática se la podemos agregar a la instancia del SpeechRecognitionEngine. A continuación suscribimos un método nuestro al evento SpeechRecognized para poder hacer algo cuando el SRE reconozca algo. Como el SRE puede escuchar no solamente de un micrófono, sino que también de un archivo de sonido, llamamos al método SetInputToDefaultAudioDevice() para que sepa de donde tiene que escuchar.

Para terminar, llamamos al método RecognizeAsync para poder empezar a reconocer voz de forma asincrónica.

El método sre_SpeechRecognized se fija que el reconocimiento tenga una certeza de más del 90% y en ese caso, escribe lo que reconoció en pantalla.

Con esto, ya tenemos la forma de reconocer los comandos de voz que le vamos a querer mandar a las otras aplicaciones. La próxima les voy a mostrar como poder elegir entre varios idiomas para usar en el reconocimiento de texto y como poder simular el uso de teclado en cualquier aplicación, incluso juegos que usan DirectX!


 

Saludos!

Z

4 comentarios:

Anónimo dijo...

-----sre.LoadGrammar(new Grammar(gramarbuild));

esta linea me arroja el error de que el idioma de la gramatica no coincide con el del reconocedor de voz como lo soluciono y disculpa la novatada no se nada de esto.

Zaiden dijo...

Posiblemente sea que estas usando una grammar con Culture distinta a la del SRE. Fijate como esta solucionado esto en el KeySimulator - Voice Commander. Si no lo arregla copiame el error y me fijo que puede ser

Anónimo dijo...

Hola Zaiden como estas?? bueno mira yo estaba trabajando en algo parecido tambien para juegos pero para hacer otras cositas en tiempo real...ahi llega el problema..que cuando el SRE analiza una palabra(o varias) se demora un tiempo como de 0.6 segundos en reconocer la palabra...esto crea unalatencia que para manejar el pc no importa, pero para un FPS por ejemplo...es muchisimo tiempo de retraso para una orden....bueno perdona todo este rollo que te escribi...pero era para saber si tu sabes algo o se te ocurre alguna idea para disminuir esta latencia. ya que algunas aplicaciones deben funcionar en tiempo real. Salu2!

cristopher dijo...

Hola, queria comentar que encontre un error en la linea siguiente:

sre.SpeechRecognized += new EventHandler(sre_SpeechRecognized);

Ninguna sobrecarga correspondiente a 'sre_SpeechRecognized' coincide con el 'System.EventHandler' delegado

ojala me pudieran ayudar a solucionarla y explicarme un poco de que trata.
Gracias!