前言
最近需要對接語音識別業務,畢竟現在是AI時代,一個產品如果能通過AI能力給使用者帶來全新的體驗,也是很值得嘗試的。技術對接上選擇的是科大訊飛開放平臺,也算是國內最早一批做語音識別的企業了,文件方面都比較全面,對接起來也很方便。
技術基礎
開發技術棧為Cordova
+Angular
+Ionic
,這篇分享會介紹如何從頭開始建立Cordova外掛,並實現科大訊飛Android sdk與App端的資料互動。
Apache Cordova
是一個開源的移動開發框架。允許你用標準的web技術——HTML5,CSS3和JavaScript做跨平臺開發。 應用在每個平臺的具體執行被封裝了起來,並依靠符合標準的API繫結去訪問每個裝置的功能,比如說:感測器、資料、網路狀態等。
在繼續閱讀之前,應該確保你有通過Cordova
建立並打包一個簡單Hybrid App
的經驗,感興趣的童鞋可以到Ionic官網和Cordova官網學習下。
建立Cordova外掛
全域性安裝plugman
plugman
用於建立Cordova外掛,在專案目錄下執行cnpm i -g plugman
建立外掛
建立一個外掛並新增android平臺,並生成package.json,外掛名xFeiVoice
,外掛idcom.qinsilk.xFeiVoice
,版本號為0.01
plugman create --name xFeiVoice --plugin_id com.qinsilk.xFeiVoice --plugin_version 0.0.1
cd xFeiVoice
plugman createpackagejson ./
plugman platform add --platform_name android
複製程式碼
建立成功可以看到對應目錄如下
- src目錄存放原生程式碼,此處為java檔案
- www目錄存放js暴露給裝置的介面,如下
var exec = require(`cordova/exec`);
exports.coolMethod = function (arg0, success, error) {
exec(success, error, `xFeiVoice`, `coolMethod`, [arg0]);
};
複製程式碼
此時我們做下修改,讓引數名跟業務命名更加相關。
var exec = require(`cordova/exec`);
exports.record = function (arg0, success, error) {
exec(success, error, `xFeiVoice`, `record`, [arg0]);
};
複製程式碼
安裝外掛
執行cordova plugin add xFeiVoice
,再執行cordova plugin ls
可以檢視當前App安裝的外掛。
此時用Android Studio開啟專案,可以看到這個外掛已經新增成功。
到這裡我們的準備工作就完成了。
對接語音識別
匯入sdk
Android sdk可以去科大訊飛開放平臺下載。將在官網下載的Android SDK 壓縮包中libs目錄下所有子檔案拷貝至Android工程的libs目錄下。如下圖所示:
新增許可權
在工程 AndroidManifest.xml 檔案中新增如下許可權:
<!--連線網路許可權,用於執行雲端語音能力 -->
<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"/>
<!--讀取手機資訊許可權 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!--讀取聯絡人許可權,上傳聯絡人需要用到此許可權 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!--外儲存寫許可權,構建語法需要用到此許可權 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--外儲存讀許可權,構建語法需要用到此許可權 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!--配置許可權,用來記錄應用配置資訊 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<!--手機定位資訊,用來為語義等功能提供定位,提供更精準的服務-->
<!--定位資訊是敏感資訊,可通過Setting.setLocationEnable(false)關閉定位請求 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!--如需使用人臉識別,還要新增:攝相頭許可權,拍照需要用到 -->
<uses-permission android:name="android.permission.CAMERA" />
複製程式碼
初始化語音識別物件
此處呼叫的是60秒語音聽寫功能。excute
是在開發外掛時,使用者的自定義方法,當頁面呼叫外掛時系統首先將會執行此方法。
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.callbackContext = callbackContext;
if (action.equals("record")) {
// 初始化語音識別物件
SpeechUtility.createUtility(cordova.getActivity(), "appid=yourAppid,force_login=true");
// 使用SpeechRecognizer物件,可根據回撥訊息自定義介面;
mIat = SpeechRecognizer.createRecognizer(cordova.getActivity(), mInitListener);
// 設定引數
setParam();
// 監聽事件
mIat.startListening(mRecognizerListener);
return true;
}
return false;
}
複製程式碼
初始化監聽器
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d(LOG_TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d(LOG_TAG, "初始化失敗,錯誤碼:" + code);
}
}
};
複製程式碼
設定引數
public void setParam() {
// 清空引數
mIat.setParameter(SpeechConstant.PARAMS, null);
// 設定聽寫引擎
mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
// 設定返回結果格式
mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");
// 設定語音前端點:靜音超時時間,即使用者多長時間不說話則當做超時處理
mIat.setParameter(SpeechConstant.VAD_BOS, "4000");
// 設定語音後端點:後端點靜音檢測時間,即使用者停止說話多長時間內即認為不再輸入, 自動停止錄音
mIat.setParameter(SpeechConstant.VAD_EOS, "1000");
// 設定標點符號,設定為"0"返回結果無標點,設定為"1"返回結果有標點
mIat.setParameter(SpeechConstant.ASR_PTT, "0");
// 設定音訊儲存路徑,儲存音訊格式支援pcm、wav,設定路徑為sd卡請注意WRITE_EXTERNAL_STORAGE許可權
// 注:AUDIO_FORMAT引數語記需要更新版本才能生效
mIat.setParameter(SpeechConstant.AUDIO_FORMAT,"wav");
mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/iat.wav");
}
複製程式碼
識別監聽器
private RecognizerListener mRecognizerListener = new RecognizerListener() {
@Override
public void onVolumeChanged(int volume, byte[] data) {
// showTip("當前正在說話,音量大小:" + volume);
Log.d(LOG_TAG, "返回音訊資料:"+data.length);
}
@Override
public void onResult(final RecognizerResult result, boolean isLast) {
//此處有坑,isLast為true時會返回標點符號
if (null != result && !isLast) {
String text = parseIatResult(result.getResultString());
JSONObject obj = new JSONObject();
try {
obj.put("searchText", text);
} catch (JSONException e) {
Log.d(LOG_TAG, "This should never happen");
}
if( null != mIat ){
// 退出時釋放連線
mIat.cancel();
mIat.destroy();
}
getSearchText(obj);
} else {
Log.d(LOG_TAG, "recognizer result : null");
}
}
@Override
public void onEndOfSpeech() {
// 此回撥錶示:檢測到了語音的尾端點,已經進入識別過程,不再接受語音輸入
Log.d(LOG_TAG, "結束說話");
}
@Override
public void onBeginOfSpeech() {
// 此回撥錶示:sdk內部錄音機已經準備好了,使用者可以開始語音輸入
Log.d(LOG_TAG, "開始說話");
}
@Override
public void onError(SpeechError error) {
Log.d(LOG_TAG, "onError Code:" + error.getErrorCode());
}
@Override
public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
// 以下程式碼用於獲取與雲端的會話id,當業務出錯時將會話id提供給技術支援人員,可用於查詢會話日誌,定位出錯原因
// 若使用本地能力,會話id為null
}
};
複製程式碼
處理結果並返回App端
// 處理結果
public static String parseIatResult(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);
ret.append(obj.getString("w"));
}
} catch (Exception e) {
e.printStackTrace();
}
return ret.toString();
}
//將結果通過callbackContext返回App端
public void getSearchText(JSONObject obj) {
this.callbackContext.success(obj);
}
複製程式碼
App端呼叫sdk並監聽資料返回
//語音識別
$scope.record = function () {
if (window.cordova && window.cordova.plugins) {
if(!$scope.recording){
//呼叫sdk
cordova.plugins.xFeiVoice.record({}, function (result) {
if(result){
//返回識別結果
$scope.search.goodKey = result.searchText;
$scope.openModal();
$scope.recording = false;
}
console.log(`success`);
}, function (result) {
console.log(`fail`);
});
}else{
//$scope.recordMedia.stopRecord();
}
$scope.recording = !$scope.recording;
}
};
複製程式碼
結語
外掛程式碼我已經上傳到github了,有需要的可以clone。走過路過給個star吧~