首页 » 技术分享 » 离线语音合成使用——科大讯飞or云知音or百度语音

离线语音合成使用——科大讯飞or云知音or百度语音

 
文章目录

本片主要讲解如果使用科大讯飞和云知音这两个离线语音合成功能。

目前语音合成的主要有科大讯飞、百度语音、云知声。

一、三大产品功能分析

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百度语音,转载请注明来源!

2