Android 百度语音合成 (含离线在线API合成方式,详细步骤+源码)
Posted 初学者-Study
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 百度语音合成 (含离线在线API合成方式,详细步骤+源码)相关的知识,希望对你有一定的参考价值。
百度语音合成
声明
本文代码请使用真机运行,别用模拟器虚拟机,谢谢!
前言
我之前写过百度的语音识别,也写过讯飞的语音识别与合成,而有读者看完后说没有百度的语音合成,想在用百度语音识别的同时使用百度的语音合成。所以就有了这篇文章,我的文章也是区别于其他人的文章,所以我有自己的风格。
感兴趣可以先扫码下载体验一下,再决定往不往下面看。
正文
首先我们登录这个百度智能云,然后找到语音技术。
点击创建应用
这里选择包名,如果你选择不需要,则只能通过网络API来实现你的语音合成,而选择android的话就不光可以使用API还能使用SDK,不过这样的话对APK的大小会有增加。
这里我选择的是Android,因此需要建一个Android项目。
一、创建项目
先把这个com.llw.speechsynthesis包名填进去。
立即创建
查看应用详情。
这几个值在后面会用到的,记下来。然后回到列表中,领取免费的使用额度。
注意看这个提示,说明这个额度是有期限的。
领取之后。
二、离线语音合成
点击左侧的离线合成SDK
选择应用后,点击确定。
可以看到激活的30天内,我是5月6号激活,可能你后面看文章的时候就已经是不能用了,所以不要拿到源码之后问我为什么用不了,那只能说明你没有看文章。
这里看这个是单台设备授权,所以你想要增多的话就要花钱了,点击下载SDK。
注意这个还要激活SDK才行的。激活是需要序列号的,那么这个序列号那里来呢?点击查看详情
下载序列号列表,下载后打开如下
现在这序列号就有了,下面回到
下载这个SDK
下载后解压,下面正式来配置这个离线的语音合成了。
1. 配置AndroidManifest.xml
打开项目的AndroidManifest.xml,添加权限。
<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.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
然后适配api 28以上版本。
<!--支持api level 28 以上编译-->
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
添加位置如下图。
2. 配置SDK
打开刚才的SDK,进入到libs文件夹下
将这个jar包复制到你的项目的libs下。
注意到它这个现在是没有展开的,说明还没有加载进去,点击工具栏右上方的小象图标进行项目资源同步。
同步后的你的jar就加载到项目中了,就是可以展开的。
进入到main文件夹下
复制assets和jniLibs这两个文件夹到你的项目的main下面。
然后展开你的assets文件夹,打开auth.properties文件。修改里面的一些内容。
这里面的五个值都需要进行修改,前三个值是我们在创建平台应用时生成的,我当时说了你要记下来,就是为了这里使用。那么你只要一一的对应填写替换就可以了,而applicationId:就是我们之前填写的包名,最后的sn:就是下载的序列号,有两个,任意一个都可以。那么将上面的数据改了之后如下所示:
3. 离线SDK初始化
离线SDK第一次初始化的时候需要联网,进行网络鉴权,鉴权成功之后就可以断网使用了,先完成这个初始化操作。修activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:text="离线SDK合成"
android:onClick="offlineSDK"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
然后在MainActivity中写入一个方法:
/**
* 离线SDK合成
* @param view
*/
public void offlineSDK(View view) {
}
当点击这个方法时会进入到离线SDK的页面,这个页面现在还没有的,不过我们的下载SDK里面有现成的,那就拿过来就用好了。
首先将layout下的activity_synth.xml文件复制到项目的layout下。
然后将res文件夹下的raw文件夹复制到你的项目的res下:
然后就是里面的一些配置类了。
将sample包下的选择的文件和文件夹复制到你的项目的包下。
4. 导包
然后依次打开里面的粘贴过来的类,首先是control包下的InitConfig类,里面是会有报错的,因为包名不一致。所以需要重新导包。
点击import左边的加号或者右边的省略号查看里面的导包信息
看到这里是报红的。删掉我标注的这一行错误的导包信息。然后往下滑动,到下方你点击报红的这个类,会出现一个提示如下图所示:可以通过快捷键Alt + Enter快速导包
导包之后这个类就不报错了,就能正常使用了
那么你刚才复制过来的类都需要重新打开一次,看看里面的包是否有异常,有的话就按刚才的方法来解决就好了。当你把所有的类检查一遍之后,确保都没有异常之后,就可以开始进行这个初始化了。
修改MainActivity中的代码
/**
* 离线SDK合成
* @param view
*/
public void offlineSDK(View view) {
startActivity(new Intent(this,SynthActivity.class));
}
点击这个按钮跳转到SynthActivity中。别忘了要在AndroidManifest.xml中注册这个Activity。
5. 运行
下面运行一下:
是有声音的,不过这是GIF图,所以你只能看到我的演示效果。那么到此为止,这个离线合成就弄完了,具体的细节你要多看这个SDK的代码,我个人觉得代码太多了,有些乱。
三、在线语音合成 - SDK方式
1. 创建页面
在线合成的方式其实和离线差不了多少,在com.llw.speechsynthesis包下新建一个OnlineActivity,布局是activity_online.xml,布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="3">
<Button
android:id="@+id/speak"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:lines="2"
android:text="合成并播放"
android:textSize="12dp" />
<Button
android:id="@+id/stop"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2"
android:lines="2"
android:text="停止合成引擎"
android:textSize="12dp" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/btn"
>
<TextView
android:id="@+id/showText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="@android:color/darker_gray"
android:minLines="3"
android:scrollbars="vertical" />
</ScrollView>
</LinearLayout>
下面再来看OnlineActivity的代码
2. 编辑代码
package com.llw.speechsynthesis;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.baidu.tts.chainofresponsibility.logger.LoggerProxy;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.TtsMode;
import com.llw.speechsynthesis.control.InitConfig;
import com.llw.speechsynthesis.listener.UiMessageListener;
import com.llw.speechsynthesis.util.Auth;
import com.llw.speechsynthesis.util.AutoCheck;
import com.llw.speechsynthesis.util.FileUtil;
import com.llw.speechsynthesis.util.IOfflineResourceConst;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 除了SDK,该类没有任何依赖,可以直接复制进你的项目
* <p>
* 默认TEMP_DIR = "/sdcard/baiduTTS"; // 重要!请手动将assets目录下的3个dat 文件复制到该目录
* 确保 TEXT_FILENAME 和 MODEL_FILENAME 存在
* Created by fujiayi on 2017/9/14.
*/
public class OnlineActivity extends AppCompatActivity implements IOfflineResourceConst {
/**
* 要合成的文本,可以自行改动。
*/
private static final String TEXT = "欢迎使用百度语音合成,请在代码中修改合成文本";
protected String appId;
protected String appKey;
protected String secretKey;
protected String sn; // 纯离线合成SDK授权码;离在线合成SDK没有此参数
//TtsMode.ONLINE 纯在线
private TtsMode ttsMode = TtsMode.ONLINE;
private boolean isOnlineSDK = TtsMode.ONLINE.equals(DEFAULT_SDK_TTS_MODE);
// ================ 纯离线sdk或者选择TtsMode.ONLINE 以下参数无用;
private static final String TEMP_DIR = "/sdcard/baiduTTS"; // 重要!请手动将assets目录下的3个dat 文件复制到该目录
// 请确保该PATH下有这个文件
private static final String TEXT_FILENAME = TEMP_DIR + "/" + TEXT_MODEL;
// 请确保该PATH下有这个文件 ,m15是离线男声
private static final String MODEL_FILENAME = TEMP_DIR + "/" + VOICE_MALE_MODEL;
// ===============初始化参数设置完毕,更多合成参数请至getParams()方法中设置 =================
protected SpeechSynthesizer mSpeechSynthesizer;
// =========== 以下为UI部分 ==================================================
private TextView mShowText;
protected Handler mainHandler;
private String desc; // 说明文件
private static final String TAG = "MiniActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appId = Auth.getInstance(this).getAppId();
appKey = Auth.getInstance(this).getAppKey();
secretKey = Auth.getInstance(this).getSecretKey();
sn = Auth.getInstance(this).getSn(); // 纯离线合成必须有此参数;离在线合成SDK没有此参数
desc = FileUtil.getResourceText(this, R.raw.mini_activity_description);
setContentView(R.layout.activity_online);
initView();
initPermission();
initTTs();
}
/**
* 注意此处为了说明流程,故意在UI线程中调用。
* 实际集成中,该方法一定在新线程中调用,并且该线程不能结束。具体可以参考NonBlockSyntherizer的写法
*/
private void initTTs() {
LoggerProxy.printable(true); // 日志打印在logcat中
boolean isSuccess;
if (!isOnlineSDK) {
// 检查2个离线资源是否可读
isSuccess = checkOfflineResources();
if (!isSuccess) {
return;
} else {
print("离线资源存在并且可读, 目录:" + TEMP_DIR);
}
}
// 日志更新在UI中,可以换成MessageListener,在logcat中查看日志
SpeechSynthesizerListener listener = new UiMessageListener(mainHandler);
// 1. 获取实例
mSpeechSynthesizer = SpeechSynthesizer.getInstance();
mSpeechSynthesizer.setContext(this);
// 2. 设置listener
mSpeechSynthesizer.setSpeechSynthesizerListener(listener);
// 3. 设置appId,appKey.secretKey
int result = mSpeechSynthesizer.setAppId(appId);
checkResult(result, "setAppId");
result = mSpeechSynthesizer.setApiKey(appKey, secretKey);
checkResult(result, "setApiKey");
// 4. 如果是纯离线SDK需要离线功能的话
if (!isOnlineSDK) {
// 文本模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, TEXT_FILENAME);
// 声学模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, MODEL_FILENAME);
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
// 该参数设置为TtsMode.MIX生效。
// MIX_MODE_DEFAULT 默认 ,wifi状态下使用在线,非wifi离线。在线状态下,请求超时6s自动转离线
// MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线,非wifi离线。在线状态下, 请求超时1.2s自动转离线
// MIX_MODE_HIGH_SPEED_NETWORK , 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线
// MIX_MODE_HIGH_SPEED_SYNTHESIZE, 2G 3G 4G wifi状态下使用在线,其它状态离线。在线状态下,请求超时1.2s自动转离线
}
// 5. 以下setParam 参数选填。不填写则默认值生效
// 设置在线发声音人: 0 普通女声(默认) 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
// 设置合成的音量,0-15 ,默认 5
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "9");
// 设置合成的语速,0-15 ,默认 5
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");
// 设置合成的语调,0-15 ,默认 5
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");
// mSpeechSynthesizer.setAudiostreamType(AudioManager.MODE_IN_CALL); // 调整音频输出
if (sn != null) {
// 纯离线sdk这个参数必填;离在线sdk没有此参数
mSpeechSynthesizer.setParam(PARAM_SN_NAME, sn);
}
// x. 额外 : 自动so文件是否复制正确及上面设置的参数
Map<String, String> params = new HashMap<>();
// 复制下上面的 mSpeechSynthesizer.setParam参数
// 上线时请删除AutoCheck的调用
if (!isOnlineSDK) {
params.put(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, TEXT_FILENAME);
params.put(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, MODEL_FILENAME);
}
// 检测参数,通过一次后可以去除,出问题再打开debug
InitConfig initConfig = new InitConfig(appId, appKey, secretKey, ttsMode, params, listener);
AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {
@Override
/**
* 开新线程检查,成功后回调
*/
public void handleMessage(Message msg) {
if (msg.what == 100) {
AutoCheck autoCheck = (AutoCheck) msg.obj;
synchronized (autoCheck) {
String message = autoCheck.obtainDebugMessage();
print(message); // 可以用下面一行替代,在logcat中查看代码
// Log.w("AutoCheckMessage", message);
}
}
}
});
// 6. 初始化
result = mSpeechSynthesizer.initTts(ttsMode);
checkResult(result, "initTts");
}
/**
* 在线SDK不需要调用,纯离线SDK会检查资源文件
*
* 检查 TEXT_FILENAME, MODEL_FILENAME 这2个文件是否存在,不存在请自行从assets目录里手动复制
*
* @return 检测是否成功
*/
private boolean checkOfflineResources() {
String[] filenames = {TEXT_FILENAME, MODEL_FILENAME};
for (String path : filenames) {
File f = new 集成Android免费语音合成功能(在线离线离在线融合)