让App“长耳朵”,Android语音识别解决方案实例

原创
2015/08/27 11:06
阅读数 1.6K

由于最近在做智能家居方向的产品,需要在App上对机器人实现一个简单的语音控制,于是开始寻找相应的解决方案,由于某种原因,google自己的语音识别API并不能在国内发挥作用,所以我们选择国内的科大讯飞语音识别服务; 示例源码下载地址在博客结尾,不用分

最后实现的效果: 界面

语音输入

指令在指令集内,识别成功

指令不在指令集内,提示错误

对特定的语音指令能够做出相应的响应,对非指令集中的指令这提示错误;

具体实现: 1.获得SDK并加入到项目中: 在科大讯飞的开放平台http://www.xfyun.cn/注册帐号,并创建应用,下载语音相关的SDK 开发包是根据创建的应用生成的,我们只需要将包中的

输入图片说明

导入即可,官方只给出了Eclipse的导入方法; 在Android Studio中导入方法如下: 先将Msc.jar 和armeabi文件夹复制到libs Msc.jar包通过File ->Project Structure -> Dependencies导入即可 armeabi包导入,在gradle(Module的)文件的Android下加入:

sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}

然后make project,这样讯飞语音的SDK就集成到项目中了;

我们如果需要语音输入是能有动画效果,官方也提供了一个解决方案: 将开发包下的iflytek文件夹加入到assets下:

输入图片说明

2)写一个语法规则文件 有时候我们对语音识别准确率要求很高(如对设备发特定指令,指挥机器人,无人机等),所以要做语音识别而不是简单的语音听写,做语音识别就需要编写我们自己的语法文件(abnf文件),实例中要一个简单的语法文件,当然,实际语法会比这更复杂:

在assets目录下建立我们的语法文件:

#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模式

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﹕ 没有匹配结果

源代码下载地址:http://download.csdn.net/detail/u012885690/9053127

展开阅读全文
打赏
0
8 收藏
分享
加载中
更多评论
打赏
0 评论
8 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部