最近須要對接語音識別業務,畢竟如今是AI時代,一個產品若是能經過AI能力給用戶帶來全新的體驗,也是很值得嘗試的。技術對接上選擇的是科大訊飛開放平臺,也算是國內最先一批作語音識別的企業了,文檔方面都比較全面,對接起來也很方便。前端
開發技術棧爲Cordova
+Angular
+Ionic
,這篇分享會介紹如何從頭開始建立Cordova插件,並實現科大訊飛Android sdk與App端的數據交互。java
Apache Cordova
是一個開源的移動開發框架。容許你用標準的web技術——HTML5,CSS3和JavaScript作跨平臺開發。 應用在每一個平臺的具體執行被封裝了起來,並依靠符合標準的API綁定去訪問每一個設備的功能,好比說:傳感器、數據、網絡狀態等。android
在繼續閱讀以前,應該確保你有經過Cordova
建立並打包一個簡單Hybrid App
的經驗,感興趣的童鞋能夠到Ionic官網和Cordova官網學習下。git
plugman
用於建立Cordova插件,在項目目錄下執行cnpm i -g plugman
github
建立一個插件並添加android平臺,並生成package.json,插件名xFeiVoice
,插件idcom.qinsilk.xFeiVoice
,版本號爲0.01
web
plugman create --name xFeiVoice --plugin_id com.qinsilk.xFeiVoice --plugin_version 0.0.1
cd xFeiVoice
plugman createpackagejson ./
plugman platform add --platform_name android
複製代碼
var exec = require('cordova/exec');
exports.coolMethod = function (arg0, success, error) {
exec(success, error, 'xFeiVoice', 'coolMethod', [arg0]);
};
複製代碼
此時咱們作下修改,讓參數名跟業務命名更加相關。npm
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安裝的插件。 json
Android sdk能夠去科大訊飛開放平臺下載。將在官網下載的Android SDK 壓縮包中libs目錄下全部子文件拷貝至Android工程的libs目錄下。以下圖所示:後端
在工程 AndroidManifest.xml 文件中添加以下權限:bash
<!--鏈接網絡權限,用於執行雲端語音能力 -->
<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
}
};
複製代碼
// 處理結果
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);
}
複製代碼
//語音識別
$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吧~