Android TTS系列一——如何讓app具備tts能力

源碼地址: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)信息
  1. 進(jìn)入百度AI開發(fā)平臺(tái),從“產(chǎn)品服務(wù)”中選擇“語言合成”谬晕,可以查看官方介紹、在線體驗(yàn)合成效果携取。
  2. 進(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。

  1. 集成jar包碰辅、so及其它資源茫负。其中assets包下為離線語音包。


    集成tts sdk資源包.png
  2. 初始化引擎乎赴。
    創(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);
    }
  1. 語音播放或僅語言合成
    語音播放調(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();
        }
    };
  1. 釋放引擎。
    調(diào)用stop堂氯、release接口蔑担。
   @Override
    protected void onDestroy() {
        if (mSpeechSynthesizer != null) {
            mSpeechSynthesizer.stop();
            mSpeechSynthesizer.release();
            mSpeechSynthesizer = null;
        }
        super.onDestroy();
    }

二、調(diào)用Android speech包下的接口

先看一下speech包咽白。

speech包.png

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乞娄。

  1. 創(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)引擎码耐。

  1. 設(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束铭。

  1. 調(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) {
      }
});
  1. 關(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)期待端三。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窟哺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子技肩,更是在濱河造成了極大的恐慌,老刑警劉巖浮声,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虚婿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡泳挥,警方通過查閱死者的電腦和手機(jī)然痊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屉符,“玉大人剧浸,你說我怎么就攤上這事〈V樱” “怎么了唆香?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吨艇。 經(jīng)常有香客問我躬它,道長(zhǎng),這世上最難降的妖魔是什么东涡? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任冯吓,我火速辦了婚禮倘待,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘组贺。我一直安慰自己凸舵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布失尖。 她就那樣靜靜地躺著啊奄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雹仿。 梳的紋絲不亂的頭發(fā)上增热,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音胧辽,去河邊找鬼峻仇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛邑商,可吹牛的內(nèi)容都是我干的摄咆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼人断,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吭从!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恶迈,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤涩金,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后暇仲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步做,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年奈附,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了全度。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡斥滤,死狀恐怖将鸵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佑颇,我是刑警寧澤顶掉,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站挑胸,受9級(jí)特大地震影響一喘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一凸克、第九天 我趴在偏房一處隱蔽的房頂上張望议蟆。 院中可真熱鬧,春花似錦萎战、人聲如沸咐容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戳粒。三九已至,卻和暖如春虫啥,著一層夾襖步出監(jiān)牢的瞬間蔚约,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工涂籽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苹祟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓评雌,卻偏偏與公主長(zhǎng)得像树枫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子景东,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容