獨居的生活很是無聊,如果有什么成精的東西和我聊聊天就好了…
“獨居的生活很是無聊,如果有什么成精的東西和我聊聊天就好了”,基于這個獨特的想法,我,決定讓某一樣東西成精,賦予它閱讀指定文字的能力,
目前市面上有兩款產品可以較好的實作語音相關的功能,分別是百度語音識別與科大訊飛語音識別,在這兩個中我選科大訊飛,如果Python、Node.js、C#、C++、PHP作為你的開發語言,百度語音識別可以找到相關檔案,如果開發的語音識別是搭載著HarmonyOS系統上,可以選科大訊飛,二者各有所長、各有所短,
在操作前需要先前往官網下載語音相關的demo,demo里面有我們集成語音技術必要的資源,

下載后,將assets、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.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"/>
接著動態申請危險權限:
/**
* android 6.0 以上需要動態申請錄制音頻、寫外部存盤權限
*/
private void initPermission() {
String permissions[] = {Manifest.permission.RECORD_AUDIO,
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);
}
}
使用科大訊飛語音識別需要初始化,初始化即創建語音配置物件,只有初始化后才可以使用MSC的各項服務,建議將初始化放在程式入口處(如Application、Activity的onCreate方法),
// 將“=”后面的字串替換成您申請的APPID,申請地址:http://www.xfyun.cn
// 請勿在“=”與appid之間添加任何空字符或者轉義符
SpeechUtility.createUtility(this, "appid=" + getString(R.string.app_id));
實作語音識別監聽
/**
* 語音識別監聽器
*/
private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
public void onResult(RecognizerResult results, boolean isLast) {
if (!isLast) {
// 識別結果
}
}
// 識別回呼錯誤.
public void onError(SpeechError error) {
Toast.makeText(MainActivity.this, error.getPlainDescription(true),
Toast.LENGTH_SHORT).show();
}
};
接著就是 call 孫子啦
public void call() {
// 使用SpeechRecognizer物件,可根據回呼訊息自定義界面;
mIat = SpeechRecognizer.createRecognizer(this, mInitListener);
if (null == mIat) {
// 創建單例失敗,與 21001 錯誤為同樣原因,參考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
Toast.makeText(this, "創建物件失敗,請確認 libmsc.so 放置正確,且有呼叫 createUtility 進行初始化",
Toast.LENGTH_SHORT).show();
return;
}
// 清除資料
mIatResults.clear();
// 設定引數
// 清空引數
mIat.setParameter(SpeechConstant.PARAMS, null);
// 設定聽寫引擎型別
mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
// 設定回傳結果的資料格式
mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");
if (language.equals("zh_cn")) {
String lag = mSharedPreferences.getString("iat_language_preference",
"mandarin");
// 設定語言
mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
// 設定語言區域
mIat.setParameter(SpeechConstant.ACCENT, lag);
} else {
mIat.setParameter(SpeechConstant.LANGUAGE, 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");
mIatDialog.setListener(mRecognizerDialogListener);//設定監聽
mIatDialog.show();// 顯示對話框
}
看到show()方法沒?隨著show()方法的執行,語音識別功能也就完成了喔

爺爺問話了,此時的孫子還不能回復爺爺的話,對于爺爺的招呼,也只能默默的看著爺爺,像個木頭人似的不知所措,
孫子發言,需要使用語音合成,語音合成,與語音聽寫相反,語音合成是將一段文字轉換為語音,可根據需要合成出不同音色、語速和語調的聲音,讓機器像人一樣開口說話,不僅如此,還能根據個人需求,更換孫子或孫女,高端的孫子、孫女往往都可以識別民族語言、多國語言的喔,
孫子怎么知道應該回復什么資訊呢?
回復的資訊是我們提前預設好的,發言的不同,回復的內容也不一樣,語音識別和命令詞識別有一點區別,命令詞識別是識別語音并提取提前預設的關鍵詞通過文本輸出,而語音識別則是直接將一串語音翻譯成文本直接輸出,
我把爺爺發言與孫子的答復提前插入Sqlite,待語音識別到的文本與存盤在Sqlite里爺爺的發言一致時,取對應的答案,通過語音輸出,孫子也就不再是一個啞巴了,
那么,我們來實作孫子的語音回復吧,
/**
* 孫子回復
* @param answer 孫子回復內容
*/
private void grandsonAnswer(String answer) {
// 初始化云端發音人名稱串列
cloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);
cloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);
// 初始化合成物件
mTts = SpeechSynthesizer.createSynthesizer(this, mInitListener);
// 清空引數
mTts.setParameter(SpeechConstant.PARAMS, null);
//設定合成
//設定使用云端引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
//設定發音人
mTts.setParameter(SpeechConstant.VOICE_NAME, voicerCloud);
//mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持實時音頻流拋出,僅在synthesizeToUri條件下支持
//設定合成語速
mTts.setParameter(SpeechConstant.SPEED, "50");
//設定合成音調
mTts.setParameter(SpeechConstant.PITCH, "50");
//設定合成音量
mTts.setParameter(SpeechConstant.VOLUME, "50");
//設定播放器音頻流型別
mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
// mTts.setParameter(SpeechConstant.STREAM_TYPE, AudioManager.STREAM_MUSIC+"");
// 設定播放合成音頻打斷音樂播放,默認為true
mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
// 設定音頻保存路徑,保存音頻格式支持pcm、wav,設定路徑為sd卡請注意WRITE_EXTERNAL_STORAGE權限
mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH,
Environment.getExternalStorageDirectory() + "/msc/tts.wav");
int code = mTts.startSpeaking(answer, mTtsListener);
if (code != ErrorCode.SUCCESS) {
Toast.makeText(this, "語音合成失敗,錯誤碼: " + code + ",請點擊網址https://www.xfyun" +
".cn/document/error-code查詢解決方案", Toast.LENGTH_SHORT).show();
}
}
代碼copy一下運行起來便是這樣的結果了 ↓

如果想聽孫子叫爺爺,那呼叫方法的時候直接傳一個爺爺的引數就可以啦
public void callGrandpa(View view) {
grandsonAnswer("爺爺~");
}

孫子的聲音聽膩了,還可以切換成孫女的聲音
/**
* 切換孫子或孫女
*/
public void update(View view) {
new AlertDialog.Builder(this).setTitle("更換孫子或孫女")
.setSingleChoiceItems(cloudVoicersEntries, // 單選框有幾項,各是什么名字
selectedNumCloud, // 默認的選項
new DialogInterface.OnClickListener() { // 點擊單選框后的處理
public void onClick(DialogInterface dialog,
int which) { // 點擊了哪一項
voicerCloud = cloudVoicersValue[which];
selectedNumCloud = which;
dialog.dismiss();
}
}).show();
}
可切換的孫子、孫女
<!-- 合成 -->
<string-array name="voicer_cloud_entries">
<item>小燕</item>
<item>小宇</item>
<item>凱瑟琳</item>
<item>亨利</item>
<item>瑪麗</item>
<item>小研</item>
<item>小琪</item>
<item>小峰</item>
<item>小梅</item>
<item>小莉</item>
<item>小蓉</item>
<item>小蕓</item>
<item>小坤</item>
<item>小強 </item>
<item>小瑩</item>
<item>小新</item>
<item>楠楠</item>
<item>老孫</item>
</string-array>
<string-array name="voicer_cloud_values">
<item>xiaoyan</item>
<item>xiaoyu</item>
<item>catherine</item>
<item>henry</item>
<item>vimary</item>
<item>vixy</item>
<item>xiaoqi</item>
<item>vixf</item>
<item>xiaomei</item>
<item>xiaolin</item>
<item>xiaorong</item>
<item>xiaoqian</item>
<item>xiaokun</item>
<item>xiaoqiang</item>
<item>vixying</item>
<item>xiaoxin</item>
<item>nannan</item>
<item>vils</item>
</string-array>
到這里,能想到的功能也就都完成了,附上demo運行GIF

最后也不要忘記當界面銷毀后,對應的變數應該釋放記憶體
@Override
protected void onDestroy() {
super.onDestroy();
if( null != mIat ){
// 退出時釋放連接
mIat.cancel();
mIat.destroy();
}
if( null != mTts ){
mTts.stopSpeaking();
// 退出時釋放連接
mTts.destroy();
}
}
本文代碼已上傳至:開發一個會叫自己“爺爺”的“孫子”,是一種什么樣的體驗?
參考文獻:
1、Android 科大訊飛語音識別(詳細步驟+原始碼)
2、科大訊飛在線語音聽寫 Android SDK 檔案
3、科大訊飛在線語音合成 Android SDK 檔案
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/294257.html
標籤:其他
