源碼地址:https://github.com/yellowgreatsun/MXTtsEngine
自2016年阿爾法狗大勝李世石以來祸憋,AI迅速普及骤肛,而其中語言AI就是落地化比較徹底的應(yīng)用冬耿,也自然成了許多應(yīng)用、智能硬件的標(biāo)配功能熄阻。本人目前主要從事Android開發(fā)膘掰,2018年曾對(duì)tts做了比較多的研究,這幾天拧抖,就想把之前的筆記再好好整理一番煤搜,寫成博客分享給更多的開發(fā)者。
Android TTS系列
- 如何讓app具備tts能力唧席?
- 如何開發(fā)一款系統(tǒng)級(jí)tts引擎擦盾?
- Android speech包源碼剖析
本篇就先從第一項(xiàng)說起。
對(duì)應(yīng)著MXTtsEngine的bdtts包和speech包淌哟。
讓app具備tts能力迹卢,有兩種方式,一種是直接集成第三方tts sdk徒仓,比如科大訊飛腐碱、百度、思必馳蓬衡、小愛等等喻杈,調(diào)用sdk的接口即可。第二種是直接調(diào)用Android speech包下的tts接口狰晚,但其前提是Android設(shè)備已經(jīng)安裝了tts引擎筒饰。
一、直接集成第三方tts sdk
這里壁晒,以集成百度tts sdk為例瓷们。其它幾種sdk,集成方式類似秒咐。
申請(qǐng)賬號(hào)信息
- 進(jìn)入百度AI開發(fā)平臺(tái),從“產(chǎn)品服務(wù)”中選擇“語言合成”谬晕,可以查看官方介紹、在線體驗(yàn)合成效果携取。
- 進(jìn)入控制臺(tái)攒钳,在“產(chǎn)品服務(wù)”中選擇“百度語音”后,點(diǎn)擊“創(chuàng)建應(yīng)用”雷滋,然后根據(jù)提示一步步填寫信息不撑,直至創(chuàng)建完成文兢。而后,就可以看到所創(chuàng)建應(yīng)用到賬號(hào)信息焕檬,包括AppID姆坚、API Key、Secret Key实愚,后面會(huì)用到兼呵。
集成tts sdk
如何集成tts sdk,詳細(xì)信息可以查看官方文檔腊敲。本文會(huì)進(jìn)行簡(jiǎn)要介紹击喂,代碼可參考bdtts包下的BaiduTtsActivity.java。
-
集成jar包碰辅、so及其它資源茫负。其中assets包下為離線語音包。
- 初始化引擎乎赴。
創(chuàng)建引擎實(shí)例、設(shè)置listener潮尝、設(shè)置所需要的參數(shù)(比如發(fā)音人榕吼、音量、音速勉失、音調(diào)羹蚣、音頻流類型、是否壓縮)乱凿。
private void initEngine() {
// 1. 獲取實(shí)例
mSpeechSynthesizer = SpeechSynthesizer.getInstance();
mSpeechSynthesizer.setContext(this);
// 2. 設(shè)置listener
mSpeechSynthesizer.setSpeechSynthesizerListener(speechSynthesizerListener);
// 3. 設(shè)置appId顽素,appKey.secretKey
mSpeechSynthesizer.setAppId(appId);
mSpeechSynthesizer.setApiKey(appKey, secretKey);
// 4. 支持離線的話,需要設(shè)置離線模型
if (ttsMode.equals(TtsMode.MIX)) {
// 檢查離線授權(quán)文件是否下載成功徒蟆,離線授權(quán)文件聯(lián)網(wǎng)時(shí)SDK自動(dòng)下載管理胁出,有效期3年,3年后的最后一個(gè)月自動(dòng)更新段审。
if (!checkAuth()) {
return;
}
// 文本模型文件路徑 (離線引擎使用)全蝶, 注意TEXT_FILENAME必須存在并且可讀
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mApp.getTextModeFile());
// 聲學(xué)模型文件路徑 (離線引擎使用), 注意TEXT_FILENAME必須存在并且可讀
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mApp.getSpeechModeFile());
}
// 5. 以下setParam 參數(shù)選填寺枉。不填寫則默認(rèn)值生效
// 設(shè)置在線發(fā)聲音人: 0 普通女聲(默認(rèn)) 1 普通男聲 2 特別男聲 3 情感男聲<度逍遙> 4 情感兒童聲<度丫丫>
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "4");
// 其它參數(shù)設(shè)置這里略去
// 6. 初始化
mSpeechSynthesizer.initTts(ttsMode);
}
- 語音播放或僅語言合成
語音播放調(diào)用接口mSpeechSynthesizer.speak(text)抑淫。
mSpeechSynthesizer.speak("青青子衿,悠悠我心姥闪。");
僅語音合成調(diào)用接口mSpeechSynthesizer.synthesize(text)始苇,在回調(diào)中獲取語音流。
mSpeechSynthesizer.synthesize("青青子衿筐喳,悠悠我心催式。");
語音流合成的開始函喉、過程、結(jié)束蓄氧;語音播放的開始函似、過程、結(jié)束喉童;出現(xiàn)錯(cuò)誤均在listener中回調(diào)撇寞。
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) {
// 合成過程中的數(shù)據(jù)回調(diào)接口
if(isNeedSaveTTS){
try {
ttsFileBufferedOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onSynthesizeFinish(String s) {
// 合成結(jié)束
if (isNeedSaveTTS)
close();
}
@Override
public void onSpeechStart(String s) {
// 播放開始
}
@Override
public void onSpeechProgressChanged(String s, int i) {
// 播放過程中的回調(diào)
}
@Override
public void onSpeechFinish(String s) {
// 播放結(jié)束
}
@Override
public void onError(String s, SpeechError speechError) {
// 合成和播放過程中出錯(cuò)時(shí)的回調(diào)
if (isNeedSaveTTS)
close();
}
};
- 釋放引擎。
調(diào)用stop堂氯、release接口蔑担。
@Override
protected void onDestroy() {
if (mSpeechSynthesizer != null) {
mSpeechSynthesizer.stop();
mSpeechSynthesizer.release();
mSpeechSynthesizer = null;
}
super.onDestroy();
}
二、調(diào)用Android speech包下的接口
先看一下speech包咽白。
Android從1.6起就提供了speech包啤握,但是,speech包僅僅提供tts接口晶框,而實(shí)現(xiàn)tts的能力排抬,則需要安裝第三方tts引擎。Android原生系統(tǒng)直接集成了pico引擎(僅支持英文合成)授段,國(guó)內(nèi)廠商也會(huì)集成一些tts引擎蹲蒲,比如我的小米手機(jī)集成了“小愛語音引擎”(早期的miui版本集成的是基于百度tts sdk開發(fā)的“語音合成引擎”)。具體可以到手機(jī)的 設(shè)置—語言和輸入法—文字轉(zhuǎn)語言(TTS)輸出 下看一下支持哪些引擎侵贵,及默認(rèn)引擎是哪一個(gè)届搁。
自己從哪可以獲取到tts引擎呢?我發(fā)現(xiàn)很難在應(yīng)用商店或者官網(wǎng)上搜到窍育,我自己收集到了幾個(gè)引擎卡睦,上傳到了百度云中。鏈接:https://pan.baidu.com/s/1E2OSb_jGG5dwm9YIe8_0QQ 密碼:t8d8漱抓。
介紹了背景信息表锻,接下來就開始看一下如何利用speech包來進(jìn)行語言合成。代碼可參考speech包下的TestSpeechActivity.java乞娄。
- 創(chuàng)建TextToSpeech對(duì)象浩嫌,創(chuàng)建時(shí)傳入OnInitListener監(jiān)聽器監(jiān)聽創(chuàng)建是否成功。
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);
}
});
備注:創(chuàng)建TextToSpeech對(duì)象也可以調(diào)用包含指定包名的引擎补胚,不指定的話就用默認(rèn)引擎码耐。
- 設(shè)置TextToSpeech所使用語言、國(guó)家選項(xiàng)溶其,通過返回值判斷TTS是否支持該語言骚腥、國(guó)家選項(xiàng)。
int result = textToSpeech.setLanguage(Locale.CHINESE);
如果tts引擎不支持該語言瓶逃,該接口就會(huì)返回TextToSpeech.LANG_MISSING_DATA或者result == TextToSpeech.LANG_NOT_SUPPORTED束铭。
- 調(diào)用speak或synthesizeToFile方法廓块,前者是語音播報(bào),后者是僅語音合成契沫。
mTTS.speak("青青子衿带猴,悠悠我心", TextToSpeech.QUEUE_FLUSH, null,id);
mTTS.synthesizeToFile("青青子衿,悠悠我心", null, file, id);
其結(jié)果會(huì)走setOnUtteranceProgressListener回調(diào)懈万。
textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onStart(String utteranceId) {
}
@Override
public void onDone(String utteranceId) {
}
@Override
public void onError(String utteranceId) {
}
});
- 關(guān)閉TTS拴清,回收資源。
@Override
protected void onDestroy() {
// 4.關(guān)閉TTS会通,回收資源
if (textToSpeech != null) {
textToSpeech.stop();
textToSpeech.shutdown();
}
super.onDestroy();
}
三口予、二者比較
第一種是需要集成tts SDK,適合于為單個(gè)應(yīng)用提供tts能力涕侈。
第二種是需要集成tts引擎沪停,適合于集成到手機(jī)或其它Android設(shè)備中,這樣該設(shè)備的所有應(yīng)用均可以直接利用Android系統(tǒng)tts接口使用tts能力裳涛。
下一篇文章會(huì)介紹木张,如何開發(fā)tts引擎,敬請(qǐng)期待端三。