1. 程式人生 > >訊飛語音聲紋識別技術——自由說(文字密碼,數字密碼也會介紹)

訊飛語音聲紋識別技術——自由說(文字密碼,數字密碼也會介紹)

訊飛科技的語音技術,個人認為非常強大,但是技術文件的介紹,還是有些不完全,也沒有線上的技術客服,相信有很多朋友都會遇到大大小小的問題,第三方SDK的整合,或者整合後方法的呼叫等。

廢話不多說,這裡我先介紹下最近我使用的聲紋識別技術——自由說(文字密碼,數字密碼也會介紹到),本人也不是技術大牛,如果有錯誤,各位請指點,當然如有不清晰的地方也可在評論區提問。

工具:AndroidStudio

第一步:註冊訊飛賬號

註冊賬號——建立新應用——立即開通(開通需要的服務)—— 選中對應SDK、平臺、你建立的應用

第二步 整合SDK

SDK檔案

把其中的libs檔案中的所有檔案全部拖到 並且分別把這兩個Msc.jar Sunflower.jar  (右鍵)add as library 。注意如果專案中有對不同機型的配置檔案 那麼需要把armeabi arm64-v8a 等裡面的.so檔案分別複製到對應的資料夾中。 (這裡我的專案沒有把sunflower.jar 引入)

最好在build.gradle裡面加上一句話

sourceSets{
    main{
        jniLibs.srcDirs=['libs']
    }
}

第三步 方法呼叫

直接粘程式碼,細節部位,看註釋,這裡給大家一個執行順序(注意 這裡官網給的Demo並沒有自由說的設定,我這Demo在官網的基礎上進行改動,詳情看註釋)

這裡重視下 自由說

大概步驟:

SpeechUtility的初始化

——佈局元件的初始化

——(這裡開始進入聲紋驗證的順序)

——設定選擇的註冊密碼型別(文字型,數字型,自由說型)

——獲取與密碼類對應的密碼(注:自由說就不需要這一步,粗暴點說不需要點選“獲得密碼”這一按鈕。   文字密碼,只支援倒黴的 “芝麻開門”,不支援自定義,好傻。 數字密碼沒什麼說的。)

——註冊,也就是開始錄取你的聲音進行儲存。

——驗證,開始驗證現在的聲音是不是已註冊的聲音,根據呼叫result的引數完成相關操作 

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.media.MediaRecorder;
import 
android.os.Bundle; import android.os.Environment; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.RadioGroup.OnCheckedChangeListener; import android.widget.TextView; import android.widget.Toast; import com.iflytek.cloud.ErrorCode; import com.iflytek.cloud.InitListener; import com.iflytek.cloud.SpeakerVerifier; import com.iflytek.cloud.SpeechConstant; import com.iflytek.cloud.SpeechError; import com.iflytek.cloud.SpeechListener; import com.iflytek.cloud.SpeechUtility; import com.iflytek.cloud.VerifierListener; import com.iflytek.cloud.VerifierResult; /**VerifierResult 返回欄位說明(重要的幾個 其他的什麼vid trs。。。滾走) * * sst 業務型別 train 或者 verify * ret 返回值 0成功 -1失敗 * suc 本次註冊已成功的train次數 * rgn 本次註冊需要的次數 * err 註冊/驗證返回的錯誤碼 * score 當前聲紋相似度 */ public class DetailActivity extends Activity implements OnClickListener { private static final String TAG = DetailActivity.class.getSimpleName(); private static final int PWD_TYPE_TEXT = 1; private static final int PWD_TYPE_FREE = 2; private static final int PWD_TYPE_NUM = 3; // 當前聲紋密碼型別,1、2、3分別為文字、自由說和數字密碼 private int mPwdType = PWD_TYPE_TEXT; // 聲紋識別物件 private SpeakerVerifier mVerifier; // 聲紋AuthId,使用者在雲平臺的身份標識,也是聲紋模型的標識 // 請使用英文字母或者字母和數字的組合,勿使用中文字元 private String mAuthId = ""; // 文字聲紋密碼 private String mTextPwd = ""; // 數字聲紋密碼 private String mNumPwd = ""; // 數字聲紋密碼段,預設有5段 private String[] mNumPwdSegs; private EditText mResultEditText; private TextView mAuthIdTextView; private RadioGroup mPwdTypeGroup; private TextView mShowPwdTextView; private TextView mShowMsgTextView; private TextView mShowRegFbkTextView; private TextView mRecordTimeTextView; private AlertDialog mTextPwdSelectDialog; private Toast mToast; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SpeechUtility.createUtility(this, "appid="+"你在官網的id"); setContentView(R.layout.activity_main); //1111111111111111111111111111111111111111111111111111 initUi(); // 每個應用不同的AuthId mAuthId = "uname"; mAuthIdTextView.setText(mAuthId); // 初始化SpeakerVerifier,InitListener為初始化完成後的回撥介面 mVerifier = SpeakerVerifier.createVerifier(DetailActivity.this, new InitListener() { @Override public void onInit(int errorCode) { if (ErrorCode.SUCCESS == errorCode) { showTip("引擎初始化成功"); } else { showTip("引擎初始化失敗,錯誤碼:" + errorCode); } } }); } // @SuppressLint("ShowToast") 111111111111111111 private void initUi() { mResultEditText = (EditText) findViewById(R.id.edt_result); mAuthIdTextView = (TextView) findViewById(R.id.txt_authorid); mShowPwdTextView = (TextView) findViewById(R.id.showPwd); mShowMsgTextView = (TextView) findViewById(R.id.showMsg); mShowRegFbkTextView = (TextView) findViewById(R.id.showRegFbk); mRecordTimeTextView = (TextView) findViewById(R.id.recordTime); findViewById(R.id.isv_register).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_verify).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_stop_record).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_cancel).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_getpassword).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_search).setOnClickListener(DetailActivity.this); findViewById(R.id.isv_delete).setOnClickListener(DetailActivity.this); // 密碼選擇RadioGroup初始化 mPwdTypeGroup = (RadioGroup) findViewById(R.id.radioGroup); 選擇當前的mPwdType 這裡重視下 自由說   mPwdTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { initTextView(); switch (checkedId) { case R.id.radioText: mPwdType = PWD_TYPE_TEXT; break; case R.id.radioNumber: mPwdType = PWD_TYPE_NUM; break; case R.id.radioFree: mPwdType = PWD_TYPE_FREE; break; default: break; } } }); mToast = Toast.makeText(DetailActivity.this, "", Toast.LENGTH_SHORT); mToast.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0); } /** * 初始化TextView和密碼文字 * ①4444444444444444444444444444444 ②44444444444444444 */ private void initTextView(){ mTextPwd = null; mNumPwd = null; mResultEditText.setText(""); mShowPwdTextView.setText(""); mShowMsgTextView.setText(""); mShowRegFbkTextView.setText(""); mRecordTimeTextView.setText(""); } /** * 設定radio的狀態 */ private void setRadioClickable(boolean clickable){ // 設定RaioGroup狀態為非按下狀態 mPwdTypeGroup.setPressed(false); findViewById(R.id.radioText).setClickable(clickable); findViewById(R.id.radioNumber).setClickable(clickable); findViewById(R.id.radioFree).setClickable(clickable); } /** * 執行模型操作 * * @param operation 操作命令 * @param listener 操作結果回撥物件 */ private void performModelOperation(String operation, SpeechListener listener) { // 清空引數 mVerifier.setParameter(SpeechConstant.PARAMS, null); //設定密碼型別(就是咱要讀的) mVerifier.setParameter(SpeechConstant.ISV_PWDT, "" + mPwdType); if (mPwdType == PWD_TYPE_TEXT) { // 文字密碼刪除需要傳入密碼 if (TextUtils.isEmpty(mTextPwd)) { showTip("請獲取密碼後進行操作"); return; } // 對於文字密碼和數字密碼,必須設定密碼的文字內容,pwdText的取值為“芝麻開門”或者是從雲端拉取的數字密碼(每8位用“-”隔開,如“62389704-45937680-32758406-29530846-58206497”)。自由說略過此步 mVerifier.setParameter(SpeechConstant.ISV_PWD, mTextPwd); } else if (mPwdType == PWD_TYPE_NUM) { }else if (mPwdType==PWD_TYPE_FREE){ } setRadioClickable(false); // 設定auth_id,不能設定為空 mVerifier.sendRequest(operation, mAuthId, listener); } //5555555555555555555555555555555點選各個部件的監聽回撥 @Override public void onClick(View v) { if( !checkInstance() ){ return; } switch (v.getId()) { 第一步:  case R.id.isv_getpassword: // 獲取密碼之前先終止之前的註冊或驗證過程 mVerifier.cancel(); initTextView(); setRadioClickable(false); // 清空引數 mVerifier.setParameter(SpeechConstant.PARAMS, null); mVerifier.setParameter(SpeechConstant.ISV_PWDT, "" + mPwdType); if (mPwdType!=PWD_TYPE_FREE) 本地的監聽引數   mVerifier.getPasswordList(mPwdListenter); break; case R.id.isv_search: performModelOperation("que", mModelOperationListener); break; case R.id.isv_delete: performModelOperation("del", mModelOperationListener); break; 第二步 註冊  case R.id.isv_register: // 清空引數 mVerifier.setParameter(SpeechConstant.PARAMS, null); mVerifier.setParameter(SpeechConstant.ISV_AUDIO_PATH, Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/test.pcm"); // 對於某些麥克風非常靈敏的機器,如nexus、samsung i9300等,建議加上以下設定對錄音進行消噪處理 // mVerify.setParameter(SpeechConstant.AUDIO_SOURCE, "" + MediaRecorder.AudioSource.VOICE_RECOGNITION); if (mPwdType == PWD_TYPE_TEXT) { // 文字密碼註冊需要傳入密碼 if (TextUtils.isEmpty(mTextPwd)) { showTip("請獲取密碼後進行操作"); return; } mVerifier.setParameter(SpeechConstant.ISV_PWD, mTextPwd); mShowPwdTextView.setText("請讀出:" + mTextPwd); mShowMsgTextView.setText("訓練 第" + 1 + "遍,剩餘4遍"); } else if (mPwdType == PWD_TYPE_NUM) { // 數字密碼註冊需要傳入密碼 if (TextUtils.isEmpty(mNumPwd)) { showTip("請獲取密碼後進行操作"); return; } mVerifier.setParameter(SpeechConstant.ISV_PWD, mNumPwd); ((TextView) findViewById(R.id.showPwd)).setText("請讀出:" + mNumPwd.substring(0, 8)); mShowMsgTextView.setText("訓練 第" + 1 + "遍,剩餘4遍"); }else if (mPwdType==PWD_TYPE_FREE){  這裡插一句嘴,自由說的註冊引數之次數 設定為“1” 音質的的設定“8000” mVerifier.setParameter(SpeechConstant.ISV_RGN,"1"); mVerifier.setParameter(SpeechConstant.SAMPLE_RATE,"8000"); } setRadioClickable(false); // 設定auth_id,不能設定為空 mVerifier.setParameter(SpeechConstant.AUTH_ID, mAuthId); // 設定業務型別為註冊 mVerifier.setParameter(SpeechConstant.ISV_SST, "train"); // 設定聲紋密碼型別 mVerifier.setParameter(SpeechConstant.ISV_PWDT, "" + mPwdType); // 開始註冊 mVerifier.startListening(mRegisterListener); break; 第三步驗證,與上一步註冊極其相似,詳情請看  case R.id.isv_verify: // 清空提示資訊 ((TextView) findViewById(R.id.showMsg)).setText(""); // 清空引數 mVerifier.setParameter(SpeechConstant.PARAMS, null); mVerifier.setParameter(SpeechConstant.ISV_AUDIO_PATH, Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/verify.pcm"); mVerifier = SpeakerVerifier.getVerifier(); // 設定業務型別為驗證 mVerifier.setParameter(SpeechConstant.ISV_SST, "verify"); // 對於某些麥克風非常靈敏的機器,如nexus、samsung i9300等,建議加上以下設定對錄音進行消噪處理 // mVerify.setParameter(SpeechConstant.AUDIO_SOURCE, "" + MediaRecorder.AudioSource.VOICE_RECOGNITION); if (mPwdType == PWD_TYPE_TEXT) { // 文字密碼註冊需要傳入密碼 if (TextUtils.isEmpty(mTextPwd)) { showTip("請獲取密碼後進行操作"); return; } mVerifier.setParameter(SpeechConstant.ISV_PWD, mTextPwd); ((TextView) findViewById(R.id.showPwd)).setText("請讀出:" + mTextPwd); } else if (mPwdType == PWD_TYPE_NUM) { // 數字密碼註冊需要傳入密碼 String verifyPwd = mVerifier.generatePassword(8); mVerifier.setParameter(SpeechConstant.ISV_PWD, verifyPwd); ((TextView) findViewById(R.id.showPwd)).setText("請讀出:" + verifyPwd); }else if (mPwdType==PWD_TYPE_FREE){ mVerifier.setParameter(SpeechConstant.SAMPLE_RATE,"8000"); ((TextView)findViewById(R.id.showPwd)).setText("請隨便說些用於驗證"); } setRadioClickable(false); // 設定auth_id,不能設定為空 mVerifier.setParameter(SpeechConstant.AUTH_ID, mAuthId); mVerifier.setParameter(SpeechConstant.ISV_PWDT, "" + mPwdType); // 開始驗證 mVerifier.startListening(mVerifyListener); break; case R.id.isv_stop_record: mVerifier.stopListening(); break; case R.id.isv_cancel: setRadioClickable(true); mVerifier.cancel(); initTextView(); break; default: break; } } 第一步的監聽引數 通過解析獲得密碼,注意 這裡自由說不需要密碼,所以這裡沒有它的case private String[] items; private SpeechListener mPwdListenter = new SpeechListener() { @Override public void onEvent(int eventType, Bundle params) { } @Override public void onBufferReceived(byte[] buffer) { setRadioClickable(true); String result = new String(buffer); switch (mPwdType) { case PWD_TYPE_TEXT: try { JSONObject object = new JSONObject(result); if (!object.has("txt_pwd")) { initTextView(); return; } JSONArray pwdArray = object.optJSONArray("txt_pwd"); items = new String[pwdArray.length()]; for (int i = 0; i < pwdArray.length(); i++) { items[i] = pwdArray.getString(i); } mTextPwdSelectDialog = new AlertDialog.Builder( DetailActivity.this) .setTitle("請選擇密碼文字") .setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface arg0, int arg1) { mTextPwd = items[arg1]; mResultEditText.setText("您的密碼:" + mTextPwd); } }).create(); mTextPwdSelectDialog.show(); } catch (JSONException e) { e.printStackTrace(); } break; case PWD_TYPE_NUM: StringBuffer numberString = new StringBuffer(); try { JSONObject object = new JSONObject(result); if (!object.has("num_pwd")) { initTextView(); return; } JSONArray pwdArray = object.optJSONArray("num_pwd"); numberString.append(pwdArray.get(0)); for (int i = 1; i < pwdArray.length(); i++) { numberString.append("-" + pwdArray.get(i)); } } catch (JSONException e) { e.printStackTrace(); } mNumPwd = numberString.toString(); mNumPwdSegs = mNumPwd.split("-"); mResultEditText.setText("您的密碼:\n" + mNumPwd); break; default: break; } } @Override public void onCompleted(SpeechError error) { setRadioClickable(true); if (null != error && ErrorCode.SUCCESS != error.getErrorCode()) { showTip("獲取失敗:" + error.getErrorCode()); } } }; private SpeechListener mModelOperationListener = new SpeechListener() { @Override public void onEvent(int eventType, Bundle params) { } @Override public void onBufferReceived(byte[] buffer) { setRadioClickable(true); String result = new String(buffer); try { JSONObject object = new JSONObject(result); String cmd = object.getString("cmd"); int ret = object.getInt("ret"); if ("del".equals(cmd)) { if (ret == ErrorCode.SUCCESS) { showTip("刪除成功"); mResultEditText.setText(""); } else if (ret == ErrorCode.MSP_ERROR_FAIL) { showTip("刪除失敗,模型不存在"); } } else if ("que".equals(cmd)) { if (ret == ErrorCode.SUCCESS) { showTip("模型存在"); } else if (ret == ErrorCode.MSP_ERROR_FAIL) { showTip("模型不存在"); } } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onCompleted(SpeechError error) { setRadioClickable(true); if (null != error && ErrorCode.SUCCESS != error.getErrorCode()) { showTip("操作失敗:" + error.getPlainDescription(true)); } } }; 第三步 監聽引數 private VerifierListener mVerifyListener = new VerifierListener() { @Override public void onVolumeChanged(int volume, byte[] data) { showTip("當前正在說話,音量大小:" + volume); Log.d(TAG, "返回音訊資料:"+data.length); } @Override public void onResult(VerifierResult result) { setRadioClickable(true); mShowMsgTextView.setText(result.source); if (result.ret == 0) { // 驗證通過 這裡就意味著通過了!!! mShowMsgTextView.setText("驗證通過,開啟****"); } else{ // 驗證不通過 switch (result.err) { case VerifierResult.MSS_ERROR_IVP_GENERAL: mShowMsgTextView.setText("核心異常"); break; case VerifierResult.MSS_ERROR_IVP_TRUNCATED: mShowMsgTextView.setText("出現截幅"); break; case VerifierResult.MSS_ERROR_IVP_MUCH_NOISE: mShowMsgTextView.setText("太多噪音"); break; case VerifierResult.MSS_ERROR_IVP_UTTER_TOO_SHORT: mShowMsgTextView.setText("錄音太短"); break; case VerifierResult.MSS_ERROR_IVP_TEXT_NOT_MATCH: mShowMsgTextView.setText("驗證不通過,您所讀的文字不一致"); break; case VerifierResult.MSS_ERROR_IVP_TOO_LOW: mShowMsgTextView.setText("音量太低"); break; case VerifierResult.MSS_ERROR_IVP_NO_ENOUGH_AUDIO: mShowMsgTextView.setText("音訊長達不到自由說的要求"); break; default: mShowMsgTextView.setText("驗證不通過,相似度僅為"+result.score+"%。"); break; } } } // 保留方法,暫不用 @Override public void onEvent(int eventType, int arg1, int arg2, Bundle arg3) { // 以下程式碼用於獲取與雲端的會話id,當業務出錯時將會話id提供給技術支援人員,可用於查詢會話日誌,定位出錯原因 // if (SpeechEvent.EVENT_SESSION_ID == eventType) { // String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID); // Log.d(TAG, "session id =" + sid); // } } @Override public void onError(SpeechError error) { setRadioClickable(true); switch (error.getErrorCode()) { case ErrorCode.MSP_ERROR_NOT_FOUND: mShowMsgTextView.setText("模型不存在,請先註冊"); break; default: showTip("onError Code:" + error.getPlainDescription(true)); break; } } @Override public void onEndOfSpeech() { // 此回調錶示:檢測到了語音的尾端點,已經進入識別過程,不再接受語音輸入 showTip("結束說話"); } @Override public void onBeginOfSpeech() { // 此回調錶示:sdk內部錄音機已經準備好了,使用者可以開始語音輸入 showTip("