Как вернуть семантику по умолчанию из System.Speech.Recognition, когда опциональные элементы опущены? - PullRequest
0 голосов
/ 18 апреля 2011

Я - программист среднего уровня 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 () , Последний вызов - это то, что должно дать нам выбор по умолчанию для клавиш «size» и «crust», если пользователь указывает только начинки (то есть «Пицца с сыром, пожалуйста», передает обратно размер = большой, crust = толстый и 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

Мне известно, что страница MSDN для конструктора SemanticResultKey включает предупреждение о том, что «в объектах GrammarBuilder, указанных строителями * 1016, должен быть один и только один непомеченный экземпляр SemanticResultValue * параметр ", иначе вы получите исключение. И действительно, я получаю исключение TargetInvocationException, когда говорю что-то вроде «сырной пиццы» в распознаватель.

Итак, мой первый вопрос: может ли кто-нибудь объяснить мне, что здесь происходит? Я не обязательно ожидал, что это ограничение будет применено здесь, так как (а) я думал, что мои объявления для skeyDefaultSize и skeyDefaultCrust действительно связывают SemanticResultValues ​​с SemanticResultKeys, поэтому эти значения не следует рассматривать как «непомеченные»; и (b) два рассматриваемых значения SemanticResult на самом деле принадлежат разным GrammarBuilders, которые, в свою очередь, находятся внутри различных ключей SemanticResult, что, по-видимому, не является сценарием, описанным на странице MSDN.

Тогда мой второй вопрос: почему работает следующий код? Разница лишь в том, что я переставил некоторые строки, чтобы две клавиши «по умолчанию» не добавлялись к грамматике последовательно.

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

Это дает точный желаемый результат, когда я говорю, например, «Пицца с сыром» - все ключи («размер», «корочка», «посыпка») присутствуют в SemanticValue, который я улавливаю в своем обработчике SpeechRecognized, с желаемыми значениями по умолчанию для размера и корки плюс пользовательские значение для начинки.

Полагаю, третий и самый важный вопрос: есть ли способ сделать это правильно? Очевидно, что изменение порядка добавления слишком «волшебно» и не всегда будет жизнеспособным решением.

Извините за огромный вопрос, и большое спасибо за вашу помощь!

1 Ответ

1 голос
/ 18 апреля 2011

Я столкнулся с той же проблемой, изучая статью MSDN.Я не знаю, что мое решение «лучшее», но именно так я обновил грамматику пиццы и обработал выбранные по умолчанию варианты.

Во-первых, так я создаю грамматику пиццы:

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);
}

AppendTextOuput - это просто мой маленький метод вывода строки.

Это казалось большой работойявно расположить все возможные перестановки в грамматике.Но это работает очень хорошо.

Как вы можете видеть, я в конечном итоге избежал проблемы с использованием грамматики по умолчанию и просто встроил ее в обработчик событий.Вероятно, есть лучший способ.

Еще один шаг для получения дополнительной информации - использовать метод SrgsDocument.WriteSrgs () и выписать документ SRGS XML, который представляет грамматику.Правило и семантические теги гораздо проще визуализировать в XML.

...