源码地址:https://github.com/yellowgreatsun/MXTtsEngine
自2016年阿尔法狗大胜李世石以来,AI迅速普及,而其中语言AI就是落地化比较彻底的应用,也自然成了许多应用、智能硬件的标配功能。本人目前主要从事Android开发,2018年曾对tts做了比较多的研究,这几天,就想把之前的笔记再好好整理一番,写成博客分享给更多的开发者。
Android TTS系列
如何让app具备tts能力?
如何开发一款系统级tts引擎?
Android speech包源码剖析
本篇就先从第一项说起。
对应着MXTtsEngine的bdtts包和speech包。
让app具备tts能力,有两种方式,一种是直接集成第三方tts sdk,比如科大讯飞、百度、思必驰、小爱等等,调用sdk的接口即可。第二种是直接调用Android speech包下的tts接口,但其前提是Android设备已经安装了tts引擎。
一、直接集成第三方tts sdk
这里,以集成百度tts sdk为例。其它几种sdk,集成方式类似。
申请账号信息
进入百度AI开发平台,从“产品服务”中选择“语言合成”,可以查看官方介绍、在线体验合成效果。
进入控制台,在“产品服务”中选择“百度语音”后,点击“创建应用”,然后根据提示一步步填写信息,直至创建完成。而后,就可以看到所创建应用到账号信息,包括AppID、API Key、Secret Key,后面会用到。
集成tts sdk
如何集成tts sdk,详细信息可以查看官方文档。本文会进行简要介绍,代码可参考bdtts包下的BaiduTtsActivity.java。
集成jar包、so及其它资源。其中assets包下为离线语音包。
集成tts sdk资源包.png
初始化引擎。
创建引擎实例、设置listener、设置所需要的参数(比如发音人、音量、音速、音调、音频流类型、是否压缩)。
private void initEngine() { // 1. 获取实例 mSpeechSynthesizer = SpeechSynthesizer.getInstance(); mSpeechSynthesizer.setContext(this); // 2. 设置listener mSpeechSynthesizer.setSpeechSynthesizerListener(speechSynthesizerListener); // 3. 设置appId,appKey.secretKey mSpeechSynthesizer.setAppId(appId); mSpeechSynthesizer.setApiKey(appKey, secretKey); // 4. 支持离线的话,需要设置离线模型 if (ttsMode.equals(TtsMode.MIX)) { // 检查离线授权文件是否下载成功,离线授权文件联网时SDK自动下载管理,有效期3年,3年后的最后一个月自动更新。 if (!checkAuth()) { return; } // 文本模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读 mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mApp.getTextModeFile()); // 声学模型文件路径 (离线引擎使用), 注意TEXT_FILENAME必须存在并且可读 mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mApp.getSpeechModeFile()); } // 5. 以下setParam 参数选填。不填写则默认值生效 // 设置在线发声音人: 0 普通女声(默认) 1 普通男声 2 特别男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫> mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "4"); // 其它参数设置这里略去 // 6. 初始化 mSpeechSynthesizer.initTts(ttsMode); }
语音播放或仅语言合成
语音播放调用接口mSpeechSynthesizer.speak(text)。
mSpeechSynthesizer.speak("青青子衿,悠悠我心。");
仅语音合成调用接口mSpeechSynthesizer.synthesize(text),在回调中获取语音流。
mSpeechSynthesizer.synthesize("青青子衿,悠悠我心。");
语音流合成的开始、过程、结束;语音播放的开始、过程、结束;出现错误均在listener中回调。
SpeechSynthesizerListener speechSynthesizerListener = new SpeechSynthesizerListener() { @Override public void onSynthesizeStart(String s) { //合成开始 if (isNeedSaveTTS) { String filename = TimeUtil.getTimeStampLocal() + ".pcm"; ttsFile = new File(destDir, filename); try { if (ttsFile.exists()) { ttsFile.delete(); } ttsFile.createNewFile(); FileOutputStream ttsFileOutputStream = new FileOutputStream(ttsFile); ttsFileBufferedOutputStream = new BufferedOutputStream(ttsFileOutputStream); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onSynthesizeDataArrived(String s, byte[] data, int i) { // 合成过程中的数据回调接口 if(isNeedSaveTTS){ try { ttsFileBufferedOutputStream.write(data); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onSynthesizeFinish(String s) { // 合成结束 if (isNeedSaveTTS) close(); } @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) { // 合成和播放过程中出错时的回调 if (isNeedSaveTTS) close(); } };
释放引擎。
调用stop、release接口。
@Override protected void onDestroy() { if (mSpeechSynthesizer != null) { mSpeechSynthesizer.stop(); mSpeechSynthesizer.release(); mSpeechSynthesizer = null; } super.onDestroy(); }
二、调用Android speech包下的接口
先看一下speech包。
speech包.png
Android从1.6起就提供了speech包,但是,speech包仅仅提供tts接口,而实现tts的能力,则需要安装第三方tts引擎。Android原生系统直接集成了pico引擎(仅支持英文合成),国内厂商也会集成一些tts引擎,比如我的小米手机集成了“小爱语音引擎”(早期的miui版本集成的是基于百度tts sdk开发的“语音合成引擎”)。具体可以到手机的 设置—语言和输入法—文字转语言(TTS)输出 下看一下支持哪些引擎,及默认引擎是哪一个。
自己从哪可以获取到tts引擎呢?我发现很难在应用商店或者官网上搜到,我自己收集到了几个引擎,上传到了百度云中。链接:https://pan.baidu.com/s/1E2OSb_jGG5dwm9YIe8_0QQ 密码:t8d8。
介绍了背景信息,接下来就开始看一下如何利用speech包来进行语言合成。代码可参考speech包下的TestSpeechActivity.java。
创建TextToSpeech对象,创建时传入OnInitListener监听器监听创建是否成功。
textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { // status : TextToSpeech.SUCCESS=0 , TextToSpeech.ERROR=-1 Log.i(TAG, "TextToSpeech onInit status = " + status); } });
备注:创建TextToSpeech对象也可以调用包含指定包名的引擎,不指定的话就用默认引擎。
设置TextToSpeech所使用语言、国家选项,通过返回值判断TTS是否支持该语言、国家选项。
int result = textToSpeech.setLanguage(Locale.CHINESE);
如果tts引擎不支持该语言,该接口就会返回TextToSpeech.LANG_MISSING_DATA或者result == TextToSpeech.LANG_NOT_SUPPORTED。
调用speak或synthesizeToFile方法,前者是语音播报,后者是仅语音合成。
mTTS.speak("青青子衿,悠悠我心", TextToSpeech.QUEUE_FLUSH, null,id);mTTS.synthesizeToFile("青青子衿,悠悠我心", null, file, id);
其结果会走setOnUtteranceProgressListener回调。
textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) { } @Override public void onDone(String utteranceId) { } @Override public void onError(String utteranceId) { }});
关闭TTS,回收资源。
@Override protected void onDestroy() { // 4.关闭TTS,回收资源 if (textToSpeech != null) { textToSpeech.stop(); textToSpeech.shutdown(); } super.onDestroy(); }
三、二者比较
第一种是需要集成tts SDK,适合于为单个应用提供tts能力。
第二种是需要集成tts引擎,适合于集成到手机或其它Android设备中,这样该设备的所有应用均可以直接利用Android系统tts接口使用tts能力。
下一篇文章会介绍,如何开发tts引擎,敬请期待。