Smart Way to Handle Voice Commands in Code to Acti

2020-03-30 03:42发布

问题:

Rather than using Switch/Case or IF Boolean checks that can get very long and awfully tedious, I wonder if a better way can be sought for handling and processing Commands.

E.G:

if(settings.getName == Command)
{
Speak("I am here");
}

if("Get News Feed" == Command)
{
MyRSSFeed RSSNewsFeed = new MyRSSFeed();
RSSNewsFeed.GetFeed();
}

The if commands go on... Here is a snippet of my Switch Statement:

switch (Command)
        {
            #region <-- Get Time Command -->

            case "Time Please":
            case "Whats the Time":
            case "What Time is it":
                GetCurrentTime();
                break;

            #endregion <-- Get Time Command -->

            #region <-- Get Date Command -->

            case "Whats the Date":
            case "What Date is it":
            case "Whats the Date Today":
            case "What is the Date Today":
                GetCurrentDate();
                break;

            #endregion <-- Get Date Command -->


            #region <-- Media Player Commands -->

            case "Play Bamboo Forest":

                Data.MusicPlayer.Play(@"\Bamboo Forest Play List.wpl");

                break;

            case "Next Song":

                Data.MusicPlayer.Next();

                break;

            case "Previous Song":

                Data.MusicPlayer.Previous();

                break;

            case "Stop Music":

                Data.MusicPlayer.Stop();

                break;

            case "Pause Music":

                Data.MusicPlayer.Pause();

                break;

            case "Resume Music":

                Data.MusicPlayer.Resume();

                break;


            case "Mute Music":

                Data.MusicPlayer.Mute();

                break;

            case "Volume Up":

                Data.MusicPlayer.VolumeUp();

                break;

            case "Volume Down":

                Data.MusicPlayer.VolumeDown();

                break;

            #endregion <-- Media Player Commands -->

            #region <-- Voice Recognition Control Commands -->

            case "Stop Listening":
                Audio.Listen.NewCommandRecognitionEngine.RecognizeAsyncCancel();
                Audio.Voice.Speak("Ok");
                Audio.Listen.Initialise(main);
                break;

            #endregion <-- Voice Recognition Control Commands -->

            #region <-- Application Commands -->

            case "Quiet":
                Audio.Voice.Stop();
                break;

            case "Download":
                Audio.Voice.Speak("Opening Download Window.");
                main.dlInterface.ShowBitsJobs();
                break;

            case "Settings":
                Audio.Voice.Speak("Opening Settings Window.");
                main.settings.Show();
                break;

            case "Close":
                if (main.dlInterface.Visable == true)
                {
                    main.dlInterface.Hide();
                    Audio.Voice.Speak("Closing Download Window.");
                }
                if (main.settings.Visible == true)
                {
                    main.settings.Hide();
                    Audio.Voice.Speak("Closing Settings Window.");
                }
                break;

            case "Out of the way":
                if (main.WindowState == System.Windows.Forms.FormWindowState.Normal)
                {
                    main.WindowState = System.Windows.Forms.FormWindowState.Minimized;
                    Audio.Voice.Speak("My apologies");
                }
                break;

            case "Where Are You":
                if (main.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                {
                    main.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    Audio.Voice.Speak("Here");
                }
                break;


            default:
                // Do Nothing here...
                break;
        }

I have a SQL Database that contains Commands. I Load Commands into it as I need. It has a Command Name Colum and a Value Colum. I can change these to add change or delete columns as needed.

Currently, once a command is recognised, I use a combination of IF Statements and a Switch/Case Catch to catch the recognised Command.

I have thought about somehow dropping dll's into a folder and some how scanning then on app load. If I add a Command then somehow use the Value Field to action the command in the dll.

I realise this is a rather complex situation but I feel a much better solution can be found to make this process much more simple.

EDIT: I have looked at this already: http://social.msdn.microsoft.com/Forums/en-US/4f962dc0-aec2-4191-9fe2-e1dfeb1da5dd/voice-command-api

Please ask if you need any more information.

[EDIT] Paqogomez has answered this question. See my working example below:

using System;
using System.Linq;
using MyApp.AppCommands;
using System.Reflection;
using System.Collections.Generic;

namespace MyApp
{
class Program
{
static void Main(string[] args)
{
MethodInfo myMethod;

var methods = new Commands();

myMethod = CommandFactory.GetCommandMethods("Time Please");
myMethod.Invoke(methods, null);

myMethod = CommandFactory.GetCommandMethods("Volume Down");
myMethod.Invoke(methods, null);

myMethod = CommandFactory.GetCommandMethods("Volume Up");
myMethod.Invoke(methods, null);

Console.ReadLine();
}
}

public static class CommandFactory
{
private static Dictionary<string, MethodInfo> commandMethods = new Dictionary<string, MethodInfo>();

public static MethodInfo GetCommandMethods(string Command)
{
MethodInfo methodInfo;

var myCommandMethods = new Commands();

if (commandMethods.Count == 0)
{
var methodNames = typeof(Commands).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);

var speechAttributeMethods = methodNames.Where(y => y.GetCustomAttributes().OfType<CommandAttribute>().Any());

foreach (var speechAttributeMethod in speechAttributeMethods)
{
foreach (var attribute in speechAttributeMethod.GetCustomAttributes(true))
{
commandMethods.Add(((CommandAttribute)attribute).CommandValue, speechAttributeMethod);
}
}
methodInfo = commandMethods[Command];
}
else
{
methodInfo = commandMethods[Command];
}

return methodInfo;
}
}
}

namespace MyApp.AppCommands
{
public class Commands
{
[Command("Time Please")]
[Command("Whats the Time")]
[Command("What Time is it")]
public void GetTime()
{
Console.WriteLine(DateTime.Now.ToLocalTime());
}

[Command("Volume Down")]
public void VolumeDown()
{
Console.WriteLine("Volume Down 1");
}

[Command("Volume Up")]
public void VolumeUp()
{
Console.WriteLine("Volume Up 1");
}
}

[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true)]
public class CommandAttribute : System.Attribute
{
public string CommandValue { get; set; }

public CommandAttribute(string textValue)
{
this.CommandValue = textValue;
}
}
}

Beautiful Work Paqogomez and thank you for Sharing! This is fast and very elegant!.

In my case, all I need to call the code is:

private static void CommandRecognized(object sender, SpeechRecognizedEventArgs e)
{
MethodInfo myMethod;

var methods = new Commands();

myMethod = CommandFactory.GetCommandMethods(e.Result.Text);
myMethod.Invoke(methods, null);
}

which is the Event Handler of the Speech Recognition Engine:

CommandRecognitionEngine.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(CommandRecognized);

回答1:

You are looking for the Strategy pattern as defined here.

This will allow you to handle multiple if statements very easily.

A factory pattern might also suit you. A factory could use reflection to determine which command to create.

You could also just dump all your commands into a dictionary.

EDIT:

Given the last bit of code example a factory is what you need. I've put one together below.

The factory simply reflects on all the methods in MySpeechMethods, looks for ones with SpeechAttributes and sends back the MethodInfo to invoke. If you need return values from your methods you can either set all the methods to return the same type (like string) or look into generics, but I'll leave that to you. :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MyApp.SpeechMethods;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var methods = new MySpeechMethods();
            MethodInfo myMethod;
            myMethod = SpeechFactory.GetSpeechMethod("Time Please");
            myMethod.Invoke(methods, null);
            myMethod = SpeechFactory.GetSpeechMethod("Volume Down");
            myMethod.Invoke(methods, null);
            myMethod = SpeechFactory.GetSpeechMethod("Volume Up");
            myMethod.Invoke(methods, null);
        }
    }

    public static class SpeechFactory
    {
        private static Dictionary<string, MethodInfo> speechMethods = new Dictionary<string, MethodInfo>();
        public static MethodInfo GetSpeechMethod(string speechText)
        {
            MethodInfo methodInfo;
            var mySpeechMethods = new MySpeechMethods();
            if (speechMethods.Count == 0)
            {
                var methodNames =
                    typeof (MySpeechMethods).GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
                var speechAttributeMethods = methodNames.Where(y => y.GetCustomAttributes().OfType<SpeechAttribute>().Any());
                foreach (var speechAttributeMethod in speechAttributeMethods)
                {
                    foreach (var attribute in speechAttributeMethod.GetCustomAttributes(true))
                    {
                        speechMethods.Add(((SpeechAttribute)attribute).SpeechValue, speechAttributeMethod);
                    }
                }
                methodInfo = speechMethods[speechText];
            }
            else
            {
                methodInfo = speechMethods[speechText];
            }

            return methodInfo;
        }
    }
}

namespace MyApp.SpeechMethods
{
    public class MySpeechMethods
    {
        [Speech("Time Please")]
        [Speech("Whats the Time")]
        [Speech("What Time is it")]
        public void GetTime()
        {
            Console.WriteLine(DateTime.Now.ToLocalTime());
        }

        [Speech("Volume Down")]
        public void VolumeDown()
        {
            Console.WriteLine("Volume Down 1");
        }

        [Speech("Volume Up")]
        public void VolumeUp()
        {
            Console.WriteLine("Volume Up 1");
        }
    }

    [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true)]
    public class SpeechAttribute : System.Attribute
    {
        public string SpeechValue { get; set; }

        public SpeechAttribute(string textValue)
        {
            this.SpeechValue = textValue;
        }
    }
}