因为最近在作智能家居方向的产品,须要在App上对机器人实现一个简单的语音控制,因而开始寻找相应的解决方案,因为某种缘由,google本身的语音识别API并不能在国内发挥做用,因此咱们选择国内的科大讯飞语音识别服务; 示例源码下载地址在博客结尾,不用分android
最后实现的效果: json
对特定的语音指令可以作出相应的响应,对非指令集中的指令这提示错误;服务器
具体实现: 1.得到SDK并加入到项目中: 在科大讯飞的开放平台http://www.xfyun.cn/注册账号,并建立应用,下载语音相关的SDK 开发包是根据建立的应用生成的,咱们只须要将包中的网络
导入便可,官方只给出了Eclipse的导入方法; 在Android Studio中导入方法以下: 先将Msc.jar 和armeabi文件夹复制到libs Msc.jar包经过File ->Project Structure -> Dependencies导入便可 armeabi包导入,在gradle(Module的)文件的Android下加入:app
sourceSets { main { jniLibs.srcDirs = ['libs'] } }
而后make project,这样讯飞语音的SDK就集成到项目中了;ide
咱们若是须要语音输入是能有动画效果,官方也提供了一个解决方案: 将开发包下的iflytek文件夹加入到assets下:gradle
2)写一个语法规则文件 有时候咱们对语音识别准确率要求很高(如对设备发特定指令,指挥机器人,无人机等),因此要作语音识别而不是简单的语音听写,作语音识别就须要编写咱们本身的语法文件(abnf文件),实例中要一个简单的语法文件,固然,实际语法会比这更复杂:动画
在assets目录下创建咱们的语法文件:ui
#ABNF 1.0 gb2312; language zh-CN; mode voice; root $main; $main = $control1 | $control2; $control1= 启动 | 中止 | 锁定 | 解锁 | 静音 | 取消静音 | 帮助; $control2 = $place1 $place2; $place1 = 打开 | 关闭; $place2 = A模式 | B模式 | C模式 | D模式;
该语法文件中根为main,main有两种可能取值,control1或者control2 Control1的可能取值问一个集合,control2的可能取值又是place1 和place2两个集合的组合,因此能够识别启动,亦能够识别打开A模式this
3)加入所需权限:
<!--链接网络权限,用于执行云端语音能力 --> <uses-permission android:name="android.permission.INTERNET"/> <!--获取手机录音机使用权限,听写、识别、语义理解须要用到此权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <!--读取网络信息状态 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!--获取当前wifi状态 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!--容许程序改变网络链接状态 --> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
4)具体代码实现:
使用科大讯飞的云语音识别SDK进行开发,在每次启动时联网构建语法(上传abnf语法文件)
构建语法过程:
mAsr = SpeechRecognizer.createRecognizer(this, mInitListener); mCloudGrammar = FucUtil.readFile(this, "contral_sample.abnf", "utf-8"); mSharedPreferences = getSharedPreferences(getPackageName(), MODE_PRIVATE); mContent = new String(mCloudGrammar); //指定引擎类型 mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType); mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8"); ret = mAsr.buildGrammar(GRAMMAR_TYPE_ABNF, mContent, mCloudGrammarListener); if(ret != ErrorCode.SUCCESS){ Log.e("tag", "语法构建失败,错误码:" + ret); } /** * 云端构建语法监听器。 */ private GrammarListener mCloudGrammarListener = new GrammarListener() { @Override public void onBuildFinish(String grammarId, SpeechError error) { if(error == null){ String grammarID = new String(grammarId); SharedPreferences.Editor editor = mSharedPreferences.edit(); if(!TextUtils.isEmpty(grammarId)) editor.putString(KEY_GRAMMAR_ABNF_ID, grammarID); editor.commit(); Log.e("GrammarListener","语法构建成功:" + grammarId); }else{ Log.e("GrammarListener","语法构建失败,错误码:" + error.getErrorCode()); } } }; private void showTip(final String str) { T.showShort(this,str); }
上传成功后得到语法ID:
利用此语法ID将监听到的Voice上传服务器进行识别,具体监听过程:
/** * 初始化监听器。 */ private InitListener mInitListener = new InitListener() { @Override public void onInit(int code) { Log.e("InitListener", "SpeechRecognizer init() code = " + code); if (code != ErrorCode.SUCCESS) { Log.e("onInitError","初始化失败,错误码:"+code); } } }; /** * 听写UI监听器 */ private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() { public void onResult(RecognizerResult results, boolean isLast) { if (null != results) { Log.e("recognizer result:", results.getResultString()); String text ; text = JsonParser.parseGrammarResult(results.getResultString()); // 显示 Log.e("text",text); parseWordFromVoice(text); } else { Log.d("onResult", "recognizer result : null"); } } /** * 识别回调错误. */ public void onError(SpeechError error) { showTip(error.getPlainDescription(true)); } };
服务器返回识别结果List,该List为一个以可信度为降序的Json List,解析Json得到可信度最高的结果并作错误处理,具体解析过程:
public static String parseGrammarResult(String json) { StringBuffer ret = new StringBuffer(); try { JSONTokener tokener = new JSONTokener(json); JSONObject joResult = new JSONObject(tokener); JSONArray words = joResult.getJSONArray("ws"); for (int i = 0; i < words.length(); i++) { JSONArray items = words.getJSONObject(i).getJSONArray("cw"); JSONObject obj = items.getJSONObject(0); if(obj.getString("w").contains("nomatch")) { ret.append("没有匹配结果"); return ret.toString(); } ret.append(obj.getString("w")); } } catch (Exception e) { e.printStackTrace(); ret.append("没有匹配结果"); } return ret.toString(); }
能够看出,咱们这段代码只解析匹配度最高的条目,其实还能够将但更多接近的条目在本地分析后再处理
在本地对解析结果作进一步断定,断定完后再作该指令所对应的操做: 效果: 08-27 10:47:10.370 16043-16043/com.example.huayu.voicetowordtest E/recognizer result:﹕ {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":"35","gm":"0","w":"静音"}]}]} 08-27 10:47:10.370 16043-16043/com.example.huayu.voicetowordtest E/text﹕ 静音 08-27 10:47:20.350 16043-16043/com.example.huayu.voicetowordtest E/recognizer result:﹕ {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":"91","gm":"0","w":"nomatch:truncated","mn":[{"id":"nomatch","name":"nomatch:truncated"}]}]}]} 08-27 10:47:20.360 16043-16043/com.example.huayu.voicetowordtest E/text﹕ 没有匹配结果