Android TTS系列一——如何讓app具備tts能力
原始碼地址: 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引擎,敬請期待。