2025-11-28 00:35:46 +09:00

1403 lines
52 KiB
C#

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright © Microsoft Corporation. All rights reserved
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Speech.Recognition;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
namespace Microsoft.Samples.Speech.Recognition.SpeechRecognition
{
public sealed partial class SpeechRecognitionWindow : System.Windows.Window , IDisposable
{
#region Internal Types
internal enum EngineType
{
SpeechRecognizer, //Shared recognizer
SpeechRecognitionEngine //In process recognizer
}
internal enum AudioInput
{
DefaultAudioDevice,
AudioFile
}
internal enum RecognitionMode
{
Asynchronous,
Synchronous
}
#endregion Internal Types
#region Private Fields
// Whether the speech recognition engine is created or not.
bool _engineCreated;
// Type of the selected engine
EngineType _engineType;
// Recognition Engines
SpeechRecognizer _speechRecognizer;
SpeechRecognitionEngine _speechRecognitionEngine;
// Input type to the engine (SpeechRecognitionEngine only)
AudioInput _audioInput;
// Sync or Async recognition (SpeechRecognitionEngine only)
RecognitionMode _recognitionMode;
// (SpeechRecognitionEngine only)
bool _isRecognizing;
// Three grammar categories in the grammar display tree
TreeViewItem _dictationGrammars;
TreeViewItem _grammarBuilders;
TreeViewItem _SRGSGrammars;
// Background brush of the grammar tree view (used for animation)
SolidColorBrush _treeViewGrammarsBackground;
// Background brushes of each grammar (used for animation)
Dictionary<Grammar, SolidColorBrush> _dictionaryBrushes = new Dictionary<Grammar, SolidColorBrush>();
// Colors (used for animation)
static Color _colorDefault = Colors.Black;
static Color _colorDefaultBackground = Colors.Transparent;
static Color _colorHypothesized = Colors.Blue;
static Color _colorRecognized = Colors.Green;
static Color _colorRejected = Colors.Red;
// Brushes derived from the colors above (used for animation)
static SolidColorBrush _brushDefault = new SolidColorBrush(_colorDefault);
static SolidColorBrush _brushHypothesized = new SolidColorBrush(_colorHypothesized);
static SolidColorBrush _brushRecognized = new SolidColorBrush(_colorRecognized);
static SolidColorBrush _brushRejected = new SolidColorBrush(_colorRejected);
// FlowDocument elements for displaying the status and result of the recognition
Paragraph _paragraphStatus;
Paragraph _paragraphResult;
Run _hypothesis;
// Stream and player for the last recognized audio
MemoryStream _recognizedAudioStream;
System.Media.SoundPlayer _recognizedAudioPlayer;
// Track whether Dispose has been called.
bool disposed;
#endregion Private Fields
#region Constructor
public SpeechRecognitionWindow()
{
// Required by WPF to initialize the window
InitializeComponent();
// Initialize the Status Group Box
InitializeStatusControls();
// Update the status of tab items
UpdateTabItemSpeechRecognizer();
UpdateTabItemSpeechRecognitionEngine();
// Initialize the Grammar Tree View and create the dictation grammars.
// Dictation grammars will be used by all recognizers, no need to recreate them
InitializeGroupBoxGrammars();
CreateDictationGrammars();
}
#endregion Constructor
#region Methods and Event Handlers for SpeechRecognizer Tab
// This method enables or disables the controls in the SpeechRecognizer Tab
// according to the current state of the engine.
private void UpdateTabItemSpeechRecognizer()
{
bool isCreated = _engineCreated && _engineType == EngineType.SpeechRecognizer;
_buttonCreateSR.IsEnabled = !isCreated;
_buttonDisposeSR.IsEnabled = isCreated;
_labelEngineStateLabel.IsEnabled = isCreated;
_labelEngineState.IsEnabled = isCreated;
// Update the recognition group box in this tab.
UpdateGroupBoxRecognitionSR();
}
// This method enables or disables the controls in the Recognition Group Box
// according to the current state of the engine.
private void UpdateGroupBoxRecognitionSR()
{
// Controls are active only if the engine is created
bool isCreated = _engineCreated && _engineType == EngineType.SpeechRecognizer;
_groupBoxRecognitionSR.IsEnabled = isCreated;
if (isCreated)
{
// Emulate button and text box are only enabled when SpeechRecognizer
// is enabled.
bool isEnabled = _speechRecognizer.Enabled;
_textBoxEmulateSR.IsEnabled = isEnabled;
_buttonEmulateSR.IsEnabled = isEnabled;
_buttonEnable.IsEnabled = !isEnabled;
_buttonDisable.IsEnabled = isEnabled;
}
}
void _buttonCreateSR_Click(object sender, RoutedEventArgs e)
{
if (_engineCreated)
{
if (AskForNewEngine() == false)
{
return;
}
}
CreateRecognitionEngine(EngineType.SpeechRecognizer);
// We create the GrammarBuilders after creating the engine becuase
// the culture of each GrammarBuilder must match the culture of
// the engine
CreateGrammarBuilders();
LoadGrammars();
// Enable the SpeechRecognizer
EnableSpeechRecognizer();
UpdateTabItemSpeechRecognizer();
}
void _buttonDispose_Click(object sender, RoutedEventArgs e)
{
// First unload all the grammars then dispose grammars created by GrammarBuilders.
// Other grammars will not be disposed since they will be used by other
// recognition engines later.
UnloadGrammars();
DisposeGrammarBuilders();
// Dispose the engine and update the controls
DisposeRecognitionEngine();
UpdateTabItemSpeechRecognizer();
UpdateTabItemSpeechRecognitionEngine();
}
void _buttonEnable_Click(object sender, RoutedEventArgs e)
{
EnableSpeechRecognizer();
UpdateGroupBoxRecognitionSR();
}
void _buttonDisable_Click(object sender, RoutedEventArgs e)
{
DisableSpeechRecognizer();
UpdateGroupBoxRecognitionSR();
}
private void _textBoxEmulate_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
EmulateRecognize();
}
}
void _buttonEmulate_Click(object sender, RoutedEventArgs e)
{
EmulateRecognize();
}
#endregion Methods and Event Handlers for SpeechRecognizer Tab
#region Methods and Event Handlers for SpeechRecognitionEngine Tab
// This method enables or disables the controls in the SpeechRecognitionEngine Tab
// according to the current state of the engine. It also creates the list of installed
// recognizers in the system.
private void UpdateTabItemSpeechRecognitionEngine()
{
bool isCreated = _engineCreated && _engineType == EngineType.SpeechRecognitionEngine;
_buttonCreateSRE.IsEnabled = !isCreated;
_buttonDisposeSRE.IsEnabled = isCreated;
_comboBoxInstalledRecognizers.IsEnabled = !isCreated;
if (!isCreated)
{
// Create and display the list of installed engines for SpeechRecognitionEngine
_comboBoxInstalledRecognizers.Items.Clear();
foreach (RecognizerInfo recognizerInfo in SpeechRecognitionEngine.InstalledRecognizers())
{
ComboBoxItem itemRecognizerInfo = new ComboBoxItem();
itemRecognizerInfo.Content = recognizerInfo.Description;
itemRecognizerInfo.Tag = recognizerInfo;
_comboBoxInstalledRecognizers.Items.Add(itemRecognizerInfo);
}
// Select the first one
if (_comboBoxInstalledRecognizers.Items.Count > 0)
{
_comboBoxInstalledRecognizers.SelectedIndex = 0;
}
else
{
throw new InvalidOperationException("No speech recognition engine is installed");
}
}
// Update the Audio Input and Recognition group boxes in this tab.
UpdateGroupBoxAudioInput();
UpdateGroupBoxRecognitionSRE();
}
// This method enables or disables the controls in the Audio Input Group Box
// according to the current state.
private void UpdateGroupBoxAudioInput()
{
// Controls are active only if the engine is created
bool isCreated = _engineCreated && _engineType == EngineType.SpeechRecognitionEngine;
_groupBoxAudioInput.IsEnabled = isCreated;
if (isCreated)
{
// Input can only be set while not recognizing
_radioButtonDefaultAudioDevice.IsEnabled = !_isRecognizing;
_radioButtonWaveFile.IsEnabled = !_isRecognizing;
_textBoxWaveFile.IsEnabled = !_isRecognizing && _audioInput == AudioInput.AudioFile;
_buttonWaveFile.IsEnabled = !_isRecognizing && _audioInput == AudioInput.AudioFile;
if (!_isRecognizing && _audioInput != AudioInput.AudioFile)
{
_textBoxWaveFile.Clear();
}
}
else
{
_radioButtonDefaultAudioDevice.IsChecked = false;
_radioButtonWaveFile.IsChecked = false;
_textBoxWaveFile.Clear();
}
}
// This method enables or disables the controls in the Recognition Group Box
// according to the current state of the engine.
private void UpdateGroupBoxRecognitionSRE()
{
// Controls are active only if the engine is created
bool isCreated = _engineCreated && _engineType == EngineType.SpeechRecognitionEngine;
_groupBoxRecognitionSRE.IsEnabled = isCreated;
if (isCreated)
{
// Recognition mode can only be changed while not recognizing
_radioButtonAsynchronous.IsEnabled = !_isRecognizing;
_radioButtonSynchronous.IsEnabled = !_isRecognizing;
// Emulate button and text box are only enabled while not recognizing because
// SpeechRecognitionEngine can't emulate while recognizing
_textBoxEmulateSRE.IsEnabled = !_isRecognizing;
_buttonEmulateSRE.IsEnabled = !_isRecognizing;
_buttonStart.IsEnabled = !_isRecognizing;
if (_recognitionMode == RecognitionMode.Asynchronous)
{
_buttonStop.Visibility = Visibility.Visible;
_buttonStop.IsEnabled = _isRecognizing;
_buttonCancel.Visibility = Visibility.Visible;
_buttonCancel.IsEnabled = _isRecognizing;
}
else
{
_buttonStop.Visibility = Visibility.Collapsed;
_buttonCancel.Visibility = Visibility.Collapsed;
}
}
else
{
_radioButtonAsynchronous.IsChecked = false;
_radioButtonSynchronous.IsChecked = false;
}
}
void _buttonCreateSRE_Click(object sender, RoutedEventArgs e)
{
if (_engineCreated)
{
if (AskForNewEngine() == false)
{
return;
}
}
CreateRecognitionEngine(EngineType.SpeechRecognitionEngine);
UpdateTabItemSpeechRecognitionEngine();
// We create the GrammarBuilders after creating the engine becuase
// the culture of each GrammarBuilder must match the culture of
// the engine.
CreateGrammarBuilders();
LoadGrammars();
// Set the input to default audio device.
_radioButtonDefaultAudioDevice.IsChecked = true;
// The default recognition method is asynchronous.
_radioButtonAsynchronous.IsChecked = true;
}
void _radioButtonDefaultAudioDevice_Checked(object sender, RoutedEventArgs e)
{
SetInputToDefaultAudioDevice();
UpdateGroupBoxAudioInput();
}
void _radioButtonWaveFile_Checked(object sender, RoutedEventArgs e)
{
// If input can be successfully set to wave file then update the controls,
// else set input to default audio device
if (SetInputToWaveFile())
{
UpdateGroupBoxAudioInput();
}
else
{
_radioButtonDefaultAudioDevice.IsChecked = true;
}
}
void _buttonWaveFile_Click(object sender, RoutedEventArgs e)
{
// If input can't be successfully set to new wave file then
// set input to default audio device
if (SetInputToWaveFile() == false)
{
_radioButtonDefaultAudioDevice.IsChecked = true;
}
}
void _radioButtonAsynchronous_Checked(object sender, RoutedEventArgs e)
{
PrepareForAsynchronousRecognition();
UpdateGroupBoxRecognitionSRE();
}
void _radioButtonSynchronous_Checked(object sender, RoutedEventArgs e)
{
PrepareForSynchronousRecognition();
UpdateGroupBoxRecognitionSRE();
}
void _buttonStart_Click(object sender, RoutedEventArgs e)
{
StartRecognition();
UpdateGroupBoxAudioInput();
UpdateGroupBoxRecognitionSRE();
}
void _buttonStop_Click(object sender, RoutedEventArgs e)
{
_speechRecognitionEngine.RecognizeAsyncStop();
UpdateGroupBoxAudioInput();
UpdateGroupBoxRecognitionSRE();
}
void _buttonCancel_Click(object sender, RoutedEventArgs e)
{
_speechRecognitionEngine.RecognizeAsyncCancel();
_isRecognizing = false;
UpdateGroupBoxAudioInput();
UpdateGroupBoxRecognitionSRE();
}
#endregion Methods and Event Handlers for SpeechRecognitionEngine Tab
#region Methods and Event Handlers for Grammars GroupBox
// This method initializes the Grammar Group Box by creating three items in the
// tree view for the three grammar types: Dictation Grammars, GrammarBuilders and
// SRGS Grammars.
private void InitializeGroupBoxGrammars()
{
// The background brush of the tree view is used for animation which takes
// place when the recognition is rejected
_treeViewGrammarsBackground = new SolidColorBrush(_colorDefaultBackground);
_treeViewGrammars.Background = _treeViewGrammarsBackground;
_dictationGrammars = new TreeViewItem();
_dictationGrammars.Header = "Dictation Grammars";
_treeViewGrammars.Items.Add(_dictationGrammars);
_grammarBuilders = new TreeViewItem();
_grammarBuilders.Header = "GrammarBuilders";
_treeViewGrammars.Items.Add(_grammarBuilders);
_SRGSGrammars = new TreeViewItem();
_SRGSGrammars.Header = "SRGS Grammars";
_treeViewGrammars.Items.Add(_SRGSGrammars);
}
// We create the dictation grammars only once
private void CreateDictationGrammars()
{
Grammar defaultDictationGrammar = new DictationGrammar();
defaultDictationGrammar.Name = "Default Dictation";
defaultDictationGrammar.Enabled = true;
_dictationGrammars.Items.Add(CreateGrammarItem(defaultDictationGrammar));
Grammar spellingDictationGrammar = new DictationGrammar("grammar:dictation#spelling");
spellingDictationGrammar.Name = "Spelling Dictation";
spellingDictationGrammar.Enabled = false;
_dictationGrammars.Items.Add(CreateGrammarItem(spellingDictationGrammar));
_dictationGrammars.IsExpanded = true;
}
// We create the GrammarBuilder objects each time after creating the recognizers
// because the culture of the GrammarBuilder must match the culture of the recognizer
private void CreateGrammarBuilders()
{
System.Globalization.CultureInfo culture = GetCulture();
Grammar flightStatusGrammar = FlightStatusGrammarBuilder(culture);
flightStatusGrammar.Name = "Flight Status Grammar";
flightStatusGrammar.Enabled = true;
_grammarBuilders.Items.Add(CreateGrammarItem(flightStatusGrammar));
_grammarBuilders.IsExpanded = true;
}
private void DisposeGrammarBuilders()
{
_grammarBuilders.Items.Clear();
}
// This method creates a TreeViewItem which will be placed under one of the three
// nodes in the grammar tree view. Each item has a check box for enabling and disabling
// it; a label for displaying the name of the grammar and a brush for animation which
// takes place when a speech event related to the grammar is raised.
private TreeViewItem CreateGrammarItem(Grammar grammar)
{
TreeViewItem grammarItem = new TreeViewItem();
// ChechBox and Label will be contained in a DockPanel
DockPanel dockPanel = new DockPanel();
CheckBox checkBox = new CheckBox();
checkBox.VerticalAlignment = VerticalAlignment.Center;
checkBox.IsChecked = grammar.Enabled;
checkBox.Tag = grammar;
checkBox.Checked += new RoutedEventHandler(checkBoxGrammarItem_Checked);
checkBox.Unchecked += new RoutedEventHandler(checkBoxGrammarItem_Unchecked);
Label label = new Label();
label.Content = grammar.Name;
label.Tag = grammar;
// Brush will be used for animation. We use a dictionary to retrieve the corresponding
// brush for each grammar
SolidColorBrush brush = new SolidColorBrush(_colorDefaultBackground);
label.Background = brush;
_dictionaryBrushes.Add(grammar, brush);
dockPanel.Children.Add(checkBox);
dockPanel.Children.Add(label);
grammarItem.Header = dockPanel;
grammarItem.Tag = grammar;
grammarItem.Selected += new RoutedEventHandler(grammarItem_Selected);
grammarItem.Unselected += new RoutedEventHandler(grammarItem_Unselected);
return grammarItem;
}
// When the CheckBox is checked for a grammar, enable it
void checkBoxGrammarItem_Checked(object sender, RoutedEventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
Grammar grammar = (Grammar)(checkBox.Tag);
grammar.Enabled = true;
LogLine(grammar.Name + " enabled");
}
// When the CheckBox is unchecked for a grammar, disable it
void checkBoxGrammarItem_Unchecked(object sender, RoutedEventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
Grammar grammar = (Grammar)(checkBox.Tag);
grammar.Enabled = false;
LogLine(grammar.Name + " disabled");
}
void grammarItem_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem grammarItem = (TreeViewItem)sender;
// If the selected grammar is an SRGS Grammar then we enable the
// "Remove SRGS" button
if (_SRGSGrammars.Items.Contains(grammarItem))
{
_buttonRemoveSRGS.IsEnabled = true;
}
}
void grammarItem_Unselected(object sender, RoutedEventArgs e)
{
_buttonRemoveSRGS.IsEnabled = false;
}
// When this button is clicked, display an OpenFileDialog and load the
// grammar from the file
void _buttonAddSRGS_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "SRGS Grammars (*.xml;*.grxml)|*.xml;*.grxml";
dialog.Title = "Select SRGS Grammar";
Nullable<bool> result = dialog.ShowDialog();
if (result == true)
{
string fileName = dialog.FileName;
Log("Opening grammar " + fileName + "... ");
Grammar grammar;
try
{
grammar = new Grammar(fileName);
if (grammar.Name.Length == 0)
{
grammar.Name = fileName;
}
}
catch (FormatException exception)
{
LogLine("Failed");
LogLine(exception.Message);
return;
}
catch (InvalidOperationException exception)
{
LogLine("Failed");
LogLine(exception.Message);
return;
}
LogLine("Done");
// The grammar is successfully opened so add it to the list of SRGS Grammars
TreeViewItem grammarItem = CreateGrammarItem(grammar);
_SRGSGrammars.Items.Add(grammarItem);
_SRGSGrammars.IsExpanded = true;
if (_engineCreated)
{
// If the engine is already created then try to load the grammar into the engine.
// Disable the item control if the load operation fails.
grammarItem.IsEnabled = LoadGrammar(grammar);
}
else
{
grammarItem.IsEnabled = true;
}
}
}
void _buttonRemoveSRGS_Click(object sender, RoutedEventArgs e)
{
_buttonRemoveSRGS.IsEnabled = false;
TreeViewItem selectedGrammarItem = (TreeViewItem)_treeViewGrammars.SelectedItem;
// Unload and dispose the selected grammar if it's an SRGS Grammar.
if (_SRGSGrammars.Items.Contains(selectedGrammarItem))
{
if (_engineCreated && selectedGrammarItem.IsEnabled)
{
UnloadGrammar((Grammar)selectedGrammarItem.Tag);
}
_SRGSGrammars.Items.Remove(selectedGrammarItem);
}
}
#endregion Methods and Event Handlers for Grammars GroupBox
#region Methods and Event Handlers for Controls for displaying the status
// This method initializes the status controls which are used to display the
// status and result of the recognition operation.
private void InitializeStatusControls()
{
// Create the paragraph which is used to display the recognition result
// and add it to the flow document
_paragraphStatus = new Paragraph();
_flowDocumentStatus.Blocks.Add(_paragraphStatus);
// Initially there's no recognized audio
SetRecognizedAudio(null);
}
// Formats the message and the arguments and then displays it in the rich text box.
private void Log(string message, params object[] args)
{
string str = string.Format(System.Globalization.CultureInfo.CurrentUICulture, message, args);
_paragraphStatus.Inlines.Add(str);
_richTextBoxStatus.ScrollToEnd();
}
// Formats the message and the arguments and then displays it in the rich text box.
// The next log starts in a new line.
private void LogLine(string message, params object[] args)
{
string str = string.Format(System.Globalization.CultureInfo.CurrentUICulture, message, args);
_paragraphStatus.Inlines.Add(str);
_paragraphStatus.Inlines.Add(new LineBreak());
_richTextBoxStatus.ScrollToEnd();
}
// A specialized log for reporting events in different colors.
private void LogEvent(Brush color, string eventName, string message, params object[] args)
{
string str = string.Format(System.Globalization.CultureInfo.CurrentUICulture, message, args);
Run run = new Run("Event " + eventName + ": " + str);
run.Foreground = color;
_paragraphStatus.Inlines.Add(run);
_paragraphStatus.Inlines.Add(new LineBreak());
_richTextBoxStatus.ScrollToEnd();
}
// First clears the semantics tree view and then create the nodes using semanticValue
private void DisplaySemantics(SemanticValue semanticValue)
{
_treeViewSemantics.Items.Clear();
if (semanticValue != null)
{
// Create the root item
_treeViewSemantics.Items.Add(CreateSemanticItem("root", semanticValue));
}
}
// Creates the semantic items recursively.
private TreeViewItem CreateSemanticItem(string key, SemanticValue semanticValue)
{
if (semanticValue == null)
{
return null;
}
else
{
// The semantics item will be displayed as "key = value". If there's no
// value then just the "key" will be displayed
TreeViewItem item = new TreeViewItem();
string header = key;
if (semanticValue.Value != null)
{
header += " = " + semanticValue.Value.ToString();
}
item.Header = header;
// Expand the item
item.IsExpanded = true;
// Generate the child items recursively
foreach (KeyValuePair<String, SemanticValue> child in semanticValue)
{
item.Items.Add(CreateSemanticItem(child.Key, child.Value));
}
return item;
}
}
// Loads the recognizedAudio into a memory stream and creates a Soundplayer object
// for playing the audio. Passing null just disables the button
private void SetRecognizedAudio(RecognizedAudio recognizedAudio)
{
if (recognizedAudio == null)
{
_recognizedAudioStream = null;
_recognizedAudioPlayer = null;
_buttonRecognizedAudio.IsEnabled = false;
}
else
{
_recognizedAudioStream = new MemoryStream();
recognizedAudio.WriteToWaveStream(_recognizedAudioStream);
_recognizedAudioStream.Position = 0;
_recognizedAudioPlayer = new System.Media.SoundPlayer(_recognizedAudioStream);
_buttonRecognizedAudio.IsEnabled = true;
}
}
void _buttonRecognizedAudio_Click(object sender, RoutedEventArgs e)
{
// Play the recognized audio
_recognizedAudioPlayer.Play();
}
#endregion Methods and Event Handlers for Controls for displaying the status
#region Methods for Speech Recognition Engines
private void CreateRecognitionEngine(EngineType engineType)
{
_engineType = engineType;
// Create the paragraph and add it to the flow document which will
// display the result. Recognition result for each individual engine
// will be on a seperate paragraph.
_paragraphResult = new Paragraph();
_hypothesis = null;
_flowDocumentResult.Blocks.Add(_paragraphResult);
if (_engineType == EngineType.SpeechRecognizer)
{
Log("Creating SpeechRecognizer... ");
_speechRecognizer = new SpeechRecognizer();
_labelEngineState.Content = _speechRecognizer.State.ToString();
// Register the events
_speechRecognizer.StateChanged +=
new EventHandler<StateChangedEventArgs>(SpeechEngine_StateChanged);
_speechRecognizer.AudioLevelUpdated +=
new EventHandler<AudioLevelUpdatedEventArgs>(SpeechEngine_AudioLevelUpdated);
_speechRecognizer.SpeechDetected +=
new EventHandler<SpeechDetectedEventArgs>(SpeechEngine_SpeechDetected);
_speechRecognizer.SpeechHypothesized +=
new EventHandler<SpeechHypothesizedEventArgs>(SpeechEngine_SpeechHypothesized);
_speechRecognizer.SpeechRecognized +=
new EventHandler<SpeechRecognizedEventArgs>(SpeechEngine_SpeechRecognized);
_speechRecognizer.SpeechRecognitionRejected +=
new EventHandler<SpeechRecognitionRejectedEventArgs>(SpeechEngine_SpeechRecognitionRejected);
}
else
{
Log("Creating SpeechRecognitionEngine... ");
ComboBoxItem selectedItem = (ComboBoxItem)_comboBoxInstalledRecognizers.SelectedItem;
RecognizerInfo selectedRecognizer = (RecognizerInfo)selectedItem.Tag;
_speechRecognitionEngine = new SpeechRecognitionEngine(selectedRecognizer);
_isRecognizing = false;
// Register the events
_speechRecognitionEngine.RecognizeCompleted +=
new EventHandler<RecognizeCompletedEventArgs>(SpeechEngine_RecognizeCompleted);
_speechRecognitionEngine.AudioLevelUpdated +=
new EventHandler<AudioLevelUpdatedEventArgs>(SpeechEngine_AudioLevelUpdated);
_speechRecognitionEngine.SpeechDetected +=
new EventHandler<SpeechDetectedEventArgs>(SpeechEngine_SpeechDetected);
_speechRecognitionEngine.SpeechHypothesized +=
new EventHandler<SpeechHypothesizedEventArgs>(SpeechEngine_SpeechHypothesized);
_speechRecognitionEngine.SpeechRecognized +=
new EventHandler<SpeechRecognizedEventArgs>(SpeechEngine_SpeechRecognized);
_speechRecognitionEngine.SpeechRecognitionRejected +=
new EventHandler<SpeechRecognitionRejectedEventArgs>(SpeechEngine_SpeechRecognitionRejected);
}
_engineCreated = true;
LogLine("Done");
}
private void DisposeRecognitionEngine()
{
if (_engineType == EngineType.SpeechRecognizer)
{
Log("Disposing SpeechRecognizer... ");
_speechRecognizer.Dispose();
_speechRecognizer = null;
_labelEngineState.Content = "N/A";
}
else
{
Log("Disposing SpeechRecognitionEngine... ");
_speechRecognitionEngine.Dispose();
_speechRecognitionEngine = null;
}
_engineCreated = false;
SetRecognizedAudio(null);
LogLine("Done");
}
private bool AskForNewEngine()
{
string SR = "System.Speech.Recognition.SpeechRecognizer";
string SRE = "System.Speech.Recognition.SpeechRecognitionEngine";
string currentEngine, newEngine;
if (_engineType == EngineType.SpeechRecognizer)
{
currentEngine = SR;
newEngine = SRE;
}
else
{
currentEngine = SRE;
newEngine = SR;
}
MessageBoxResult result = MessageBox.Show(
"This tool allows you to use only one engine at a time. You have to dispose the " +
currentEngine + " object that you've created before in order to create a new engine. " +
"Do you want me to dispose it now and create a new " + newEngine + "?",
"Engine Already Created", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
// First unload all the grammars then dispose grammars created by GrammarBuilders.
// Other grammars will not be disposed since they will be used by other
// recognition engines later.
UnloadGrammars();
DisposeGrammarBuilders();
// Dispose the engine and update the controls
DisposeRecognitionEngine();
UpdateTabItemSpeechRecognizer();
UpdateTabItemSpeechRecognitionEngine();
return true;
}
else
{
return false;
}
}
private System.Globalization.CultureInfo GetCulture()
{
if (_engineType == EngineType.SpeechRecognizer)
{
return _speechRecognizer.RecognizerInfo.Culture;
}
else
{
return _speechRecognitionEngine.RecognizerInfo.Culture;
}
}
private void LoadGrammars()
{
LogLine("Loading grammars");
LoadGrammars(_dictationGrammars);
LoadGrammars(_grammarBuilders);
LoadGrammars(_SRGSGrammars);
}
private void LoadGrammars(TreeViewItem grammars)
{
foreach (TreeViewItem grammarItem in grammars.Items)
{
grammarItem.IsEnabled = LoadGrammar((Grammar)grammarItem.Tag);
}
}
private bool LoadGrammar(Grammar grammar)
{
try
{
if (_engineType == EngineType.SpeechRecognizer)
{
_speechRecognizer.LoadGrammar(grammar);
}
else
{
_speechRecognitionEngine.LoadGrammar(grammar);
}
}
catch (InvalidOperationException e)
{
LogLine("Failed to load grammar {0}: {1}", grammar.Name, e.Message);
return false;
}
return true;
}
private bool UnloadGrammar(Grammar grammar)
{
try
{
if (_engineType == EngineType.SpeechRecognizer)
{
_speechRecognizer.UnloadGrammar(grammar);
}
else
{
_speechRecognitionEngine.UnloadGrammar(grammar);
}
}
catch (InvalidOperationException e)
{
LogLine("Failed to unload grammar {0}: {1}", grammar.Name, e.Message);
return false;
}
return true;
}
private void UnloadGrammars()
{
Log("Unloading grammars... ");
if (_engineType == EngineType.SpeechRecognizer)
{
_speechRecognizer.UnloadAllGrammars();
}
else
{
_speechRecognitionEngine.UnloadAllGrammars();
}
LogLine("Done");
EnableGrammarItems(_dictationGrammars);
EnableGrammarItems(_grammarBuilders);
EnableGrammarItems(_SRGSGrammars);
}
static private void EnableGrammarItems(TreeViewItem grammars)
{
foreach (TreeViewItem grammarItem in grammars.Items)
{
grammarItem.IsEnabled = true;
}
}
private bool SetInputToWaveFile()
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.DefaultExt = ".wav";
dialog.Filter = "Audio Files (*.wav)|*.wav";
dialog.Title = "Select Audio Input file";
Nullable<bool> result = dialog.ShowDialog();
if (result == true)
{
string fileName = dialog.FileName;
Log("Setting input to wave file " + fileName + "... ");
try
{
_speechRecognitionEngine.SetInputToWaveFile(fileName);
}
catch (FormatException)
{
LogLine("Failed");
LogLine(fileName + " is not a valid wave file.");
return false;
}
catch (InvalidOperationException e)
{
LogLine("Failed");
LogLine(e.Message);
return false;
}
_textBoxWaveFile.Text = fileName;
_audioInput = AudioInput.AudioFile;
LogLine("Done");
return true;
}
else
{
return false;
}
}
private void SetInputToDefaultAudioDevice()
{
Log("Setting input to default audio device... ");
_speechRecognitionEngine.SetInputToDefaultAudioDevice();
_audioInput = AudioInput.DefaultAudioDevice;
LogLine("Done");
}
private void EnableSpeechRecognizer()
{
Log("Enabling SpeechRecognizer... ");
_speechRecognizer.Enabled = true;
LogLine("Done");
}
private void DisableSpeechRecognizer()
{
Log("Disabling SpeechRecognizer... ");
_speechRecognizer.Enabled = false;
LogLine("Done");
}
private void PrepareForAsynchronousRecognition()
{
_recognitionMode = RecognitionMode.Asynchronous;
LogLine("Ready for asynchronous recognition");
}
private void PrepareForSynchronousRecognition()
{
_recognitionMode = RecognitionMode.Synchronous;
LogLine("Ready for synchronous recognition");
}
// Calls either the RecognizeAsync or Recognize method of SpeechRecognitionEngine
// class depending on the recognition mode selection of the user.
private bool StartRecognition()
{
if (_recognitionMode == RecognitionMode.Asynchronous)
{
Log("Starting asynchronous recognition... ");
try
{
_speechRecognitionEngine.RecognizeAsync(RecognizeMode.Multiple);
}
catch (InvalidOperationException e)
{
_isRecognizing = false;
LogLine("Failed");
LogLine(e.Message);
return false;
}
_isRecognizing = true;
LogLine("Done, Please Speak");
return true;
}
else
{
LogLine("Recognizing synchronously");
try
{
_speechRecognitionEngine.Recognize();
}
catch (InvalidOperationException e)
{
LogLine(e.Message);
return false;
}
return true;
}
}
// Emulates recognition by calling EmulateRocgnizeAsync method.
private void EmulateRecognize()
{
try
{
if (_engineType == EngineType.SpeechRecognizer)
{
_speechRecognizer.EmulateRecognizeAsync(_textBoxEmulateSR.Text);
}
else
{
_speechRecognitionEngine.EmulateRecognizeAsync(_textBoxEmulateSRE.Text);
}
}
catch (ArgumentException)
{
LogLine("Please enter a text to emulate");
}
catch (InvalidOperationException e)
{
LogLine("Can't emulate: " + e.Message);
}
}
#endregion Methods for Speech Recognition Engines
#region Event Handlers for Speech Recognition Engines
// StateChanged event is generated when the state of the SpeechRecognizer
// object changes
void SpeechEngine_StateChanged(object sender, StateChangedEventArgs e)
{
_labelEngineState.Content = e.RecognizerState.ToString();
LogEvent(_brushDefault, "StateChanged", "RecognizerState={0}", e.RecognizerState);
}
// RecognizeCompleted event is generated by SpeechRecognitionEngine when
// an asynchronous recognition operation completes.
void SpeechEngine_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)
{
_isRecognizing = false;
UpdateGroupBoxAudioInput();
UpdateGroupBoxRecognitionSRE();
}
// AudioLevelUpdated event is generated by recognition engines (both SpeechRecognizer and
// SpeechRecognitionEngine) when there's a change in the input audio level.
void SpeechEngine_AudioLevelUpdated(object sender, AudioLevelUpdatedEventArgs e)
{
_progressBarAudioLevel.Value = e.AudioLevel;
}
// SpeechDetected event is generated by recognition engines (both SpeechRecognizer and
// SpeechRecognitionEngine) when speech is detected.
void SpeechEngine_SpeechDetected(object sender, SpeechDetectedEventArgs e)
{
LogEvent(_brushDefault, "SpeechDetected", "AudioPosition={0}", e.AudioPosition);
}
// SpeechHypothesized event is generated by recognition engines (both SpeechRecognizer and
// SpeechRecognitionEngine) when part of the audio input speech has been tentatively recognized.
// This Method displays the last hypothesis in different color temporarily in the result text box
// and starts a background color animation on the grammar which generated the hypothesis.
void SpeechEngine_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
{
LogEvent(_brushHypothesized, "SpeechHypothesized", "Confidence={0:0.00} Grammar={1} Hypothesis=\"{2}\"",
e.Result.Confidence, e.Result.Grammar.Name, e.Result.Text);
if (_hypothesis != null)
{
// Remove the previous hypothesis from the result.
_paragraphResult.Inlines.Remove(_hypothesis);
}
// Display the new hypothesis in a different color.
_hypothesis = new Run(e.Result.Text);
_hypothesis.Foreground = _brushHypothesized;
_paragraphResult.Inlines.Add(_hypothesis);
_richTextBoxResult.ScrollToEnd();
// Get the background brush for the grammar which generated the hypothesis.
SolidColorBrush brush = _dictionaryBrushes[e.Result.Grammar];
// Start a two second color animation.
ColorAnimation animation = new ColorAnimation(_colorHypothesized, _colorDefaultBackground, new Duration(TimeSpan.FromSeconds(2)));
brush.BeginAnimation(SolidColorBrush.ColorProperty, animation);
}
// SpeechRecognized event is generated by recognition engines (both SpeechRecognizer and
// SpeechRecognitionEngine) when speech has been recognized.
// This Method displays the recognition result in the result text box, starts a background
// color animation on the grammar which recognized the speech, displays the semantic
// information and stores the recognized audio so it can be listened later.
void SpeechEngine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
LogEvent(_brushRecognized, "SpeechRecognized", "Confidence={0:0.00} Grammar={1} Result=\"{2}\"",
e.Result.Confidence, e.Result.Grammar.Name, e.Result.Text);
// Remove the hypothesis from the result if there's any.
if (_hypothesis != null)
{
_paragraphResult.Inlines.Remove(_hypothesis);
_hypothesis = null;
}
// Display the result.
Run result = new Run(e.Result.Text + ". ");
result.Foreground = _brushRecognized;
_paragraphResult.Inlines.Add(result);
_richTextBoxResult.ScrollToEnd();
// Get the background brush for the grammar which recognized the speech.
SolidColorBrush brush = _dictionaryBrushes[e.Result.Grammar];
// Start a two second color animation.
ColorAnimation animation = new ColorAnimation(_colorRecognized, _colorDefaultBackground, new Duration(TimeSpan.FromSeconds(3)));
brush.BeginAnimation(SolidColorBrush.ColorProperty, animation);
// Display the semantics for the result.
DisplaySemantics(e.Result.Semantics);
// Store the reconized audio.
SetRecognizedAudio(e.Result.Audio);
}
// SpeechRecognitionRejected event is generated by recognition engines (both SpeechRecognizer and
// SpeechRecognitionEngine) when the engine detects speech, but can only return candidate phrases
// with low confidence levels.
void SpeechEngine_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
LogEvent(_brushRejected, "SpeechRecognitionRejected", "Confidence={0:0.00}", e.Result.Confidence);
if (_hypothesis != null)
{
_paragraphResult.Inlines.Remove(_hypothesis);
_hypothesis = null;
}
// Start a color animation.
ColorAnimation animation = new ColorAnimation(_colorRejected, _colorDefaultBackground, new Duration(TimeSpan.FromSeconds(1)));
_treeViewGrammarsBackground.BeginAnimation(SolidColorBrush.ColorProperty, animation);
}
#endregion Event Handlers for Speech Recognition Engines
#region Sample GrammarBuilders
// Recognizes phrases like:
// Please check the status of yesterday's flight from Seattle to Boston
// What is the status of today's flight from Sea-Tac to T. F. Green
// Status of tomorrow's flight from Providence to Boston Logan International Airport
static private Grammar FlightStatusGrammarBuilder(System.Globalization.CultureInfo culture)
{
//Providence T. F. Green Internatinoal Airport
GrammarBuilder PVD = new GrammarBuilder();
Choices PVDchoices = new Choices("Providence",
"T. F. Green",
"Providence T. F. Green Internatinoal Airport");
PVD.Append(PVDchoices);
PVD.Append(new SemanticResultValue("PVD"));
//Boston Logan Internatinoal Airport
GrammarBuilder BOS = new GrammarBuilder();
Choices BOSchoices = new Choices("Boston",
"Logan",
"Boston Logan Internatinoal Airport");
BOS.Append(BOSchoices);
BOS.Append(new SemanticResultValue("BOS"));
//Seattle-Tacoma International Airport
GrammarBuilder SEA = new GrammarBuilder();
Choices SEAchoices = new Choices("Seattle",
"Tacoma",
"Sea-Tac",
"Seattle-Tacoma Internatinoal Airport");
SEA.Append(SEAchoices);
SEA.Append(new SemanticResultValue("SEA"));
//All airports
GrammarBuilder airports = new GrammarBuilder(new Choices(PVD, BOS, SEA));
//Dates
DateTime today = DateTime.Today;
DateTime yesterday = today.AddDays(-1.0);
DateTime tomorrow = today.AddDays(1.0);
Choices datesChoices = new Choices();
datesChoices.Add(new GrammarBuilder(new SemanticResultValue("yesterday's", yesterday.ToShortDateString())),
new GrammarBuilder(new SemanticResultValue("today's", today.ToShortDateString())),
new GrammarBuilder(new SemanticResultValue("tomorrow's", tomorrow.ToShortDateString())));
GrammarBuilder dates = new GrammarBuilder(datesChoices);
//Final Grammar
GrammarBuilder flightStatusGrammar = new GrammarBuilder();
flightStatusGrammar.Culture = culture;
flightStatusGrammar.Append(new Choices("Please check the status of",
"What is the status of",
"Status of"));
flightStatusGrammar.Append(new SemanticResultKey("date", dates));
flightStatusGrammar.Append("flight from");
flightStatusGrammar.Append(new SemanticResultKey("from", airports));
flightStatusGrammar.Append("to");
flightStatusGrammar.Append(new SemanticResultKey("to", airports));
return new Grammar(flightStatusGrammar);
}
#endregion Sample GrammarBuilders
#region IDisposable Members
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// Dispose managed resources.
if (_speechRecognizer != null)
{
_speechRecognizer.Dispose();
_speechRecognizer = null;
}
if (_speechRecognitionEngine != null)
{
_speechRecognitionEngine.Dispose();
_speechRecognitionEngine = null;
}
if (_recognizedAudioStream != null)
{
_recognizedAudioStream.Dispose();
_recognizedAudioStream = null;
}
if (_recognizedAudioPlayer != null)
{
_recognizedAudioPlayer.Dispose();
_recognizedAudioPlayer = null;
}
}
// There are no unmanaged resources to release.
}
disposed = true;
}
#endregion
}
}