訊飛語音命令詞識別的SDK配置與運用
之前一直想開始寫技術部落格,倒不是說自己有多牛,只是覺得自己弄了點東西出來想和大家分享,而且聽說有利於自己技術水平的提高,就權當是一次記錄吧,這是第一篇。
之前因為一次比賽App要用到語音識別,做一個通過手機輸入語音指令然後根據輸入內容做出響應的功能,當時請教了一下別人決定採用科大訊飛的線上命令詞識別工程整合的SDK,當時先申請Appid,再下載SDK。(這裡推薦大家先到訊飛官方平臺上下載資料,資料上寫得很詳細,我這裡主要寫一些自己遇到的問題)
第一步:配置jar包和.so檔案,在這一步卡了一下,現在網上大部分資料還是關於eclipse軟體上的配置,現在大部分都使用AS,在這裡推薦一篇部落格文章,當時就是靠大神的方法配置完的
我的配置完是這樣的,具體以官方文件為準
第二步:在工程AndroidManifest.xml檔案中新增許可權,參考官方下載的檔案裡的doc/MSC Develop Manual for Android檔案
第三步:初始化,同上。我是通過先建立一個類繼承自Application 如圖
忘記自己申請的appid可以上官網查,接著在AndroidManifest.xml檔案中配置新建的class為應用程式入口:主要是改變android:name
第四步:可以開始編寫程式碼了,我所需要使用的功能是命令詞識別功能,目前訊飛的離線語音識別功能需要收費,所以我使用的是線上的,但是兩者程式碼相差不大。
(1)線上命令詞識別需要先編寫命令詞語法檔案,採用ABNF語法格式,官網上有詳盡的資料,我們用記事本按照規定格式編寫後上傳到官網上就可以了,這裡附上我的語法檔案截圖:主要修改
(2)接下來直接貼程式碼,大部分都是官方demo:
//語音聽寫UI初始化
// private RecognizerDialog mRecognizerDialog;
//建立SpeechRecognizer,語音識別物件
private SpeechRecognizer mSpeechRecognizer;
//快取
private SharedPreferences mSharedPreferences;
//雲端語法檔案
private String mCloudGrammar = null;
private static final String KEY_GRAMMAR_ABNF_ID = "grammar_abnf_id";
private static final String GRAMMAR_TYPE_ABNF = "abnf";
private static final String GRAMMAR_TYPE_BNF = "bnf";
//引擎型別
private String mEngineType = null;
//語法,詞典臨時變數
String mContent;
//函式呼叫返回值
int ret = 0 ;
接著在onCreate()中
button_voice = (Button)findViewById(R.id.button_voice);
button_voice.setOnTouchListener(this);
//語音識別引擎型別為雲端
mEngineType = SpeechConstant.TYPE_CLOUD;
//建立SpeechRecognizedr物件,並初始化,注意不要匯入錯誤的SpeechRecognizer
mSpeechRecognizer = SpeechRecognizer.createRecognizer(RgbActivity.this,mInitListener);
//初始化語法,命令詞(FucUtil在下面)
mCloudGrammar = FucUtil.readFile(this,"Command.abnf","utf-8");
mContent = new String(mCloudGrammar);
//設定指定引擎型別
mSpeechRecognizer.setParameter(SpeechConstant.ENGINE_TYPE,"cloud");
mSpeechRecognizer.setParameter(SpeechConstant.SUBJECT,"asr");
ret = mSpeechRecognizer.buildGrammar(GRAMMAR_TYPE_ABNF,mContent,mCloudGrammarlistener);
if (ret != ErrorCode.SUCCESS){
LogUtil.e(TAG,"語音語法構建失敗,錯誤碼:"+ ret);
}
語音識別的按鈕的觸碰監聽:
//語音識別按鈕的觸碰監聽
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//開始識別
ret = mSpeechRecognizer.startListening(mRecognizerListener);
//ToastUtils工具類,自定義Toast3500ms內顯示時間
ToastUtils.makeText(getApplicationContext(), "開始識別", 500);
if(ret != ErrorCode.SUCCESS){
LogUtil.e(TAG,"識別失敗,錯誤碼:"+ret);
}
button_voice.setAlpha((float)0.5);
break;
case MotionEvent.ACTION_UP:
//停止識別
mSpeechRecognizer.stopListening();
ToastUtils.makeText(getApplicationContext(), "停止識別", 500);
button_voice.setAlpha((float) 1);
break;
case MotionEvent.ACTION_CANCEL:
ToastUtils.makeText(getApplicationContext(),"識別取消",500);
break;
case MotionEvent.ACTION_BUTTON_PRESS:
break;
}
return false;
}
初始化監聽器:
//初始化監聽器
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
LogUtil.e(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS){
LogUtil.e(TAG,"mInitListener 初始化失敗,錯誤碼:"+code);
}
}
};
雲端構建語法監聽器:
//雲端構建語法監聽器
private GrammarListener mCloudGrammarlistener = new GrammarListener() {
@Override
public void onBuildFinish(String grammarId, SpeechError speechError) {
if (speechError == null){
String grammarID = new String(grammarId);
Editor editor = mSharedPreferences.edit();
if (!TextUtils.isEmpty(grammarId)){
editor.putString(KEY_GRAMMAR_ABNF_ID,grammarID);}
editor.commit();
LogUtil.e(TAG,"mCloudGrammarlistener 語法構建成功:"+grammarId);
}else {
LogUtil.e(TAG,"語法構建失敗錯誤碼:"+speechError.getErrorCode());
}
}
};
識別監聽器
//識別監聽器
private RecognizerListener mRecognizerListener = new RecognizerListener() {
//音量變化回撥
@Override
public void onVolumeChanged(int i, byte[] bytes) {
//在此新增動畫
LogUtil.e(TAG,"返回音訊資料:"+ bytes.length);
}
//開始說話
@Override
public void onBeginOfSpeech() {}
//結束說話
@Override
public void onEndOfSpeech() {}
//返回結果
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
LogUtil.e(TAG, "呼叫了onResult....................");
if (recognizerResult != null){
LogUtil.e(TAG ,"recognizer Result:" + recognizerResult.getResultString());
String text;
//JsonParser:Json解析(程式碼在下方),text為解析出的命令詞
text = JsonParser.parseGramarResult(recognizerResult.getResultString());
switch(text){
case "開燈":
break;
case "關燈":
break;
case "求救":
break;
case "安全":
break;
case "變亮":
break;
case "變暗":
break;
case "進餐":
break;
case "閱讀":
break;
case "睡覺":
break;
case "復位":
break;
default:
Toast.makeText(getApplicationContext(),"無法匹配命令詞,請重新發送命令",Toast.LENGTH_SHORT).show();
}
}else{
LogUtil.e(TAG,"recognizerResult為null....................");
}
}
//錯誤回撥
@Override
public void onError(SpeechError speechError) {
LogUtil.e("mRecognizerListener","出錯,錯誤程式碼:"+speechError.getErrorCode());
Toast.makeText(getApplicationContext(),"出錯,錯誤程式碼:"+speechError.getErrorCode(),Toast.LENGTH_SHORT).show();
}
//事件回撥
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {}
};
FucUtil:
public class FucUtil {
/*
讀取asset目錄下檔案
@return content
* */
public static String readFile(Context mContext ,String file,String code)
{
int len = 0 ;
byte []buf = null;
String result = "";
try{
InputStream in = mContext.getAssets().open(file);
len = in.available();
buf = new byte[len];
in.read(buf,0,len);
result = new String(buf,code);
}catch (Exception e){
e.printStackTrace();
}
return result;
}
}
JsonParser:
public class JsonParser {
public static String TAG = "JsonParser";
public static int index = 0;
public static String parseGramarResult(String json){
StringBuffer ret = new StringBuffer() ;
try{
JSONTokener mJSONTokener = new JSONTokener(json);
JSONObject mJSONObject = new JSONObject(mJSONTokener);
JSONArray words = mJSONObject.getJSONArray("ws");
for (int i = 0 ; i < words.length(); i++){
JSONArray items = words.getJSONObject(i).getJSONArray("cw");
for (int j = 0 ; j < items.length() ; j++){
JSONObject obj = items.getJSONObject(j);
if (obj.getString("w").contains("nomatch")){
ret.append("沒有匹配結果");
return ret.toString();
}
//ret=命令詞 置信度 命令詞 置信度
ret.append(obj.getString("w"));
ret.append(obj.getInt("sc"));
}
}
}catch (Exception e){
e.printStackTrace();
ret.append("沒有匹配結果");
}
for (; index < ret.length();index++){
//判斷ret的內容,找到最接近的命令詞,isDigit判斷字元是否是數字,是數字則跳出迴圈
if(Character.isDigit(ret.charAt(index)))
break;
}
//retCommand = 最匹配的命令詞
String retCommand = ret.substring(0, index);
LogUtil.e(TAG, "Json解析結果: " + ret);
LogUtil.e(TAG, "retComand: " + retCommand);
return retCommand;
}
}
ToasrUtil(3500ms內自定義Toast顯示時間)
public class ToastUtils{
public static void makeText(Context mContext,String mString,final int time){
final Toast mToast = Toast.makeText(mContext,mString,Toast.LENGTH_LONG);
mToast.show();
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (time <= 3500){
mToast.cancel();
}
}
},time);
}
}
LogUtil:類似於Log,設定規格,編寫除錯時開啟,完成編譯成apk時設定LEVEL為NOTHING,出自郭霖大神《第一行程式碼》
public class LogUtil {
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
//測試時LEVEL=VERBOSE,上架後LEVEL=NOTHING
public static final int LEVEL = VERBOSE;
public static void v(String tag,String msg){
if (LEVEL <= VERBOSE){
Log.v(tag,msg);
}
}
public static void d(String tag,String msg){
if (LEVEL <= DEBUG){
Log.d(tag, msg);
}
}
public static void i(String tag,String msg){
if (LEVEL <= INFO){
Log.i(tag, msg);
}
}
public static void w(String tag,String msg){
if (LEVEL <= WARN){
Log.w(tag, msg);
}
}
public static void e(String tag,String msg){
if (LEVEL <= ERROR){
Log.e(tag,msg);
}
}
}
這次記錄到此已經結束了,大家轉載的話說明原址,有哪裡做得不好或者不對也希望大家不吝賜教。。。萬分感謝