100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Android集成百度TTS 实现离的中英语音合成

Android集成百度TTS 实现离的中英语音合成

时间:2023-07-19 13:35:37

相关推荐

Android集成百度TTS 实现离的中英语音合成

百度的离在线TTS,没有调用量限制,免费但是有QPS限制(是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准),增大QPS需要收费,所以对商用并不是很好友。如果想用完全免费的纯离线可参考我另一篇博客:

/sunyFS/article/details/97936551

话不多说开始!

第一步:先从百度tts官网下载离在线融合SDK,网址:/sdk#tts,解压后最好先运行一下demo。

参考技术文档:/docs#/TTS-Android-SDK/top

1.将com.baidu.tts_2.3.2.0713_6101c2a.jar添加到你项目的libs(注意要添加依赖同步)

implementation files(‘libs/com.baidu.tts_2.3.2.0713_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_0505.dat为离线男声模型;

// assets目录下bd_etts_common_speech_f7_mand_eng_high_am-mix_v3.0.0_0512.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;}}```

最后要记得释放资源

@Overrideprotected 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:/sunfusong/baiduTtsDemo如果想了解纯离线、免费TTS(android原生TTS+语言引擎)也可以看下我的另一篇博客/sunyFS/article/details/97936551项目在这 github:/sunfusong/NativeTTS写demo遇到的error:.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是本人结合官方文档和大佬们的相关博客写的,有什么不足地方可以提出,谢谢大家了!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。