Example application of ISpGrammarBuilder

Microsoft Speech SDK

The Microsoft.com Speech website Microsoft Speech SDK SAPI 5.1

Example application of ISpGrammarBuilder

The code example below illustrates an implementation of a travel grammar, using the ISpGrammarBuilder interface.

An approximation of the XML form is included for each of the following three grammar authoring approaches.

    // HRESULT checking code omitted for brevity

    SPSTATEHANDLE hStateTravel;
    // create (if rule does not already exist) top-level Rule, defaulting to Active
    hr = pGrammarBuilder->GetRule(L"Travel", 0, SPRAF_TopLevel | SPRAF_Active, TRUE, &hStateTravel);


    { // Approach 1: list all possible phrases
       // This is the most intuitive approach, and it does not sacrifice efficiency
       //    because the grammar builder will merge shared sub-phrases when possible.
       //    Internally, SAPI may break the transitions into separate transitions if
       //    there are common roots (e.g. "fly to Seattle" and "fly to New York").
       // There is only one root state, hStateTravel, and the terminal NULL state, and there are 6 unique
       //     transitions between the root state and the NULL state.

       /* XML Approximation:
            <RULE NAME="Travel" TOPLEVEL="ACTIVE">
                <PHRASE>fly to Seattle</PHRASE>
                <PHRASE>fly to New York</PHRASE>
                <PHRASE>fly to Washington DC</PHRASE>
                <PHRASE>drive to Seattle</PHRASE>
                <PHRASE>drive to New York</PHRASE>
                <PHRASE>drive to Washington DC</PHRASE>
            </RULE>
       */

       // create set of peer phrases, each containing complete phrase
       // Note: the word delimiter is set as " ", so that the text we attach to the transition can be
       //           multiple words (e.g. "fly to Seattle" is implicitly "fly" + "to" + "Seattle")
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to Seattle", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to New York", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to New York", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
    }


    { // Approach 2: construct the directed-graph using intermediate states
       // This approach gives you more control of the grammar layout, and may be
       //    easier to implement when you have some combinations.
       // Using this approach, there is one root state (hStateTravel), one interim state
       //   (hStateTravel_Second), and the final terminal NULL state. There are three
       //   unique transitions between the root state and the interim state. And there are
       //   three more unique transitions between the interim state, and the final NULL state.
       // Note that graph includes only 2-transition paths. The user is not capable of saying
       //   only the first transition or the second transition (e.g. "fly to" is an invalid
       //   phrase as is "Seattle", but "fly to Seattle" is valid.)

       /* XML Approximation:
            <RULE NAME="Travel" TOPLEVEL="ACTIVE">
                <LIST>
                    <PHRASE>fly to</PHRASE>
                    <PHRASE>drive to</PHRASE>
                    <PHRASE>take train to</PHRASE>
                </LIST>
                <LIST>
                    <PHRASE>Seattle</PHRASE>
                    <PHRASE>New York</PHRASE>
                    <PHRASE>Washington DC</PHRASE>
                </LIST>
            </RULE>
       */

       SPSTATEHANDLE hStateTravel_Second;
       // create a new transition which starts at the root state and ends at a second state
       hr = pGrammarBuilder->CreateNewState(hStateTravel, &hStateTravel_Second);

       // attach the first part of the phrase to to first transition
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, hStateTravel_Second, L"fly to", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, hStateTravel_Second, L"drive to", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel, hStateTravel_Second, L"take train to", L" ", SPWT_LEXICAL, 1, NULL);

       // attach the second and final part of the phrase to the last transition (ending with the NULL state)
       hr = pGrammarBuilder->AddWordTransition(hStateTravel_Second, NULL, L"Seattle", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel_Second, NULL, L"New York", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateTravel_Second, NULL, L"Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
    }

    { // Approach 3: using sub rules
       // This approach let you structure the grammars and is useful when building large grammars,
       //    since it allows for reusable component rules (see also the XML Grammar tag, RULEREF).
       // Note that forward-declarations are allowed, since the grammar validation is not performed
       //    until the XML is compiled or the GrammarBuilder instance is 'Commit'ted.
       // The main difference between Approach 2 and Approach 3 is the use of component rules, which
       //    are combined into one top-level rule. This facilitates the reuse of the component rules
       //    in other rules (e.g. create a second rule called "Geography" which combines the phrase
       //    "where is" with the "Dest" rule, allowing the user to say "where is New York", without
       //    requiring the grammar author/designer to place the same phrase text in multiple places
       //    of the grammar leading to grammar maintenance problems.

       /* XML Approximation:
            <RULE NAME="Travel" TOPLEVEL="ACTIVE">
                <RULEREF NAME="Method"/>
                <RULEREF NAME="Dest"/>
            </RULE>
            <RULE NAME="Method">
                <LIST>
                    <PHRASE>fly to</PHRASE>
                    <PHRASE>drive to</PHRASE>
                    <PHRASE>take train to</PHRASE>
                </LIST>
            </RULE>
            <RULE NAME="Dest" DYNAMIC="TRUE">
                <LIST>
                    <PHRASE>Seattle</PHRASE>
                    <PHRASE>New York</PHRASE>
                    <PHRASE>Washington DC</PHRASE>
                </LIST>
            </RULE>
            </RULE>
       */

       SPSTATEHANDLE hStateMethod;
       SPSTATEHANDLE hStateDest;
       // Note the two new rules ("Method" & "Dest") are NOT marked Top-level, since they are
       //    reused by other top-level rules, and are not by themselves recognizable phrases.
       hr = pGrammarBuilder->GetRule(L"Method", 0, 0, TRUE, &hStateMethod);
       // Marking the "Dest" rules as Dynamic allows the programmatic grammar author to
       //    update only the "Dest" rule after the initial ::Commit (e.g. to add more travel
       //    destinations depending on user history, preferences, or geographic data).
       hr = pGrammarBuilder->GetRule(L"Dest", 0, SPRAF_Dynamic, TRUE, &hStateDest);

       SPSTATEHANDLE hStateTravel_Second;
       // Create an interim state (same as Approach 2)...
       hr = pGrammarBuilder->CreateNewState(hStateTravel, &hStateTravel_Second);
       // ... then attach rules to the transitions from Root->Interim and Interim->NULL state
       hr = pGrammarBuilder->AddRuleTransition(hStateTravel, hStateTravel_Second, hStateMethod, 1, NULL);
       hr = pGrammarBuilder->AddRuleTransition(hStateTravel_Second, NULL, hStateDest, 1, NULL);

       // Add the set of sibling transitions for travel "method"
       hr = pGrammarBuilder->AddWordTransition(hStateMethod, NULL, L"fly to", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateMethod, NULL, L"drive to", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateMethod, NULL, L"take train to", L" ", SPWT_LEXICAL, 1, NULL);

       // Add the set of sibling transitions for travel "destinations"
       hr = pGrammarBuilder->AddWordTransition(hStateDest, NULL, L"Seattle", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateDest, NULL, L"New York", L" ", SPWT_LEXICAL, 1, NULL);
       hr = pGrammarBuilder->AddWordTransition(hStateDest, NULL, L"Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
    }

    // Must Commit before the grammar changes before using the grammar.
    // Note: grammar changes are only given to the engine at synchronize points (see ISpSREngineSite::Synchronize)
    hr = pGrammarBuilder->Commit(0);