android studio集成百度语音合成
离开老师的指导有一段时间了,每天都会给自己找点事情做,或则简单的安排计划一下,收货不多不少刚刚好。
这是2017年的第一篇比较正式的博客,也算是我在CSDN上的第一次吧!哈哈。今天记录的是最近在集成百度语音上的一些步骤和小部分总结。最开始准备使用讯飞语音的,然而在下载集成后多次测试都没有反应,感觉是哪儿出现了问题,并且讯飞语音在语音唤醒上面也有限制,最后无意间发现了百度语音,就尝试着试试,当然目前只完成了语音合成部分,接下来当然会去玩玩语音识别和唤醒咯。
步骤如下:
- Android Studio新建一个Mudule,比如我的:MyTest_AutoAnswer,你可以随意叫啥名。
- 登录网址百度语音开发者平台注册账号并创建应用。http://yuyin.baidu.com/ 。
- 进入离线下载资源,先开通服务,再下载。我这里仅仅下载的为语音合成部分的离在线融合SDK–安卓版本。
- 进入应用管理在该项目的右边点开管理包名,并在应用包名处输入android Studio中创建的应用包名,比如我的为:com.bk120.mytest_autoanswer。保存修改。此处可以不用下载什么临时授权文件,我就没下载因为你已经把包名都填上了。
- 同理点击查看Key,查看当前应用的所需的主要三个参数 AppId APIKey SecretKey,后面会用得到。
- 将我们前面几步中下载的SDk解压,为如下目录结构:
BaiduTtsSample:为一个模板代码,eclipse版本的,我就是借鉴里面稍微修改了一下。
data:为百度语音资源,声音文件,它为一个必须文件,中英文资源。最后使用是放在手机物理存储下的。
doc:为一个pdf的简介使用方法以及网络的使用Api文档说明。我们用不到,可以下去读一读的。
libs:为资源jar包和语音引擎文件.so库。也是我们集成必须使用到的。
7.接下来的步骤是,我们家语音资源和libs下的资源方法android studio我们的项目里面。将data里面的文件全部复制到Asserts文件夹下。将libs下的两个jar文件复制到项目的libs中,并添加Add As library关联。在项目中的main路径下新建一个jnilibs文件夹,用于放置剩余的libs下的文件。所以后的文件目录为:
libs下面多余的两个okhttp和okio是我的网络访问的库,此处没有影响,不用添加的。
8.最后在AndroidManifest文件中申明如下权限,我申请的比较多,因为做了一个网络判断的事件处理,没事全部Ctrl+C就好没影响的。
<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" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
到此,集成就结束了,接下来就是如何使用。当然可以参照BaiduTtsSample文件夹下的src里面的一个MainActvity的写法。也可以按照我下面的总结的工具类来直接使用,方便快捷省事。
工具类如下
package com.bk120.mytest_autoanswer;
import android.content.Context;
import android.os.Environment;
import com.baidu.tts.client.SpeechError;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.SynthesizerTool;
import com.baidu.tts.client.TtsMode;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/*** Created by bk120 on 2017/1/3.
* 语音合成工具类
*/
public class VoiceUtils {
//////////////////需配置部分////////////////////////
//AppId
private String APPID="9156383";
//ApiKey
private String APIKEY="BZQ1owOxjYzf0ADT1agq8SxU";
//SecretKey
private String SECRETKEY="43b8a384182f2af0b935cf4ee9dce03f";
////////////////////配置结束////////////////////////////////////
private SpeechSynthesizer mSpeechSynthesizer;
private String mSampleDirPath;
private static final String SAMPLE_DIR_NAME = "baiduTTS";
private static final String SPEECH_FEMALE_MODEL_NAME = "bd_etts_speech_female.dat";
private static final String SPEECH_MALE_MODEL_NAME = "bd_etts_speech_male.dat";
private static final String TEXT_MODEL_NAME = "bd_etts_text.dat";
private static final String LICENSE_FILE_NAME = "temp_license.txt";
private static final String ENGLISH_SPEECH_FEMALE_MODEL_NAME = "bd_etts_speech_female_en.dat";
private static final String ENGLISH_SPEECH_MALE_MODEL_NAME = "bd_etts_speech_male_en.dat";
private static final String ENGLISH_TEXT_MODEL_NAME = "bd_etts_text_en.dat";
//初始化
public void init(Context context,int speaker){
initialEnv(context);
initialTts(context,speaker);
}
//获取解析器
public SpeechSynthesizer getSyntheszer(){
return mSpeechSynthesizer;
}
//初始化配置文件
private void initialEnv(Context context) {
if (mSampleDirPath == null) {
String sdcardPath = Environment.getExternalStorageDirectory().toString();
mSampleDirPath = sdcardPath + "/" + SAMPLE_DIR_NAME;
}
makeDir(mSampleDirPath);
copyFromAssetsToSdcard(context,false, SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_MALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, TEXT_MODEL_NAME, mSampleDirPath + "/" + TEXT_MODEL_NAME);
copyFromAssetsToSdcard(context,false, LICENSE_FILE_NAME, mSampleDirPath + "/" + LICENSE_FILE_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_SPEECH_FEMALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_SPEECH_MALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_TEXT_MODEL_NAME);
}
private void makeDir(String dirPath) {
File file = new File(dirPath);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 将资源语音文件复制到手机SD卡中
* @param isCover 是否覆盖已存在的目标文件
* @param source
* @param dest
*/
private void copyFromAssetsToSdcard(Context context,boolean isCover, String source, String dest) {
File file = new File(dest);
if (isCover || (!isCover && !file.exists())) {
InputStream is = null;
FileOutputStream fos = null;
try {
is = context.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();
}
}
}
}
//初始化解析器
private void initialTts(Context context,int speaker) {
this.mSpeechSynthesizer = SpeechSynthesizer.getInstance();
this.mSpeechSynthesizer.setContext(context);
this.mSpeechSynthesizer.setSpeechSynthesizerListener(new MyListnener());
// 文本模型文件路径 (离线引擎使用)
this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/"
+ TEXT_MODEL_NAME);
// 声学模型文件路径 (离线引擎使用)
this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/"
+ SPEECH_FEMALE_MODEL_NAME);
// 请替换为语音开发者平台上注册应用得到的App ID (离线授权)
this.mSpeechSynthesizer.setAppId(APPID);
// 请替换为语音开发者平台注册应用得到的apikey和secretkey (在线授权)
this.mSpeechSynthesizer.setApiKey(APIKEY,
SECRETKEY);
// 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,4--情感女声,3--普通男声,6--特殊男声。。。)
this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, speaker+"");
// 设置Mix模式的合成策略
this.mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
// 授权检测接口(只是通过AuthInfo进行检验授权是否成功。)
// 初始化tts
mSpeechSynthesizer.initTts(TtsMode.MIX);
// 加载离线英文资源(提供离线英文合成功能)
mSpeechSynthesizer.loadEnglishModel(mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath
+ "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);
}
//合成监听器
class MyListnener implements SpeechSynthesizerListener{
@Override
public void onSynthesizeStart(String s) {
//合成准备工作
}
@Override
public void onSynthesizeDataArrived(String s, byte[] bytes, int i) {
//合成数据和进度的回调接口,分多次回调
}
@Override
public void onSynthesizeFinish(String s) {
//合成正常结束,每句合成正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
}
@Override
public void onSpeechStart(String s) {
//播放开始,每句播放开始都会回调
}
@Override
public void onSpeechProgressChanged(String s, int i) {
//播放进度回调接口,分多次回调
}
@Override
public void onSpeechFinish(String s) {
// 播放正常结束,每句播放正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
}
@Override
public void onError(String s, SpeechError speechError) {
//当合成或者播放过程中出错时回调此接口
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
该工具类我就不多解释了,唯一需要配置的地方在前面获取到的AppId,ApiKey和SecretKey直接替换成你自己的就可以了。该工具类做的事就是第一步将由于资源文件及Asserts下面的文件复制到SD卡上,然后初始化一个语音合成器SpeechSynthesizer。并提供放回的方法getSyntheszer供外部使用。合成监听器SpeechSynthesizerListener能处理各个阶段的事件。当然这个工具类不好,存在构造器还需要传入参数,有待改进,int speaker 为发声人,从0到7都可以使用,男声,女声,自己可以试着玩玩。
在MainActivity使用:
- 布局文件我们用一个输入框EditText和四个按钮来测试不同的发声。
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical"
android:background="#7EBF4B"
tools:context="com.bk120.mytest_autoanswer.MainActivity">
<EditText
android:layout_height="120dp"
android:layout_width="match_parent"
android:hint="输入:"
android:layout_marginTop="10dp"
android:id="@+id/mainactivity_et"
android:paddingLeft="10dp"
android:background="@drawable/et_shape"
/>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_marginTop="10dp"
>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:onClick="puTongWoman"
android:layout_weight="1"
android:text="普通女声"
android:textSize="15sp"
android:textColor="#71A7C7"
android:background="@drawable/btn_shape"
/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:onClick="qingGanWoman"
android:layout_weight="1"
android:text="情感女声"
android:textSize="15sp"
android:textColor="#71A7C7"
android:background="@drawable/btn_shape"
/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:onClick="puTongMan"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#71A7C7"
android:text="普通男声"
android:background="@drawable/btn_shape"
/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:onClick="qingGanMan"
android:layout_weight="1"
android:textSize="15sp"
android:textColor="#71A7C7"
android:text="情感男声"
android:background="@drawable/btn_shape"
/>
</LinearLayout>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
2.Activity中直接获取EditText对象并处理点击事件,如下:
MainActivity中:
–
package com.bk120.mytest_autoanswer;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.baidu.tts.client.SpeechSynthesizer;
public class MainActivity extends Activity {
private EditText mInput;
private SpeechSynthesizer mSpeechSynthesizer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mInput= (EditText) this.findViewById(R.id.mainactivity_et);
}
//普通女声
public void puTongWoman(View view){
speak(0);
}
//特殊女声
public void qingGanWoman(View view){
speak(4);
}
//普通男声
public void puTongMan(View view){
speak(3);
}
//特殊男声
public void qingGanMan(View view){
speak(6);
}
private void speak(int speaker) {
//若是每次都这样是不是会有内存问题呢?需要思考改进
VoiceUtils utils=new VoiceUtils();
utils.init(this,speaker);
mSpeechSynthesizer=utils.getSyntheszer();
String text = this.mInput.getText().toString();
//需要合成的文本text的长度不能超过1024个GBK字节。
if (TextUtils.isEmpty(mInput.getText())) {
text = "你好!";
mInput.setText(text);
}
this.mSpeechSynthesizer.speak(text);
}
//释放缓存
@Override
protected void onDestroy() {
this.mSpeechSynthesizer.release();
super.onDestroy();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
通过上面的步骤语音合成就算完事了,在没有网络连接的情况下默认speaker为0–普通女声,即点击所有按钮都是普通女声朗读出文本框中的文字,英文中文都可以,网络状态下其他按钮才能发出不同的声音。
结果就是这样,点击按钮就能听见朗读声音咯。图中的按钮和输入框shape文件如下,文件名分别为btn_shape和et_shape,都放在res下的drawable文件夹中,适可参考Ctrl+C吧!
btn_shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="#8BE5E8" android:width="1dp"/>
<corners android:radius="5dp"/>
<solid android:color="#C46F5F"/>
</shape>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
et_shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:color="#facd89" android:width="5dp"/>
<corners android:radius="8dp"/>
</shape>
- 1
- 2
- 3
- 4
- 5
- 6
第一次写这个东东,是不是太详细了竟然写了老久了,这打字速度有点坑啊,还要多锻炼!
上一篇: button按钮的四种监听及实例