Windows Phone 8 app提供了新的API可以使用语音输入和输出。
对用户进行通信:语音合成 API
计算机生成的语音输出分别调用文本到语音 (TTS) 或语音合成
语音合成的简单示例代码示例:
当用户点击麦克风按钮时,就会听到语音播放。在下面的部分我会扩展此示例通过添加代码,以接受用户的输入使用语音识别。
TTS 功能先进的方案严重依赖语音输出的应用程序可能使用的情况下,需要更改音量、 音调或在致辞中的讲率输出。有两种方法:SpeakSsmlAsync 和 SpeakSsmlFromUriAsync。
语音输入:语音识别 API语音识别应用程序中的使用案例两大类是文本输入和命令和控制。在第一个使用案例,文本输入的 app 只是捕获用户的话语作为文本 ; 这是有用的当用户可以说几乎所有东西,如"新"备注示例代码的功能。
在第二个使用案例、 指挥和控制,用户操作 app 口语话语而不是通过点击按钮或跨手机的脸上滑动手指。此使用案例是无手驾驶或烹饪等的方案中尤其有用。
语音识别的简单示例关于语音识别应用程序中的功能的详细信息之前,让我们看看最简单的例子:文本输入几行代码。
图 1 显示 GetNewMemoByVoice 方法显示较早前,但添加初始化一个识别器对象,该对象的行数与启动识别会话和处理结果。
图 1 初始化一个识别器对象、 启动识别会话及处理结果
当然,在实际代码中就永远不会这样,一样简单,如果你看魔术备注示例中,您将看到一个 try/catch 块,并检查成功识别。
如果您尝试此示例应用程序中通过点击麦克风图标,您会发现你说的话,您的备忘录后,出现"思维"屏幕,其次是 UI,确认后的结果在备注文本框中插入。幕后的很多事情上,最重要的是要识别语音的远程服务器上的"语法"的使用。语法是基本上是一套规则,指定哪些词汇条目 ("字"),引擎需要认识到并以何种顺序。下一节将探讨语音识别 API 和与识别语法如何使用它。
语音识别 API 的概述我进入语音识别编码的细节之前,让我们看高级别看 API 和他们的角色中的类别。图 2 显示 API 的基本布局。您会注意到的第一件是两个框有语音在名称中的识别器。
图 2 语音识别 API 设计概述
如果您的应用程序不需要显示 UI 与语音识别,或者如果您想要显示您自己的自定义 UI,您应实例化示的左侧中部的 SpeechRecognizer 类的副本图 2。此对象看作是此 API 中的语音识别的基本业务单位。这是该应用程序中添加了任何语法它需要。初始化后,调用 RecognizeAsync 来做实际的承认。由于 SpeechRecognizer 实现 IAsyncOperation <SpeechRecognitionResult>,状态和结果对象是已完成回调函数中可用。因此,没有任何单独的事件,识别完成或拒绝与其他托管语音的 Api。
顾名思义,顶级的 SpeechRecognizerUI 类提供了语音识别的默认 GUI,与手机的全球语音用户界面反馈、 消歧和确认是一致的。与全球语音用户界面保持兼容性,并简化编码,大多数应用程序应使用此类,而不是前面提到的非 UI 类。当你实例化一个 SpeechRecognizerUI 对象时,它附带的两个重要的对象:设置对象,设置要显示给用户 ; 的 UI 文本的位置 和一个 SpeechRecognizer 对象,使您可以指定语法,如以下各节中所述。初始化后,您应该调用父 SpeechRecognizerUI 对象要启动识别会话的 RecognizeWithUIAsync。如果您在子 SpeechRecognizer 对象上使用 RecognizeAsync,它将识别好像 SpeechRecognizer 对象正在使用的独立,就没有 UI。此后,术语 SpeechRecognizer 和承认异步应理解为泛型对象和方法有和无的 UI,酌情参考文件。
使用语音识别步骤有了 Windows Phone 8 应用程序中使用语音识别的四个基本步骤:
在定位core dump问题,看栈是必要的一步。因为栈是反映了程序崩溃那一瞬间的情况,包括函数调用关系,参数,局部变量。要找出程序崩溃的地方,必须以栈为依据。而在开发过程中,一般会有调试版本和发布版本,其中调试版本会包含很多符号信息便于调试,而发布版本往往为了商业秘密的保护,在产品发布时去掉了调试信息。这两种方式会导致栈在可读性上不一样。
调试版本:
(gdb) bt #0 recurse (num=1, level=9) at xuzhina_dump_c3.cpp:9 #1 0x080484a0 in recurse (num=2, level=8) at xuzhina_dump_c3.cpp:6 #2 0x080484a0 in recurse (num=3, level=7) at xuzhina_dump_c3.cpp:6 #3 0x080484a0 in recurse (num=4, level=6) at xuzhina_dump_c3.cpp:6 #4 0x080484a0 in recurse (num=5, level=5) at xuzhina_dump_c3.cpp:6 #5 0x080484a0 in recurse (num=6, level=4) at xuzhina_dump_c3.cpp:6 #6 0x080484a0 in recurse (num=7, level=3) at xuzhina_dump_c3.cpp:6 #7 0x080484a0 in recurse (num=8, level=2) at xuzhina_dump_c3.cpp:6 #8 0x080484a0 in recurse (num=9, level=1) at xuzhina_dump_c3.cpp:6 #9 0x080484a0 in recurse (num=10, level=0) at xuzhina_dump_c3.cpp:6 #10 0x080484d5 in main () at xuzhina_dump_c3.cpp:14
发布版本:
(gdb) bt #0 0x080484a9 in recurse(int, int) () #1 0x080484a0 in recurse(int, int) () #2 0x080484a0 in recurse(int, int) () #3 0x080484a0 in recurse(int, int) () #4 0x080484a0 in recurse(int, int) () #5 0x080484a0 in recurse(int, int) () #6 0x080484a0 in recurse(int, int) () #7 0x080484a0 in recurse(int, int) () #8 0x080484a0 in recurse(int, int) () #9 0x080484a0 in recurse(int, int) () #10 0x080484d5 in main ()
同时在开发过程中,由于逻辑太过复杂,不小心引入了栈溢出,导致栈混乱,如前言里:
(gdb) bt #0 0x6f745374 in ?? () #1 0x57735571 in ?? () #2 0xbff80065 in ?? () Backtrace stopped: previous frame inner to this frame (corrupt stack?)
第一种栈非常简单,也非常容易定位。但生活并不是那么容易。在产品的生命周期中,调试版本的时间是非常短的,而且在调试版本阶段,产品测试环境无论怎么模拟都没有产品发布后的客户环境那么复杂,很多潜在问题往往并不能在这个阶段发现。第二种栈比较麻烦,因为不知道函数的参数和局部变量,也不和代码行对应,出现问题非常难确定原因,且它在产品的生命周期的时间就非常长,从发布之前(一般发布版本在发布前一个阶段已经制作了)到产品该版本完全退出市场,甚至长达几年时间,而这几年时间内,相应的调试版本或调试信息文件可能已经由于历经几个版本的迭代而无从查找或者删除。第三种栈是最麻烦,因为根本就不知道问题出在哪个函数,特别在大型项目中,面对几百,几千万行的代码,更加无从入手,虽然它出现的机率可能很低,它的时间有可能从第一版本到整个产品退出市场为止,长达十几年,甚至二十几年。
由于第二,三种栈复杂性,所以希望找出栈布局的规律, 在遇到这种问题时不会措手不及。由于函数调用树在调试版本和发布版本一样,所以发布版本和调试版本的堆栈是一样的。
栈存放着函数相关的信息。这种情况叫做”调用约定(CallingConvention)”。如果阅读x86的规范,就会知道栈存放着函数桢指针,函数返回地址,函数参数,局部变量,及它们之间的布局。但是,也可以自己找出这种关系,这样印象会更深刻一些。
可以通过下面的步骤来探究”调用约定”:
1. 构造一些没有参数和局部变量的空函数来找出桢指针,返回地址的布局
2. 构造一些没有参数但有局部变量的函数来找出桢指针,返回地址,局部变量的布局
3. 构造一些有参数和局部变量的函数来看一下桢指针,返回地址,局部变量,参数的布局
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href=/blog_article/"<%=basePath%>">/index.html <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href=/blog_article/"styles.css"> --> <link rel="stylesheet" type="text/css" href=/blog_article/"extjs/resources/css/ext-all.css" /> <script type="text/javascript" src=/blog_article/"ext/bootstrap.js" ></script> <script type="text/javascript" src=/blog_article/"ext/ext-all.js" ></script> <script type="text/javascript" > //预加载 Ext.require( [ 'Ext.grid.*', 'Ext.toolbar.Paging', 'Ext.data.*' ] ); Ext.onReady( function(){ //创建Model Ext.define( 'User', { extend:'Ext.data.Model', fields:[ {name:'name',mapping:'name'}, {name:'sex',mapping:'sex'}, {name:'age',mapping:'age'} ] } ) //创建数据源 var store = Ext.create( 'Ext.data.Store', { model:'User', //设置分页大小 pageSize:5, proxy: { type: 'ajax', url : 'pageServlet', reader: { //数据格式为json type: 'json', root: 'bugs', //获取数据总数 totalProperty: 'totalCount' } }, autoLoad:true } ); //创建grid var grid = Ext.create('Ext.grid.Panel',{ store:store, columns:[ {text:'姓名',width:120,dataIndex:'name',sortable:true}, {text:'性别',width:120,dataIndex:'sex',sortable:true}, {text:'年龄',width:120,dataIndex:'age',sortable:true} ], height:200, width:480, x:20, y:40, title: 'ExtJS4 Grid分页查询示例示例', renderTo: 'grid', //分页功能 bbar: Ext.create('Ext.PagingToolbar', { store: store, displayInfo: true, displayMsg: '显示 {0} - {1} 条,共计 {2} 条', emptyMsg: "没有数据" } ) } ) store.loadPage(1); } ) </script> </head> <body> <div id="grid"> </div> </body> </html>
package xuyan; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class pageServlet extends HttpServlet { /** * The doGet method of the servlet. <br> * * This method is called when a form has its tag value method equals to get. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Connection con = null; PreparedStatement pstmt = null; ResultSet rs = null; String start = request.getParameter("start"); String limit = request.getParameter("limit"); StringBuilder sb = null; //数据总数 int total = 0; try { Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/test", "root", "1234"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } //查询数据总数语句 String countSql = "select count(*) from users"; try { pstmt = con.prepareStatement(countSql); rs = pstmt.executeQuery(); while(rs.next()){ total = rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } //分页查询语句 String sql = "select * from users limit " + start + ", " + limit; try { pstmt = con.prepareStatement(sql); rs = pstmt.executeQuery(); sb = new StringBuilder(); //设置json数据格式 sb.append("{totalCount:"+total+",bugs:["); while (rs.next()) { sb.append("{"); sb.append("name:" + "\'" + rs.getString(1) + "\',"); sb.append("sex:" + "\'" + rs.getString(2) + "\',"); sb.append("age:" + "\'" + rs.getString(3) + "\'"); sb.append("},"); } } catch (SQLException e) { e.printStackTrace(); } try