集成在线命令词识别能力的Tips
在之前的文章中已经详细介绍了在线语音合成能力的使用和一些常见问题,今天介绍一下在线命令词识别。
在线命令词识别又称在线语法识别或者在线识别。顾名思义,它的作用就是识别一组预先定于好的命令词,比如:北京到上海,合肥到南京,等等。
在官网SDK下载中心下载在线命令词识别SDK(这里以Android版本为例)后,可以发现压缩包里面的目录结构和听写的非常类似,这里就不过多介绍了,下面直接把demo导入到eclipse中看看。
其中SpeechApp.java、AsrDemo.java里面就是在线命令词的代码了,grammar_sample.abnf是在线命令词需要使用到的在线语法文件。
下面看一下demo运行的效果,今天demo后,点击“立即体验语法识别”就进入下面的界面。
可以看到上面的输入框中是读取的grammar_sample.abnf语法文件的内容,复选框选中“云端”表示当前使用的是在线命令词识别(“本地”是离线命令词识别)。
下面分别是“构建语法”“开始识别”“停止录音”“取消按钮”,在线命令词识别是不支持“更新词典”的。
在线命令词识别的过程主要就是先构建语法,然后启动录音开始识别。为了方便大家理解整个识别的过程,这里我详细介绍一下这里客户端和服务端都干了什么。
- 首先是大家非常容易忘记的SpeechApp.java中的初始化:
注意一步由于是单独放在一个java文件里面做的,所以很多开发者总是忘记这个初始化。但是这一步是必须的,如果没有做,后面的操作会报错的。
- // 应用程序入口处调用,避免手机内存过小,杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null
- // 如在Application中调用初始化,需要在Mainifest中注册该Applicaiton
- // 注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请增加参数:SpeechConstant.FORCE_LOGIN+"=true"
- // 参数间使用半角“,”分隔。
- // 设置你申请的应用appid,请勿在'='与appid之间添加空格及空转义符
- // 注意: appid 必须和下载的SDK保持一致,否则会出现10407错误
- SpeechUtility.createUtility(SpeechApp.this, "appid=" + getString(R.string.app_id));
- // 以下语句用于设置日志开关(默认开启),设置成false时关闭语音云SDK日志打印
- 然后是识别对象的创建、读取abnf语法文件:
- mAsr = SpeechRecognizer.createRecognizer(AsrDemo.this, mInitListener);
- mCloudGrammar = FucUtil.readFile(this,"grammar_sample.abnf","utf-8");
- 然后开始构建语法:
- mContent = new String(mLocalGrammar);
- mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8");
- //指定引擎类型
- mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
- ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, mLocalGrammarListener);
- if(ret != ErrorCode.SUCCESS){
- if(ret == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){
- //未安装则跳转到提示安装页面
- mInstaller.install();
- }else {
- showTip("语法构建失败,错误码:" + ret);
- }
- }
构 建语法就是把abnf语法内容上传到服务器,服务器收到语法内容后,会返回一个grammarID(在mLocalGrammarListener监听器 回调中返回),这个ID是你本次构建语法的唯一标示,多次构建相同的语法会覆盖之前的ID,在多个appid应用下或者多个不同的设备下构建相同的语法也 会返回不同的ID。需要这一步需要注意的是,构建语法(buildGrammar)之前是需要设置TEXT_ENCODING和ENGINE_TYPE 的。TEXT_ENCODING文本编码需要和语法文件的实际编码还有第一步中 FucUtil.readFile(this,"grammar_sample.abnf","utf-8");的保持一致。ENGINE_TYPE是引 擎类型,由于是在线命令词识别,所以这里设置成SpeechConstant.TYPE_CLOUD。
- /**
- * 云端构建语法监听器。
- */
- private GrammarListener mCloudGrammarListener = new GrammarListener() {
- @Override
- public void onBuildFinish(String grammarId, SpeechError error) {
- if(error == null){
- String grammarID = new String(grammarId);
- Editor editor = mSharedPreferences.edit();
- if(!TextUtils.isEmpty(grammarId))
- editor.putString(KEY_GRAMMAR_ABNF_ID, grammarID);
- editor.commit();
- showTip("语法构建成功:" + grammarId);
- }else{
- showTip("语法构建失败,错误码:" + error.getErrorCode());
- }
- }
- };
- 构建语法成功后,就可以开始启动识别了:
- //设置识别引擎
- mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
- //设置返回结果为json格式
- mAsr.setParameter(SpeechConstant.RESULT_TYPE, "json");
- //设置云端识别使用的语法id
- mAsr.setParameter(SpeechConstant.CLOUD_GRAMMAR, grammarId);
- mAsr.startListening(mRecognizerListener);
注意这里一定要等构建语法回调返回成功后才能启动识别(不然你也拿不到grammarID不是?嘿嘿)。其实通俗的说,识别的过程就是把你说话的音频数据 上传到服务器,服务器把你说的话与之前构建(上传)的语法进行匹配,来判断你说的可能是里面的哪个命令词,然后将判断结果(可能是多个或者零个)以 json格式返回回来。
- 然后就可以在mRecognizerListener监听器里获取返回的识别结果了。
- /**
- * 识别监听器。
- */
- private RecognizerListener mRecognizerListener = new RecognizerListener() {
-
- @Override
- public void onVolumeChanged(int volume, byte[] data) {
- showTip("当前正在说话,音量大小:" + volume);
- Log.d(TAG, "返回音频数据:"+data.length);
- }
-
- @Override
- public void onResult(final RecognizerResult result, boolean isLast) {
- if (null != result) {
- Log.d(TAG, "recognizer result:" + result.getResultString());
- String text ;
- if("cloud".equalsIgnoreCase(mEngineType)){
- text = JsonParser.parseGrammarResult(result.getResultString());
- }else {
- text = JsonParser.parseLocalGrammarResult(result.getResultString());
- }
-
- // 显示
- ((EditText)findViewById(R.id.isr_text)).setText(text);
- } else {
- Log.d(TAG, "recognizer result : null");
- }
- }
-
- @Override
- public void onEndOfSpeech() {
- // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
- showTip("结束说话");
- }
-
- @Override
- public void onBeginOfSpeech() {
- // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
- showTip("开始说话");
- }
- @Override
- public void onError(SpeechError error) {
- showTip("onError Code:" + error.getErrorCode());
- }
- @Override
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
- // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
- // 若使用本地能力,会话id为null
- // if (SpeechEvent.EVENT_SESSION_ID == eventType) {
- // String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
- // Log.d(TAG, "session id =" + sid);
- // }
- }
- };
json格式的结果在onResult回调中返回,通过demo中的json解析类解析后就可以得到下图中的结果了。
实际使用中一般选择置信度最高的结果。如果你说的内容不在你定义的语法范围内(比如北京北京),则会返回20005错误码。
到这里一个完成的在线命令词识别过程就结束了,相对离线命令词识别来说要简单一些。