1. 程式人生 > >使用VB.net 建立簡單的TTS中英文混合語音朗讀

使用VB.net 建立簡單的TTS中英文混合語音朗讀

最初的想法很簡單,只是想弄個小程式可以實現單詞朗讀功能。我瞭解到TTS(Text-to-Speech)技術可以做到閱讀文字文字,用的語音引擎是微軟的Speech SAPI5.1 SDK,搜尋一下,網路上大把文章談到這個,還會告訴你下載地址,大小在60M以上。我的執行環境是xpSP2不需要再去下載。開啟你的“控制面板”,開啟“語音”,是不是看到了2個語音引擎?一個叫"Microsoft Simplified Chinese",一個叫"Microsoft Sam",OK,讓我們設定"Microsoft Simplified Chinese"為預設值。

一切就緒,我們先在工程裡引用saip.dll,這個檔案在我的電腦里路徑是C:/Program Files/Common Files/Microsoft Shared/Speech/saip.dll  (Written by HarryGlory .)

接下來,當然是引用了

Imports SpeechLib

現在開始重頭戲了,我們在Form1_Load事件裡面寫上:

Dim MyVoice AsNew SpeechLib.SpVoice
            
Dim tmpStr AsString
            tmpStr 
="I love Chou!"
            MyVoice.Speak(tmpStr, SpeechVoiceSpeakFlags.SVSFlagsAsync)

按下F5執行,馬上出來結果了,有聲音了,耶!簡單吧?

什麼?你聽到的是一個一個字母讀出來?試試把tmpStr改成中文 "我愛周星星!

" 看看,是不是讀出中文了?
但是你很執著,想讀英文單詞,那怎麼辦?
OK,讓我們稍微改一下程式碼,如下:

Dim MyVoice AsNew SpeechLib.SpVoice
            
Dim tmpStr AsString
            tmpStr 
="I love Chou!"
            MyVoice.Voice 
= MyVoice.GetVoices([String].Empty, [String].Empty).Item(1)
            MyVoice.Speak(tmpStr, SpeechVoiceSpeakFlags.SVSFlagsAsync)

你再按F5執行一下。哇,真的可以耶~!太棒了!
你很聰明,知道了這裡Item(0)表示中文朗讀,Item(1)表示英文朗讀。可是,怎麼判斷語音引擎呢?順便設定一下音量啊。呵呵,可以這樣做的:

Dim language AsString="MSSimplifiedChineseVoice"'此處判斷預設語音引擎. 英文為 MSSam
            MyVoice.Volume =100'設定音量,0到100
Dim cnVoice, enVoice AsInteger
            
If Strings.Right(MyVoice.GetVoices.Item(0).Id, language.Length) = language Then'如果Item(0)是中文
                cnVoice =0
                enVoice 
=1
            
Else'如果Item(0)是英文
                cnVoice =1
                enVoice 
=0
            
EndIf
似乎有點不對勁哦,如果我不止中文和英文這兩個語音引擎怎麼辦?
呃,好吧,我承認我做的不夠完美。你要想知道你都有哪些語音引擎,可以用列舉的辦法,通過MyVoice.GetVoices.Count得到語音引擎的數量,再通過MyVoice.GetVoices.Item(i).Id得到每個語音引擎的名稱 (注意:此處id返回的是登錄檔裡的值,也可以用MyVoice.GetVoices.Item(0).GetDescription得到語音引擎的描述,隨便你喜歡哪一種)。只是這已經不在本文討論範圍內了,本文只想說說簡單的效果。

你腦筋轉得很快,又問:“如果我要中英文混合來讀,該怎麼辦呢?”
這個問題問得好,首先,你必須懂得判斷一個字元到底是中文字元,還是英文字元。
這裡通過ascii來判斷,我們另外寫一個函式: PrivateFunction isChinese(ByVal asciiv AsIntegerAsBoolean
        
Try
            
IfLen(Hex$(asciiv)) >2Then
                isChinese 
=True
            
Else
                isChinese 
=False
            
EndIf
        
Catch ex As Exception

        
EndTry

    
End Function

舉個例子,引用這個函式:isChinese(Asc("我")),對了,這個函式我們只用來判斷一個字元,千萬不要寫多,不能把"我愛周星星!"全部寫進去哦。
寫完了這個函式,記得返回Form1_load事件裡面,我們繼續。

你努力想了想,問:“那我要讀一句中英文混合的話,只要用strings.mid語句把這句話一個字元一個字元地讀出來,然後判斷是中文還是英文字元,最後交給語音去朗讀就可以了,對不對?”
我哈哈大笑,說:“你的作法沒錯,可以實現朗讀功能,而且也是準確地區分了語音引擎,可是,逐個字元讀的話,一個完整的單詞也會讓你讀成字母啊!而且每個字元切換一次語音引擎,很消耗系統資源的,這樣做有什麼意義呢?”

最好的辦法是,將中英文區分開來,並且用自定義的關鍵詞把它們連接出來,最後通過split語句將它分成陣列,這樣就OK了。
比如,一句"你真是lucky. I 服了 you.",我們先用關鍵詞"/HarryGlory/"將它們分成
"你真是"/HarryGlory/lucky. I "/HarryGlory/服了"/HarryGlory/ you."
最終通過split語句用陣列儲存起來,將其分成

a(0)="你真是"
a(1)="lucky. I "
a(2)="服了"
a(3)="you."

明白了嗎?中英文是間隔地出來的,這是關鍵!這樣我們可以很方便地根據陣列的序號,設定當其為偶數時,用中文語音朗讀,奇數時用英文語音朗讀了!
這個功能也可以拓展開來,如果你只要判斷、讀取中文,那就只取序號為偶數的陣列就可以了,哈哈,方便吧?

當然,關鍵詞這一步驟顯得多餘和麻煩了點,你可以自己去試試直接用陣列來分開儲存的,呵呵,就當是作業吧。

以下程式碼通過關鍵詞將中英文分開:
Dim strSource AsString="你真是lucky. I 服了 you."
            
Dim strDestination AsString=Mid(strSource, 11)
            
Dim i AsInteger
            
Dim strSelect AsString=""
            
Dim splitKey AsString="/HarryGlory/"'這裡可以改變臨時分割用的關鍵詞,這個關鍵詞一定不能在文章中出現
            
'以下for語句功能是把中英文用splitKey分開
For i =2ToLen(strSource)
                strSelect 
=Mid(strSource, i, 1)
                
If isChinese(Asc(strSelect)) Then'如果是中文
If isChinese(Asc(Strings.Right(strDestination, 1))) Then'如果前一個字元是中文
                        strDestination = strDestination &Mid(strSource, i, 1)
                    
Else
                        strDestination 
= strDestination & splitKey &Mid(strSource, i, 1)
                    
EndIf
                    
'strDestination = strDestination + Mid(strSource, i, 1)
Else'如果不是中文
If isChinese(Asc(Strings.Right(strDestination, 1))) Then'前一個字元是中文
                        strDestination = strDestination & splitKey &Mid(strSource, i, 1)
                    
Else
                        strDestination 
= strDestination &Mid(strSource, i, 1)
                    
EndIf
                
EndIf
            
Next i


以下程式碼是實現陣列儲存剛才生成的結果

Dim a(), p AsString
            a 
=Split(strDestination, splitKey)
以下程式碼實現逐句朗讀

Dim OK AsBoolean=True'OK為true的時候讀中文
If isChinese(Asc(a(0))) Then'如果第一個字元是中文
                OK =True
            
Else'如果第一個字元不是中文
                OK =False
            
EndIf

            
'以下語句是逐句朗讀
ForEach p In a
                
If OK Then
                    MyVoice.Voice 
= MyVoice.GetVoices([String].Empty, [String].Empty).Item(cnVoice)
                    MyVoice.Speak(p, SpeechVoiceSpeakFlags.SVSFlagsAsync)
                    OK 
=False
                
Else
                    MyVoice.Voice 
= MyVoice.GetVoices([String].Empty, [String].Empty).Item(enVoice)
                    MyVoice.Speak(p, SpeechVoiceSpeakFlags.SVSFlagsAsync)
                    OK 
=True
                
EndIf
            
Next
整個過程就完成了,很簡單,是吧?趕緊執行一下啦,哈哈。

還可以進一步開發出更多功能,如放一個文字框輸入字串來朗讀,或者拖放文字檔案來朗讀,或者讀xml檔案,還有將朗讀的結果輸出為wav聲音檔案等等,這個就靠你自己發揮想象力啦!