-
音频与视频:

语音识别-让WPF听

在前面的文章中我们提到我们可以使用 SpeechSynthesizer 类把文字变成语音. 这篇文章中我们反过来, 把语音变成文字, 使用 System.Speech 中的 SpeechRecognition 类. 默认的情况下你的解决方案不包括这个引用, 不过我们可以很简答的添加它. 不同版本的Visual Studio有点不同, 不过它大概长这样:

添加完引用之后, 我们来做一个非常简单的语音识别:

<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionTextSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SpeechRecognitionTextSample" Height="200" Width="300">
    <DockPanel Margin="10">
        <TextBox Margin="0,10" Name="txtSpeech" AcceptsReturn="True" />
    </DockPanel>
</Window>
using System;
using System.Speech.Recognition;
using System.Windows;

namespace WpfTutorialSamples.Audio_and_Video
{
	public partial class SpeechRecognitionTextSample : Window
	{
		public SpeechRecognitionTextSample()
		{
			InitializeComponent();
			SpeechRecognizer speechRecognizer = new SpeechRecognizer();
		}
	}
}

就这样, 上面截图中的文字是 SpeechRecognizer 通过我的耳麦听写并插入到输入框中的.

SpeechRecognizer对象初始化的时候, Windows 启动语音识别程序, 处理剩下的事情, 把听写的结果输入到活动的程序中, 在我的电脑中, 语音识别程序长这样:

如果你从来没有使用过语音识别程序的话, Windows 会使用向导来帮助你开始使用和做些微小的调整

第一个例子允许你听写文字到你的应用程序中, 这很好, 但如果你要听到指令怎么办? Windows 和 WPF 会一起把你的按钮控件变成可以通过语音识别程序点到的指令, 你不用做什么, 就像这样:

<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionTextCommandsSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SpeechRecognitionTextCommandsSample" Height="200" Width="300">
    <DockPanel Margin="10">
        <WrapPanel DockPanel.Dock="Top">
            <Button Name="btnNew" Click="btnNew_Click">New</Button>
            <Button Name="btnOpen" Click="btnOpen_Click">Open</Button>
            <Button Name="btnSave" Click="btnSave_Click">Save</Button>
        </WrapPanel>
        <TextBox Margin="0,10" Name="txtSpeech" AcceptsReturn="True" TextWrapping="Wrap" />
    </DockPanel>
</Window>
using System;
using System.Speech.Recognition;
using System.Windows;

namespace WpfTutorialSamples.Audio_and_Video
{
	public partial class SpeechRecognitionTextCommandsSample : Window
	{
		public SpeechRecognitionTextCommandsSample()
		{
			InitializeComponent();
			SpeechRecognizer recognizer = new SpeechRecognizer();
		}

		private void btnNew_Click(object sender, RoutedEventArgs e)
		{
			txtSpeech.Text = "";
		}

		private void btnOpen_Click(object sender, RoutedEventArgs e)
		{
			MessageBox.Show("Command invoked: Open");
		}

		private void btnSave_Click(object sender, RoutedEventArgs e)
		{
			MessageBox.Show("Command invoked: Save");
		}
	}
}

你可以试下在运行上面的例子的时候说出一些指令, 比如, "New" 或者 "Open". 这会允许你听写文字到文本框中, 同时使用语音指令控制按钮, 这很酷吧?

特定的指令

在上面的例子中, Windows 会自动的进入听写模式并在文本框获得焦点额度同时, Window会尝试区分语音指令和听写文字, 然而, 某些时候, 这很困难,

上面的例子专注于听写和UI的互动, 下面我们来尝试关注特定的语音指令, 这也意味着我们将忽略听写, 就算文本框已经获得焦点.

我们将使用 SpeechRecognitionEngine 而不是 SpeechRecognizer. 这两个类最大的不同在于 SpeechRecognitionEngine 不会需要 Windows 语音识别程序运行也不需要语音识别向导, 它只关注于你在类中配置的特定语法.

在下一个例子中, 我们将会输入一些语音指定到 语音识别引擎. 我们希望它听到一个指令(属性) 和 一个值. 我们将用听到的属性和值来改变Label控件文字的颜色大小和粗细. 在我给出完整的例子前, 我想指出指令是怎么添加到语音识别引擎中的.

GrammarBuilder grammarBuilder = new GrammarBuilder();
Choices commandChoices = new Choices("weight", "color", "size");
grammarBuilder.Append(commandChoices);

Choices valueChoices = new Choices();
valueChoices.Add("normal", "bold");
valueChoices.Add("red", "green", "blue");
valueChoices.Add("small", "medium", "large");
grammarBuilder.Append(valueChoices);

speechRecognizer.LoadGrammar(new Grammar(grammarBuilder));

我们使用 GrammarBuilder来定义一些语法规则. 它有几个添加方法, 最简单的是Append(). 这个方法接受一个choices列表. 我们实例化 Choices 并加入语音指令的第一部分(命令/属性)的选项.

现在每次你使用 GrammerBuilder 的 Append 方法的时候, 你指示它听取一个单词. 对于这个例子而言, 我们希望听取两个单词, 一个用于 命令/属性, 另一个用于值(大小/颜色/粗细), 所以我们创建了两个Choices, Append 到了 GrammerBuilder 里面.

最后, 我们把整个语法 使用 LoadGrammer() 载入到了 GrammarBuilder 实例里.

以上就是指令怎么添加到语音识别引擎中的, 来看看完整的例子:

<Window x:Class="WpfTutorialSamples.Audio_and_Video.SpeechRecognitionCommandsSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SpeechRecognitionCommandsSample" Height="200" Width="325"
        Closing="Window_Closing">
    <DockPanel>
        <WrapPanel DockPanel.Dock="Bottom" HorizontalAlignment="Center" Margin="0,10">
            <ToggleButton Name="btnToggleListening" Click="btnToggleListening_Click">Listen</ToggleButton>
        </WrapPanel>
        <Label Name="lblDemo" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48">Hello, world!</Label>
    </DockPanel>
</Window>
using System;
using System.Globalization;
using System.Speech.Recognition;
using System.Windows;
using System.Windows.Media;

namespace WpfTutorialSamples.Audio_and_Video
{
	public partial class SpeechRecognitionCommandsSample : Window
	{
		private SpeechRecognitionEngine speechRecognizer = new SpeechRecognitionEngine();

		public SpeechRecognitionCommandsSample()
		{
			InitializeComponent();
			speechRecognizer.SpeechRecognized += speechRecognizer_SpeechRecognized;

			GrammarBuilder grammarBuilder = new GrammarBuilder();
			Choices commandChoices = new Choices("weight", "color", "size");
			grammarBuilder.Append(commandChoices);

			Choices valueChoices = new Choices();
			valueChoices.Add("normal", "bold");
			valueChoices.Add("red", "green", "blue");
			valueChoices.Add("small", "medium", "large");
			grammarBuilder.Append(valueChoices);

			speechRecognizer.LoadGrammar(new Grammar(grammarBuilder));
			speechRecognizer.SetInputToDefaultAudioDevice();
		}

		private void btnToggleListening_Click(object sender, RoutedEventArgs e)
		{
			if(btnToggleListening.IsChecked == true)
				speechRecognizer.RecognizeAsync(RecognizeMode.Multiple);
			else
				speechRecognizer.RecognizeAsyncStop();
		}

		private void speechRecognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
		{
			lblDemo.Content = e.Result.Text;
			if(e.Result.Words.Count == 2)
			{
				string command = e.Result.Words[0].Text.ToLower();
				string value = e.Result.Words[1].Text.ToLower();
				switch(command)
				{
					case "weight":
						FontWeightConverter weightConverter = new FontWeightConverter();
						lblDemo.FontWeight = (FontWeight)weightConverter.ConvertFromString(value);
						break;
					case "color":
						lblDemo.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString(value));
						break;
					case "size":
						switch(value)
						{
							case "small":
								lblDemo.FontSize = 12;
								break;
							case "medium":
								lblDemo.FontSize = 24;
								break;
							case "large":
								lblDemo.FontSize = 48;
								break;
						}
						break;
				}
			}
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			speechRecognizer.Dispose();
		}
	}
}

截图中就是例子运行后我说了 "weight bold" 和 "color blue" 的结果, 很酷吧?

这个例子语法相关的已经解释了, 这个程序的界面也非常简单, 让我们来看下剩下的代码吧.

我们使用 ToggleButton 来启用或禁用识别, 对应的方法是 RecognizeAsync() 和 RecognizeAsyncStop(). RecognizeAsync() 使用一个参数来告诉识别引擎这是一个单一指令识别或多指令识别. 比如, 我们想要程序接受多个指令, 这个参数就是 Multiple. 按下这个按钮就会启动识别, 按钮按下的时候就是识别已启用, 按钮松开的时候就是识别已禁用, 按钮视觉上指示引擎的状态.

在语法建立完后最有意思的地方在于我们怎么解释指令, 我们在SpeechRecognitionCommandsSample() 中订阅了 SpeechRecognized 事件. 我们使用完全识别的文本来更新label控件的内容, 然后我们使用 e.Result.Words 来处理指令具体的要求.

首先我们检查确实有两个单词, 如果确实有两个, 那么我们根据情况分别处理.

对于粗细和颜色, 我们可以使用对应的FontWeightConverter/ColorConverter转换指令的String值到label控件可以处理的形式, 然而对于大小, 我们手动处理因为我们选择的单词不是可以自动转换的形式. 请注意你应该处理异常, 应为如果识别结果为 "weight blue" 的话你将试图为label控件的粗细设定为蓝色, 这当然会引发异常.

小结

正如你所看到的, WPF中的语音识别是简单而强大的, 你可以同过听写和(或)识别语音指令的方式来为你的应用程序提供额外的输入方式.