版權聲明:本文爲HaiyuKing原創文章,轉載請註明出處!html
本Demo將百度語音SDK(其中一部分功能)和自定義的UI對話框封裝到一個module中,便於後續的SDK版本更新以及調用。java
本Demo使用的百度語音SDK版本是audiobd_speech_sdk_asr_v3.0.7.3_bdasr_20180313_726f26e。react
本Demo中使用的appkey已失效,請自行建立應用,使用新的appkey。android
官網地址:http://ai.baidu.com/tech/speechgit
參考《接入指南》github
2.一、點擊百度AI開放平臺導航右側的控制檯,選擇須要使用的AI服務項【這裏選擇語音技術】。json
2.二、建立應用網絡
2.三、填寫應用信息app
2.四、建立成功ide
2.五、應用列表
3.一、管理應用
3.二、下載SDK
下載地址:https://ai.baidu.com/sdk#asr
普通話 search搜索模型:參考SpeechBottomSheetDialog.java類
普通話 input輸入法模型,適用於長句及長語音,有逗號分割,無語義:參考SpeechLongBottomSheetDialog.java類
注意:關於語音識別狀態維護,API調用的代碼,是本身根據官網demo的理解進行整理的,可能有所偏頗,僅供參考。【但願官網demo能夠添加百度APP的語音對話框效果就行了】
注意事項:
一、 導入類文件後須要change包名以及從新import R文件路徑
二、 Values目錄下的文件(strings.xml、dimens.xml、colors.xml等),若是項目中存在,則複製裏面的內容,不要整個覆蓋
從官方demo的AndroidManifest.xml中找到以下信息,而後複製到您本身的同名文件中。此處須要您複製一、權限二、官網申請的應用信息三、SDK的Service。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.why.project.baiduspeech"> <!-- ======================百度語音====================== --> <!-- begin: baidu speech sdk 權限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- end: baidu speech sdk 權限 --> <application> <!-- ======================百度語音========================== --> <!-- 正式發佈時,請替換成您本身的appId 本demo的appId會不定時下線 --> <meta-data android:name="com.baidu.speech.APP_ID" android:value="11588936" /> <meta-data android:name="com.baidu.speech.API_KEY" android:value="XRF3IOf2tNGePzlv47cBnvF3" /> <meta-data android:name="com.baidu.speech.SECRET_KEY" android:value="diC8lQ7XDcGBKQ6FzCpvnan54F5CnMZI" /> <service android:name="com.baidu.speech.VoiceRecognitionService" android:exported="false" /> </application> </manifest>
注意:此時<service>標籤那裏是紅色錯誤的標記,暫時不用管,導入jar、so文件後編譯下就正常了。
將官方demo中的app\libs\bdasr_V3_20180320_9066860.jar複製進您項目的同名目錄。
在build.gradle中確認是否含有如下紅色標記的代碼
複製官方demo中 app\src\main\jniLibs 至項目的同名目錄。
這個時候編譯下,就會發現AndroidManifest.xml文件的<service>標籤那裏正常了。
package com.why.project.baiduspeech.recognization; import android.os.Handler; import android.os.Message; import android.util.Log; /** * Created by fujiayi on 2017/6/16. */ public class MessageStatusRecogListener extends StatusRecogListener { private Handler handler; private long speechEndTime; private boolean needTime = true; private static final String TAG = "MesStatusRecogListener"; public MessageStatusRecogListener(Handler handler) { this.handler = handler; } @Override public void onAsrReady() { super.onAsrReady(); sendStatusMessage("引擎就緒,能夠開始說話。"); } @Override public void onAsrBegin() { super.onAsrBegin(); sendStatusMessage("檢測到用戶說話"); } @Override public void onAsrEnd() { super.onAsrEnd(); speechEndTime = System.currentTimeMillis(); sendMessage("檢測到用戶說話結束"); } @Override public void onAsrPartialResult(String[] results, RecogResult recogResult) { sendStatusMessage("臨時識別結果,結果是「" + results[0] + "」;原始json:" + recogResult.getOrigalJson()); super.onAsrPartialResult(results, recogResult); } @Override public void onAsrFinalResult(String[] results, RecogResult recogResult) { super.onAsrFinalResult(results, recogResult); //String message = "識別結束,結果是」" + results[0] + "」";//why 實際中能夠去掉,不須要 String message = recogResult.getOrigalJson();//{"results_recognition":["什麼什麼"],"origin_result":{"corpus_no":6522034498058113957,"err_no":0,"result":{"word":["什麼什麼"]},"sn":"bfa8b286-ab0e-4f86-9209-1d36d38b1224","voice_energy":16191.7705078125},"error":0,"best_result":"什麼什麼","result_type":"final_result"} sendStatusMessage(message + "「;原始json:" + recogResult.getOrigalJson()); if (speechEndTime > 0) { long diffTime = System.currentTimeMillis() - speechEndTime; //message += ";說話結束到識別結束耗時【" + diffTime + "ms】";// why 實際中能夠去掉,不須要 } speechEndTime = 0; sendMessage(message, status, true); } @Override public void onAsrFinishError(int errorCode, int subErrorCode, String errorMessage, String descMessage, RecogResult recogResult) { super.onAsrFinishError(errorCode, subErrorCode, errorMessage, descMessage, recogResult); //String message = "識別錯誤, 錯誤碼:" + errorCode + " ," + subErrorCode + " ; " + descMessage;// why 實際中能夠去掉,不須要 String message = recogResult.getOrigalJson();//{"origin_result":{"sn":"","error":7,"desc":"No recognition result match","sub_error":7001},"error":7,"desc":"No recognition result match","sub_error":7001} sendStatusMessage(message + ";錯誤消息:" + errorMessage + ";描述信息:" + descMessage); if (speechEndTime > 0) { long diffTime = System.currentTimeMillis() - speechEndTime; //message += "。說話結束到識別結束耗時【" + diffTime + "ms】";// why實際中能夠去掉,不須要 } speechEndTime = 0; sendMessage(message, status, true); speechEndTime = 0; } @Override public void onAsrOnlineNluResult(String nluResult) { super.onAsrOnlineNluResult(nluResult); if (!nluResult.isEmpty()) { sendStatusMessage("原始語義識別結果json:" + nluResult); } } @Override public void onAsrFinish(RecogResult recogResult) { super.onAsrFinish(recogResult); sendStatusMessage("識別一段話結束。若是是長語音的狀況會繼續識別下段話。"); } /** * 長語音識別結束 */ @Override public void onAsrLongFinish() { super.onAsrLongFinish(); sendStatusMessage("長語音識別結束。"); } /** * 使用離線命令詞時,有該回調說明離線語法資源加載成功 */ @Override public void onOfflineLoaded() { sendStatusMessage("【重要】asr.loaded:離線資源加載成功。沒有此回調可能離線語法功能不能使用。"); } /** * 使用離線命令詞時,有該回調說明離線語法資源加載成功 */ @Override public void onOfflineUnLoaded() { sendStatusMessage(" 離線資源卸載成功。"); } @Override public void onAsrExit() { super.onAsrExit(); sendStatusMessage("識別引擎結束並空閒中"); } private void sendStatusMessage(String message) { sendMessage(message, status); } private void sendMessage(String message) { sendMessage(message, WHAT_MESSAGE_STATUS); } private void sendMessage(String message, int what) { sendMessage(message, what, false); } private void sendMessage(String message, int what, boolean highlight) { if (needTime && what != STATUS_FINISHED) { message += " ;time=" + System.currentTimeMillis(); } if (handler == null){ Log.i(TAG, message ); return; } Message msg = Message.obtain(); msg.what = what; msg.arg1 = status; if (highlight) { msg.arg2 = 1; } msg.obj = message + "\n"; handler.sendMessage(msg); } }
至此,百度語音SDK集成到baiduspeech中了,下一步就是在baiduspeech中建立UI對話框。
一、在baiduspeech的build.gradle中引用recyclerview【版本號和項目的appcompat保持一致】【由於demo中用到了】
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//RecyclerView compile "com.android.support:recyclerview-v7:27.1.1"
}
二、對話框類、列表適配器類、佈局文件xml文件、圖片資源、動畫style樣式等複製到baiduspeech中
三、這裏主要標註下SpeechBottomSheetDialog.java中百度語音的相關代碼
package com.why.project.baiduspeech.dialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.DialogFragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.Button; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.baidu.speech.asr.SpeechConstant; import com.baidu.speech.utils.LogUtil; import com.why.project.baiduspeech.R; import com.why.project.baiduspeech.control.MyRecognizer; import com.why.project.baiduspeech.recognization.IStatus; import com.why.project.baiduspeech.recognization.MessageStatusRecogListener; import com.why.project.baiduspeech.recognization.StatusRecogListener; import com.why.project.baiduspeech.util.Logger; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; /** * Created by HaiyuKing * Used 語音識別底部對話框 */ public class SpeechBottomSheetDialog extends DialogFragment { private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName(); private Context mContext; /**View實例*/ private View myView; private ImageView img_close; private ProgressBar loadProgressBar; private TextView tv_tishi; private RecyclerView result_list; private Button btn_start; private ArrayList<String> resultWordList; private SpeechResultAdapter speechResultAdapter; private String BtnStartText = "按一下開始聽音"; private String BtnStopText = "按一下結束聽音"; private String BtnSearchingText = "正在識別"; private String TishiNoText = "沒聽清,請重說一遍"; /**識別控制器,使用MyRecognizer控制識別的流程*/ protected MyRecognizer myRecognizer; /**控制UI按鈕的狀態*/ protected int status; protected Handler handler; public static SpeechBottomSheetDialog getInstance(Context mContext) { SpeechBottomSheetDialog speechBottomSheetDialog = new SpeechBottomSheetDialog(); speechBottomSheetDialog.mContext = mContext; return speechBottomSheetDialog; } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//設置背景爲透明,而且沒有標題 myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false); return myView; } @Override public void onActivityCreated(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); initHandler();//初始化handler initRecog();//初始化語音 initViews(); initDatas(); initEvents(); } /** * 設置寬度和高度值,以及打開的動畫效果 */ @Override public void onStart() { super.onStart(); //設置對話框的寬高,必須在onStart中 DisplayMetrics metrics = new DisplayMetrics(); this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); Window window = this.getDialog().getWindow(); window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height); window.setGravity(Gravity.BOTTOM);//設置在底部 //打開的動畫效果 //設置dialog的 進出 動畫 getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); LogUtil.w(TAG,"{onDismiss}"); //當對話框消失的時候統一執行銷燬語音功能 destroyRecog();//銷燬語音 } private void initViews() { img_close = (ImageView) myView.findViewById(R.id.img_close); loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar); tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi); result_list = (RecyclerView) myView.findViewById(R.id.result_list); btn_start = (Button) myView.findViewById(R.id.btn_start); } /**初始化數據*/ private void initDatas() { resultWordList = new ArrayList<String>(); speechResultAdapter = null; //設置佈局管理器 LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity()); result_list.setLayoutManager(linerLayoutManager); //能夠設置爲打開後自動識別語音 startRecog(); showProgress(); } private void initEvents() { //關閉圖標的點擊事件 img_close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); //按鈕的點擊事件 btn_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { switch (status) { case IStatus.STATUS_NONE: // 初始狀態 startRecog(); status = IStatus.STATUS_WAITING_READY; updateBtnTextByStatus();//更改按鈕的文本 //顯示加載區域 showProgress(); break; case IStatus.STATUS_WAITING_READY: // 調用本類的start方法後,即輸入START事件後,等待引擎準備完畢。 case IStatus.STATUS_READY: // 引擎準備完畢。 case IStatus.STATUS_SPEAKING: case IStatus.STATUS_FINISHED: // 長語音狀況 case IStatus.STATUS_RECOGNITION: stopRecog(); status = IStatus.STATUS_STOPPED; // 引擎識別中 updateBtnTextByStatus();//更改按鈕的文本 break; case IStatus.STATUS_STOPPED: // 引擎識別中 cancelRecog(); status = IStatus.STATUS_NONE; // 識別結束,回到初始狀態 updateBtnTextByStatus();//更改按鈕的文本 break; default: break; } } }); } /** * 顯示加載進度區域,隱藏其餘區域*/ private void showProgress(){ loadProgressBar.setVisibility(View.VISIBLE); tv_tishi.setVisibility(View.GONE); result_list.setVisibility(View.GONE); } /** * 顯示文本提示區域,隱藏其餘區域*/ private void showTishi(){ tv_tishi.setVisibility(View.VISIBLE); loadProgressBar.setVisibility(View.GONE); result_list.setVisibility(View.GONE); } /** * 顯示語音結果區域,隱藏其餘區域*/ private void showListView(){ result_list.setVisibility(View.VISIBLE); loadProgressBar.setVisibility(View.GONE); tv_tishi.setVisibility(View.GONE); } //======================================語音相關代碼========================================== /** * 初始化handler*/ private void initHandler(){ handler = new Handler() { /*@param msg*/ @Override public void handleMessage(Message msg) { super.handleMessage(msg); handleMsg(msg); } }; Logger.setHandler(handler); } /** * 在onCreate中調用。初始化識別控制類MyRecognizer */ protected void initRecog() { StatusRecogListener listener = new MessageStatusRecogListener(handler); myRecognizer = new MyRecognizer(mContext,listener); status = IStatus.STATUS_NONE;//默認什麼也沒有作 } /** * 銷燬時須要釋放識別資源。 */ protected void destroyRecog() { myRecognizer.release(); Log.i(TAG, "destroyRecog"); } /** * 開始錄音,點擊「開始」按鈕後調用。 */ protected void startRecog() { Map<String, Object> params = new LinkedHashMap<String, Object>(); params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音頻 params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用標點符號,在選擇輸入法模型的前提下生效【不由用的話,說完一段話,就自帶標點符號】 params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暫時不知道什麼意思 params.put(SpeechConstant.PID, 1536); // 普通話 search搜索模型,默認,適用於短句,無逗號,能夠有語義 //params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 長語音,建議搭配input輸入法模型 myRecognizer.start(params); } /** * 開始錄音後,手動中止錄音。SDK會識別在此過程當中的錄音。點擊「中止」按鈕後調用。 */ private void stopRecog() { myRecognizer.stop(); } /** * 開始錄音後,取消此次錄音。SDK會取消本次識別,回到原始狀態。點擊「取消」按鈕後調用。 */ private void cancelRecog() { myRecognizer.cancel(); } protected void handleMsg(Message msg) { Log.e(TAG,"msg.what="+msg.what); Log.e(TAG,"msg.obj.toString()="+msg.obj.toString()); Log.e(TAG,"msg.arg2="+msg.arg2); switch (msg.what) { // 處理MessageStatusRecogListener中的狀態回調 case IStatus.STATUS_FINISHED: //識別結束時候的調用【判斷顯示結果列表區域仍是提示區域】 if (msg.arg2 == 1) { //解析json字符串 try { JSONObject msgObj = new JSONObject(msg.obj.toString()); String error = msgObj.getString("error"); if(error.equals("0")){ //解析結果集合,展示列表 JSONObject origin_resultObj = msgObj.getJSONObject("origin_result"); JSONObject resultObj = origin_resultObj.getJSONObject("result"); JSONArray wordList = resultObj.getJSONArray("word"); initList(wordList);//初始化集合數據 showListView(); }else if(error.equals("7")){ tv_tishi.setText(TishiNoText); showTishi(); }else{//應該根據不一樣的狀態值,顯示不一樣的提示 tv_tishi.setText(TishiNoText); showTishi(); } } catch (JSONException e) { e.printStackTrace(); tv_tishi.setText(TishiNoText); showTishi(); } }else if(msg.arg2 == 0){//無網絡的狀況 //解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100} try { JSONObject msgObj = new JSONObject(msg.obj.toString()); JSONObject origin_resultObj = msgObj.getJSONObject("origin_result"); String error = origin_resultObj.getString("error"); if(error.equals("2")){ //解析結果集合,展示列表 String desc = origin_resultObj.getString("desc"); Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { e.printStackTrace(); } } status = msg.what; updateBtnTextByStatus(); break; case IStatus.STATUS_NONE: case IStatus.STATUS_READY: case IStatus.STATUS_SPEAKING: case IStatus.STATUS_RECOGNITION: status = msg.what; updateBtnTextByStatus(); break; default: break; } } /**更改按鈕的文本*/ private void updateBtnTextByStatus() { switch (status) { case IStatus.STATUS_NONE: btn_start.setText(BtnStartText); btn_start.setEnabled(true); break; case IStatus.STATUS_WAITING_READY: case IStatus.STATUS_READY: case IStatus.STATUS_SPEAKING: case IStatus.STATUS_RECOGNITION: btn_start.setText(BtnStopText); btn_start.setEnabled(true); break; case IStatus.STATUS_STOPPED: btn_start.setText(BtnSearchingText); btn_start.setEnabled(true); break; default: break; } } //========================================更改列表========================== /**獲取集合數據,並顯示*/ private void initList(JSONArray wordList){ //先清空 if(resultWordList.size() > 0){ resultWordList.clear(); } //再賦值 for(int i=0;i<wordList.length();i++){ String wordItem = ""; try { wordItem = wordList.getString(i); } catch (JSONException e) { e.printStackTrace(); } resultWordList.add(wordItem); } if(speechResultAdapter == null){ //設置適配器 speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList); result_list.setAdapter(speechResultAdapter); //添加分割線 //設置添加刪除動畫 //調用ListView的setSelected(!ListView.isSelected())方法,這樣就能及時刷新佈局 result_list.setSelected(true); }else{ speechResultAdapter.notifyDataSetChanged(); } speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() { @Override public void onItemClick(int position) { dismiss(); if(mOnResultListItemClickListener != null){ mOnResultListItemClickListener.onItemClick(resultWordList.get(position)); } } }); } //=========================語音列表項的點擊事件監聽============================== public static abstract interface OnResultListItemClickListener { //語音結果列表項的點擊事件接口 public abstract void onItemClick(String title); } private OnResultListItemClickListener mOnResultListItemClickListener; public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener) { this.mOnResultListItemClickListener = mOnResultListItemClickListener; } }
四、若是想要使用長語音功能,請參考SpeechLongBottomSheetDialog.java文件
package com.why.project.baiduspeech.dialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.DialogFragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.Button; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.baidu.speech.asr.SpeechConstant; import com.baidu.speech.utils.LogUtil; import com.why.project.baiduspeech.R; import com.why.project.baiduspeech.control.MyRecognizer; import com.why.project.baiduspeech.recognization.IStatus; import com.why.project.baiduspeech.recognization.MessageStatusRecogListener; import com.why.project.baiduspeech.recognization.StatusRecogListener; import com.why.project.baiduspeech.util.Logger; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; /** * Created by HaiyuKing * Used 普通話 input輸入法模型,適用於長句及長語音,有逗號分割,無語義【基本上和SpeechBottomSheetDialog代碼相同】 */ public class SpeechLongBottomSheetDialog extends DialogFragment { private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName(); private Context mContext; /**View實例*/ private View myView; private ImageView img_close; private ProgressBar loadProgressBar; private TextView tv_tishi; private RecyclerView result_list; private Button btn_start; private ArrayList<String> resultWordList; private SpeechResultAdapter speechResultAdapter; private String BtnStartText = "按一下開始聽音"; private String BtnStopText = "按一下結束聽音"; private String BtnSearchingText = "正在識別"; private String TishiNoText = "沒聽清,請重說一遍"; /**識別控制器,使用MyRecognizer控制識別的流程*/ protected MyRecognizer myRecognizer; /**控制UI按鈕的狀態*/ protected int status; protected Handler handler; public static SpeechLongBottomSheetDialog getInstance(Context mContext) { SpeechLongBottomSheetDialog speechLongBottomSheetDialog = new SpeechLongBottomSheetDialog(); speechLongBottomSheetDialog.mContext = mContext; return speechLongBottomSheetDialog; } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//設置背景爲透明,而且沒有標題 myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false); return myView; } @Override public void onActivityCreated(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onActivityCreated(savedInstanceState); initHandler();//初始化handler initRecog();//初始化語音 initViews(); initDatas(); initEvents(); } /** * 設置寬度和高度值,以及打開的動畫效果 */ @Override public void onStart() { super.onStart(); //設置對話框的寬高,必須在onStart中 DisplayMetrics metrics = new DisplayMetrics(); this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics); Window window = this.getDialog().getWindow(); window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height); window.setGravity(Gravity.BOTTOM);//設置在底部 //打開的動畫效果 //設置dialog的 進出 動畫 getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); LogUtil.w(TAG,"{onDismiss}"); //當對話框消失的時候統一執行銷燬語音功能 destroyRecog();//銷燬語音 } private void initViews() { img_close = (ImageView) myView.findViewById(R.id.img_close); loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar); tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi); result_list = (RecyclerView) myView.findViewById(R.id.result_list); btn_start = (Button) myView.findViewById(R.id.btn_start); } /**初始化數據*/ private void initDatas() { resultWordList = new ArrayList<String>(); speechResultAdapter = null; //設置佈局管理器 LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity()); result_list.setLayoutManager(linerLayoutManager); btn_start.setText(BtnStartText);//顯示文字,和下面的二選一便可 why //能夠設置爲打開後自動識別語音 /*startRecog(); showProgress();*/ } private void initEvents() { //關閉圖標的點擊事件 img_close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); //按鈕的點擊事件 btn_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { switch (status) { case IStatus.STATUS_NONE: // 初始狀態 startRecog(); status = IStatus.STATUS_WAITING_READY; updateBtnTextByStatus();//更改按鈕的文本 //顯示加載區域 showProgress(); break; case IStatus.STATUS_WAITING_READY: // 調用本類的start方法後,即輸入START事件後,等待引擎準備完畢。 case IStatus.STATUS_READY: // 引擎準備完畢。 case IStatus.STATUS_SPEAKING: case IStatus.STATUS_FINISHED: // 長語音狀況 case IStatus.STATUS_RECOGNITION: stopRecog(); status = IStatus.STATUS_STOPPED; // 引擎識別中 updateBtnTextByStatus();//更改按鈕的文本 //對於長語音來說,須要手動執行代碼,不然還得點擊一次才能取消why btn_start.callOnClick(); break; case IStatus.STATUS_STOPPED: // 引擎識別中 cancelRecog(); hiddenAll();//隱藏加載區域why status = IStatus.STATUS_NONE; // 識別結束,回到初始狀態 updateBtnTextByStatus();//更改按鈕的文本 break; default: break; } } }); } /** * 顯示加載進度區域,隱藏其餘區域*/ private void showProgress(){ loadProgressBar.setVisibility(View.VISIBLE); tv_tishi.setVisibility(View.GONE); result_list.setVisibility(View.GONE); } /** * 顯示文本提示區域,隱藏其餘區域*/ private void showTishi(){ tv_tishi.setVisibility(View.VISIBLE); loadProgressBar.setVisibility(View.GONE); result_list.setVisibility(View.GONE); } /** * 顯示語音結果區域,隱藏其餘區域*/ private void showListView(){ result_list.setVisibility(View.VISIBLE); loadProgressBar.setVisibility(View.GONE); tv_tishi.setVisibility(View.GONE); } /**隱藏全部的區域【主要用於長語音】why*/ private void hiddenAll(){ result_list.setVisibility(View.GONE); loadProgressBar.setVisibility(View.GONE); tv_tishi.setVisibility(View.GONE); } //======================================語音相關代碼========================================== /** * 初始化handler*/ private void initHandler(){ handler = new Handler() { /*@param msg*/ @Override public void handleMessage(Message msg) { super.handleMessage(msg); handleMsg(msg); } }; Logger.setHandler(handler); } /** * 在onCreate中調用。初始化識別控制類MyRecognizer */ protected void initRecog() { StatusRecogListener listener = new MessageStatusRecogListener(handler); myRecognizer = new MyRecognizer(mContext,listener); status = IStatus.STATUS_NONE;//默認什麼也沒有作 } /** * 銷燬時須要釋放識別資源。 */ protected void destroyRecog() { myRecognizer.release(); Log.i(TAG, "destroyRecog"); } /** * 開始錄音,點擊「開始」按鈕後調用。 */ protected void startRecog() { Map<String, Object> params = new LinkedHashMap<String, Object>(); params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音頻 params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用標點符號,在選擇輸入法模型的前提下生效【不由用的話,說完一段話,就自帶標點符號】 params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暫時不知道什麼意思 //下面的1936和1537選擇其中一個 why //params.put(SpeechConstant.PID, 1936); // 普通話 far,遠場模型,高級,適用於音源離麥克風較遠(>1m)的錄音,有逗號分隔,能夠有語義 params.put(SpeechConstant.PID, 1537); // 普通話 input輸入法模型,適用於長句及長語音,有逗號分割,無語義 params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 長語音,建議搭配input輸入法模型 myRecognizer.start(params); } /** * 開始錄音後,手動中止錄音。SDK會識別在此過程當中的錄音。點擊「中止」按鈕後調用。 */ private void stopRecog() { myRecognizer.stop(); } /** * 開始錄音後,取消此次錄音。SDK會取消本次識別,回到原始狀態。點擊「取消」按鈕後調用。 */ private void cancelRecog() { myRecognizer.cancel(); } protected void handleMsg(Message msg) { switch (msg.what) { // 處理MessageStatusRecogListener中的狀態回調 case IStatus.STATUS_FINISHED: //識別結束時候的調用【判斷顯示結果列表區域仍是提示區域】 if (msg.arg2 == 1) { //解析json字符串 try { JSONObject msgObj = new JSONObject(msg.obj.toString()); String error = msgObj.getString("error"); if(error.equals("0")){ //直接輸入到文本框中 why JSONArray recognitionObj = msgObj.getJSONArray("results_recognition"); String result = recognitionObj.getString(0); if(mOnResultListItemClickListener != null){ mOnResultListItemClickListener.onItemClick(result); } }else if(error.equals("7")){ tv_tishi.setText(TishiNoText); showTishi(); }else{//應該根據不一樣的狀態值,顯示不一樣的提示 tv_tishi.setText(TishiNoText); showTishi(); } } catch (JSONException e) { e.printStackTrace(); tv_tishi.setText(TishiNoText); showTishi(); } }else if(msg.arg2 == 0){//無網絡的狀況 //解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100} try { JSONObject msgObj = new JSONObject(msg.obj.toString()); JSONObject origin_resultObj = msgObj.getJSONObject("origin_result"); String error = origin_resultObj.getString("error"); if(error.equals("2")){ //解析結果集合,展示列表 String desc = origin_resultObj.getString("desc"); Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show(); } } catch (JSONException e) { e.printStackTrace(); } } status = msg.what; updateBtnTextByStatus(); break; case IStatus.STATUS_NONE: case IStatus.STATUS_READY: case IStatus.STATUS_SPEAKING: case IStatus.STATUS_RECOGNITION: status = msg.what; updateBtnTextByStatus(); break; default: break; } } /**更改按鈕的文本*/ private void updateBtnTextByStatus() { switch (status) { case IStatus.STATUS_NONE: btn_start.setText(BtnStartText); btn_start.setEnabled(true); break; case IStatus.STATUS_WAITING_READY: case IStatus.STATUS_READY: case IStatus.STATUS_SPEAKING: case IStatus.STATUS_RECOGNITION: btn_start.setText(BtnStopText); btn_start.setEnabled(true); break; case IStatus.STATUS_STOPPED: btn_start.setText(BtnSearchingText); btn_start.setEnabled(true); break; default: break; } } //========================================更改列表========================== /**獲取集合數據,並顯示*/ private void initList(JSONArray wordList){ //先清空 if(resultWordList.size() > 0){ resultWordList.clear(); } //再賦值 for(int i=0;i<wordList.length();i++){ String wordItem = ""; try { wordItem = wordList.getString(i); } catch (JSONException e) { e.printStackTrace(); } resultWordList.add(wordItem); } if(speechResultAdapter == null){ //設置適配器 speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList); result_list.setAdapter(speechResultAdapter); //添加分割線 //設置添加刪除動畫 //調用ListView的setSelected(!ListView.isSelected())方法,這樣就能及時刷新佈局 result_list.setSelected(true); }else{ speechResultAdapter.notifyDataSetChanged(); } speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() { @Override public void onItemClick(int position) { dismiss(); if(mOnResultListItemClickListener != null){ mOnResultListItemClickListener.onItemClick(resultWordList.get(position)); } } }); } //=========================語音列表項的點擊事件監聽============================== public static abstract interface OnResultListItemClickListener { //語音結果列表項的點擊事件接口 public abstract void onItemClick(String title); } private OnResultListItemClickListener mOnResultListItemClickListener; public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener) { this.mOnResultListItemClickListener = mOnResultListItemClickListener; } }
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.why.project.baiduspeechdemo.MainActivity"> <TextView android:id="@+id/tv_result" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0.448" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.325"/> <Button android:id="@+id/btn_openSpeechDialog" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="32dp" android:text="打開搜索模型語音識別對話框" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.419" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <Button android:id="@+id/btn_openSpeechLongDialog" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:text="打開input輸入模型語音識別對話框" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.4" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_openSpeechDialog"/> </android.support.constraint.ConstraintLayout>
package com.why.project.baiduspeechdemo; import android.Manifest; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.tbruyelle.rxpermissions2.RxPermissions; import com.why.project.baiduspeech.dialog.SpeechBottomSheetDialog; import com.why.project.baiduspeech.dialog.SpeechLongBottomSheetDialog; import io.reactivex.functions.Action; import io.reactivex.functions.Consumer; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private Button mOpenSpeechDialogBtn; private Button mOpenSpeechLongDialogBtn; private TextView mResultTv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); onePermission(); initViews(); initEvents(); } private void initViews() { mOpenSpeechDialogBtn = findViewById(R.id.btn_openSpeechDialog); mOpenSpeechLongDialogBtn = findViewById(R.id.btn_openSpeechLongDialog); mResultTv = findViewById(R.id.tv_result); } private void initEvents() { mOpenSpeechDialogBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //打開百度語音對話框 SpeechBottomSheetDialog speechBottomSheetDialog = SpeechBottomSheetDialog.getInstance(MainActivity.this); speechBottomSheetDialog.seOnResultListItemClickListener(new SpeechBottomSheetDialog.OnResultListItemClickListener() { @Override public void onItemClick(String title) { //填充到輸入框中 mResultTv.setText(title); } }); speechBottomSheetDialog.show(getSupportFragmentManager(), TAG); } }); mOpenSpeechLongDialogBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //打開百度語音對話框 SpeechLongBottomSheetDialog speechLongBottomSheetDialog = SpeechLongBottomSheetDialog.getInstance(MainActivity.this); speechLongBottomSheetDialog.seOnResultListItemClickListener(new SpeechLongBottomSheetDialog.OnResultListItemClickListener() { @Override public void onItemClick(String title) { //填充到輸入框中 mResultTv.setText(mResultTv.getText()+title); } }); speechLongBottomSheetDialog.show(getSupportFragmentManager(), TAG); } }); } /**只有一個運行時權限申請的狀況*/ private void onePermission(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE) //權限名稱,多個權限之間逗號分隔開 .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { Log.e(TAG, "{accept}granted=" + granted);//執行順序——1【多個權限的狀況,只有全部的權限均容許的狀況下granted==true】 if (granted) { // 在android 6.0以前會默認返回true // 已經獲取權限 } else { // 未獲取權限 Toast.makeText(MainActivity.this, "您沒有受權該權限,請在設置中打開受權", Toast.LENGTH_SHORT).show(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG,"{accept}");//多是受權異常的狀況下的處理 } }, new Action() { @Override public void run() throws Exception { Log.e(TAG,"{run}");//執行順序——2 } }); } }
#=====================百度語音混淆=====================
-keep class com.baidu.speech.**{*;}
http://ai.baidu.com/tech/speech