百度语音SDK提供:
语音识别:将声音转成文字
语音合成:将文字转成语音文件,然后播放语音文件,即文字变声音。
语音唤醒:语音唤醒,激活运用程序
在这里,本篇介绍百度语音合成的使用。
百度语音介绍:
详情参考,百度语音介绍
百度语音使用流程指南:
详情参考,百度语音接入指南、百度语音SDK下载.
项目集成百度语音SDK:
在androidStudio中按照其AS特有的使用方式添加添加so库,相关的jar。
添加完so库,jar库后,需Gradle中配置如下:
android {
........
sourceSets {
main {
//设置so库依赖路径
jniLibs.srcDirs = ['libs']
}
}
}
dependencies {
.....
compile files('libs/com.baidu.tts_2.3.0.jar')
}
在androidStudio中assert文件夹下添加文本模型文件.dat,和声音模型文件.dat(无网络,离线下使用的)。这里因项目需求,只添加下载到女生模型文件。
添加以下权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Proguard配置(不需要混淆sdk中jar包)
编写相关使用代码如下:
5.1 加载离线资源,先创建存储的文件夹,然后将assert中模型文件一个个通过Stream读写(异步操作,可考虑工作线程或者RxJava完成)指定的文件夹下:
/**
* 百度tts文件夹
*/
public static final String BAIDU_TTS_DIR_NAME = "baiduTts";
public static final String SPEECH_FEMALE_MODE_NAME = "bd_etts_ch_speech_female.dat";
public static final String TEXT_MODEL_NAME = "bd_etts_ch_text.dat";
/**
* 初始化语音文件的配置
*
* @param context
*/
private void initFileConfig(Context context) {
File dirFile = getDirFile(context);
if (dirFile != null && !dirFile.exists()) {
dirFile.mkdir();
copyFromAssertsToSDCard(context, SPEECH_FEMALE_MODE_NAME, getFilePath(dirFile, SPEECH_FEMALE_MODE_NAME));
copyFromAssertsToSDCard(context, TEXT_MODEL_NAME, getFilePath(dirFile, TEXT_MODEL_NAME));
}
}
/**
* 获取目录
*
* @param context
* @return
*/
public File getDirFile(Context context) {
return MyUtils.getCacheFile(context, BAIDU_TTS_DIR_NAME);
}
/**
* 获取文件路径
*
* @param dirFile
* @param fileName
* @return
*/
public String getFilePath(File dirFile, String fileName) {
return dirFile.getAbsolutePath() + File.separator + fileName;
}
/**
* 将文件写入sdcard中
*
* @param context
* @param assertFileName
* @param filePath
*/
private void copyFromAssertsToSDCard(Context context, String assertFileName, String filePath) {
FileOutputStream outputStream = null;
InputStream inputStream = null;
try {
inputStream = context.getResources().getAssets().open(assertFileName);
outputStream = new FileOutputStream(filePath);
byte[] buffer = new byte[1024];
int length = 0;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
MyUtil工具类中创建文件夹的代码如下:
/**
* 获得存储文件
*
* @param
* @param
* @return
*/
public static File getCacheFile(Context context,String name) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + name);
}
5.2 配置语音合成客户端:
/**
* 运用的配置信息,由百度语音官网给定
*/
public static final String API_KEY = "hBuMkBgzGR0YQQHflkufAWcvFRaqTxxx";
public static final String SECRET_KEY = "FemUYlEKxzr0moQ0jydZcmQ3fo11xxx";
public static final String APP_ID = "9369xxx";
/**
* 语音合成的客户端
*/
private SpeechSynthesizer speechSynthesizer;
/**
* 合成状态的监听器
*/
private SpeechSynthesizerListener speechSynthesizerListener;
/**
* 初始化语音的client
*/
private void initBaiduTts(Context context) {
speechSynthesizer = SpeechSynthesizer.getInstance();
speechSynthesizer.setContext(context);
//设置监听器
if (getSpeechSynthesizerListener() != null) {
speechSynthesizer.setSpeechSynthesizerListener(getSpeechSynthesizerListener());
}
//设置运用的api_key和secret_key
speechSynthesizer.setApiKey(API_KEY, SECRET_KEY);
//设置运用的app_id
speechSynthesizer.setAppId(APP_ID);
File dirFile = getDirFile(context);
//设置语音合成中文本模型文件(离线使用)
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, getFilePath(dirFile, TEXT_MODEL_NAME));
//设置语音合成中声音模型文件(离线使用)
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, getFilePath(dirFile, SPEECH_FEMALE_MODE_NAME));
//发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,1--普通男声,2--特别男声,3--情感男声。。。)
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
//设置Mix模式的合成策略
speechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
//上线的时候,移除授权检查操作,即移除以下if语句
if (isAuthSuccess()) {
LogController.i(BaiduTtsController.class.getSimpleName(), "首次验证成功");
}
//初始化tts
speechSynthesizer.initTts(TtsMode.MIX);
}
/**
* 用于首次使用的使用,测试检查运用是成功申请到授权。注意点:若是测试无误后,可省略该步骤。
*
* @return
*/
public boolean isAuthSuccess() {
AuthInfo info = speechSynthesizer.auth(TtsMode.MIX);
if (!info.isSuccess()) {
LogController.i(BaiduTtsController.class.getSimpleName(), "错误信息: " + info.getTtsError().getMessage());
}
return info.isSuccess();
}
public SpeechSynthesizerListener getSpeechSynthesizerListener() {
return speechSynthesizerListener;
}
/**
* 设置语音合成的监听器
*
* @param speechSynthesizerListener
*/
public void setSpeechSynthesizerListener(SpeechSynthesizerListener speechSynthesizerListener) {
this.speechSynthesizerListener = speechSynthesizerListener;
if(speechSynthesizerListener!=null){
this.speechSynthesizer.setSpeechSynthesizerListener(this.speechSynthesizerListener);
}
}
5.3 语音合成客户端的生命周期管理:
在Activity或者Service的生命周期中使用,onCreate()调用客户端的初始化操作(即以上两个配置步骤), onResume()调用客户端的resume(),Onstop()中调用客户端的stop().最后在onDestory()中调用客户端的release()
/**
* 释放资源
*/
public void release() {
this.speechSynthesizer.release();
}
/**
* 停止正在执行的任务
*/
public void stop() {
this.speechSynthesizer.stop();
}
/**
* 恢复暂停的任务
*/
public void resume() {
this.speechSynthesizer.resume();
}
5.4 通过语音合成的客户端,将多段文本按一定顺序或者单独一段文本进行播放:
/**
* 将text转成一个语音文件,然后自动播放。
*
*
* @param text
*/
public void speeckText(String text) {
if (!TextUtils.isEmpty(text)) {
this.speechSynthesizer.speak(text);
}
}
/**
* 有顺序的合成多段语音,然后按一定顺序播放。
*
* @param values
*/
public void speeckTextValues(String[] values) {
if (values == null && values.length == 0) {
return;
}
List bags = new ArrayList<>();
for (int i = 0; i < values.length; ++i) {
bags.add(getSpeechSynthesizeBag(values[i], String.valueOf(i)));
}
this.speechSynthesizer.batchSpeak(bags);
}
/**
*
* @param text 播放的文本内容
* @param utterancedId 播放顺序,即第几个播放
* @return
*/
private SpeechSynthesizeBag getSpeechSynthesizeBag(String text, String utterancedId) {
SpeechSynthesizeBag speechSynthesizeBag = new SpeechSynthesizeBag();
//需要合成的文本text的长度不能超过1024个GBK字节。
speechSynthesizeBag.setText(text);
speechSynthesizeBag.setUtteranceId(utterancedId);
return speechSynthesizeBag;
}
5.5 监控语音合成客户端的监听器(即SpeechSynthesizerListener)
public class MessageDialogActivity extends BaseActivity implements View.OnClickListener,SpeechSynthesizerListener {
private static final String TAG=MessageDialogActivity.class.getSimpleName();
@Override
public void onSynthesizeStart(String s) {
LogController.i(TAG," onSynthesizeStart "+s);
}
@Override
public void onSynthesizeDataArrived(String s, byte[] bytes, int i) {
LogController.i(TAG," onSynthesizeDataArrived "+s);
}
@Override
public void onSynthesizeFinish(String s) {
LogController.i(TAG," onSynthesizeFinish "+s);
}
@Override
public void onSpeechStart(String s) {
LogController.i(TAG," onSpeechStart "+s);
}
@Override
public void onSpeechProgressChanged(String s, int i) {
LogController.i(TAG," onSpeechProgressChanged "+s);
}
@Override
public void onSpeechFinish(String s) {
LogController.i(TAG," onSpeechFinish "+s);
}
@Override
public void onError(String s, SpeechError speechError) {
LogController.i(TAG," onError "+speechError.description);
}
}
(个人)在实际项目中做法:
将百度语音客户端的初始化操作,生命周期管理操作,播放语音操作,封装成一个操作类。
public class BaiduTtsController {
public static BaiduTtsController instance;
private BaiduTtsController(Context context) {
initConfig(context);
}
public synchronized static BaiduTtsController getInstance(Context context) {
return instance = instance == null ? new BaiduTtsController(context) : instance;
}
public void initConfig(Context context) {
initFileConfig(context);
initBaiduTts(context);
}
//剩下的是,初始化,生命周期,播放的具体代码,以上步骤已经贴出。
}
将这个操作类保存到自定义的Application子类中,作为一个全局共享的对象。
public class BaseApplication extends Application {
private BaiduTtsController controller;
public BaiduTtsController initBaiduTts(){
return controller= controller==null?BaiduTtsController.getInstance(this):controller;
}
public BaiduTtsController getBaiduTtsController(){
return controller;
}
public void setBaiduTtsController(BaiduTtsController controller){
this.controller=controller;
}
}
在service中通过Application子类对象,获取到操作类对象,拿到需要播放的文本,调用播放语音的操作。
private BaiduTtsController controller;
//获取操作类的对象
controller=BaseApplication.getAppContext().getBaiduTtsController();
controller.setSpeechSynthesizerListener(this);
//播放文本
controller.speeckText(msg1);
开发遇到问题:
No implementation found for int com.baidu.tts.jni.EmbeddedSynthesizerEngine.bdTTSGetLicense
No implementation found for int com.baidu.speechsynthesizer.utility.SpeechDecoder.decodeWithCallback
解决方式:正确导入so库,可以在手机中安装的运用查看so库是否随运用一起安装在手机上。
运用的签名错误,控制台日志如下:
Authentication Error
=============================================
----------------- 鉴权错误信息 ------------
sha1;package:B3:88:6A:66:18:70:4E:FE:48:60:4C:4C:37:A9:2D:84:5D:16:56:xx;com.xxx.mjqzclient
key:RWpAlKWQE8SBoE354xUYeRcy906Asxxx
errorcode: -11 uid: -1 appid -1 msg: httpsPost failed,IOException:Unable to resolve host "api.map.baidu.com": No address associated with hostname
请仔细核查 SHA1、package与key申请信息是否对应,key是否删除,平台是否匹配
errorcode为230时,请参考论坛链接:
http://bbs.lbsyun.baidu.com/forum.php?mod=viewthread&tid=106461
解决方式:正确配置App的签名,然后在开发平台上,填写对应的包名,和sha1值。