Win10开发中最具有系统特色的功能点绝对少不了集成Cortana语音指令,其实Cortana语音指令在以前的wp8/8.1时就已经存在了,发展到了Win10,Cortana最明显的进步就是开始支持调用App 的App Service。当然App Service也是Win10 App的新特性之一,通过调用App Service就可以在App没有前台运行的时候为Cortana提供数据交互。这样一来Cortana就具有了两种App交互方式:

  • Cortana语音指令与前台App的交互
  • Cortana语音指令与后台服务的交互

今天我们以前先看看Cortana与前台App的交互,与前台App的交互允许Cortana通过特定的语音指令来唤起App,并传递给App用户想要的是怎样的信息,App被唤起后通过用户的语音指令参数就可以判断接下来要做怎样的处理。

下面我们做一个App来集成Cortana,通过语音指令来启动App并根据参数来响应不同的用户请求,先看效果图:

Ok 开工!

创建VCD语音指令文件

在项目中添加一个xml文件并输入文件名,我这里创建的为VoiceCommandsFile.xml

编辑VCD文件

针对App的实际情况,可以创建不同语言的指令集CommandSet,

Win10使用的是V1.2的语音指令模板,具体的模板使用详情可访问:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/dn706593.aspx

每个VCD文件中声明的Command都必须包含以下信息:

  • AppName元素,应用程序用于Cortana中的语音识别的名称
  • Example元素,告诉用户该App所支持的语音指令的描述
  • ListenFor 元素,监听并识别用户的指令,每个Command都至少有一个该元素
  • Feedback 元素,指令识别成功后的反馈
  • Navigate 元素,声明语音指令将在前台启动应用。和VoiceCommandService互斥
  • VoiceCommandService 元素,声明语音指令将启动后台应用

VCD文件示例:

 1 <?xml version="1.0" encoding="utf-8"?>
 2  
 3 <!--UWP使用的是语音命令v1.2模板  wp8.1是v1.1 wp8是v1.0-->
 4 <!--等多V1.2模板详情请访问:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/dn706593.aspx-->
 5  
 6 <!--可以创建多个 CommandSet 元素,每个都带有不同的 xml:lang 属性以使你的应用可用于不同的市场。
 7     例如,用于美国的应用可能有一个英语版本的 CommandSet 和一个西班牙语版本的 CommandSet-->
 8  
 9 <!--
10       -每个CommandSet代表一种语言的语音指令,
11       -每个Command代表一种指定情景下的语音指令,每个CommandSet可以包含多种语音使用情景
12       -Example 会出现在Cortana -> App 帮助界面中,提示用户可以怎么使用该语音场景
13       -ListenFor 表示Cortana要监听的语音语法,每个命令都需要具有至少一个 ListenFor 元素
14       -Feedback 指识别语音命令成功时,Cortana将显示该元素内的内容
15       -Navigate 用于指示语音命令将在前台启动应用,如果语音命令改在后台启动应用,则指定VoiceCommandService
16       -VoiceCommandService 标签表示Cortana要启用后台应用服务来处理用户需求,
17           例: <VoiceCommandService Target="BusQueryService"/> Target填写后台应用服务的名称
18         -->
19  
20  
21 <VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
22   <CommandSet xml:lang="zh-cn" Name="set">
23     <AppName>小秘</AppName>
24     <Example> 打开主页 或 查询航班 </Example>
25  
26     <Command Name="OpenMainPage">
27       <Example>打开主页</Example>
28       <ListenFor>打开主页</ListenFor>
29       <Feedback>正在启动航班助手……</Feedback>
30       <Navigate  />
31     </Command>
32  
33     <Command Name="QueryFlight">
34       <Example> 查询去西雅图的航班  </Example>
35       <ListenFor >[搜索]去{City}[的]航班</ListenFor>
36       <ListenFor >[查询]去{City}[的]航班</ListenFor>
37       <Feedback> 正在查询去{City}的航班 </Feedback>
38       <Navigate />
39     </Command>
40  
41     <Command Name="NavToPage">
42       <Example> 跳转到某个界面  </Example>
43       <ListenFor >[跳转]到{Destination}界面</ListenFor>
44       <Feedback> 正在跳转到{Destination}界面 </Feedback>
45       <Navigate />
46     </Command>
47  
48     <!--PhraseList用来定义一组语音字符,指定相应规定的字符,用来消除歧义-->
49     <!--使用 PhraseList 限制识别适用于一组相对较小的单词。当单词组过大(例如数百个单词)或者根本不应被限制时,
50         请使用 PhraseTopic 元素和 Subject 元素来优化语音识别结果的相关性,从而增强可扩展性。-->
51     <PhraseList Label="Destination">
52       <Item>设置</Item>
53       <Item>关于</Item>
54     </PhraseList>
55  
56     <!--可以提高识别率,内部属性Subject可指定该关键字类型,比如 城市名 姓名  地址 等类型-->
57     <PhraseTopic Label="City" Scenario="Natural Language">
58       <Subject>City/State</Subject>
59     </PhraseTopic>
60   </CommandSet>
61  
62   <!-- 其他语言的 CommandSet -->
63  
64 </VoiceCommands>

上面例子中我们定义了三种Command指令,分别是"打开主页","查询去某城市的航班","跳转到某个界面"。

ListenFor元素可以存在多个,其中认为可忽略的词可以用[]符号进行修饰。

PhraseList用来定义一组语音字符,指定相应规定的字符,用来消除歧义, 使用 PhraseList 限制识别适用于一组相对较小的单词。当单词组过大(例如数百个单词)或者根本不应被限制时,要使用 PhraseTopic 元素和 Subject 元素来优化语音识别结果的相关性,从而增强可扩展性。

PhraseTopic可以提高识别率,内部属性Subject可指定该关键字类型,比如 城市名 姓名 地址 等类型

安装VCD命令文件

我们的App安装后必须运行一次才能安装VCD指令集,我们可以在App的OnLaunched中调用下面方法去注册并激活语音指令集,代码如下:

1 /// <summary>
2 /// 注册语音指令
3 /// </summary>
4 private async Task InsertVoiceCommands()
5 {
6     await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(
7         await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///VoiceCommandsFile.xml")));
8 }

处理语音指令

在VCD程序集安装正确的情况下,Cortana的帮助界面就会出现我们的App的语音指令集的调用说明:

 

通过相应的语音指令,Cortana就会启动我们的App,我们需要在App中重写OnActivated事件,判断用户使用的是哪个语音指令,并实现相应的逻辑,代码如下:

 1 protected override void OnActivated(IActivatedEventArgs args)
 2 {
 3     base.OnActivated(args);
 4     // 如果程序不是因为语音命令而激活的,就不处理
 5     if (args.Kind != ActivationKind.VoiceCommand) return;
 6  
 7     //将参数转为语音指令事件对象
 8     var vcargs = (VoiceCommandActivatedEventArgs)args;
 9     // 分析被识别的命令
10     var res = vcargs.Result;
11     // 获取被识别的命令的名字
12     var cmdName = res.RulePath[0];
13     Type navType = null;
14     string propertie = null;
15     //判断用户使用的是哪种语音指令
16     switch (cmdName)
17     {
18         case "OpenMainPage":
19             navType = typeof(MainPage);
20             break;
21         case "QueryFlight":
22             navType = typeof(QueryPage);
23             //获取语音指令的参数
24             propertie = res.SemanticInterpretation.Properties["City"][0];
25             break;
26         case "NavToPage":
27             //获取语音指令的参数
28             propertie = res.SemanticInterpretation.Properties["Destination"][0];
29  
30             //根据 propertie 参数决定跳转到指定界面,这里就不判断了
31             navType = typeof(QueryPage);
32             break;
33     }
34     //获取页面引用
35     var root = Window.Current.Content as Frame;
36     if (root == null)
37     {
38         root = new Frame();
39         Window.Current.Content = root;
40     }
41     root.Navigate(navType, propertie);
42  
43     // 确保当前窗口处于活动状态
44     Window.Current.Activate();
45 }

Ok,至此代码部分已经完工,可以通过Cotrana语音指令启动我们的App了,这种启动方式为前台启动,下篇文章中将介绍Cortana调用App后台service是如何实现的。这里推荐一个UWP开发群:53078485 大家可以一起学习~~