本片主要讲解如果使用科大讯飞和云知音这两个离线语音合成功能。
目前语音合成的主要有科大讯飞、百度语音、云知声。
一、三大产品功能分析
1.百度语音
百度语音合成只有离在线语音服务(免费),没有纯离线,离在线语音合成,在首次使用语音功能,必须联网,让其通过网络获得百度的授权文件之后,方可正常使用,之后不联网也可以正常使用,联网使用优先使用在线包,不联网则使用离线包。百度语音合成文档地址:http://ai.baidu.com/docs#/TTS-Android-SDK/top,每天有调用次数限制,如下图:
2.科大讯飞
讯飞语语音合成有在线和离线两种,在线免费,离线收费,收费标准如下:
在线只有联网的时候才可以正常使用,离线则无需任何联网,就可正常使用,如果选择离线包,体验版SDk下载后就可以为你生产专用的SDK,SDK的demo中会配置好一切,比如说key这些,都会使用你建应用所给的,无需你手动再进行配置了,简单易用,但下载下来还得调试一番方可正常使用。科大讯飞官网:http://www.xfyun.cn/,SDK文档地址:http://mscdoc.xfyun.cn/android/api/
3.云知声
云知声有在线和离线的,在线的官方号称永久免费,离线的也是收费的,但是没能查到收费标准,语音文档地址:http://admin-web-files.ks3-cn-shanghai.ksyun.com/docs/sdk/USC_DevelGuide_Android_common.pdf,置于其他详细信息可以上官网了解,地址:http://dev.hivoice.cn/
二、三者使用
1.百度语音
如何使用请见文档,文档写的相当详细,地址:http://ai.baidu.com/docs#/Begin/top,截图如下:
包括如何注册如何使用,都明确标出来了,我只将自己的关于语音合成的代码贴出来,如下:
public class BaiDuSpeechUtil {
private final String TAG = this.getClass().getSimpleName();
private SpeechSynthesizer mSpeechSynthesizer;
private String mSampleDirPath;
private static final String SAMPLE_DIR_NAME = "baiduTTS";
//-------以下全是在assets下的文件,使用离线时必须全部copy到手机中方可使用----start--
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 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";
//--------end-------------------------------------------------------------
private static BaiDuSpeechUtil baiDuSpeechUtil = null;
public static BaiDuSpeechUtil getInstance(){
if(baiDuSpeechUtil == null) {
synchronized (BaiDuSpeechUtil.class) {
if(baiDuSpeechUtil == null) {
baiDuSpeechUtil = new BaiDuSpeechUtil();
}
}
}
return baiDuSpeechUtil;
}
/**
* 初始化百度语音资源
* @param context
*/
public void setInitialEnv(Context context) {
initialEnv(context);
}
/**
* 初始化百度语音播报相关
* @param context
*/
public void setInitialTts(Context context, SpeechSynthesizerListener speechSynthesizerListener){
initialTts(context,speechSynthesizerListener);
}
private void initialEnv(Context context) {
// long start_time= System.currentTimeMillis();
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, "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);
// Log.d(TAG,"initialEnv cost:"+ (System.currentTimeMillis()-start_time));
}
private void makeDir(String dirPath) {
File file = new File(dirPath);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 将sample工程需要的资源文件拷贝到SD卡中使用(授权文件为临时授权文件,请注册正式授权)
* 主要是在离线时候用到,只需执行一次即可,这里写的不严谨,应该去判断一下离线用的那些文件,sd卡是否存在,如果不存在,则copy,如果存在则无需在copy,可在子线程操作
* @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.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)) != -1) {
fos.write(buffer, 0, size);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
//此方法可在子线程中操作,由于这个初始化过程比较费时,大概在1s左右,看项目需求而定,如果是进入界面就必须播放(伴随UI改变的)的,在UI线程,如无其他特殊要求,放在子线程中即可
private void initialTts(Context context,SpeechSynthesizerListener speechSynthesizerListener) {
// long start_time= System.currentTimeMillis();
mSpeechSynthesizer = SpeechSynthesizer.getInstance();
mSpeechSynthesizer.setContext(context);
mSpeechSynthesizer.setSpeechSynthesizerListener(speechSynthesizerListener);
mSpeechSynthesizer.setApiKey(Constant.APP_KEY_BAIDU, Constant.APP_SECRET_BAIDU);
mSpeechSynthesizer.setAppId(Constant.APP_ID_BAIDU);
// 文本模型文件路径 (离线引擎使用)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/"
+ TEXT_MODEL_NAME);
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/"
+ SPEECH_FEMALE_MODEL_NAME);
// 本地授权文件路径,如未设置将使用默认路径.设置临时授权文件路径,LICENCE_FILE_NAME请替换成临时授权文件的实际路径,仅在使用临时license文件时需要进行设置,如果在[应用管理]中开通了正式离线授权,不需要设置该参数,建议将该行代码删除(离线引擎)
// 如果合成结果出现临时授权文件将要到期的提示,说明使用了临时授权文件,请删除临时授权即可。
// 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,1--普通男声,2--特别男声,3--情感男声。。。)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
// 设置Mix模式的合成策略, //mix模式下,wifi使用在线合成,非wifi使用离线合成)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI);
// if(SystemUtil.isNetWorkConnected(getCurrentActivity())) {
// // AuthInfo接口用于测试开发者是否成功申请了在线或者离线授权,如果测试授权成功了,可以删除AuthInfo部分的代码(该接口首次验证时比较耗时),不会影响正常使用(合成使用时
// AuthInfo authInfo=this.mSpeechSynthesizer.auth(TtsMode.MIX);
//
// if (authInfo.isSuccess()){
// toPrint("auth success");
// }else{
// String errorMsg=authInfo.getTtsError().getDetailMessage();
// toPrint("auth failed errorMsg=" + errorMsg);
// }
// }
// 初始化tts
mSpeechSynthesizer.initTts(TtsMode.MIX);
// 加载离线英文资源(提供离线英文合成功能)
int result = mSpeechSynthesizer.loadEnglishModel(mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);
// Log.d(TAG,"initialTts cost:"+ (System.currentTimeMillis()-start_time));
}
/**
* 播报的文字
* @param content
*/
public void speakText(String content) {
try{
if(mSpeechSynthesizer != null) {
int result = mSpeechSynthesizer.speak(content);
if (result < 0) {
Log.d(TAG,"error,please look up error code in doc or URL:http://yuyin.baidu.com/docs/tts/122 ");
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 暂停
*/
public void pauseSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.pause();
}
}
/**
* 停止播放
*/
public void stopSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.stop();
}
}
/**
* 接着停止后的地方播放
*/
public void resumeSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.resume();
}
}
/**
* 释放mSpeechSynthesizer,在使用完之后必须调用,确保下个界面使用的时候资源已经释放掉了,否则下个界面将无法正常播放
*/
public void releaseSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.release();
}
}
public void setSpeechSynthesizerNull(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer = null;
}
}
public void endSpeechSynthesizer(){
pauseSpeechSynthesizer();
stopSpeechSynthesizer();
releaseSpeechSynthesizer();
setSpeechSynthesizerNull();
}
}
如何调用:
public class MeasurePromptActivity extends BaseActivity implements SpeechSynthesizerListener {
private BaiDuSpeechUtil mBaiDuSpeechUtil;
private static final String WARM_PROMPT = "百度语音测试,你好开发者";
@Override
protected void setCurrentContentView() {
setContentView(R.layout.activity_measure_prompt);
}
@Override
protected void init(Bundle savedInstanceState) {
}
// 语音初始化及播报
private void startSpeakText() {
mBaiDuSpeechUtil = BaiDuSpeechUtil.getInstance();
mBaiDuSpeechUtil.setInitialEnv(getCurrentActivity());
mBaiDuSpeechUtil.setInitialTts(getCurrentActivity(), this);
speakText(WARM_PROMPT);
}
private void speakText(String content) {
if (mBaiDuSpeechUtil != null) {
mBaiDuSpeechUtil.speakText(content);
}
}
@Override
protected void onResume() {
super.onResume();
startSpeakText();
}
//在离开次页面时调用
private void releaseSpeechSynthesizer() {
if (mBaiDuSpeechUtil != null) {
mBaiDuSpeechUtil.releaseSpeechSynthesizer();
}
}
@Override
public void onSynthesizeStart(String s) {
}
@Override
public void onSynthesizeDataArrived(String s, byte[] bytes, int i) {
}
@Override
public void onSynthesizeFinish(String s) {
}
@Override
public void onSpeechStart(String s) {
}
@Override
public void onSpeechProgressChanged(String s, int i) {
}
@Override
public void onSpeechFinish(String s) {
}
@Override
public void onError(String s, SpeechError speechError) {
}
}
以上就是百度语音合成的使用,百度其他使用亲自行下载官网提供的sample即可。
2.科大讯飞
科大讯飞在线和离线无非就是配置的问题,这里我只贴重点代码,代码都是copy官网提供的sample下的,如下:
/**
* 参数设置
* @return
*/
private void setParam(){
// 清空参数
mTts.setParameter(SpeechConstant.PARAMS, null);
//设置合成
if(mEngineType.equals(SpeechConstant.TYPE_CLOUD))
{
//设置使用云端引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME,voicerCloud);
}else {
//设置使用本地引擎
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
//设置发音人资源路径
mTts.setParameter(ResourceUtil.TTS_RES_PATH,getResourcePath());
//设置发音人
mTts.setParameter(SpeechConstant.VOICE_NAME,voicerLocal);
}
//设置合成语速
mTts.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString("speed_preference", "50"));
//设置合成音调
mTts.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString("pitch_preference", "50"));
//设置合成音量
mTts.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString("volume_preference", "50"));
//设置播放器音频流类型
mTts.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString("stream_preference", "3"));
// 设置播放合成音频打断音乐播放,默认为true
mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/tts.wav");
}
回调监听,什么时候开始,什么时候停止....这些都有的
* 合成回调监听。
*/
private SynthesizerListener mTtsListener = new SynthesizerListener() {
@Override
public void onSpeakBegin() {
showTip("开始播放");
}
@Override
public void onSpeakPaused() {
showTip("暂停播放");
}
@Override
public void onSpeakResumed() {
showTip("继续播放");
}
@Override
public void onBufferProgress(int percent, int beginPos, int endPos,
String info) {
// 合成进度
mPercentForBuffering = percent;
showTip(String.format(getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onSpeakProgress(int percent, int beginPos, int endPos) {
// 播放进度
mPercentForPlaying = percent;
showTip(String.format(getString(R.string.tts_toast_format),
mPercentForBuffering, mPercentForPlaying));
}
@Override
public void onCompleted(SpeechError error) {
if (error == null) {
showTip("播放完成");
} else if (error != null) {
showTip(error.getPlainDescription(true));
}
}
@Override
public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
// 若使用本地能力,会话id为null
// if (SpeechEvent.EVENT_SESSION_ID == eventType) {
// String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
// Log.d(TAG, "session id =" + sid);
// }
}
};
切记在结束当前页面的时候必须将SpeechSynthesizer mTts 释放掉
if( null != mTts ){
mTts.stopSpeaking();
// 退出时释放连接
mTts.destroy();
}
注意:使用时切记一定要配置正确,离线资源播报人必须copy到正确的目录下,libs的so库根据需要添加,否则可能导致包体积过大,gradle必须配置正确libs的目录,否则就会找不见so库了,报常见的java.lang.UnsatisfiedLinkError错误,如下图
3.云知声
云之声只介绍离线的,代码截取都是官网的sample中的,用法和百度的类似,如下:
public class TTSOfflineActivity extends Activity {
private static boolean TTS_PLAY_FLAGE = false;
private EditText mTTSText;
private TextView mTextViewTip;
private TextView mTextViewStatus;
private Button mTTSPlayBtn;
private SpeechSynthesizer mTTSPlayer;
private final String mFrontendModel= "frontend_model";
// private final String mBackendModel = "backend_lzl";
private final String mBackendModel = "backend_female";
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offline_tts);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.status_bar_main);
mTTSText = (EditText) findViewById(R.id.textViewResult);
mTextViewStatus = (TextView) findViewById(R.id.textViewStatus);
mTextViewTip = (TextView) findViewById(R.id.textViewTip);
mTTSPlayBtn = (Button) findViewById(R.id.recognizer_btn);
mTTSPlayBtn.setEnabled(false);
mTTSPlayBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
TTSPlay();
}
});
initialEnv();
// 初始化本地TTS播报
initTts();
}
/**
* 初始化本地离线TTS,sample中判断了是否将assets中的文件copy到sd卡中了,正式项目无需此操作,直接copy即可,但注意权限问题,尤其是高版本
*/
private void initTts() {
// 初始化语音合成对象
mTTSPlayer = new SpeechSynthesizer(this, Config.appKey, Config.secret);
// 设置本地合成
mTTSPlayer.setOption(SpeechConstants.TTS_SERVICE_MODE, SpeechConstants.TTS_SERVICE_MODE_LOCAL);
// File _FrontendModelFile = new File(mFrontendModel);
// if (!_FrontendModelFile.exists()) {
// toastMessage("文件:" + mFrontendModel + "不存在,请将assets下相关文件拷贝到SD卡指定目录!");
// }
// File _BackendModelFile = new File(mBackendModel);
// if (!_BackendModelFile.exists()) {
// toastMessage("文件:" + mBackendModel + "不存在,请将assets下相关文件拷贝到SD卡指定目录!");
// }
// 设置前端模型
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_FRONTEND_MODEL_PATH, mDirPath+ "/" + mFrontendModel);
// 设置后端模型
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_BACKEND_MODEL_PATH, mDirPath + "/" + mBackendModel);
// 设置回调监听
mTTSPlayer.setTTSListener(new SpeechSynthesizerListener() {
@Override
public void onEvent(int type) {
switch (type) {
case SpeechConstants.TTS_EVENT_INIT:
// 初始化成功回调
log_i("onInitFinish");
mTTSPlayBtn.setEnabled(true);
break;
case SpeechConstants.TTS_EVENT_SYNTHESIZER_START:
// 开始合成回调
log_i("beginSynthesizer");
break;
case SpeechConstants.TTS_EVENT_SYNTHESIZER_END:
// 合成结束回调
log_i("endSynthesizer");
break;
case SpeechConstants.TTS_EVENT_BUFFER_BEGIN:
// 开始缓存回调
log_i("beginBuffer");
break;
case SpeechConstants.TTS_EVENT_BUFFER_READY:
// 缓存完毕回调
log_i("bufferReady");
break;
case SpeechConstants.TTS_EVENT_PLAYING_START:
// 开始播放回调
log_i("onPlayBegin");
break;
case SpeechConstants.TTS_EVENT_PLAYING_END:
// 播放完成回调
log_i("onPlayEnd");
setTTSButtonReady();
break;
case SpeechConstants.TTS_EVENT_PAUSE:
// 暂停回调
log_i("pause");
break;
case SpeechConstants.TTS_EVENT_RESUME:
// 恢复回调
log_i("resume");
break;
case SpeechConstants.TTS_EVENT_STOP:
// 停止回调
log_i("stop");
break;
case SpeechConstants.TTS_EVENT_RELEASE:
// 释放资源回调
log_i("release");
break;
default:
break;
}
}
@Override
public void onError(int type, String errorMSG) {
// 语音合成错误回调
log_i("onError");
toastMessage(errorMSG);
setTTSButtonReady();
}
});
// 初始化合成引擎
mTTSPlayer.init("");
}
private void TTSPlay() {
if (!TTS_PLAY_FLAGE) {
mTTSPlayer.playText(mTTSText.getText().toString());
setTTSButtonStop();
} else {
mTTSPlayer.stop();
setTTSButtonReady();
}
}
private void setTTSButtonStop() {
TTS_PLAY_FLAGE = true;
mTTSPlayBtn.setText(R.string.stop_tts);
}
private void setTTSButtonReady() {
mTTSPlayBtn.setText(R.string.start_tts);
TTS_PLAY_FLAGE = false;
}
protected void setTipText(String tip) {
mTextViewTip.setText(tip);
}
protected void setStatusText(String status) {
mTextViewStatus.setText(getString(R.string.lable_status) + "(" + status + ")");
}
@Override
public void onPause() {
super.onPause();
// 主动停止识别
if (mTTSPlayer != null) {
mTTSPlayer.stop();
}
}
private void log_i(String log) {
Log.i("demo", log);
}
@Override
protected void onDestroy() {
// 主动释放离线引擎
if (mTTSPlayer != null) {
mTTSPlayer.release(SpeechConstants.TTS_RELEASE_ENGINE, null);
}
super.onDestroy();
}
private void toastMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
/**
* 将assets文件下的离线资源copy到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.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)) != -1) {
fos.write(buffer, 0, size);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private void makeDir(String dirPath) {
File file = new File(dirPath);
if (!file.exists()) {
file.mkdirs();
}
}
private String mDirPath = null;
private void initialEnv(){
if(mDirPath == null) {
String sdcardPath = Environment.getExternalStorageDirectory().toString();
mDirPath = sdcardPath + "/unsound/tts/";
}
makeDir(mDirPath);
copyFromAssetsToSdcard(this,false,mFrontendModel,mDirPath +"/" + mFrontendModel);
copyFromAssetsToSdcard(this,false,mBackendModel,mDirPath + "/" + mBackendModel);
}
}
assets下的文件入下图所示:
这样基本就正常使用了,还是主要gradle的配置
总结:三种方案中,百度无离线,离线使用个人觉得讯飞优于云知音,但奈何价格贵呀;如果是定位互联网产品的话,百度的还是比较好的,离在线融合使用,主要是免费了,科大讯飞的在线也是不错的;语音合成播报离线,可能是由于模型训练问题,对于英文和特殊数字的处理有时候不准确,例如110,分场合使用,有时候读一百一十,有时候读一一零,还有离线包对英文的识别不准确,有时候会逐个字母的读,在线一般不会有问题的,由于大多是互联网产品,都会联网的,所以上述问题可以忽略的。
关于上方SDK的使用,个人认为先去研究其demo,先保证下载下来能正常使用,大多数出现最多的问题可能是由于无法正确配置导致的,一般主要有以下两点:
1.so库没放对位置
2.gradle文件配置错误(包括lib/jinLibs的路径配置,gradle版本等等)
3.AndroidManifest配置错误,包括权限等等
这些问题只要细心点都是可以避免的,如果出现问题仔细阅读官网的文档,寻找解决方法,如果无法解决可以联系其技术帮忙解析
以上使用如果不想去官网下载,可以去github下载以上项目,地址:https://github.com/2966325911/SpeechSynthesis.git
转载自原文链接, 如需删除请联系管理员。
原文链接:离线语音合成使用——科大讯飞or云知音or百度语音,转载请注明来源!