/// <summary> /// Keyword-triggered intent recognition using microphone. This is useful for when you don't have a push-to-talk feature /// and want to activate your device with voice only. A keyword model is used for local recognition and activation. /// NOTE: It is possible to still call recognize once during a keyword spotting session if you want to have both /// push-to-talk and keyword activation. /// Example interaction: "Computer turn on the lights". /// </summary> public static async Task IntentPatternMatchingWithMicrophoneAndKeywordSpottingAsync() { // Creates an instance of a speech config with specified subscription key and service region. Note that in // contrast to the other samples this DOES NOT require a LUIS application. // The default recognition language is "en-us". var config = SpeechConfig.FromSubscription( "YourSubscriptionKey", "YourSubscriptionRegion"); // Creates an instance of a keyword recognition model. Update this to // point to the location of your keyword recognition model. var keywordModel = KeywordRecognitionModel.FromFile(@"PathToKeywordModel\Keyword.table"); // The phrase your keyword recognition model triggers on. var keyword = "YourKeyword"; // Creates an intent recognizer using microphone as audio input. using (var recognizer = new IntentRecognizer(config)) { // Create a string containing the keyword with the optional pattern tags on it. This can be useful if you // are using push to talk and keyword activation. var keywordOptionalPattern = "[" + keyword + "]"; // Creates a Pattern Matching model and adds specific intents from your model. The Id is used to identify // this model from others in the collection. var patternMatchingModel = new PatternMatchingModel("YourPatternMatchingModelId"); // Creates the "floorName" entity and set it to type list. // Adds acceptable values. NOTE the default entity type is Any and so we do not need // to declare the "action" entity. patternMatchingModel.Entities.Add(PatternMatchingEntity.CreateListEntity( "floorName", EntityMatchMode.Strict, "ground floor", "lobby", "1st", "first", "one", "1", "2nd", "second", "two", "2")); // Creates the "parkingLevel" entity as a pre-built integer patternMatchingModel.Entities.Add(PatternMatchingEntity.CreateIntegerEntity("parkingLevel")); // Creates a string with a pattern that uses groups of optional words. Optional phrases in square brackets can // select one phrase from several choices by separating them inside the brackets with a pipe '|'. Here, // "[Go | Take me]" will match either "Go", "Take me", or "". Note the space after the keyword. var patternWithOptionalWords = keywordOptionalPattern + " " + "[Go | Take me] to [floor|level] {floorName}"; // Creates a string with a pattern that uses an optional entity and group that could be used to tie commands // together. Optional patterns in square brackets can also include a reference to an entity. "[{parkingLevel}]" // includes a match against the named entity as an optional component in this pattern. var patternWithOptionalEntity = keywordOptionalPattern + " " + "Go to parking [{parkingLevel}]"; // You can also have multiple entities of the same name in a single pattern by adding appending a unique identifier // to distinguish between the instances. For example: var patternWithTwoOfTheSameEntity = keywordOptionalPattern + " " + "Go to floor {floorName:1} [and then go to floor {floorName:2}]"; // NOTE: Both floorName:1 and floorName:2 are tied to the same list of entries. The identifier can be a string // and is separated from the entity name by a ':' // Adds some intents to look for specific patterns. patternMatchingModel.Intents.Add(new PatternMatchingIntent( "ChangeFloors", patternWithOptionalWords, patternWithOptionalEntity, patternWithTwoOfTheSameEntity)); patternMatchingModel.Intents.Add(new PatternMatchingIntent("DoorControl", keywordOptionalPattern + " " + "{action} the doors", keywordOptionalPattern + " " + "{action} doors", keywordOptionalPattern + " " + "{action} the door", keywordOptionalPattern + " " + "{action} door")); // Add the model to a new language model collection var modelCollection = new LanguageUnderstandingModelCollection(); modelCollection.Add(patternMatchingModel); // Apply the language model collection to the recognizer. recognizer.ApplyLanguageModels(modelCollection); var stopRecognition = new TaskCompletionSource <int>(); // Subscribes to events. recognizer.Recognizing += (s, e) => { if (e.Result.Reason == ResultReason.RecognizingKeyword) { Console.WriteLine($"RECOGNIZING KEYWORD: Text={e.Result.Text}"); } else if (e.Result.Reason == ResultReason.RecognizingSpeech) { Console.WriteLine($"RECOGNIZING: Text={e.Result.Text}"); } }; recognizer.Recognized += (s, e) => { // Checks result. var result = e.Result; if (result.Reason == ResultReason.RecognizedKeyword) { Console.WriteLine($"RECOGNIZED KEYWORD: Text={e.Result.Text}"); } else if (result.Reason == ResultReason.RecognizedIntent) { Console.WriteLine($"RECOGNIZED: Text={result.Text}"); Console.WriteLine($"{"Intent Id=",13} {result.IntentId}."); var entities = result.Entities; switch (result.IntentId) { case "ChangeFloors": if (entities.TryGetValue("floorName", out string floorName)) { Console.WriteLine($"{"FloorName=",17} {floorName}"); } if (entities.TryGetValue("floorName:1", out floorName)) { Console.WriteLine($"{"FloorName:1=",17} {floorName}"); } if (entities.TryGetValue("floorName:2", out floorName)) { Console.WriteLine($"{"FloorName:2=",17} {floorName}"); } if (entities.TryGetValue("parkingLevel", out string parkingLevel)) { Console.WriteLine($"{"ParkingLevel=",17} {parkingLevel}"); } break; case "DoorControl": if (entities.TryGetValue("action", out string action)) { Console.WriteLine($"{"Action=",17} {action}"); } break; default: Console.WriteLine($"Unknown intent ID: {result.IntentId}"); break; } } else if (result.Reason == ResultReason.RecognizedSpeech) { Console.WriteLine($"RECOGNIZED: Text={result.Text}"); Console.WriteLine($"{"Intent not recognized.",17}"); } else if (result.Reason == ResultReason.NoMatch) { Console.WriteLine($"NOMATCH: Speech could not be recognized."); } }; recognizer.Canceled += (s, e) => { Console.WriteLine($"CANCELED: Reason={e.Reason}"); if (e.Reason == CancellationReason.Error) { Console.WriteLine($"CANCELED: ErrorCode={e.ErrorCode}"); Console.WriteLine($"CANCELED: ErrorDetails={e.ErrorDetails}"); Console.WriteLine($"CANCELED: Did you update the subscription info?"); } stopRecognition.TrySetResult(0); }; recognizer.SessionStarted += (s, e) => { Console.WriteLine($"{"Session started event.",17}"); }; recognizer.SessionStopped += (s, e) => { Console.WriteLine($"{"Session stopped event.",17}"); Console.WriteLine($"{"Stop recognition.",17}"); stopRecognition.TrySetResult(0); }; // Starts recognizing. Console.WriteLine($"Say something starting with the keyword '{keyword}' followed by whatever you want..."); // Starts continuous recognition using the keyword model. Use // StopKeywordRecognitionAsync() to stop recognition. await recognizer.StartKeywordRecognitionAsync(keywordModel).ConfigureAwait(false); // Waits for a single successful keyword-triggered speech recognition (or error). await stopRecognition.Task; // Stops recognition. await recognizer.StopKeywordRecognitionAsync().ConfigureAwait(false); } }