说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。
音频
在WPF中播放音频有以下几种方式:
SoundPlayer
SoundPlayer是随.NET Framework 2.0一起发布的类,其实对Win32 PlaySound API的一个封装,这是在WPF中播放声音最简单的方式,所以其也有一系列限制。
下面是一段简单的示例:
1 SoundPlayer player = new SoundPlayer("lily.wav"); 2 player.Play();
SoundPlayer构造函数接受一个文件名或一个URL。上面代码中调用的Play方法将会在另一个线程上异步播放声音。另一个方法PlaySync在当前线程上播放该声音,PlayLooping会实现循环异步播放声音,直至显式调用Stop方法停止为止。
默认情况下,SoundPlayer会在第一次调用Play相关方法时才加载文件,但由于文件可能存在于网络上,由于缓冲文件等原因可能造成播放前的停顿。解决这个问题,我们可以手动调用Load或LoadAsync方法提前加载文件。
使用位于System.Media下的SystemSounds类可以播放一些系统声音。SystemSounds包含很多类,如Beep,Exclamation,Question等表示不同的系统声音,这些属性都是SystemSound类型,可以直接调用它们的Play方法异步非循环的播放声音。
SoundPlayerAction
这个类最大的作用就是可以将SoundPlayer无缝的融入XAML,在不用编写C#代码的情况下播放声音。SoundPlayerAction派生自TriggerAction,我们可以直接使用它来设置Trigger的Actions属性。下面的例子中我们给按钮添加一个事件触发器,实现在点击按钮时播放一段声音。
1 <Button>A Button With Sounds 2 <Button.Triggers> 3 <EventTrigger RoutedEvent="Button.Click"> 4 <EventTrigger.Actions> 5 <SoundPlayerAction Source="click.wav"/> 6 </EventTrigger.Actions> 7 </EventTrigger> 8 <EventTrigger RoutedEvent="Button.MouseEnter"> 9 <EventTrigger.Actions> 10 <SoundPlayerAction Source="hover.wav"/> 11 </EventTrigger.Actions> 12 </EventTrigger> 13 </Button.Triggers> 14 </Button>
同样使用这个类有更多的限制,你甚至不能循环播放或预先加载声音。
MediaPlayer
这个类位于System.Windows.Media命名空间,它是WPF中一个可选的较高级的音频播放功能工具。这个类基于Windows Media Player构建,支持Windows Media Player所支持的多种音频格式(当然其也支持视频,在介绍WPF中视频一节还会再次深入讲述这个类)。通过多个Media Player的实例,可以实现音频同时播放(一个实例同一时间只能播放一个文件)。float类型的Volumn属性用于调整音量,范围由0到1,默认值是0.5。下面列举更多的音频控制方式:
照例给出一段简单的示例代码:
1 MediaPlayer player = new MediaPlayer(); 2 player.Open(new Uri("moon.wma", UriKind.Relative)); 3 player.Play();
如代码所示,我们用Open打开文件,Play播放文件。相应的还有Pause,Stop和Close来控制文件播放。
视频
WPF对视频的支持也是通过前面介绍的Media Player,MediaElement级MediaTimeLine来实现。所以Windows Media Player所支持的视频格式如wmv,avi,mpg都可以在WPF中使用。但在程序中使用MediaPlayer类时,要保证最终运行的系统中安装有Windows Media Player 10或更高版本。有关Media Element的话题同样见多媒体控件部分。
语音
.NET Framework中新增了一系列语音相关的API,位于System.Speech命名空间下,这些类主要支持语音识别和语音合成,这套API并没有为WPF做特殊处理,即没有添加依赖属性,路由事件等特性,所以我们只能在C#代码中使用这些类。
语言合成由Microsoft SAPI SDK来支持。SAPI SDK中包含了几套不同的语音,使合成语音有不同的效果。仍然通过一段代码来展示语音合成。
1 SpeechSynthesizer synthesizer = new SpeechSynthesizer(); 2 synthesizer.Speak("测试语音合成");
要使这段代码工作,需要引用System.Speech.dll,并引用System.Speech.Synthesis命名空间。合成语音的声音、速度和音量可以在控制面板语音合成项中设置。
使用SpeakAsync方法可以异步播放合成语言,此时也可以通过设置SpeechSynthesizer的Rate和Volumn属性来改变声音的速度和音量。Rate的范围是-10到10,而Volumn的范围是0到100。通过调用SpeakAsyncCancelAll取消处于等待状态的语音合成。
调用SelectVoice方法可以改变合成语音使用的声音,如代码:
1 synthesizer.SelectVoice("Microsoft Sam");
或者通过指定性别和年龄来选择一个声音,如:
1 synthesizer.SelectVoiceByHints(VoiceGender.Female,VoiceAge.Senior);
上面介绍的所有例子,转换后的语音会默认输出到扬声器,通过以下代码你可以将其输出到一个.wav文件:
1 synthesizer.SetOutputToWaveFile(@"C:\mySpeech.wav");
而如下的代码可以将输出恢复到扬声器:
1 synthesizer.SetOutputToDefaultAudioDevice();
新的Speech API支持W3C标准 – 语言合成标记语言SSML。通过SSML,可以将复杂的语言封装在一个单独的模块中。通过调用SpeakSsml和SpeakSsmlAsync方法可以向SpeechSynthesizer传入SSML内容,SSML是标准的XML格式文件,我们可以通过PromptBuilder以编程方式轻松的构件SSML。下面是一段PromptBuilder的使用示例:
1 //构造PromptBuilder 2 PromptBuilder promptBuilder = new PromptBuilder(); 3 //普通文本 4 promptBuilder.AppendText("A common text Line"); 5 //逐字读出每一个字母 6 promptBuilder.AppendTextWithHint("WPF", SayAs.SpellOut); 7 //如下可以更好的阅读一个时间 8 promptBuilder.AppendTextWithHint(DateTime.Now.ToString("hh:mm"), SayAs.Time); 9 //停顿 10 promptBuilder.AppendBreak(new TimeSpan(0, 0, 2)); 11 //单独设置一段文本的语音效果,也可使文本形成段落效果 12 promptBuilder.StartVoice("Microsoft Sam"); 13 promptBuilder.StartStyle(new PromptStyle(PromptRate.ExtraFast)); 14 promptBuilder.AppendText("A special text line"); 15 promptBuilder.EndStyle(); 16 promptBuilder.EndVoice(); 17 //甚至可以这样播放一段SSML 18 promptBuilder.AppendAudio("sample.wav"); 19 //传入SpeechSynthesizer 20 SpeechSynthesizer synthesizer = new SpeechSynthesizer(); 21 synthesizer.SpeakAsync(promptBuilder);
如代码,我们将PromptBuilder对象直接传给Speak/SpeakAsync中接受PromptBuilder的重载。通过PromptBuilder的ToXml()方法,我们可以得到以xml表示的SSML。作为一个W3C标准XML格式的SSML可以很方便的用于.NET以外的平台。
Speak与SpeakAsync还有一个接受FilePrompt的重载,通过这个重载我们可以方便的将多种格式的文件传入SpeechSynthesizer,下面代码展示了部分FilePrompt的构建:
1 synthesizer.SpeakAsync(new FilePrompt("text.txt", SynthesisMediaType.Text)); 2 synthesizer.SpeakAsync(new FilePrompt("content.ssml", SynthesisMediaType.Ssml)); 3 synthesizer.SpeakAsync(new FilePrompt("sound.wav", SynthesisMediaType.WaveAudio));
这里我们看到了第二种实现在文本转语言中播放wav的方法。
语音识别
语音识别与上文语言合成进行恰好相反的工作,其由音频中提取语言,并将其转换为文本,要使用语音识别需要计算机中安装有语音识别引擎,Windows Vista自带了一个语音识别引擎,Office Xp之后的版本也带有一个语音识别引擎。
要在代码中实现语音识别也要先添加对System.Speech.dll引用,不同的是这里要引用System.Speech.Recognition命名空间。下面代码给出一个最基本的使用的范例:
1 SpeechRecognizer recognizer = new SpeechRecognizer(); 2 recognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(recognizer_SpeechRecognized);
当语音被识别后,事件被触发,回调函数被调用,在其中可以处理识别到的文本:
1 void recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e) 2 { 3 textBox.Text += e.Result.Text; 4 }
提示:在一个系统上第一次使用语音识别时要先完成一个语音识别向导,另外,在Windows Vista以上版本中,语音识别被集成到任何一个可以获取输入的文本框(WPF或WinForm等程序中的)中,只需要对着麦克风讲话系统就会将识别结果填入文本框中。
使用SRGS
语音识别规范(SRGS – Speech Recognition Grammar Specification)是W3C标准,你可以定义一个SRGS语法,通过其方便SpeechRecognizer捕获一些有效的输入,并忽略无意义的结果。SRGS也被定义成标准的XML格式,在.NET中使用SRGS,我们需要先构造一个SrgsDocument对象(所有SRGS相关的对象都定义于System.Speech.Recognition.SrgsGrammar命名空间中),下面的代码中我们定义了一个SrgsDocument并将其传入SpeechRecognizer:
1 SpeechRecognizer recognizer = new SpeechRecognizer(); 2 SrgsDocument doc = new SrgsDocument("grammar.xml"); 3 recognizer.LoadGrammar(new Grammar(doc));
上面的例子中,Srgs来自xml文件,我们也可以在内存中定义并使用SRGS。下面的代码中我们定义了一个SrgsDocument,这个文档表示由输入音频中匹配stop和go两条命令。
1 SpeechRecognizer recognizer = new SpeechRecognizer(); 2 SrgsDocument doc = new SrgsDocument(); 3 SrgsRule command = new SrgsRule("command",new SrgsOneOf("stop","go")); 4 doc.Rules.Add(command); 5 doc.Root = command; 6 recognizer.LoadGrammar(new Grammar(doc));
使用GrammarBuilder
如同我们可以使用PromptBuilder方便的构建SSML,.NET中也提供了一个GrammarBuilder对象来定义SRGS;以解决手工编写SRGS xml或使用SrgsGrammar API定义SRGS都很麻烦的问题,下面的代码使用GrammerBuilder实现了与上面代码等价的功能:
1 SpeechRecognizer recognizer = new SpeechRecognizer(); 2 GrammarBuilder builder = new GrammarBuilder(new Choices("stop","go")); 3 recognizer.LoadGrammar(new Grammar(builder));
VideoBrush
通过使用视频刷可以对一个区域使用视频进行填充,这个画刷SourceName的值需要设置为一个MediaElement控件的x:Name属性。这个MediaElement用来加载视频,我们需要其隐藏并不响应任何鼠标事件,这可以通过将MediaElement的透明度设置为0并将IsHitTestVisible属性设置为false来做到。在加载视频后我们可以通过Stretch等属性控制视频刷的效果。
下面的示例中,我们通过视频刷绘制文本框的前景色:
1 <TextBlock FontFamily="Courier New" FontSize="72" FontWeight="Bold" TextWrapping="Wrap" Text="Hello"> 2 <TextBlock.Foreground> 3 <VideoBrush SourceName="vid" /> 4 </TextBlock.Foreground> 5 </TextBlock>
关于MediaElement,其对音频及视频的支持统一放到控件之多媒体控件一节介绍
本文完
参考:
《WPF揭秘》