/************************************************************** * Program: Lorei * Class: * Description: * This class handles all of the logic required for lorei to * opperate. The class is currently designed around a * Dictonary that is used to store and look up created * processes based on a file path to the launched exe. * This may or maynot be the best solution but it works well. * drawbacks: 1) Can only have one instance of a program * open at a time. But since i havent created a * way to identify more then one program instance * at a time this is a mute point. * The class primarily focuses on the creation and handleing of * the grammer classes used to control diffrent programs. The * grammer classes are what alow the speech api to understand * english. This class also hosts the LUA scripting engine * used to read and setup the grammers required for a * specific program this provides a large ammout of flexability * and change to programs without the need to recompile lorei. * This class acts as a interface providing LUA with the * functions that can be used in the script files. * * **************************************************************/ using System; using System.Collections.Generic; using System.Text; using System.Speech.Recognition; using System.Speech.Synthesis; using System.Diagnostics; using System.Windows.Forms; using System.Runtime.InteropServices; using LuaInterface; namespace Lorei { class LoreiLanguageProcesser { /************ Constructors ************/ public LoreiLanguageProcesser() { // Setup LUA functions SetupLuaFunctions(); // Setup Variables SetupSpeechSynthesizer(); // Setup Engine SetupSpeechRecognitionEngine(); // Start Lorei LoreiStartListening(); } /************ Destructors ************/ /************ Methods ************/ public void LoreiStartListening() { m_speechRecognizer.Enabled = true; if (StateChanged != null) StateChanged(this, m_speechRecognizer.Enabled); } public void LoreiStopListening() { m_speechRecognizer.Enabled = false; if( StateChanged != null ) StateChanged(this, m_speechRecognizer.Enabled); } // Event Handlers private void m_speechRecognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e) { // Interaction Message m_speechSynthesizer.SpeakAsync("Speech Recognized"); // Parse Speech ParseSpeech(e); } private void m_speechRecognizer_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e) { m_speechSynthesizer.SpeakAsync("No"); } /************ Helper Methods ************/ // Helper Methods For Lua Engine private void SetupLuaFunctions() { // Start up the lua engine m_luaEngine = new Lua(); // Setup global functions m_luaEngine.RegisterFunction("SendMessage", this, this.GetType().GetMethod("DispatchMessageToWindow")); m_luaEngine.RegisterFunction("LaunchProgram", this, this.GetType().GetMethod("LaunchProgram")); RegisterFunctionTemplate("ExitProgram"); //m_luaEngine.RegisterFunction("RegisterLoreiName", this, this.GetType().GetMethod("RegisterLoreiName")); RegisterFunctionTemplate("RegisterLoreiName"); RegisterFunctionTemplate("RegisterLoreiFunction"); RegisterFunctionTemplate("RegisterProgramWithScript"); RegisterFunctionTemplate("RegisterLoreiProgramName"); RegisterFunctionTemplate("RegisterLoreiProgramCommand"); } private void RegisterFunctionTemplate(string functionName) { m_luaEngine.RegisterFunction(functionName, this, this.GetType().GetMethod(functionName)); } // Helper Methods For Speech Recognition Engine private void SetupSpeechRecognitionEngine() { // Setup Speech Engine m_speechRecognizer = new SpeechRecognizer(); if (m_speechRecognizer == null) { m_speechSynthesizer.SpeakAsync("Speech Recognizer Creation Failed is Null"); } else m_speechSynthesizer.SpeakAsync("Speech Recognizer Created"); // Call Lua Junk m_luaEngine.DoFile("setup.lua"); // Setup Grammars LoadSpeechInformation(); // Setup Event Handlers SetupEventHandlers(); } private void LoadSpeechInformation() { // Data Choices keywords; Choices functions; Choices programs; Choices actions; Choices programActions; // Setup Grammars m_FunctionExecution = new GrammarBuilder(); m_ProgramControl = new GrammarBuilder(); // Setup Keywords keywords = new Choices(m_Keywords.ToArray()); // Setup Function List functions = new Choices(m_Functions.ToArray()); // Setup List of Programs; programs = new Choices(m_Programs.ToArray()); // Program Functions programActions = new Choices(m_ProgramActions.ToArray()); // Setup Grammar m_FunctionExecution.Append(keywords); m_FunctionExecution.Append(functions); m_FunctionExecution.Append(programs); m_ProgramControl.Append(keywords); m_ProgramControl.Append(programs); m_ProgramControl.Append(programActions); // Setup Engine m_FunctionGrammar = new Grammar(m_FunctionExecution); m_ProgramGrammar = new Grammar(m_ProgramControl); m_FunctionGrammar.Name = "m_FunctionGrammar"; m_ProgramGrammar.Name = "m_ProgramGrammar"; m_speechRecognizer.LoadGrammar(m_FunctionGrammar); m_speechRecognizer.LoadGrammar(m_ProgramGrammar); } private void SetupEventHandlers() { m_speechRecognizer.SpeechRecognized += new EventHandler(m_speechRecognizer_SpeechRecognized); m_speechRecognizer.SpeechRecognitionRejected += new EventHandler(m_speechRecognizer_SpeechRecognitionRejected); } // Helper Methods For Speech Synthesis Engine private void SetupSpeechSynthesizer() { m_speechSynthesizer = new SpeechSynthesizer(); } // Helper Methods For Parsing Speech private void ParseSpeech(SpeechRecognizedEventArgs e) { // Check if disabled by voice // HACK::::::HACK::::::HACK if (m_disabledByVoice == true) { if (e.Result.Words[1].Text != "Enable") return; if (e.Result.Words[2].Text != "Speech") return; m_disabledByVoice = false; return; } // ENDHACK:::::ENDHACK::::ENDHACK //m_luaEngine.DoString("winamp:ToLaunch()"); // This function calles the approprate function in the lua file so that // programs can be controled from the lua script and lorei needs to know // nothing about the code that is ran there. // This creates a nice clean operational seperation between dispatch and // operation. Lorei is the dispatcher and LUA is the axual thing doing // all of the work. Most of the time the scripts just call back to lorei // functions to do all of the axual "Work" but it still prevents a lot of // boiler plate code. This replaces alot of stupid code. Look below to see // what i replaced. Every program would have had to be hard coded into lorei // and this system works much much better. switch (e.Result.Grammar.Name) { case "m_FunctionGrammar": string s = e.Result.Words[2].Text.ToLower() + ".To" + e.Result.Words[1].Text + "()"; //m_luaEngine.DoString("winamp.ToLaunch()"); m_luaEngine.DoString(s); break; case "m_ProgramGrammar": //m_luaEngine["event"] = e; //m_luaEngine.DoString(e.Result.Words[1].Text + ".OnSpeechReceved()"); m_luaEngine.GetFunction(e.Result.Words[1].Text + ".OnSpeechReceved").Call(e.Result.Words[2].Text); break; default: break; } // If i catch anyone doing something like this now that i have the LUA system // working i will hunt you down and break your little fingers. // Love Greg // PS: i left this in so you know what not to do // Not disabled so do the work /*switch (e.Result.Grammar.Name) { case "m_FunctionGrammar": switch (e.Result.Words[1].Text) { case "Launch": switch (e.Result.Words[2].Text) { case "Ventrilo": LaunchProgram("C:\\Program Files\\Ventrilo\\Ventrilo.exe"); break; case "Command": LaunchProgram("C:\\Windows\\system32\\cmd.exe"); break; case "Winamp": LaunchProgram("C:\\Program Files\\Winamp\\winamp.exe"); break; case "Windows": LaunchProgram("C:\\WINDOWS\\explorer.exe"); break; default: break; } break; case "Close": switch (e.Result.Words[2].Text) { case "Ventrilo": ExitProgram("C:\\Program Files\\Ventrilo\\Ventrilo.exe"); break; case "Command": ExitProgram("C:\\Windows\\system32\\cmd.exe"); break; case "Winamp": ExitProgram("C:\\Program Files\\Winamp\\winamp.exe"); break; case "Windows": ExitProgram("C:\\WINDOWS\\explorer.exe"); break; default: break; } break; case "Disable": if (e.Result.Words[2].Text == "Speech") m_disabledByVoice = true ; break; default: break; } break; case "m_ProgramGrammar": switch (e.Result.Words[1].Text) { case "Winamp": switch (e.Result.Words[2].Text) { case "Previous": DispatchMessageToWindow("C:\\Program Files\\Winamp\\winamp.exe", WM_COMMAND, WINAMP_BUTTON1, 0); break; case "Play": DispatchMessageToWindow("C:\\Program Files\\Winamp\\winamp.exe", WM_COMMAND, WINAMP_BUTTON2, 0); break; case "Pause": DispatchMessageToWindow("C:\\Program Files\\Winamp\\winamp.exe", WM_COMMAND, WINAMP_BUTTON3, 0); break; case "Next": DispatchMessageToWindow("C:\\Program Files\\Winamp\\winamp.exe", WM_COMMAND, WINAMP_BUTTON5, 0); break; case "Stop": DispatchMessageToWindow("C:\\Program Files\\Winamp\\winamp.exe", WM_COMMAND, WINAMP_BUTTON4, 0); break; } break; default: break; } break; default: break; }*/ } public void LaunchProgram(String p_program) { // If Key exists if (m_runningPrograms.ContainsKey(p_program)) { // and If the key in the collection is null then we remake the process if (m_runningPrograms[p_program] == null) { m_runningPrograms[p_program] = Process.Start(p_program); return; } // Else we quit because we are allready manageing one m_speechSynthesizer.SpeakAsync("Unable to launch program"); return; } // Add the program to the dictonary and continue as normal m_runningPrograms.Add(p_program, Process.Start(p_program)); } public void ExitProgram(String p_program) { // Check and see if the process exists Process procToKill; if ( m_runningPrograms.TryGetValue(p_program, out procToKill) ) { // Close the main window so the program exits procToKill.CloseMainWindow(); // Then remove the process from the process map so we can // launch the program again later. m_runningPrograms.Remove(p_program); } // work done go home } public void DispatchMessageToWindow(String p_program, int p_myMessage, int p_myWParam, int p_myLParam) { // This is cool Process myProcess; int myMessage = p_myMessage; int myWParam = p_myWParam; int myLParam = p_myLParam; // Check and make shure the program exists because trying with a bad handle would be bad..... if (m_runningPrograms.TryGetValue(p_program, out myProcess)) { // With out this call to refresh the process never updates the information // about windows handles so when a program creates its main window it never changes // the information in this class. Evil Evil Evil thing.... myProcess.Refresh(); // This command took me days to find gota love msdn docs // Import the win32 send message function so we can drop messages into the program LoreiLanguageProcesser.SendMessage(myProcess.MainWindowHandle, myMessage, myWParam, myLParam); } return; } // Lua General Script Methods private void SayMessage(string p_Message) { // This makes the speach engine for the program say things // this function is made accessable to lua so scripts can say stuff m_speechSynthesizer.SpeakAsync(p_Message); } // Lua Helper Methods for Registration public void RegisterProgramWithScript(string p_ProgramName) { if (m_RegistrationComplete) return; m_luaEngine.DoFile(p_ProgramName + ".lua"); } private void RegisterTemplate(string p_String, List p_list) { // This function is a template to create other functions because // im lazy and copy paste is a bad idea so i make metafunctions... if (m_RegistrationComplete) return; // Check each element in list to see if new item exists already foreach (string x in p_list) { if (x == p_String) return; } p_list.Add(p_String); } public void RegisterLoreiName(string p_NameForLorei) { // Do the work.... shame to need a function for this RegisterTemplate(p_NameForLorei, m_Keywords); } public void RegisterLoreiFunction(string p_NameOfFunction) { RegisterTemplate(p_NameOfFunction, m_Functions); } public void RegisterLoreiProgramName(string p_NameOfProgram) { RegisterTemplate(p_NameOfProgram, m_Programs); } public void RegisterLoreiProgramCommand(string p_NameOfProgramCommand) { RegisterTemplate(p_NameOfProgramCommand, m_ProgramActions); } public void RegistrationDone() { // Check to see if i should be called if (m_RegistrationComplete) return; // Set flag so we disable all register functions m_RegistrationComplete = true; } // Imported Stuff [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); // Constants private const int WM_USER = 0x0400; private const int WM_COMMAND = 0x0111; private const int WM_QUIT = 0x0012; private const int IPC_STARTPLAY = 0x0102; private const int IPC_ISPLAYING = 0x0104; private const int WINAMP_BUTTON1 = 40044; // Winamp Previous private const int WINAMP_BUTTON2 = 40045; // Winamp Play private const int WINAMP_BUTTON3 = 40046; // Winamp Pause private const int WINAMP_BUTTON4 = 40047; // Winamp Stop private const int WINAMP_BUTTON5 = 40048; // Winamp Next private const int WINAMP_PLAYBOX = 40029; /************ Events ************/ public event ParseSpeech TextReceived; public event ProcesserSwitchChanged StateChanged; /************ Accessers ************/ public bool Active { set { if (value == true) LoreiStartListening(); else LoreiStopListening(); } get { return m_speechRecognizer.Enabled; } } /************ Data ************/ private List m_Keywords = new List(); private List m_Functions = new List(); private List m_Programs = new List(); private List m_Aliases = new List(); private List m_ProgramActions = new List(); // Speech Components private SpeechRecognizer m_speechRecognizer; private SpeechSynthesizer m_speechSynthesizer; private GrammarBuilder m_FunctionExecution; private GrammarBuilder m_ProgramControl; private Grammar m_FunctionGrammar; private Grammar m_ProgramGrammar; // Program Control Data private Dictionary m_runningPrograms = new Dictionary(); private bool m_disabledByVoice = false; // Lua Engine Data Lua m_luaEngine; bool m_RegistrationComplete = false; } enum WinampCommands { WINAMP_PREVIOUS = 40044, WINAMP_PLAY = 40045, WINAMP_PAUSE = 40046, WINAMP_STOP = 40047, WINAMP_NEXT = 40048 } }