开发一个会叫自己“爷爷”的“孙子”,是一种什么样的体验?
Posted 宾有为
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开发一个会叫自己“爷爷”的“孙子”,是一种什么样的体验?相关的知识,希望对你有一定的参考价值。
独居的生活很是无聊,如果有什么成精的东西和我聊聊天就好了…
“独居的生活很是无聊,如果有什么成精的东西和我聊聊天就好了”,基于这个独特的想法,我,决定让某一样东西成精,赋予它阅读指定文字的能力。
目前市面上有两款产品可以较好的实现语音相关的功能,分别是百度语音识别与科大讯飞语音识别,在这两个中我选科大讯飞。如果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 文档
以上是关于开发一个会叫自己“爷爷”的“孙子”,是一种什么样的体验?的主要内容,如果未能解决你的问题,请参考以下文章