百度的离在线TTS,没有调用量限制,免费但是有QPS限制(是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准),增大QPS需要收费,所以对商用并不是很好友。如果想用完全免费的纯离线可参考我另一篇博客:
https://blog.csdn.net/sunyFS/article/details/97936551
话不多说开始!
第一步:先从百度tts官网下载离在线融合SDK,网址:https://ai.baidu.com/sdk#tts,解压后最好先运行一下demo。
参考技术文档:https://ai.baidu.com/docs#/TTS-Android-SDK/top
1.将com.baidu.tts_2.3.2.20180713_6101c2a.jar添加到你项目的libs(注意要添加依赖同步)
implementation files(‘libs/com.baidu.tts_2.3.2.20180713_6101c2a.jar’)
2.将assert文件下dat文件复制到你项目的assets下(没有该文件夹就创建)
// assets目录下bd_etts_text.dat为文本模型文件,
// assets目录下bd_etts_common_speech_m15_mand_eng_high_am-mix_v3.0.0_20170505.dat为离线男声模型;
// assets目录下bd_etts_common_speech_f7_mand_eng_high_am-mix_v3.0.0_20170512.dat为离线女声模型;
3.将jniLibs文件夹下的文件复制到你项目的jniLibs下
最终的目录结构为:

第二步:进入百度的控制台,创建语音合成的应用,包名可在配置清单文件的package查看

获得对应的APPID,API KEY,Secret Key,包名,后面需要用到。

前期准备工作已经做好了,开始写代码!
按照文档在工程app目录下的proguard-rules.pro(混淆规则)文件里最后添加一下代码:

-keep class com.baidu.tts.**{*;}
-keep class com.baidu.speechsynthesizer.**{*;}

在配置清单文件中设置权限

   <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><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.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
android6.0需要进行动态权限的申请,需要将离线资源文件下载到本地,需要sd读写的权限,代码如下:
    private void initPermission() {String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,};ArrayList<String> mPermissionList = new ArrayList<String>();mPermissionList.clear();for (int i = 0; i < permissions.length; i++) {if (ContextCompat.checkSelfPermission(this, permissions[i]) !=PackageManager.PERMISSION_GRANTED) {mPermissionList.add(permissions[i]);//添加还未授予的权限到mPermissionList中}}//申请权限if (mPermissionList.size() > 0) {ActivityCompat.requestPermissions(this, permissions, 100);} else {//权限都已通过,进行初始化isFirstRun();}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);boolean hasPermissionDismiss = false;//权限是否都已通过的标记if (requestCode == 100) {for (int i = 0; i < grantResults.length; i++) {if (grantResults[i] == -1) {hasPermissionDismiss = true;break;}}}if (hasPermissionDismiss) {//有未被允许的权限showPermissionDialog();} else {//初始化isFirstRun();}}/*** 手动设置权限*/private void showPermissionDialog() {if (mPermissionDialog == null) {mPermissionDialog = new AlertDialog.Builder(this).setMessage("已禁用权限,请手动授予").setPositiveButton("设置", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {mPermissionDialog.cancel();Uri packageURI = Uri.parse("package:" + mPackName);Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);startActivity(intent);//打开应用设置MainActivity.this.finish();}}).setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {mPermissionDialog.cancel();MainActivity.this.finish();}}).create();}mPermissionDialog.show();}

百度离在线模式的离线功能首次需要联网下载正式授权文件才可使用,所以进行首次启动app进行判断是否联网,使用sp保存首次启动的标记,用网络工具类进行判断是否有网,有网则初始化tts,无网则开辟线程进行循环判断(耗时操作,使用线程防止ANR),代码如下:

    private void isFirstRun() {SharedPreferences sp = getSharedPreferences("ttsFlag", MODE_PRIVATE);boolean firstFlag = sp.getBoolean("firstFlag", true);final SharedPreferences.Editor edit = sp.edit();Log.i("msg", "isFirstRun  firstFlag: " + firstFlag);if (firstFlag) {//第一次启动app,判断是否联网final int netFlag = NetUtil.getNetWorkState(MainActivity.this);Log.i("msg", "isFirstRun  netFlag: " + netFlag);if (netFlag == 0 || netFlag == 2) {//移动或者无线网络edit.putBoolean("firstFlag", false);edit.apply();initialEnv();initTts();initView();} else {//没有网络,Toast.makeText(this, "使用离线合成功能,首次联网!", Toast.LENGTH_SHORT).show();new Thread() {@Overridepublic void run() {int netFlag1 = NetUtil.getNetWorkState(MainActivity.this);while (netFlag1 == 1) {netFlag1 = NetUtil.getNetWorkState(MainActivity.this);}runOnUiThread(new Runnable() {@Overridepublic void run() {edit.putBoolean("firstFlag", false);edit.apply();initialEnv();initTts();initView();}});}}.start();}} else {//非第一次启动appinitialEnv();initTts();initView();}}

网络工具类代码如下:

public class NetUtil {//没有网络private static final int NETWORK_NONE = 1;//移动网络private static final int NETWORK_MOBILE = 0;//无线网络private static final int NETWORK_WIFI = 2;//获取网络启动public static int getNetWorkState(Context context) {ConnectivityManager connectivityManager = (ConnectivityManager) context//连接服务 CONNECTIVITY_SERVICE.getSystemService(Context.CONNECTIVITY_SERVICE);//网络信息 NetworkInfoNetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {//判断是否是wifiif (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {//返回无线网络
//                Toast.makeText(context, "当前处于无线网络", Toast.LENGTH_SHORT).show();return NETWORK_WIFI;//判断是否移动网络} else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {
//                Toast.makeText(context, "当前处于移动网络", Toast.LENGTH_SHORT).show();//返回移动网络return NETWORK_MOBILE;}} else {//没有网络
//            Toast.makeText(context, "当前没有网络", Toast.LENGTH_SHORT).show();return NETWORK_NONE;}//默认返回  没有网络return NETWORK_NONE;}}

tts初始化,设置参数,离线资源路径等,记得替换成自己的apiid,apiKey, secretKey代码如下:

 private void initTts() {//获取实例mSpeechSynthesizer = SpeechSynthesizer.getInstance();mSpeechSynthesizer.setContext(this);mSpeechSynthesizer.setAppId(apiId);mSpeechSynthesizer.setApiKey(apiKey, secretKey);//文本模型文件路径 (离线引擎使用)mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/"+ ENGLISH_TEXT_MODEL_NAME);//声学模型文件路径 (离线引擎使用)mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/"+ ENGLISH_SPEECH_FEMALE_MODEL_NAME);Log.i("msg", "initTts param: " + mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME);Log.i("msg", "initTts param: " + mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);//模式:离在线混合mSpeechSynthesizer.auth(TtsMode.MIX);//对语音合成进行监听mSpeechSynthesizer.setSpeechSynthesizerListener(new listener());//设置参数mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");//标准女声mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");//音量 范围["0" - "15"], 不支持小数。 "0" 最轻,"15" 最响。mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");//语速 范围["0" - "15"], 不支持小数。 "0" 最慢,"15" 最快mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");//语调 范围["0" - "15"], 不支持小数。 "0" 最慢,"15" 最快mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_HIGH_SPEED_NETWORK);//WIFI,4G,3G 使用在线合成,其他使用离线合成 6s超时mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME);//文本模型文件路径mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);//声学模型文件路径mSpeechSynthesizer.initTts(TtsMode.MIX);}

将app的离线资源文件复制到本地,在首次运行下载到本地后,后续进行文件存在判断,存在则不用在下载,代码如下:

    private void initialEnv() {if (mSampleDirPath == null) {String sdcardPath = Environment.getExternalStorageDirectory().toString();mSampleDirPath = sdcardPath + "/" + SAMPLE_DIR_NAME;Log.i("msg", "initialEnv mSampleDirPath: " + mSampleDirPath);// /storage/emulated/0/baiduTTS}File file = new File(mSampleDirPath);if (!file.exists()) {file.mkdirs();}copyFromAssetsToSdcard(false, ENGLISH_SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/"+ ENGLISH_SPEECH_FEMALE_MODEL_NAME);copyFromAssetsToSdcard(false, ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + "/"+ ENGLISH_TEXT_MODEL_NAME);}/*** 将离线资源文件拷贝到SD卡中** @param isCover 是否覆盖已存在的目标文件* @param source  dat文件* @param dest    保存文件路径*/public void copyFromAssetsToSdcard(boolean isCover, String source, String dest) {File file = new File(dest);if (isCover || (!isCover && !file.exists())) {InputStream is = null;FileOutputStream fos = null;try {is = getResources().getAssets().open(source);String path = dest;fos = new FileOutputStream(path);byte[] buffer = new byte[1024];int size = 0;while ((size = is.read(buffer, 0, 1024)) >= 0) {fos.write(buffer, 0, size);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}try {if (is != null) {is.close();}} catch (IOException e) {e.printStackTrace();}}}}

tts监听类

public class listener implements SpeechSynthesizerListener {@Overridepublic void onSynthesizeStart(String s) {Log.i("msg", "合成开始");}@Overridepublic void onSynthesizeDataArrived(String s, byte[] bytes, int i) {Log.i("msg", "合成进度 :"+i);}@Overridepublic void onSynthesizeFinish(String s) {Log.i("msg", "合成结束");}@Overridepublic void onSpeechStart(String s) {Log.i("msg", "开始播放");}@Overridepublic void onSpeechProgressChanged(String s, int i) {Log.i("msg", "播放进度 :"+i);}@Overridepublic void onSpeechFinish(String s) {Log.i("msg", "合成结束");}@Overridepublic void onError(String s, SpeechError speechError) {Log.i("msg", "error :"+speechError);}

使用相关方法进行播放,暂停,恢复播放,代码如下:

    @Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.bt_start:Log.i("msg", "onClick text: " + et_input.getText().toString());mSpeechSynthesizer.speak(et_input.getText().toString());break;case R.id.bt_pause:mSpeechSynthesizer.pause();break;case R.id.bt_resume:mSpeechSynthesizer.resume();break;default:break;}}```

最后要记得释放资源

@Override
protected void onDestroy() {//释放tts资源if (mSpeechSynthesizer != null) {mSpeechSynthesizer.stop();mSpeechSynthesizer.release();mSpeechSynthesizer = null;}super.onDestroy();
}

该demo有几处缺陷;1.离线合成功能需要首次联网下载正式授权文件才可使用(官方sdk必须,除非你买纯离线)
2.在有网打开demo,合成引擎需要1s才初始化成功,无网络则大概3s才初始化成功(官方demo也是一样情况)。
3.调用量无限制,但是有QPS有限制(可以花钱扩大)。
关于正式授权文件,由于工作需要,想获取正式文件的路径,于是去问了下相关社区,回答是不提供的,想要的需要合作咨询(要钱!)。demo的github:https://github.com/sunfusong/baiduTtsDemo
如果想了解纯离线、免费TTS(android原生TTS+语言引擎)也可以看下我的另一篇博客https://blog.csdn.net/sunyFS/article/details/97936551项目在这 github:https://github.com/sunfusong/NativeTTS写demo遇到的error:
1.org.apache.http.legacy.jar 找不到
原因:android 9.0变更
解决方法:在配置清单文件下的<application 以下代码
<uses-library android:name="org.apache.http.legacy" android:required="false"/>2.xml布局无法显示:Failed to load AppCompat ActionBar with unknown error.
原因:AS版本3.1发现的变化
解决方法:在style中修改 <style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">

备注:该demo是本人结合官方文档和大佬们的相关博客写的,有什么不足地方可以提出,谢谢大家了!

Android集成百度TTS,实现离在线的中英语音合成相关推荐

  1. Android 集成百度地图之申请TTS授权最新版

    Android 集成百度地图之申请TTS授权最新版. 前提:登录百度地图开放平台,且已创建好应用. 开发文档-Android 导航SDK-TTS授权申请. 这里有个坑,简单说下,进入http://yu ...

  2. android百度导航实现,Android 集成百度地图实现设备定位

    Android 集成百度地图实现设备定位 步骤1: 申请android 端SDK : http://lbsyun.baidu.com/ 步骤2: 下载基础版SDK 步骤3: 下载示例程序 步骤4: 开 ...

  3. Android集成百度语音识别到HelloWorld需要注意什么?(保姆级教学)

    Android集成百度语音识别怎么避坑? 首先先放一张集成失败的图(记得一定要用真机,因为它不支持VAD,我这里使用Pixel2): 首先你去百度搜索"百度语音识别",或者点击我下 ...

  4. Android集成百度定位以及导航详解

    Android集成百度定位以及导航详解                    百度地图Android SDK 官方下载地址:                            包括类参考.示例代码 ...

  5. Android集成百度定位,超详细,拒绝坑,附demo!!!

    众所周知,百度地图作为定位,地图,导航界的大佬之一,我们的项目中有这方面的需求,百度地图集成也是个不错的选择. 百度地图集成成本极低,首先,它免费,官网文档清晰,demo明确,但还是有很多人要才坑,正 ...

  6. android中英文混合,搜狗手机输入法Android3.3中英混输无障碍之终结篇

    搜狗手机输入法Android3.3中英混输无障碍之终结篇 (2012-10-30 18:23:42) 标签: 娱乐 本周小编对于搜狗.百度两款手机输入法在中英文混输功能上进行了两轮测试,通过测试我们会 ...

  7. Android集成百度语音识别

    实现这个功能的目的,是我看见我公司硬件工程师给客户回答问题的时候用公司研发的APP,每次都是手动输入打字,看着他带着老花镜的样子,于心不忍,毕竟咱就是干这个的. 实现效果 集成 百度语音实时识别 ht ...

  8. Android 集成百度地图AR识别SDK(二)

    废话 今天我们开始集成百度地图AR识别SDK(后面简称AR SDK)的第二章,这一章我们主要讲Android Studio如何配置AR SDK 我们如果单单只看文档的话,很难看懂如何集成,我们需要结合 ...

  9. Android集成百度地图SDK

    本Demo中所含功能 1:定位,显示当前位置 2:地图多覆盖物(地图描点,弹出该点的详细信息) 3:坐标地址互相换算 4:POI兴趣点检索 5:线路查询(步行,驾车,公交) 6:绘制线路(OpenGL ...

最新文章

  1. Qt中如何改变三角形图形项的包围盒
  2. [error] OpenEvent(Global\ngx_stop_25184) failed (2: The system cannot find the file specified)
  3. BUUCTF(pwn)mrctf2020_easy_equation
  4. JAVA-JAVA WEB开发工具下载与安装
  5. java 正则表达式验证邮箱格式是否合规 以及 正则表达式元字符
  6. python中decode函数在哪个库_python encode和decode函数说明
  7. 使用图形芯片加速电子自动化设计应用程序
  8. 工业互联网解决方案创新应用报告(2020)
  9. unidac连接ORACLE免装客户端驱动
  10. 快速搭建java后台管理系统
  11. BASE16、BASE32、BASE64编码特征及正则匹配
  12. plm系统 服务器不存在,PLM服务器和客户端使用方式
  13. 计算机网络科研项目申请书,唐乾利:如何进一步完善医药类科研课题申请书
  14. Flask-SQLAlchemy relationship中的 lazy屬性
  15. 父级fixed_父元素设置absolute,子元素设置fixed定位失效
  16. 手写迷你Spring框架
  17. 猴年猴赛雷,曙光服务器全面升级E5-v4平台
  18. 蚂蚁金服 CTO 程立新加坡演讲:小蚂蚁是如何“爬”上区块链的?
  19. 期权和期货的定义及区别
  20. 网络摄像机中的IR-CUT详解

热门文章

  1. Chapter007-FPGA学习之IIC总线EEPROM读取
  2. [转载]十六款值得关注的NoSQL与NewSQL数据库
  3. oracle11 exp query,EXP的QUERY参数
  4. 调研报告--结构--指导
  5. 软件项目管理作业实施方案 草案
  6. 程序分析过程中遇到疑难问题解决办法
  7. ios计算机器代码,JS实现苹果计算器
  8. 新冠病毒中招|第一天
  9. 直播app源代码,android弹框的几种操作
  10. 拼多多新店铺怎么运营?