本網頁以打造無障礙閱讀為目標,可以用任何瀏覽器來觀看本網頁
我們可以發現雖然在國外有像Festival等的open source語音合成軟體可供研究,但並未針對中文作處理,而國內的研究雖然先進但尚未能找到開放原始碼的版本,因此希望能善用Festival現有的成果,不要重新發明 輪子,期許創造一個雖然距離國內許多先進研究成果差一段距離,但卻是一個屬於中國人專屬的語音合成自由軟體,同時藉由此計劃拋磚引玉的效果快速的集聚OSS的社群力量,將 Festival修改後使其能夠做國語語音合成的基石。
#rpm -Uvh fest*
| 主要系統套件 | ||
| * | speech_tools-1.2.3-release.tar.gz | 語音工具的函式庫與源碼檔 |
| * | festival_1.4.3-release.tar.gz | festival的源碼檔 |
| * | festvox-2.0-release.tar.gz | festvox的源碼檔 |
| 辭典套件 | ||
| * | festlex_POSLEX.tar.gz | 語音零件的辭典模式 |
| * | festlex_CMU.tar.gz | 美式英語辭典(需結合美式聲音檔) |
| festlex_OALD.tar.gz | 英式英語辭典(需結合英式聲音檔) | |
| 主要聲音套件 | ||
| * | festvox_kallpc16k.tar.gz | 美式男聲 `kal' 16kHz版本 |
| festvox_kallpc8k.tar.gz | 美式男聲 `kal' 8kHz版本 | |
| festvox_kedlpc16k.tar.gz | 美式男聲 `ked' 16kHz版本 | |
| festvox_kedlpc8k.tar.gz | 美式男聲 `ked' 8kHz版本 | |
| festvox_rablpc16k.tar.gz | 英式男聲 `rab' 16kHz版本 | |
| festvox_rablpc8k.tar.gz | 英式男聲 `rab' 8kHz版本 | |
| 其他聲音套件 | ||
| festvox_us1.tar.gz | 其他不完整的美式聲音檔1 | |
| festvox_us2.tar.gz | 其他不完整的美式聲音檔2 | |
| festvox_us3.tar.gz | 其他不完整的美式聲音檔3 | |
| festvox_don.tar.gz | 其他不完整的美式聲音檔 | |
| festvox_en1.tar.gz | 其他不完整的美式聲音檔 | |
| festvox_ellpc11k.tar.gz | 西班牙聲音檔 | |
| 標註*者為最小安裝所需套件 | ||
假設使用者家目錄為peter, 請將上列下載的所有檔案複製到使用者家目錄中的 projects 子目錄中,以下步驟也可整個寫成一支shell script方便執行。
| #festival | //呼叫交談模式 |
| #echo "hello world" | festival --tts | //echo 字串給 festival發音(2個dash喔) |
| #festival --tts news.txt | //命令模式下直接唸出指定的檔名內容(2個dash喔) |
| #festival -b filename.scm | //命令模式下執行指定的批次檔 |
| #/usr/share/festival/examples/saytime |
//範例:說出現在時間(以rpm安裝時的目錄) |
festival 因為連結了readline函式庫,因此按鍵方式與shell下與emacs相同
| festival>(SayText "hello") | //唸出指定的文字 |
| festival>(SayPhone "hh eh l ow") | //唸出指定的音素(音素的種類需視所採用的音素集而定,目前系統有 US phoneset 與 UK phoneset ) 二種 |
| festival>(voice_kal_diphone) 或 (voice_ked_diphone) |
//更換語音資料為kal 或 ked 1.95版的女聲: (voice_cmu_us_slt_arctic_hts) |
| festival>(PhoneSet.list) |
//列出目前所使用的音素集 (radio) |
| festival>(voice_rab_diphone) |
//更換語音資料為rab |
| festival>(PhoneSet.list) |
//列出目前所使用的音素集 (radio mrpa) |
| festival>(PhoneSet.description '(silences phones)) | //列出所有可用的音素 |
| festival>(tts "filename") | //可唸出所指定的檔名內容 |
| festival>(Parameter.set 'Duration_Stretch 2.0) | //將發音的時間拉長(唸慢一點) |
| festival>help | //Help |
| festival>(quit) | //離開交談模式,或按Ctrl+D |
| festival>(lex.lookup 'hello) | //查出hello這個字的音素為何 |
| festival>(intro) | //發音測試:2句話簡介Festival |
| festival>(festival_warranty) | //顯示Festival的授權範圍 |
| festival>libdir | //library存放的目錄 |
| festival>(pwd) | //顯示目前目錄 |
| festival>(pow 5 3) | //5的3次方,顯示125 |
| festival>(load 'test.scm) | //載入scheme的script檔 |
本文主要在探討語音合成的資料結構,在此使用了語音合成的自由軟體巨擘Festival(http://www.festvox.org/packed/festival/1.4.3/ ),來剖析,並以Lisp家族中的Scheme來作為研究的工具。
Festival這套語音合成系統是由蘇格蘭愛丁堡大學(University of Edinburgh)因為愛丁堡藝術節而命名 ,其核心以C++撰寫而成,但因為配合使用了SIOD函式庫,所以可以使用Scheme來控制與使用。 目前的穩定版本為1.43版,最新版本為2004/7/14的 1.95版。
卡內基美濃大學 配合 Festival 發展了一個子計畫:Festvox(http://festvox.org/),目的在提供更多樣化的語音與更好的語音品質與手冊文件。
在安裝Festival完成之後,我們可以簡單的試一下要如何發聲,在提示符號下輸入 (SayText "This is an example")
festival> (SayText "This is an example")
#<Utterance 0x404054e8>
回應表示festival將使用者鍵入的文字轉換為一個名為Utterance的資料結構中,並儲存在記憶體0x404054e8位址上 ,因為後續要針對這個語音的資料結構進行拆解,因此了定義一個「sen」 的變數指向它,以便接下來可以對這個Utterance 結構進行剖析
festival> (set! sen (SayText "This is an example"))
#<Utterance 0x40554b98>
從語音學(phonetics)的層次上,至少可以將一句話(Utterance)從不同的觀點來解析,如詞組(phrase)、語詞(Word)、音節(Syllable)、音節(Segment)等,這些不同的觀點在此我們可以稱為Relations。 例如:從Word這個Relation來看,它是由 This<->is<->an<->example所串連而成,而這個「This」、「is」、「an」、「example」在此稱之為 item,而每一個item有一組屬性,用來描述本身的特徵,在此稱之為Features。
![]() |
![]() |
Utterance relations的組成 |
Word relation的組成 |
|---|
如果不好理解的話,我們另外舉一個例子,有一個學生Peter (like utterance) ,他的社會關係(relations)可以有 朋友關係 (like word relation) 、學校關係(like syllable relation)、家庭關係(like segment relation),而以朋友關係為例,Peter認識Cathy、Cathy認識Tom、Tom認識Emma,這些人(like item)本身各有特色(like features),如性別,年齡,身高,血型等。
![]() |
![]() |
Peter同學社會關係的組成 |
朋友關係的組成 |
|---|
而在Festival中對於語音(speech)或語言的(linguistic)的結構上也因而有三個主要的形式
接下來我們來瞭解Word relation的組成,在此我們要利用上面定義的sen變數,先看看在Word relation有幾個 item
festival> (utt.relation.leafs sen 'Word)
(#<item 0x87a1680>
#<item 0x87a2170>
#<item 0x87a2418>
#<item 0x87a26c8>)
festival> (utt.relation.first sen 'Word)
#<item 0x87a1680>
festival> (set! firstword (utt.relation.first sen 'Word))
既然我們有了一個名為firstword的item,我們當然要看看它有哪些features
festival> (item.features firstword)
((id "_5")
(name "This")
(pos_index 2)
(pos_index_score 0)
(pos "dt")
(phr_pos "dt")
(pbreak_index 1)
(pbreak_index_score 0)
(pbreak "NB"))
我們也可以指定只看單一的feature,例如:要看firstword這個item名為「name」的feature
festival> (item.feat firstword "name")
"This"
除了直接指定item的順序之外,我們還可以利用目前的item位置,往後取得下一個item或往前取得前一個item
festival> (set! secondword (item.next firstword))
#<item 0x87a2170>
festival> (item.feat secondword "name")
"is"
festival> (item.feat secondword "p.name")
"This"
接下來我們來瞭解 SylStructure relation的組成,在此我們要利用上面定義的sen變數,先看看在 SylStructure relation有幾個 item
festival> (utt.relation.leafs sen 'SylStructure)
(#<item 0x87a30b8>
#<item 0x87c3060>
#<item 0x87a57b8>
#<item 0x87a59e8>
#<item 0x87c2cb8>
#<item 0x87c2d48>
#<item 0x87a42d8>
#<item 0x87a4758>
#<item 0x87a48f8>
#<item 0x87a4c78>
#<item 0x87a4e18>
#<item 0x87a4fb8>
#<item 0x87a5158>
#<item 0x87a54d8>
#<item 0x87be800>)
festival> (utt.relation.first sen 'SylStructure)
#<item 0x87a1680>
| (set! sen (Utterance Text "This is an example")) | 定義 sen 變數指向Utterance物件 |
| (utt.play sen) | 會出現Feature Wave Not defined的錯誤 |
| (utt.relation.first sen 'Word) | 會出現Feature Word Not defined的錯誤 |
| (utt.synth sen) | 必須做完此波形合成的動作才不會有錯誤 |
| (set! sen (SayText "This is an example")) | 定義 sen 變數指向完整的Utterance物件結構 (可省略合成的步驟) |
| (set! firstword (utt.relation.first sen 'Word)) | 定義 firstword 變數指向Word relation 的第一個 item |
| (item.features firstword) | 觀察 firstword這個 item的所有feature |
| (item.feat firstword "name") (item.name firstword) |
觀察 firstword這個 item的其中叫「name」的feature |
| (item.feat firstword "n.name") | 觀察 firstword這個 item的下一個 item的「name」feature |
| (set! secondword (item.next firstword)) | 定義 secondword 變數指向 firstword 的下一個 item |
| (item.feat secondword "name") | 觀察 secondword這個 item的其中叫「name」的feature |
| (item.feat secondword "p.name") | 觀察 secondword這個 item的上一個 item的「name」feature |
| (utt.features sen 'Word '(id name pos)) | 觀察sen 以Word relation時的 features(類似mapleaf自訂函數) |
| (set! firstsyllable (utt.relation.first sen 'Syllable)) | 定義 firstsyllable 變數指向Syllable relation 的第一個 item |
| (item.features firstsyllable) | 觀察firstsyllable這個 item的所有feature |
| (item.feat firstsyllable "name") (item.name firstsyllable) |
觀察firstsyllable這個 item的其中叫「name」的feature |
| (item.feat firstsyllable"n.name") | 觀察 firstsyllable這個 item的下一個 item的「name」feature |
| (set! secondsyllable (item.next firstsyllable)) | 定義 secondsyllable 變數指向 firstword 的下一個 item |
| (item.feat secondsyllable"id") | 觀察 secondsyllable這個 item的其中叫「id」的feature |
| (item.feat secondsyllable"p.id") | 觀察 secondsyllable這個 item的上一個 item的「id」feature |
| (utt.features sen 'Syllable '(id name stress)) | 觀察sen 以Syllable relation時的 features |
| (set! firstsegment (utt.relation.first sen 'Segment)) | 定義 firstsegment 變數指向Segment relation 的第一個 item |
| (item.features firstsegment) | 觀察firstsegment這個 item的所有feature |
| (item.feat firstsegment"name") (item.name firstsegment) |
觀察firssegment這個 item的其中叫「name」的feature |
| (item.feat firstsegment "n.name") | 觀察 firstsegment這個 item的下一個 item的「name」feature |
| (set! secondsegment (item.next firstsegment)) | 定義 secondsegment 變數指向 firstsegment 的下一個 item |
| (item.feat secondsegment "id") | 觀察 secondsegment這個 item的其中叫「id」的feature |
| (item.feat secondsegment "p.id") | 觀察 secondsyllable這個 item的上一個 item的「id」feature |
| (utt.features sen 'Segment '(id name dur_factor end)) | 觀察sen 以Syllable relation時的 features |
| (set! firstphrase (utt.relation.first sen 'Phrase)) | 定義 firstphrase 變數指向Phrase relation 的第一個 item |
| (item.features firstphrase) | 觀察firstphrase這個 item的所有feature |
| (set! secondphrase (item.next firstphrase)) | 定義secondphrase 變數指向 firstphrase 的下一個 item (傳回nil) |
| (item.daughters firstphrase) | 觀察 firstphrase 這個 item有哪幾個女兒 (有4個女兒) |
| (set! fpl (item.daughter1 firstphrase)) | 定義 fpl 變數指向 firstword這個 item的女兒 |
| (item.features fpl) | 觀察fpl的feature,得知是Word relation |
| (item.feat fp1 "R:SylStructure.daughter1.stress") | 改由SylStructure的觀連,察看女兒的 stress feature |
| (set! firstword (utt.relation.first sen 'SylStructure)) | 定義 firstword 變數指向 SylStructure relation 的第一個 item |
| (item.features firstword) | 觀察firstword這個 item的所有feature |
| (item.feat firstword "name") (item.name firstword) |
觀察 firstword這個 item的其中叫「name」的feature |
| (item.feat firstword"n.name") | 觀察 firstword這個 item的下一個 item的「name」feature |
| (item.daughters firstword) | 觀察 firstword這個 item有哪幾個女兒 (只有1個女兒) |
| (set! firstsyl (item.daughter1 firstword)) | 定義 firstsyl 變數指向 firstword這個 item的女兒 |
| (item.features firstsyl) | 觀察 firstsyl的feature,得知是Syllable relation |
| (item.name (item.parent firstsyl)) | 觀察 firstsyl這個 item的父親,得知是Word relation:「This」 |
| (item.daughters firstsyl) | 觀察 firstsyl這個 item有幾個女兒 (有3個女兒) |
| (set! secondseg (item.daughter2 firstsyl)) | 定義 secondseg 變數指向 firstsyl這個 item的第2個女兒 |
| (item.features secondseg) | 觀察 secondseg 的feature,得知是Segment relation |
| (utt.features sen 'SylStructure '(id name) | 觀察sen 在 SylStructure relation時的 features |
| 關係 | 資料結構 | 特徵 | ||
|---|---|---|---|---|
| Word | list |
|
||
| Syllable | list |
|
||
| Segment | list |
|
||
| Phrase | trees roots are Phrase |
|
|
|
| SylStructure | trees roots are Words |
|
|
|
(define (valot utt rel)
to_end (utt.relation.first utt rel)
)
(define (to_end node)
(if node
(cons (sub_tree node) (to_end (item.next node)))
nil
)
)
(define (sub_tree node)
(if (item.daughters node)
(mapcar sub_tree (item.daughters node))
node
)
)
(define (mapleaf fun 1st)
(cond
((null? 1st) nil)
((atom 1st) (fun 1st))
(t (cons (mapleaf fun (car 1st)) (mapleaf fun (cdr 1st))))
)
)
| (valot sen 'Word) | 以自訂函數觀察其4個 item是以鍊結串列結構 |
| (mapleaf item.features (valot sen 'Word)) | 以自訂函數觀察4個 item各有何種 features (有9個) |
| (valot sen 'Syllable) | 以自訂函數觀察其6個 item也是以鍊結串列結構 |
| (mapleaf item.features (valot sen 'Syllable)) | 以自訂函數觀察6個 item各有何種 features (有3個: id,name,stress) |
| (valot sen 'Segment) | 以自訂函數觀察其17個 item也是以鍊結串列結構 |
| (mapleaf item.features (valot sen 'Segment)) | 以自訂函數觀察17個 item各有何種 features (有5個: id,name,dur_factor, end, source_end) |
| (valot sen 'SylStructure) | 以自訂函數觀察 |
| (mapleaf item.features (valot sen 'SylStructure)) | 以自訂函數觀察 |
首先想到的是利用羅馬拼音以 SayText 來試試看,發出 "我的名字叫朱孝國" |
festival>(SayText "wo di ming zi giao ju hsiao koa" ) |
| 感覺還不錯,但是要這樣試過所有的音也太累了,況且也無法解決tone的問題,因此改由從festival的架構上來觀察,首先拿"hello"這個字來看看在word的層次的作法 | festival>(set ! sen (Utterance Words (hello))) |
以 grep 搜尋 "SayText" 發現在 lib\synthesis.scm中有定義 ,另外定義了"SynthText"與"SayPhones" 可說明,一個Text 的Utterance必須先經由utt.synth然後才可以utt.play播出聲音來 |
(define (SayText text) (utt.play (utt.synth (eval (list 'Utterance 'Text text))))) (define (SynthText text) (utt.synth (eval (list 'Utterance 'Text text)))) (define (SayPhones phones) (utt.play (utt.synth (eval (list 'Utterance 'Phones phones))))) |
| festival中的 scheme script 大都放在 lib 目錄下的 synthesis.scm 與 festival.scm |
|
很明顯的要從utt.synth與utt.play這兩個函數開始追蹤,首先追蹤utt.play 再來追蹤utt.wave----------------------------------------> |
(define (utt.play utt) (wave.play (utt.wave utt)) utt) (define (utt.wave utt) (item.feat (utt.relation.first utt "Wave") "wave")) |
以上2個函數沒什麼可下手的地方,再來追蹤utt.synth--------------------------------------------------> 在倒數第4行插入 (print body) ,存檔後重新載入festival ,重新設定變數,重新作(utt.synth sen) (CLOSURE (utt) (begin (Initialize utt) (POS utt)
(Phrasify utt) (Word utt) (Pauses utt) (Intonation utt) (PostLex utt) (Duration utt) (Int_Targets utt) (Wave_Synth utt) )) |
(define (utt.synth utt) |
試著以自訂函數 step10 來取代上述的無名函數 (define (step10 utt) (begin (Initialize utt) (POS utt) (Phrasify utt) (Word utt) (Pauses utt) (Intonation utt) (PostLex utt) (Duration utt) (Int_Targets utt) (Wave_Synth utt) ))
再依照上述定義 step10方式,根據10個步驟再定義 step9 ~ step1 總共有10個函數,然後針對 sen 作用後再觀察其資料結構,整理如下:
(utt.relation.item sen 'Word)
亦可利用下列指令觀察指定資料結構的詳細樹狀內容:
(utt.relation_tree sen 'Word)
在執行step9之後來利用 utt.relationnames 觀看已產生的結構 ,發現有8種結構,再以(utt.play sen)測試是否可以發聲,發現無法發出聲音,證明少掉最後一個步驟就無法發聲了 在執行step10之後來利用 utt.relationnames 觀看已產生的結構 ,發現有14種結構(增加了6個),再以(utt.play sen)測試是否可以發聲,發現果然可以發聲 |
festival>(step1 sen) |
|
festival>(utt.relation.delete sen 'Word) |
改在做 Wave_Synth步驟前, 嘗試以下的方法
因此可說明 Wave_Synth 必須參考的資料結構是 Segment
|
festival>(set ! wd (Utterance Words (hello))) |
重新定義變數 wd ,進行9個步驟的函數處理 除了Segment之外,嘗試是否可以刪除其後的資料結構
因此可說明 Wave_Synth 還必須參考的資料結構是 Target |
festival>(set ! wd (Utterance Words (hello))) |
觀察以上的結果,覺得可能可以不要參考到Segment結構,因此想針對Segment結構做刪減,看看是否能再簡化下去,希望能達到「只要自訂 Target 資料結構,就可以交給 Wave_Synth 來合成」 刪除所有的 7 個 item 後,雖然能正確執行第10個步驟「Wave_Synth」,卻無法正確發聲,若改成只刪除4個 item後,再執行 Wave_Synth,會發覺發出的聲音被切一半,由此證明合成聲音時的確會參考到 Segment 原因是 Target 的結構與 Segment 的結構交叉參照了,若刪除所有Segment結構,則Target結構也不見了 真的要刪,就只能刪除Segment的 first 與 last 的item (pau),而看起來甚至連m與b都可以刪,但實際還是會影響 |
festival>(utt.relation_tree wd 'Segment) |
希望能手動產生Segment與Target兩個資料結構,可以有2個作法: (set ! sen '((id "_10") (name "pau") (dur_factor 0) (end 0.22) ))
2.修改舊有的結構 (item.insert (item.next utt.relation.first sen 'Segment) 'before)
3.新增Segment與Target二個結構,如右 |
festival>(set! w1 (Utterance Words ())) |
尚未完成...... |
|
嘗試以 segment 的層次來切入,首先看看在 lexicon 中的 diphone 為何 定義好Utterance,在合成時就發生錯誤了 |
festival>(lex.lookup 'hello) |
lexicon中的對應是否可以修改呢? 果然發出reagan的聲音喔 |
festival>(lex.lookup 'hello) |
| 要利用festival發出中文相當的受限,原因在於我們必須利用festival現有的 language 與 lexicon 配合才能組合出相近的聲音,但目前提供的 lexicon有US , UK , spanish 等三種phoneset ,在載入festival之後,我們先來看看預設的lexicon並注意聆聽是美聲還是英聲,並在改變language之後,再看看其變化 | festival>(PhoneSet.list) |
以上的範例,對於非英語系國家的我們而言是有點難以分別美式英語與英式英語的腔調的,但的確看到了PhoneSet的改變,我們再來進一步看看這些PhoneSet的詳細內容 英式英文:voice_rab_diphone 西班牙文:voice_el_diphone 奇怪的是原以為選擇對應的語言,其PhoneSet 應該也會跟著變,但實測的結果並不一定耶? |
festival>(voice_rab_diphone) |
|
festival>(set ! sen (Utterance Words (hello))) festival>(utt.synth sen) (Word Phrase Syllable Segment SylStructure IntEvent Intronation Target Unit SourceCoef f0 TargetCoef US_map Wave) |
|
festival>(utt.relation.items sen 'Segment) |
|
festival>(utt.relation_tree sen 'Segment) |
| festival>(set! utt1 (Utterance Text "hello world")) | //產生一個發音的物件儲存在utt1這個變數中 |
| festival>(utt.synth utt1) | //將utt1的物件合成波形 |
| festival>(utt.play utt1) | //將utt1的物件拿去發聲 |
| festival>(SayText "hello world") | //以上3步驟的簡化 |
| festival>(utt.save.wave utt1 'myutterance.wav) | //將utt1的物件合成波形後產生指定的wav檔 |
| festival>(Utterance Words (hello world)) | //產生一個Words的物件 |
| festival>(Utterance Phones (pau hh ah l ow pau)) | //產生一個Phones的物件 |
| festival>(Utterance Phrase ((Phrase ((name B))I saw))) | //產生一個Phrase的物件 |
| festival>(tts "filename.sable" 'sable ) | //可唸出所指定的sable格式檔 |
| #festival --tts filename.sable | //命令模式下直接唸出指定的sable格式檔 |
festival語音合成的基本物件就是utterance,以下列出如何將文字轉換為發音的步驟
本文介紹了一個可靠又有效率的方法來建立應用於專屬領域(limited domain)的合成語音,完整的合成語音要能夠發出所有的字詞甚至是符號,但在某些應用系統上卻只要發出部份且固定的字詞而不需要每個字都能夠發聲,像是 117 的報時台或預報天氣等系統就僅需要專屬領域的合成語音,以下讓我們來實作一套以自己的聲音錄製的報時語音檔藉著Festival語音合成工具與Festvox聲音建立工具。
語音合成又名為文句轉語音 (Text-to-Speech),它可應用的範圍相當廣泛,例如,氣象或報時等訊息發佈系統、電話查號系統、盲胞語音輔助系統 、有聲書等,隨著進來語音應用需求的增加可以發現,大多數的語音仍舊是採取預先錄製的方法而不使用語音合成系統來產生語音,其主要原因在於, 無論接受的是一段文字的輸入或是一篇文章,這些文字本身並沒有包含任何聲學特性 (acustic feature,例如聲調的高低,停頓方式,發音長短等韻律 參數),只有語言學的特性,所以必須透過自動預測的機制來產生這些文字的可能的聲學特性 而所謂自動預測的機制,一般有 rule-based 跟 data driven 兩大類手法,但是這兩種手法合成的品質音色平淡又缺乏吸引力且遇到連續發音或要保留語者音色時表現都不好, 因此近來流行音節串接法(corpus-based syllable concatenation),就是以一個錄好聲音的語料庫來當作比對的標的,從語料庫中抓出相對應的合成單元,相較於在 rule-based 跟 data driven 手法下所需要做細節的聲韻調整也因此減少了許多,如此一來不但具有了與預錄方式一樣自然度之外更減少了大量錄製所耗費的時間與成本。
本文使用了英國愛丁堡大學語言研究中心所研發的語音合成軟體Festival,它使用了二種增進合成語音品質的技術,語音單元選取合成法(unit selection synthesis)與泛用型雙音素合成法(general diphone synthesis),而配合專屬領域的語音少量而固定變化的特性,在本篇所使用到的語音單元選取合成法是簡化過了的,方法是事先將所需要的語音錄製下來,語音合成時會自動選取其中相似的語音單元來作發聲,因此與泛用型的語音單元選取合成語音系統(general unit-selection based TTS)比較起來,聲音更為流暢與自然。
在製作報時語音檔前,我們要先作一些準備工作
首先我們在使用者家目錄中建立存放語音的目錄 data/time,然後透過festvox提供的命令稿(scheme file)讓我們可根據應用的「機構」,「語者」,「領域」的參數建立在語音目錄下所需的資料庫
執行 setup_ldom 這個命令稿後我們可以發現它在 data/time 下產生了許多為了存放合成語音的目錄與檔案,特別的是在 data/time/festvox 子目錄下有4個用來控制 festival 的 scheme 命令稿,我們稍後可能需要編輯它。(檔案變動列表)
文後所提及之目錄名稱,除非特別指定,否則皆以 ~/data/time 為相對目錄起始點。
檔案變動列表利用 ls -1R 取得目錄列表。
根據應用的領域我們必須設計一個提示語的參考檔,通常是以 DOMAIN.data 命名,如本例應用在語音領域就應該取名為 time.data 這個提示語的參考檔必須放在語音目錄的etc下,但因為我們使用了 festvox 提供的自製報時語音命令稿(下面例子的 build_ldom.scm ),因此 etc/time.data 已被自動建立,值得注意的是若我們要自行產生 DOMAIN.data ,其中不可有空行,且左括號之後要空一格。
festival提供了一個命令稿 build_ldom.scm 來根據 etc/time.data 產生錄製語音時所用的提示語,它將會在 prompt-lab, prompt-utt, prompt-wav 等3個目錄中產生 time0001~time0024 各24個檔案。(檔案變動列表)
接下來需鍵入 prompt_them 指令, festival 會根據已建立的提示語念出句子來,當我們看到"starting recording"後就跟著念一遍,festival會將語者所發的語音儲存在wav目錄中產生time0001~time0024等24個wav檔,要注意的是超過10秒的語音會花費很多的swap空間。(檔案變動列表)
取出的合成單元包含了語言學(phonetic)與聲韻學(prosodic)的特性來建立決策樹(decision tree)。
若不算錄音的時間,大約只要3分鐘的時間就可以建立完成了。
也可以使用英語之外的語言建立報時所用的語音檔喔。
-min,-max,-def(無聲的範圍)這些參數都需根據與者的音高範圍來調整.以上適合男性的語者.-fill這個參數代表無聲的段落需要被填上等量的空白音高記號
| coefficients | sig2fv wav/file001.wav -o lpc/file001.lpc -otype est -lpc_order 16 -coefs "lpc" -pm pm/file001.pm -preemph 0.95 -factor 3 -window_type hamming |
| residual | sig2fv wav/file001.wav -o lpc/file001.res -otype nist -lpcfilter lpc/file001.lpc -inv_filter |
CSTR是一個有關各種學問的研究中心,目前著手語音研究的應用導向
主 網 站:http://peterju.notlong.com (目前轉址至 http://irw.ncut.edu.tw/peterju/)