独居的生活很是无聊,如果有什么成精的东西和我聊聊天就好了…
“独居的生活很是无聊,如果有什么成精的东西和我聊聊天就好了”,基于这个独特的想法,我,决定让某一样东西成精,赋予它阅读指定文字的能力。
目前市面上有两款产品可以较好的实现语音相关的功能,分别是百度语音识别与科大讯飞语音识别,在这两个中我选科大讯飞。如果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"/>
<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"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
接着动态申请危险权限:
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方法)。
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() {
mIat = SpeechRecognizer.createRecognizer(this, mInitListener);
if (null == mIat) {
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);
}
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"));
mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString(
"iat_punc_preference", "1"));
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 里爷爷的发言一致时,取对应的答案,通过语音输出,孙子也就不再是一个哑巴了。
那么,我们来实现孙子的语音回复吧。
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.SPEED, "50");
mTts.setParameter(SpeechConstant.PITCH, "50");
mTts.setParameter(SpeechConstant.VOLUME, "50");
mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
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 文档
|