0

我是一名中级 C# 程序员,但绝对是 System.Speech 的初学者。我正在通过一些示例来了解 API 的工作原理,并且我已经挂断了第一个示例......我想要做的是有一个语法,它返回一个或多个预期选择的默认语义如果用户没有明确地为这些选择之一提供值。(对不起,如果我的术语不太正确......)我在 Windows Vista 上安装了 Visual Studio 2010(试用版)和 .NET 4.0。

我从以下文章中的“披萨订购”示例开始,这似乎在论坛上出现了很多:

http://msdn.microsoft.com/en-us/magazine/cc163663.aspx#S5

我开始使用的代码在该文章的图 9 中。不幸的是,由于某种原因(可能从 SAPI 的一个版本更改为下一个版本?),许多函数调用在 .NET 4.0/SAPI 5.3 中实际上是无效的,例如 GrammarBuilder.AppendChoices() 和 GrammarBuilder.AppendResultKeyValue() . 如果用户只指定浇头(即“A cheese Pizza, please” 递回 size=large、crust=thick 和 topping=奶酪)...所以我想弄清楚如何使这项工作。

这是我的代码的相关部分(应该只是对上面文章中代码的重写):

// [I'd like] a [< size >] [< crust >] [< topping >] pizza [please]

// build the core set of choices  
GrammarBuilder grbSizes = new GrammarBuilder(new Choices("small", "regular", "large"));  
GrammarBuilder grbCrusts = new GrammarBuilder(new Choices("thin crust", "thick crust"));  
GrammarBuilder grbToppings = new GrammarBuilder(new Choices("vegetarian", "pepperoni", "cheese"));    

// Wrap them in semantic result keys
SemanticResultKey skeySize = new SemanticResultKey("size", grbSizes);  
SemanticResultKey skeyCrust = new SemanticResultKey("crust", grbCrusts);  
SemanticResultKey skeyTopping = new SemanticResultKey("topping", grbToppings);  

// And some default values for later on...    
SemanticResultKey skeyDefaultSize = new SemanticResultKey("size", new GrammarBuilder(new SemanticResultValue("large")));  
SemanticResultKey skeyDefaultCrust = new SemanticResultKey("crust", new GrammarBuilder(new SemanticResultValue("thick crust")));  


// [...snip...]  
// Here's the builder for one of several sub-grammars, the one with two default  
// values... This should allow "cheese" in "A cheese pizza" to be intepreted  
// as large+thick-crust+cheese

//choose topping only, and assume the rest
GrammarBuilder toppingOnly = new GrammarBuilder();
toppingOnly += skeyTopping;
toppingOnly += skeyDefaultSize;
toppingOnly += skeyDefaultCrust;

// [...snip...]
// Later code builds up the full pattern just as in the original article

我知道SemanticResultKey 构造函数的 MSDN 页面包含一个警告,即“在由builders参数指定的 GrammarBuilder 对象中应该有一个且只有一个未标记的 SemanticResultValue 实例”,否则您将收到异常。事实上,当我在识别器中说“A cheese Pizza”之类的内容时,我确实在这里得到了 TargetInvocationException。

所以我的第一个问题是,有人可以向我解释这里发生了什么吗?我不一定期望此约束适用于此,因为 (a) 我认为我对 skeyDefaultSize 和 skeyDefaultCrust 的声明确实将 SemanticResultValues 与 SemanticResultKeys 相关联,因此不应将这些值视为“未标记”;(b) 有问题的两个 SemanticResultValues 实际上来自不同的 GrammarBuilders,它们又位于不同的 SemanticResultKeys 内部,这似乎不是 MSDN 页面上描述的场景。

那么我的第二个问题是,为什么下面的代码有效?唯一的区别是我重新排序了一些行,以便两个“默认”键不会连续附加到语法中。

//choose topping only, and assume the rest
GrammarBuilder toppingOnly = new GrammarBuilder();
toppingOnly += skeyDefaultSize;
toppingOnly += skeyTopping;
toppingOnly += skeyDefaultCrust;

当我说“A cheese Pizza”时,这给出了确切的期望结果——所有键(“size”、“crust”、“topping”)都存在于我在 SpeechRecognized 处理程序中捕获的 SemanticValue 中,具有所需的默认值大小和外壳的值加上用户指定的浇头值。

我想第三个也是最重要的问题是:有什么方法可以正确地做到这一点吗?显然,调整追加的顺序太“神奇”,并不总是可行的解决方案。

很抱歉这个大问题,非常感谢您的帮助!

4

1 回答 1

1

我在从 MSDN 文章中学习时遇到了同样的问题。我不知道我的解决方案是“最好的”,但这就是我更新 Pizza 语法并处理默认选择的方式。

首先,这是我创建比萨语法的方式:

private Grammar CreatePizzaGrammar()
{
    //create the pizza grammar
    GrammarBuilder pizzaRequest = CreatePizzaGrammarBuilder();
    Grammar pizzaGrammar = new Grammar(pizzaRequest);
    return pizzaGrammar;
}

private GrammarBuilder CreatePizzaGrammarBuilder()
{
    // this is adapted from the sample in http://msdn.microsoft.com/en-us/magazine/cc163663.aspx
    // but the API changed before Vista was released so some changes were made.

    //[I'd like] a [<size>] [<crust>] [<topping>] pizza [please]

    //build the core set of choices

    // size
    Choices sizes = new Choices();
    SemanticResultValue sizeSRV;
    sizeSRV = new SemanticResultValue("small", "small");
    sizes.Add(sizeSRV);
    sizeSRV = new SemanticResultValue("regular", "regular");
    sizes.Add(sizeSRV);
    sizeSRV = new SemanticResultValue("medium", "regular");
    sizes.Add(sizeSRV);
    sizeSRV = new SemanticResultValue("large", "large");
    sizes.Add(sizeSRV);
    SemanticResultKey sizeSemKey = new SemanticResultKey("size", sizes);

    // crust
    Choices crusts = new Choices();
    SemanticResultValue crustSRV;
    crustSRV = new SemanticResultValue("thin crust", "thin crust");
    crusts.Add(crustSRV);
    crustSRV = new SemanticResultValue("thin", "thin crust");
    crusts.Add(crustSRV);
    crustSRV = new SemanticResultValue("thick crust", "thick crust");
    crusts.Add(crustSRV);
    crustSRV = new SemanticResultValue("thick", "thick crust");
    crusts.Add(crustSRV);
    SemanticResultKey crustSemKey = new SemanticResultKey("crust", crusts);

    // toppings
    Choices toppings = new Choices();
    SemanticResultValue toppingSRV;
    toppingSRV = new SemanticResultValue("vegetarian", "vegetarian");
    toppings.Add(toppingSRV);
    toppingSRV = new SemanticResultValue("veggie", "vegetarian");
    toppings.Add(toppingSRV);
    toppingSRV = new SemanticResultValue("pepperoni", "pepperoni");
    toppings.Add(toppingSRV);
    toppingSRV = new SemanticResultValue("cheese", "cheese");
    toppings.Add(toppingSRV);
    toppingSRV = new SemanticResultValue("plain", "cheese");
    toppings.Add(toppingSRV);
    SemanticResultKey toppingSemKey = new SemanticResultKey("topping", toppings);

    //build the permutations of choices...

    // 1. choose all three
    GrammarBuilder sizeCrustTopping = new GrammarBuilder();
    sizeCrustTopping.Append(sizeSemKey);
    sizeCrustTopping.Append(crustSemKey);
    sizeCrustTopping.Append(toppingSemKey);

    // 2. choose size and topping
    GrammarBuilder sizeAndTopping = new GrammarBuilder();
    sizeAndTopping.Append(sizeSemKey);
    sizeAndTopping.Append(toppingSemKey);
    // sizeAndTopping.Append(new SemanticResultKey("crust", "thick crust"));
    // sizeAndTopping.AppendResultKeyValue("crust", "thick crust");

    // 3. choose size and crust, and assume cheese
    GrammarBuilder sizeAndCrust = new GrammarBuilder();
    sizeAndCrust.Append(sizeSemKey);
    sizeAndCrust.Append(crustSemKey);

    // 4. choose topping and crust, and assume cheese
    GrammarBuilder toppingAndCrust = new GrammarBuilder();
    toppingAndCrust.Append(crustSemKey);
    toppingAndCrust.Append(toppingSemKey);


    // 5. choose topping only, and assume the rest
    GrammarBuilder toppingOnly = new GrammarBuilder();
    toppingOnly.Append(toppingSemKey);         //, "topping");

    // 6. choose size only, and assume the rest
    GrammarBuilder sizeOnly = new GrammarBuilder();
    sizeOnly.Append(sizeSemKey);

    // 7. choose crust only, and assume the rest
    GrammarBuilder crustOnly = new GrammarBuilder();
    crustOnly.Append(crustSemKey);


    //assemble the permutations             
    Choices permutations = new Choices();
    permutations.Add(sizeCrustTopping);
    permutations.Add(sizeAndTopping);
    permutations.Add(sizeAndCrust);
    permutations.Add(toppingAndCrust);
    permutations.Add(toppingOnly);
    permutations.Add(sizeOnly);
    permutations.Add(crustOnly);

    GrammarBuilder permutationList = new GrammarBuilder();
    permutationList.Append(permutations);

    //now build the complete pattern...
    GrammarBuilder pizzaRequest = new GrammarBuilder();
    //pre-amble "[I'd like] a"
    pizzaRequest.Append(new Choices("I'd like a", "a", "I need a", "I want a"));
    //permutations "[<size>] [<crust>] [<topping>]"
    pizzaRequest.Append(permutationList, 0, 1);
    //post-amble "pizza [please]"
    pizzaRequest.Append(new Choices("pizza", "pizza please", "pie", "pizza pie"));

    return pizzaRequest;
}

然后我为 SpeechRecognized 事件设置了一个事件处理程序:

void recognizer_SpeechRecognizedPizza(object sender, SpeechRecognizedEventArgs e)
{

    // set the default semantic key values if the result does not include these
    string size = "regular";
    string crust = "thick crust";
    string topping = "cheese";

    if (e.Result.Semantics != null && e.Result.Semantics.Count != 0)
    {
        if (e.Result.Semantics.ContainsKey("size"))
        {
            size = e.Result.Semantics["size"].Value.ToString();
            AppendTextOuput(String.Format("\r\n  Size = {0}.", size));
        }

        if (e.Result.Semantics.ContainsKey("crust"))
        {
            crust = e.Result.Semantics["crust"].Value.ToString();
            AppendTextOuput(String.Format("\r\n  Crust = {0}.", crust));
        }

        if (e.Result.Semantics.ContainsKey("topping"))
        {
            topping = e.Result.Semantics["topping"].Value.ToString();
            AppendTextOuput(String.Format("\r\n  Topping = {0}.", topping));
        }
    }
    String sOutput = String.Format("\r\nRecognized: You have orderd a {0}, {1}, {2} pizza.", size, crust, topping);
    AppendTextOuput(sOutput);
}

AppendTextOutput 只是我自己的小输出字符串方法。

这似乎需要大量的工作来明确地布局语法中所有可能的排列。但是,它真的很好用。

如您所见,我最终避免了让语法提供默认值的问题,而是简单地将其构建到事件处理程序中。可能有更好的方法。

了解更多信息的另一个步骤是使用 SrgsDocument.WriteSrgs()方法并写出代表语法的 SRGS XML 文档。规则和语义标签在 XML 中更容易可视化。

于 2011-04-18T16:30:37.287 回答