昨天,老周演示了语音命令集成这一高大上功能,今天咱们来点更高级的语音命令。

在昨天的例子中,响应语音命令是需要启动应用程序的,那么如果可以不启动应用程序,就直接在小娜面板上进行交互,是不是会更高大小呢。

面向Win 10的API给应用程序增加了一种叫App Service的技术,应用程序可以通过App Service公开服务来让其他应用程序调用。App Service是通过后台任务来处理的,故不需要启动应用程序,调用者只需要知道提供服务的应用程序的程序包名称,以及要调用的服务名称即可以进行调用了。关于App Service,老周曾做过相关视频,有时间的话再补上博文。

正因为App Service是通过后台任务来处理的,再与小娜语音命令一集成,应用程序就可以在后台响应语音操作,而不必在前台启动。

好了,基本理论依据有了,接下来,老规矩,老周向来不喜欢讲XYZ理论的,还是直接说说如何用吧。

1、定义语音命令文件。老周写了个新的文件。

<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="zh-hans">
<AppName>乐器收藏</AppName>
<Example>“乐器收藏 展现列表”,或者“乐器收藏 显示列表”</Example>
<Command Name="show">
<Example>展现列表,或者 显示列表</Example>
<ListenFor>[展现]列表</ListenFor>
<ListenFor>显示列表</ListenFor>
<VoiceCommandService Target="vcfav"/>
</Command>
</CommandSet>
</VoiceCommands>

其他元素我在上一篇烂文中已经介绍过,不过大家会发现有个家伙比较陌生——VoiceCommandService元素。对,实现语音命令和App Service集成,这个元素的配很关键,Target属性就是你要集成的App Service的名字,本例子应用待会要公开的一个App Service名字叫vcfav,记好了。

这个应用的用途是向大家Show一下老周收藏的几款乐器,都是高大上的乐器,能奏出醉人心弦的仙乐。注意,这里的命令文件用了VoiceCommandService元素,就不需要用Navigate元素。

2、实现后台任务。在解决方案中添加一个Runtime组件项目,记得老周N年前说过,实现后台的类型是放到一个运行时组件项目中的。

    public sealed class BTask : IBackgroundTask
{
BackgroundTaskDeferral taskDerral = null;
VoiceCommandServiceConnection serviceConnection = null;
public async void Run(IBackgroundTaskInstance taskInstance)
{

后台任务的功能当然是响应小娜收到的语音命令,因为可以通过App service来触发,所以我们就能在后台任务中进行交互。

要与小娜面板进行交互,我们需要一个连接类——VoiceCommandServiceConnection类,它的实例可以从后台任务实例的触发器数据中获得,就是这样:

        public async void Run(IBackgroundTaskInstance taskInstance)
{
taskDerral = taskInstance.GetDeferral();
AppServiceTriggerDetails details = taskInstance.TriggerDetails as AppServiceTriggerDetails; // 验证是否调用了正确的app service
if (details == null || details.Name != "vcfav")
{
taskDerral.Complete();
return;
} serviceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);

关键是这句:serviceConnection = VoiceCommandServiceConnection.FromAppServiceTriggerDetails(details);
连接对象就是这样获取的。

3、之后,我们就可以在代码中与小娜交互了。在交互过程中,发送到小娜面板的消息都由VoiceCommandUserMessage类来封装,它有两个属性:

DisplayMessage:要显示在小娜面板上的文本。

SpokenMessage:希望小娜说出来的文本。

如果你希望小娜说出的内容和面板上显示的内容相同,也可以把这两个属性设置为相同的文本。

与小娜交互的操作自然是由VoiceCommandServiceConnection实例来完成了,不然我们上面获取它干吗呢,就是为了在后面的交互操作中使用。

VoiceCommandServiceConnection通过以下几个方法来跟小娜交互:

ReportSuccessAsync:告诉小娜,处理已经完成,并返回一条消息,传递给小娜面板。

ReportFailureAsync:向小娜反馈错误信息。

ReportProgressAsync:报告进度,不指定具体进度值,只是在小娜面板上会显示长达5秒钟的进度条,你的代码处理不应该超过这个时间,不然用户体验不好。最好控制在2秒钟之内。

RequestAppLaunchAsync:请求小娜启动当前应用。

RequestConfirmationAsync:向小娜面板发送一条需要用户确认的消息。比如让小娜问用户:“你确定还没吃饭?”或者:“你认为老周很帅吗?”,用户只需回答Yes or No。后台任务会等待用户的确认结果,以决定下一步做什么。

RequestDisambiguationAsync:同样,也是向用户发出一条询问消息,与上面的方法不同的是,这个方法会在小娜面板上列出一串东西,让用户说出选择哪一项。比如,“请选择你要看的电影:”,然后选项有:《弱智仙侠》、《花钱骨》、《烧脑时代》、《菊花传奇》,你说出要选择的项,或者点击对应的项,小娜会把用户选择的项返回给应用程序后台任务,以做进一步处理。

在老周这个示例中,如果语音命令被识别,就会在小娜面板上列出老周收藏的五件乐器,然后你可以选择一件进行收藏,当然是不包邮的,你还要付等价费。

            if (serviceConnection != null)
{
serviceConnection.VoiceCommandCompleted += ServiceConnection_VoiceCommandCompleted;
// 获取被识别的语音命令
VoiceCommand cmd = await serviceConnection.GetVoiceCommandAsync();
if (cmd.CommandName == "show")
{
// 获取测试数据,用于生成磁块列表
var tiles = await BaseData.GetData(); // 定义返回给小娜面板的消息
VoiceCommandUserMessage msgback = new VoiceCommandUserMessage();
msgback.DisplayMessage = msgback.SpokenMessage = "请选择要收藏的乐器。"; // 第二消息,必须项
VoiceCommandUserMessage msgRepeat = new VoiceCommandUserMessage();
msgRepeat.DisplayMessage = msgRepeat.SpokenMessage = "请选择你要收藏的乐器。"; // 把消息发回到小娜面板,待用户选择
VoiceCommandResponse response = VoiceCommandResponse.CreateResponseForPrompt(msgback, msgRepeat, tiles);
VoiceCommandDisambiguationResult selectedRes = await serviceConnection.RequestDisambiguationAsync(response); // 看看用户选了什么
VoiceCommandContentTile selecteditem = selectedRes.SelectedItem;
// 保存已选择的乐器
SaveSettings(selecteditem.Title);
// 回传给小娜面板,报告本次操作完成
msgback.DisplayMessage = msgback.SpokenMessage = "好了,你收藏了" + selecteditem.Title + "。";
response = VoiceCommandResponse.CreateResponse(msgback);
await serviceConnection.ReportSuccessAsync(response);
taskDerral.Complete();
}
}

SaveSettings方法是把用户选择的收藏保存到应用程序本地设置中,以便在前台应用中访问。

        private void SaveSettings(object value)
{
ApplicationDataContainer data = ApplicationData.Current.LocalSettings;
data.Values["fav"] = value;
}

在调用RequestDisambiguationAsync方法向小娜面板添加可供选择的列表项时,一定要注意一点,作为方法参数的VoiceCommandResponse实例一定要使用CreateResponseForPrompt静态方法来创建,因为上面说过,提供有待用户确认的交互有两类:一类是yes or no,另一类就是从列表中选一项。此处就是后者。

这里老周也定义了一个BaseData类,用来产生显示在小娜面板上的项的图标,用VoiceCommandContentTile类来封装,每个VoiceCommandContentTile实例就是一个列表项,显示的格式由ContentTileType属性来指定,比如显示纯文本,还是显示图标加文本,为了让大家看清楚老周的收藏品,此处选用图标 + 文本的方式呈现。

    internal class BaseData
{
public static async Task<IEnumerable<VoiceCommandContentTile>> GetData()
{
IList<VoiceCommandContentTile> tiles = new List<VoiceCommandContentTile>();
// 获取数据
var filedatas = await GetFiles(); // 添加磁块列表
for (uint n = ; n < filedatas.Length; n++)
{
if (tiles.Count >= VoiceCommandResponse.MaxSupportedVoiceCommandContentTiles)
{
break;
}
VoiceCommandContentTile tile = new VoiceCommandContentTile();
tile.ContentTileType = VoiceCommandContentTileType.TitleWith68x68IconAndText;
tile.Image = filedatas[n].Item1;
tile.Title = filedatas[n].Item2;
tile.TextLine1 = filedatas[n].Item3;
tiles.Add(tile);
} return tiles.ToArray(); ;
} private async static Task<Tuple<StorageFile, string, string>[]> GetFiles()
{
string uh = "ms-appx:///Assets/";
// 笛子
Uri u1 = new Uri(uh + "笛子.png");
// 鼓
Uri u2 = new Uri(uh + "鼓.png");
// 大提琴
Uri u3 = new Uri(uh + "大提琴.png");
// 二胡
Uri u4 = new Uri(uh + "二胡.png");
// 古琴
Uri u5 = new Uri(uh + "古琴.png"); // 获取文件对象
StorageFile imgFile1 = await StorageFile.GetFileFromApplicationUriAsync(u1);
StorageFile imgFile2 = await StorageFile.GetFileFromApplicationUriAsync(u2);
StorageFile imgFile3 = await StorageFile.GetFileFromApplicationUriAsync(u3);
StorageFile imgFile4 = await StorageFile.GetFileFromApplicationUriAsync(u4);
StorageFile imgFile5 = await StorageFile.GetFileFromApplicationUriAsync(u5); // 创建三元组列表
Tuple<StorageFile, string, string> item1 = new Tuple<StorageFile, string, string>(imgFile1, "笛子", "声音空远悠扬,灵动飘逸。");
Tuple<StorageFile, string, string> item2 = new Tuple<StorageFile, string, string>(imgFile2, "鼓", "乐声雄浑,劲力深透。");
Tuple<StorageFile, string, string> item3 = new Tuple<StorageFile, string, string>(imgFile3, "大提琴", "音质宏厚。");
Tuple<StorageFile, string, string> item4 = new Tuple<StorageFile, string, string>(imgFile4, "二胡", "意绵绵,略带凄婉。");
Tuple<StorageFile, string, string> item5 = new Tuple<StorageFile, string, string>(imgFile5, "古琴", "音质沉厚,古朴淡雅,可传情达意。"); return new Tuple<StorageFile, string, string>[] { item1, item2, item3, item4, item5 };
}
}

4、回到主项目,引用刚才写完的后台任务。有的朋友说后台任务不起作用,如果后台类没问题的话,可能的两个问题是:a、主项目没有引用后台任务类所在的项目;b、清单文件没有配置好。

5、最后,不要忘了配置清单文件,打开Package.appxmanifest文件,找到Application节点。

      <Extensions>
<uap:Extension Category="windows.appService" EntryPoint="BgService.BTask">
<uap:AppService Name="vcfav"/>
</uap:Extension>
</Extensions>

扩展点的Category属性要指定windows.appService,表示扩展类型为App Service,EntryPoint指定入口点,即后台任务类的名字,包括命名空间和类型名。

在App类的OnLaunched方法中,记得安装VCD文件。

            StorageFile vcdfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///vcd.xml"));
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdfile);

现在,你可以测试了。运行应用程序,然后对着小娜说“收藏乐器 显示列表”,然后给出选择列表。

识别后,显示操作结果。

源代码下载地址:http://files.cnblogs.com/files/tcjiaan/VoicecmdWithrespApp.zip

【Win10 应用开发】语音命令与App Service集成的更多相关文章

  1. 【Win10 应用开发】集成语音命令

    记得老周以前在写WP8应用开发的文章时,曾经写过语音命令集成的文章,后来8.1的时候“小娜”问世,但考虑到其变化不大,故老周没有补写相应的文章. 今天,老周打算补一下Win 10通用应用开发中,有关语 ...

  2. 与众不同 windows phone (45) - 8.0 语音: TTS, 语音识别, 语音命令

    [源码下载] 与众不同 windows phone (45) - 8.0 语音: TTS, 语音识别, 语音命令 作者:webabcd 介绍与众不同 windows phone 8.0 之 语音 TT ...

  3. Win10/UWP开发—使用Cortana语音与App后台Service交互

    上篇文章中我们介绍了使用Cortana调用前台App,不熟悉的移步到:Win10/UWP开发—使用Cortana语音指令与App的前台交互,这篇我们讲讲如何使用Cortana调用App的后台任务,相比 ...

  4. Win10/UWP开发—使用Cortana语音指令与App的前台交互

    Win10开发中最具有系统特色的功能点绝对少不了集成Cortana语音指令,其实Cortana语音指令在以前的wp8/8.1时就已经存在了,发展到了Win10,Cortana最明显的进步就是开始支持调 ...

  5. Win10/UWP开发—使用Cortana语音指令启动前台App

    这两天进群(53078485)找大咖的童鞋比较多,只是大咖比较忙,目前Demo还没有要到,这里先给大家转载一篇Aran大咖的博客学习下,以下是原文: Win10开发中最具有系统特色的功能点绝对少不了集 ...

  6. Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

    安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneG ...

  7. UWP/Win10新特性系列—App Service

    Win10中,新增了一个很实用的新特性叫做App Service,App Service允许App不在前台运行的情况下提供出一个或多个对外服务供其他App使用,这看起来就好像Web开发中的Web Ap ...

  8. 浅谈传统语音通信和APP语音通信音频软件开发之不同点

    本人在传统的语音通信公司做过手机和IP电话上的语音软件开发,也在移动互联网公司做过APP上的语音软件开发.现在带实时语音通信功能的APP有好多,主流的有微信语音.QQ电话.钉钉等,当然也包括我开发过的 ...

  9. 微信小程序开发---逻辑层(App Service)

    再说逻辑层之前,先说说微信小程序框架(MINA) 小程序开发框架的目标是通过尽可能简单.高效的方式让开发者可以在微信中开发具有原生APP体验的服务. 框架提供了自己的视图层描述语言WXML和WXSS, ...

随机推荐

  1. JavaScript中严格模式"use strict";需注意的几个雷区:

    1.with语句会抛错误 2.未声明的变量被赋值会报错 3.arguments在严格模式下变为静态,传入的参数与arguments无关系 4.delete会报错 5.对象的重复属性名会报错 6.禁止八 ...

  2. 做 Web 开发少不了这些的

    抱歉,似乎有些标题党了.最近做服务器的热备,整理了些李纳斯工具的适用方法.看看还有不错的. 基本命令 sleep 500 暂停 ctrl + z 暂停 progress & 后台运行 jobs ...

  3. Python3.5 day3作业一:实现简单的shell sed替换功能

    需求: 1.使python具有shell中sed替换功能. #!/usr/bin/env python #_*_conding:utf-8_*_ #sys模块用于传递参数,os模块用于与系统交互. i ...

  4. c#文本转语音以及语音阅读小实例

    c#实现语音阅读以及文本转语音文件是基于c#的一个类库(SpeechSynthesizer )实现的,使用该类必须要添加引用using System.Speech.Synthesis;直接是无法添加引 ...

  5. linux shell中不显示路径了,显示为-bash-4.1#的两种解决办法

    出现这个问题的原因是因为没有配置.bash_profile的问题,或者是我们不小心清空或删除了.bash_profile文件. 办法一:修改 ~/.bash_profile文件 步骤如下: vim ~ ...

  6. 【Beta】Daily Scrum Meeting第五次

    1.任务进度 学号 已完成 接下去要做 502 登陆时将返回的个人信息更新到本地数据库 发布任务到服务器 509 给所有api添加注释 添加及修改职工信息并同步到服务器 517 将提交报课移到报课表界 ...

  7. CI生成查询记录集result(),row(),row_array().....

    result() 该方法执行成功返回一个对象数组,失败则返回一个空数组. 一般情况下,我们使用下面的方法遍历结果,代码就像这样: $query = $this->db->query(&qu ...

  8. can't connect to mysql server on 'localhost'(10061)

    在linux下安装Navicat,想说在windows下试一试phpmyadmin之外的mysql图形工具. 显示下载安装了mysql workbench,链接成功.然后又弄了一下输入法重启,想说试一 ...

  9. express-session 保存遇到的问题

    今天在用express4 试着做网站的时候,发现request.session 中一直不能保存新的值,还一直报一个错 express-session deprecated undefined resa ...

  10. Android 6.0 使用HttpURLConnection 使用Get提交遇到405等问题。

    HttpURLConnection 在调用connection.setDoOutput(true)之后会自动把提交方式改为POST.然后调用方法的时候有可能会出现这种情况 在调用get的时候设置为co ...