在离线命令的基础上,我们可以添加语音合成功能,因为讯飞语音合成于离线命令识别有相同的地方,所以在这来主要就讲语音合成不同的地方,详细资料可以查看文末博客和仓库源码。
SpeechSynthesizer 就是语音合成的对象,主要去调用它的startSpeaking(text, SynthesizerListener)方法来实现发声操作。
/****************************
* 语音合成
***************************/
// 语音合成对象
private SpeechSynthesizer mTts;
public static String voicerCloud = "xiaoyan";
// 默认本地发音人
public static String voicerLocal = "xiaoyan";
public static String voicerXtts = "xiaoyan";
//缓冲进度
private int mPercentForBuffering = 0;
//播放进度
private int mPercentForPlaying = 0;
离线命令识别和语音合成都有各自的参数设置,可以在代码中固定,也可以做成界面来动态调节。
// 语音合成参数设置
private void setParam_to_compound() {
// 清空参数
mTts.setParameter(SpeechConstant.PARAMS, null);
//设置合成
if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
//设置使用云端引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME, voicerCloud);
} else if (mEngineType.equals(SpeechConstant.TYPE_LOCAL)) {
//设置使用本地引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
//设置发音人资源路径
mTts.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath());
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME, voicerLocal);
} else {
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_XTTS);
//设置发音人资源路径
mTts.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath());
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME, voicerXtts);
}
//mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持实时音频流抛出,仅在synthesizeToUri条件下支持
//设置合成语速
mTts.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString("speed_preference", "50"));
//设置合成音调
mTts.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString("pitch_preference", "50"));
//设置合成音量
mTts.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString("volume_preference", "50"));
//设置播放器音频流类型
mTts.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString("stream_preference", "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,
getExternalFilesDir("msc").getAbsolutePath() + "/tts.pcm");
}
在这里获取的离线资源其实就是获取发音人文件(.jet文件),这个是官方提供的有免费的,也有付费的(比较贵),详情就需要去讯飞官网查看了。
/**
*获取文件下的离线资源
* @return
*/
private String getResourcePath() {
String type = "tts";
StringBuffer tempBuffer = new StringBuffer();
//离线命令识别通用资源
tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, "iat/common.jet"));
tempBuffer.append(";");
tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, "iat/sms_16k.jet"));
//语音合成通用资源
tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, type + "/common.jet"));
tempBuffer.append(";");
//发音人资源
if (mEngineType.equals(SpeechConstant.TYPE_XTTS)) {
tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, type + "/" + IatActivity.voicerXtts + ".jet"));
} else {
tempBuffer.append(ResourceUtil.generateResourcePath(this, ResourceUtil.RESOURCE_TYPE.assets, type + "/" + IatActivity.voicerLocal + ".jet"));
}
return tempBuffer.toString();
}
在语音合成这个过程有很多操作,类使用方法的生命周期,可以在这个过程中做一些操作。
/**
* 合成回调监听 合成过程中状态监听
*/
private SynthesizerListener mTtsListener = new SynthesizerListener() {
@Override
public void onSpeakBegin() {
//showTip("开始播放");
Log.d(MainActivity.TAG, "开始播放:" + System.currentTimeMillis());
}
@Override
public void onSpeakPaused() {
showTip("暂停播放");
}
@Override
public void onSpeakResumed() {
showTip("继续播放");
}
@Override
public void onBufferProgress(int percent, int beginPos, int endPos,
String info) {
// 合成进度
mPercentForBuffering = percent;
showTip(String.format(getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onSpeakProgress(int percent, int beginPos, int endPos) {
// 播放进度
mPercentForPlaying = percent;
showTip(String.format(getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onCompleted(SpeechError error) {
if (error == null) {
showTip("播放完成");
} else {
showTip(error.getPlainDescription(true));
}
}
@Override
public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
// 若使用本地能力,会话id为null
if (SpeechEvent.EVENT_SESSION_ID == eventType) {
String sid = obj.getString(SpeechEvent.KEY_EVENT_AUDIO_URL);
Log.d(TAG, "session id =" + sid);
}
//实时音频流输出参考
/*if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
Log.e("MscSpeechLog", "buf is =" + buf);
}*/
}
};
/**
* 语音合成方法
* @param text 需要合成的内容
*/
private void anwerRobot(String text){
// 语音合成参数设置
setParam_to_compound();
Log.d(TAG, "准备点击: " + System.currentTimeMillis());
int code = mTts.startSpeaking(text, mTtsListener);
if (code != ErrorCode.SUCCESS) {
showTip("语音合成失败,错误码: " + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
}
}
// 初始化合成对象
mTts = SpeechSynthesizer.createSynthesizer(this, mInitListener);
Button_Compound();
<Button
android:id="@+id/button_compound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="396dp"
android:text="语音合成"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_result" />
private void Button_Compound(){
Button button = findViewById(R.id.button_compound);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null == mIat) {
// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
showTip("创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化");
return;
}
if(edit_result !=null){
try {
Thread.sleep(1000);
anwerRobot("好的主人");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
}
本文的功能是基于上一篇博客的基础上整合的,代码不是完整的,但是这是语音合成的核心部分代码。想要实现完整功能可以查看以下博客或去源码仓库拉取代码查看。
离线语音识别
源码仓库