前言
語音識別在現在的APP中是常見的,但是通常小的項目中我們不會去費心思自己去做這一塊的業務功能開發,常規的是接入第三方的SDK快速實現功能,比如百度、訊飛之類的,百度語音識別之前我已經寫過了,本着雨露均沾的原則的,寫這篇訊飛的SDK對接步驟,開始吧~
效果圖
點擊
識別中,識別到之後
源碼地址
正文
首先呢,你先去註冊訊飛的開發者賬號,點擊訊飛開放平臺前往註冊,
註冊好之後你可以選擇實名認證或者不認證都可以,然後登錄進入控制檯或者我的應用
創建一個應用
填寫信息然後提交
點擊這個應用名稱查看詳細信息
右邊的是對接過程需要用到的值,APPID用SDK中,APIKey或APISecret適用於WebAPI調用方式,這兩者有什麼區別呢?SDK的話很好理解,複製一些文件到你的項目中,然後添加依賴,你就可以使用訊飛的SDK中的語音識別功能了,而WebAPI調用顧名思義是通過網絡接口攜帶參數的方式去實現的,需要自己去做網絡請求,本文中目前使用SDK的方式進行對接實現功能,如果有想看WebAPI調用方式的請在評論區留言,
OK,現在滑動到下面下載Android SDK
下載到本地,然後解壓,打開文件目錄如下:
然後就是創建你自己項目了,打開AS
① 配置資源文件
我取名是XFASRDemo,然後Finish,等待項目創建好之後,複製文件中libs裏面的三個文件,到項目的libs中
右鍵Mac.jar添加Add As Library
點擊OK添加,添加好之後你的jar包就會有一個三角箭頭,可以展開,這個時候你就可以使用裏面的方法了。
然後複製資源文件到main下面
② 配置項目
創建SpeechApplication.java
package com.llw.xfasrdemo;
import android.app.Application;
import com.iflytek.cloud.SpeechUtility;
public class SpeechApplication extends Application {
@Override
public void onCreate() {
// 5ef048e1 爲在開放平臺註冊的APPID 注意沒有空格,直接替換即可
SpeechUtility.createUtility(SpeechApplication.this, "appid=5ef048e1");
super.onCreate();
}
}
打開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"/>
配置SpeechApplication和註冊權限
然後修改app模塊下面的build.gradle
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
implementation files('libs/Msc.jar')
改完記得Sync一下
然後修改佈局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="識別到的內容"
android:textColor="#000" />
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="開始識別" />
</LinearLayout>
接下來就是MainActivity了
③ 編碼
一、聲明變量和初始化
private static final String TAG = "MainActivity";
private SpeechRecognizer mIat;// 語音聽寫對象
private RecognizerDialog mIatDialog;// 語音聽寫UI
// 用HashMap存儲聽寫結果
private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
private SharedPreferences mSharedPreferences;//緩存
private String mEngineType = SpeechConstant.TYPE_CLOUD;// 引擎類型
private String language = "zh_cn";//識別語言
private TextView tvResult;//識別結果
private Button btnStart;//開始識別
private String resultType = "json";//結果內容數據格式
同時你要實現這個點擊事件的監聽
實現一個方法
@Override
public void onClick(View v) {
//寫入點擊之後處理邏輯
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvResult = findViewById(R.id.tv_result);
btnStart = findViewById(R.id.btn_start);
btnStart.setOnClickListener(this);//實現點擊監聽
}
二、動態權限請求
/**
* android 6.0 以上需要動態申請權限
*/
private void initPermission() {
String permissions[] = {Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
ArrayList<String> toApplyList = new ArrayList<String>();
for (String perm : permissions) {
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
toApplyList.add(perm);
}
}
String tmpList[] = new String[toApplyList.size()];
if (!toApplyList.isEmpty()) {
ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
}
}
/**
* 權限申請回調,可以作進一步處理
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 此處爲android 6.0以上動態授權的回調,用戶自行實現。
}
在onCreate方法中調用
initPermission();//權限請求
三、語音監聽
/**
* 初始化監聽器。
*/
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
showMsg("初始化失敗,錯誤碼:" + code + ",請點擊網址https://www.xfyun.cn/document/error-code查詢解決方案");
}
}
};
/**
* 聽寫UI監聽器
*/
private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
public void onResult(RecognizerResult results, boolean isLast) {
printResult(results);//結果數據解析
}
/**
* 識別回調錯誤.
*/
public void onError(SpeechError error) {
showMsg(error.getPlainDescription(true));
}
};
/**
* 提示消息
* @param msg
*/
private void showMsg(String msg) {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
裏面用到一個方法,用於解析監聽到的結果
四、數據解析
/**
* 數據解析
*
* @param results
*/
private void printResult(RecognizerResult results) {
String text = JsonParser.parseIatResult(results.getResultString());
String sn = null;
// 讀取json結果中的sn字段
try {
JSONObject resultJson = new JSONObject(results.getResultString());
sn = resultJson.optString("sn");
} catch (JSONException e) {
e.printStackTrace();
}
mIatResults.put(sn, text);
StringBuffer resultBuffer = new StringBuffer();
for (String key : mIatResults.keySet()) {
resultBuffer.append(mIatResults.get(key));
}
tvResult.setText(resultBuffer.toString());//聽寫結果顯示
}
裏面用到一個JsonParser的工具類,需要手動去創建一個
代碼如下:
package com.llw.xfasrdemo;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
/**
* Json結果解析類
*/
public class JsonParser {
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"));
// 如果需要多候選結果,解析數組其他字段
// for(int j = 0; j < items.length(); j++)
// {
// JSONObject obj = items.getJSONObject(j);
// ret.append(obj.getString("w"));
// }
}
} catch (Exception e) {
e.printStackTrace();
}
return ret.toString();
}
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");
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.append("【結果】" + obj.getString("w"));
ret.append("【置信度】" + obj.getInt("sc"));
ret.append("\n");
}
}
} catch (Exception e) {
e.printStackTrace();
ret.append("沒有匹配結果.");
}
return ret.toString();
}
public static String parseLocalGrammarResult(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");
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.append("【結果】" + obj.getString("w"));
ret.append("\n");
}
}
ret.append("【置信度】" + joResult.optInt("sc"));
} catch (Exception e) {
e.printStackTrace();
ret.append("沒有匹配結果.");
}
return ret.toString();
}
public static String parseTransResult(String json,String key) {
StringBuffer ret = new StringBuffer();
try {
JSONTokener tokener = new JSONTokener(json);
JSONObject joResult = new JSONObject(tokener);
String errorCode = joResult.optString("ret");
if(!errorCode.equals("0")) {
return joResult.optString("errmsg");
}
JSONObject transResult = joResult.optJSONObject("trans_result");
ret.append(transResult.optString(key));
/*JSONArray words = joResult.getJSONArray("results");
for (int i = 0; i < words.length(); i++) {
JSONObject obj = words.getJSONObject(i);
ret.append(obj.getString(key));
}*/
} catch (Exception e) {
e.printStackTrace();
}
return ret.toString();
}
}
五、參數配置
/**
* 參數設置
*
* @return
*/
public void setParam() {
// 清空參數
mIat.setParameter(SpeechConstant.PARAMS, null);
// 設置聽寫引擎
mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
// 設置返回結果格式
mIat.setParameter(SpeechConstant.RESULT_TYPE, resultType);
if (language.equals("zh_cn")) {
String lag = mSharedPreferences.getString("iat_language_preference",
"mandarin");
Log.e(TAG, "language:" + language);// 設置語言
mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
// 設置語言區域
mIat.setParameter(SpeechConstant.ACCENT, lag);
} else {
mIat.setParameter(SpeechConstant.LANGUAGE, language);
}
Log.e(TAG, "last language:" + mIat.getParameter(SpeechConstant.LANGUAGE));
//此處用於設置dialog中不顯示錯誤碼信息
//mIat.setParameter("view_tips_plain","false");
// 設置語音前端點:靜音超時時間,即用戶多長時間不說話則當做超時處理
mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));
// 設置語音後端點:後端點靜音檢測時間,即用戶停止說話多長時間內即認爲不再輸入, 自動停止錄音
mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));
// 設置標點符號,設置爲"0"返回結果無標點,設置爲"1"返回結果有標點
mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));
// 設置音頻保存路徑,保存音頻格式支持pcm、wav,設置路徑爲sd卡請注意WRITE_EXTERNAL_STORAGE權限
mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");
}
六、代碼組裝
在onCreate中增加如下代碼,進行初始化
// 使用SpeechRecognizer對象,可根據回調消息自定義界面;
mIat = SpeechRecognizer.createRecognizer(MainActivity.this, mInitListener);
// 使用UI聽寫功能,請根據sdk文件目錄下的notice.txt,放置佈局文件和圖片資源
mIatDialog = new RecognizerDialog(MainActivity.this, mInitListener);
mSharedPreferences = getSharedPreferences("ASR",
Activity.MODE_PRIVATE);
然後再onClick中
@Override
public void onClick(View v) {
if( null == mIat ){
// 創建單例失敗,與 21001 錯誤爲同樣原因,參考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
showMsg( "創建對象失敗,請確認 libmsc.so 放置正確,且有調用 createUtility 進行初始化" );
return;
}
mIatResults.clear();//清除數據
setParam(); // 設置參數
mIatDialog.setListener(mRecognizerDialogListener);//設置監聽
mIatDialog.show();// 顯示對話框
}
最後在onDestory中
@Override
protected void onDestroy() {
super.onDestroy();
if (null != mIat) {
// 退出時釋放連接
mIat.cancel();
mIat.destroy();
}
}
這樣邏輯就很清楚了
七、運行效果圖
點擊
識別中,識別到之後
④ 梳理
從平臺創建應用之後的到一個appid,然後新建一個項目,先配置AndroidManifest.xml,配置了靜態權限和Application,在Application中對訊飛語音插件進行初始化,這樣就可以影響到全局,而不是在某一個Activity的中onCreate進行初始化,當然你也可以這樣做。然後初始化控件和訊飛的一些工具類,並實現點擊監聽,之後進行動態權限的獲取,成功之後,進行語音監聽(包括UI對話框)的接口實現,在返回值中做數據解析,得到需要的數據顯示在頁面的TextView上,然後再配置一些需要在調用語音識別之前需要的參數,比如語言類型和結果類型之類的,之後就是點擊出現語音識別的UI對話框,當你說完之後會消失,並識別到你說的內容解析顯示在頁面上。至此,語音識別完成。
尾聲
這裏無非就是一些感想而已,其實實現功能的方法有很多,文章中只是其中之一而已,歡迎留言討論,我是初學者-Study,山高水長,後會有期~