离开老师的指导有一段时间了,每天都会给自己找点事情做,或则简单的安排计划一下,收货不多不少刚刚好。
这是2017年的第一篇比较正式的博客,也算是我在CSDN上的第一次吧!哈哈。今天记录的是最近在集成百度语音上的一些步骤和小部分总结。最开始准备使用讯飞语音的,然而在下载集成后多次测试都没有反应,感觉是哪儿出现了问题,并且讯飞语音在语音唤醒上面也有限制,最后无意间发现了百度语音,就尝试着试试,当然目前只完成了语音合成部分,接下来当然会去玩玩语音识别和唤醒咯。
到此,集成就结束了,接下来就是如何使用。当然可以参照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) {
//当合成或者播放过程中出错时回调此接口
}
}
}
该工具类我就不多解释了,唯一需要配置的地方在前面获取到的AppId,ApiKey和SecretKey直接替换成你自己的就可以了。该工具类做的事就是第一步将由于资源文件及Asserts下面的文件复制到SD卡上,然后初始化一个语音合成器SpeechSynthesizer。并提供放回的方法getSyntheszer供外部使用。合成监听器SpeechSynthesizerListener能处理各个阶段的事件。当然这个工具类不好,存在构造器还需要传入参数,有待改进,int speaker 为发声人,从0到7都可以使用,男声,女声,自己可以试着玩玩。
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();
}
}
通过上面的步骤语音合成就算完事了,在没有网络连接的情况下默认speaker为0–普通女声,即点击所有按钮都是普通女声朗读出文本框中的文字,英文中文都可以,网络状态下其他按钮才能发出不同的声音。
结果就是这样,点击按钮就能听见朗读声音咯。图中的按钮和输入框shape文件如下,文件名分别为btn_shape和et_shape,都放在res下的drawable文件夹中,适可参考Ctrl+C吧!
btn_shape:
et_shape:
第一次写这个东东,是不是太详细了竟然写了老久了,这打字速度有点坑啊,还要多锻炼!