讓App「長耳朵」,Android語音識別解決方案實例

因爲最近在作智能家居方向的產品,須要在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﹕ 沒有匹配結果

源代碼下載地址:http://download.csdn.net/detail/u012885690/9053127

相關文章
相關標籤/搜索