關於Microsoft Speech SDK 中TTS的研究

 與上一篇相比,這篇是用的C#.net開發,講述的更加清晰

1.   TTS概述

 

隨着語音技術的發展,微軟也推出了相應的語音開發工具,即Microsoft Speech SDK,這個SDK中包含了語音應用設計接口(SAPI)、微軟的連續語音識別引擎(MCSR)以及微軟的語音合成(TTS)引擎等等。它其中的TTStext-to-speech)引擎可以用於實現語音合成,我們通過TTS引擎可以分析文本內容並且將其朗讀出。實現TTS技術的方法有很多種,現在主要採用三種:連詞技術、語音合成技術、子字連接技術。目前的5.1版本的SDK一共可以支持3種語言的識別 (英語,漢語和日語)以及2種語言的合成(英語和漢語)。其中還包括對於低層控制和高度適應性的直接語音管理、訓練嚮導、事件、語法編譯、資源、語音識別(SR)管理以及TTS管理等強大的設計接口。

 

 

2.   實現原理

 

     下是SpeechAPI的總體結構:

      

       從圖中我們可以看出語音引擎則通過DDI層(設備驅動接口)和SAPI(SpeechAPI)進行交互,應用程序通過API層和SAPI通信。通過使用這些API,用戶可以快速開發在語音識別或語音合成方面應用程序。

     應用程序使用ISpVoice接口來控制TTS,通過調用其中的Speak方法可以朗讀出文本內容,通過調用SetVoice / GetVoice方法(在.NET中已經轉變成Voice屬性)來獲取或設置朗讀的語音,而通過調用GetVolume / SetVolumeGetRate / SetRate等方法(在.NET中已經轉變成VolumeRate屬性)來獲取或設置朗讀的音量和語速。

功能強大之處在於TTS能識別XML標記,通過給文本加上XML標記,我們讓TTS朗讀出更加符合語言閱讀習慣的句子。例如:

l   <volume level=”60”></volume> 用於設置文本朗讀的音量;

l   <rate absspeed=”1”/><rate speed=”5”/> 分別用於設置文本朗讀的絕對速度和相對速度;

l   <pitch absmiddle=”2”/><pitch middle=”5”/> 分別用於設置文本朗讀的絕對語調和相對語調;

l   <emph></emph> 在他們之間的句子被視爲強調;

l   <spell></spell> 可以將單詞逐個字母的拼寫出來;

l   <silence msec=”500”/> 表示停止發聲,並保持500微秒;

l   <context id=”date_mdy”>02/03/07</context> 可以按要求朗讀出日期

l   <voice required="Language=409"></voice> 用於設置朗讀所用的語言,其中409表示使用英語,804表示使用漢語,而411表示日語。

 

 

3.   軟件的開發

 

3.1. 開發環境的搭建

 

由於Microsoft Speech SDK是以COM組件的形式提供給我們的,因此在使用.NET開發時必須引入Interop.SpeechLib.dll文件,如圖:

 

 

在引入DLL文件後,我們就可以通過添加using SpeechLib;”引入命名空間,或直接使用SpeechLib前綴來使用SpeechLib空間下的所有類。

 

3.2.    二次封裝TTS

 

我們將使用Singleton設計模式來對TTS進行封裝,首先聲明一個SpVoice接口,並用SpVoiceClass 對象來實例化,這個接口是實現文本朗讀的核心。接着提供用於朗讀文本的方法,例如:

  /// <summary>

        /// 讀出Xml文件內容

        /// </summary>

        /// <param name="xml">Xml文件內容</param>

        public void SpeakXml(string xml)

        {

            voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync);

        }

並且使Pause()Resume()Stop()等方法來控制朗讀暫停、繼續和停止。至於保存音頻文件,我們可以使用以下方法,將音頻輸出流指向一個文件流,來完成保存工作。

        /// <summary>

        /// 保存音頻到文件

        /// </summary>

        /// <param name="xml">要讀的Xml格式的內容</param>

        /// <param name="fileName">要保存的文件名</param>

        public void Save(string xml, string fileName)

        {

            SpFileStream stream = new SpFileStream();

            stream.Open(fileName, SpeechStreamFileMode.SSFMCreateForWrite, false);

            voice.AudioOutputStream = stream;

            voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFIsXML);

            voice.WaitUntilDone(Timeout.Infinite);

            stream.Close();

        }

 

 

3.3. 實現中英文的混合朗讀

 

如果我們直接調用SpVoice接口中Speak方法來朗讀文本,那麼在朗讀過程中,要麼使用英文朗讀引擎,要麼使用中文朗讀引擎,這樣就只能朗讀純英文文本或純中文文本。要怎樣才能朗讀混合的文本呢?第一種方法,我們可以在朗讀過程中,根據文本的內容來切換朗讀所用的引擎,即調用SetChinaVoice()SetEnglishVoice()方法。第二種方法,我們在朗讀文本之前,先分析文本,將屬於英文的句子加上英文語音XML標記,即<voice required=”Language=409”></voice>,將屬於中文的句子加上中文語音XML記,即<voice required=”Language=804”></voice>。這樣調用SpeakXml方法就可以實現中英文混合朗讀。

在這裏我選擇第二種方法,在類中增加靜態方法:AddXmlLangTag,返回添加過標記得文本內容。

/// <summary>

        /// 設置中文語音

        /// </summary>

        public void SetChinaVoice()

        {

            voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0);

        }

 

        /// <summary>

        /// 設置英文語音

        /// </summary>

        public void SetEnglishVoice()

        {

            voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(1);

        }

 

 

3.4. 界面的實現

 

 

在打開文件時,可以選擇打開文本文件(*.Txt)和XML文件(*.Xml),如果打開的是XML文件,將不對內容作任何修改,並且也不允許調節音量、語速、語調,因爲這些都應該在XML文件中寫好;如果打開的是文本文件,則在朗讀前,會調用AddXmlLangTag方法給文本加上語言標記,調用AddXmlPitchTag方法給文本加上語調標記,同時也允許調節音量、語速、語調。

   

 

4.   總結

     通過爲普通文本內容設置語音XML標記,並調用SpVoice接口的Speak方法,可以實現中英文文本的混合朗讀。如果要使朗讀的效果更佳,就必須手工爲每一個句子設置相應的XML標記,這樣可使朗讀更接近人性化。

 

 

 

 

 

【參考文獻】

1Microsoft Speech SDK 幫助 sapi.chm

2http://www.codeproject.com/vb/net/TTSinVBpackage.asp

3http://www.c-sharpcorner.com/SpeechNet.asp

4http://www.supinfo-projects.com/cn/2006/xing_jin_inter_2006/

5http://www.microsoft.com/china/community/program/originalarticles/TechDoc/Cnspeech.mspx

6http://www.microsoft.com/speech/default.mspx

 

【源碼】

Talker.cs

  1. using System; 
  2. using System.Collections.Generic; 
  3. using System.Text; 
  4. using System.Threading; 
  5. using SpeechLib; 
  6.   
  7. namespace VoiceTalker 
  8.     /// <summary> 
  9.     /// TTS語音朗讀類 
  10.     /// </summary> 
  11.     public class Talker 
  12.     { 
  13.         private static Talker talker = null;        //Talker對象 
  14.         private SpVoice voice = null;               //SpVoice對象,用於實現TTS 
  15.   
  16.         /// <summary> 
  17.         /// 默認的英文語音ID 
  18.         /// </summary> 
  19.         public const string DefaultEnglishLangID = "409"
  20.   
  21.         /// <summary> 
  22.         /// 默認的中文語音ID 
  23.         /// </summary> 
  24.         public const string DefaultChineseLangID = "804"
  25.   
  26.         /// <summary> 
  27.         /// 構造函數 
  28.         /// </summary> 
  29.         private Talker() 
  30.         { 
  31.             voice = new SpeechLib.SpVoiceClass(); 
  32.         } 
  33.   
  34.         /// <summary> 
  35.         /// 獲取或設置音量 
  36.         /// </summary> 
  37.         public int Volume 
  38.         { 
  39.             get 
  40.             { 
  41.                 return voice.Volume; 
  42.             } 
  43.             set 
  44.             { 
  45.                 voice.Volume = value; 
  46.             } 
  47.         } 
  48.   
  49.         /// <summary> 
  50.         /// 獲取或設置語速 
  51.         /// </summary> 
  52.         public int Rate 
  53.         { 
  54.             get 
  55.             { 
  56.                 return voice.Rate; 
  57.             } 
  58.             set 
  59.             { 
  60.                 voice.Rate = value; 
  61.             } 
  62.         } 
  63.   
  64.         /// <summary> 
  65.         /// 獲取或設置語音 
  66.         /// </summary> 
  67.         public SpObjectToken Voice 
  68.         { 
  69.             get 
  70.             { 
  71.                 return voice.Voice; 
  72.             } 
  73.             set 
  74.             { 
  75.                 voice.Voice = value; 
  76.             } 
  77.         } 
  78.   
  79.   
  80.         /// <summary> 
  81.         /// 獲得實例 
  82.         /// </summary> 
  83.         /// <returns>Talker對象</returns> 
  84.         public static Talker Instance() 
  85.         { 
  86.             if (talker == null
  87.             { 
  88.                 talker = new Talker(); 
  89.             } 
  90.             return talker; 
  91.         } 
  92.   
  93.         /// <summary> 
  94.         /// 給文本內容添加語言標記 
  95.         /// </summary> 
  96.         /// <param name="src">文本內容</param> 
  97.         /// <returns>經過語言標記的文本內容</returns> 
  98.         public static string AddXmlLangTag(string src) 
  99.         { 
  100.             return AddXmlLangTag(src, Talker.DefaultEnglishLangID, Talker.DefaultChineseLangID); 
  101.         } 
  102.   
  103.         /// <summary> 
  104.         /// 給文本內容添加語言標記 
  105.         /// </summary> 
  106.         /// <param name="src">文本內容</param> 
  107.         /// <param name="englishLangID">英文語音ID</param> 
  108.         /// <param name="chineseLangID">中文語音ID</param> 
  109.         /// <returns>經過語言標記的文本內容</returns> 
  110.         public static string AddXmlLangTag(string src, string englishLangID, string chineseLangID) 
  111.         { 
  112.             if (src.Length < 1) 
  113.             { 
  114.                 return ""
  115.             } 
  116.   
  117.             StringBuilder dest = new StringBuilder(); 
  118.             int startPos = 0, endPos = 0; 
  119.             bool isAscii = !(src[0] > 128); 
  120.             for (int i = 0; i < src.Length; i++) 
  121.             { 
  122.                 /* 判斷每個字符是否爲ASCII,如果是則加上<voice required="Language=englishLangID"></voice> 
  123.                  * 如果不是就加上<voice required="Language=chineseLangID></voice> 
  124.                  */ 
  125.                 if (src[i] > 128) 
  126.                 { 
  127.                     if (isAscii) 
  128.                     { 
  129.                         string sub = src.Substring(startPos, endPos - startPos); 
  130.                         dest.Append("<voice required=\"Language=" + englishLangID + "\">" + sub + "</voice>"); 
  131.                         startPos = endPos; 
  132.                     } 
  133.                     isAscii = false
  134.                     endPos++; 
  135.                 } 
  136.                 else 
  137.                 { 
  138.                     if (!isAscii) 
  139.                     { 
  140.                         string sub = src.Substring(startPos, endPos - startPos); 
  141.                         dest.Append("<voice required=\"Language=" + chineseLangID + "\">" + sub + "</voice>"); 
  142.                         startPos = endPos; 
  143.                     } 
  144.                     isAscii = true
  145.                     endPos++; 
  146.                 } 
  147.             } 
  148.             string r = src.Substring(startPos, endPos - startPos); 
  149.             string langID = isAscii == true ? englishLangID : chineseLangID; 
  150.             dest.Append("<voice required=\"Language=" + langID + "\">" + r + "</voice>"); 
  151.             return dest.ToString(); 
  152.         } 
  153.   
  154.         /// <summary> 
  155.         /// 給文本內容添加語調標記 
  156.         /// </summary> 
  157.         /// <param name="src">文本內容</param> 
  158.         /// <param name="pitch">語調</param> 
  159.         /// <returns>經過語調標記的文本內容</returns> 
  160.         public static string AddXmlPitchTag(string src, int pitch) 
  161.         { 
  162.             string pitchTag = "<pitch absmiddle=\"" + pitch.ToString() + "\"/>"
  163.             return pitchTag + src; 
  164.         } 
  165.   
  166.         /// <summary> 
  167.         /// 設置中文語音 
  168.         /// </summary> 
  169.         public void SetChinaVoice() 
  170.         { 
  171.             voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(0); 
  172.         } 
  173.   
  174.         /// <summary> 
  175.         /// 設置英文語音 
  176.         /// </summary> 
  177.         public void SetEnglishVoice() 
  178.         { 
  179.             voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(1); 
  180.         } 
  181.   
  182.         /// <summary> 
  183.         /// 讀出文件內容 
  184.         /// </summary> 
  185.         /// <param name="text">文件內容</param> 
  186.         public void SpeakText(string text) 
  187.         { 
  188.             voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault | SpeechVoiceSpeakFlags.SVSFlagsAsync); 
  189.         } 
  190.   
  191.         /// <summary> 
  192.         /// 讀出Xml文件內容 
  193.         /// </summary> 
  194.         /// <param name="xml">Xml文件內容</param> 
  195.         public void SpeakXml(string xml) 
  196.         { 
  197.             voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFIsXML | SpeechVoiceSpeakFlags.SVSFlagsAsync); 
  198.         } 
  199.   
  200.         /// <summary> 
  201.         /// 讀出文件內容 
  202.         /// </summary> 
  203.         /// <param name="fileName">文件名</param> 
  204.         public void SpeakFile(string fileName) 
  205.         { 
  206.             voice.Speak(fileName, SpeechVoiceSpeakFlags.SVSFIsFilename | SpeechVoiceSpeakFlags.SVSFlagsAsync); 
  207.         } 
  208.   
  209.         /// <summary> 
  210.         /// 暫停 
  211.         /// </summary> 
  212.         public void Pause() 
  213.         { 
  214.             voice.Pause(); 
  215.         } 
  216.   
  217.         /// <summary> 
  218.         /// 繼續 
  219.         /// </summary> 
  220.         public void Resume() 
  221.         { 
  222.             voice.Resume(); 
  223.         } 
  224.   
  225.         /// <summary> 
  226.         ///   停止說話 
  227.         /// </summary> 
  228.         public void Stop() 
  229.         { 
  230.             voice.Speak(string.Empty, SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak); 
  231.         } 
  232.   
  233.         /// <summary> 
  234.         /// 保存音頻到文件 
  235.         /// </summary> 
  236.         /// <param name="xml">要讀的Xml格式的內容</param> 
  237.         /// <param name="fileName">要保存的文件名</param> 
  238.         public void Save(string xml, string fileName) 
  239.         { 
  240.             SpFileStream stream = new SpFileStream(); 
  241.             stream.Open(fileName, SpeechStreamFileMode.SSFMCreateForWrite, false); 
  242.             voice.AudioOutputStream = stream; 
  243.             voice.Speak(xml, SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFIsXML); 
  244.             voice.WaitUntilDone(Timeout.Infinite); 
  245.             stream.Close(); 
  246.         } 
  247.     } 

VoiceForm.cs

  1. using System; 
  2. using System.Collections.Generic; 
  3. using System.ComponentModel; 
  4. using System.Data; 
  5. using System.Drawing; 
  6. using System.Text; 
  7. using System.IO; 
  8. using System.Windows.Forms; 
  9.   
  10. namespace VoiceTalker 
  11.     public partial class VoiceForm : Form 
  12.     { 
  13.         private bool isXml = false
  14.         private Talker talker = Talker.Instance(); 
  15.   
  16.         public VoiceForm() 
  17.         { 
  18.             InitializeComponent(); 
  19.         } 
  20.   
  21.         private void openButton_Click(object sender, EventArgs e) 
  22.         { 
  23.             DialogResult dr = openFileDialog.ShowDialog(); 
  24.             if (dr == DialogResult.OK) 
  25.             { 
  26.                 FileInfo fi = new FileInfo(openFileDialog.FileName); 
  27.                 //判斷是否爲Xml格式的文件 
  28.                 if (fi.Extension.ToLower() == ".xml"
  29.                 { 
  30.                     controlBox.Enabled = false
  31.                     isXml = true
  32.                 } 
  33.                 else 
  34.                 { 
  35.                     controlBox.Enabled = true
  36.                 } 
  37.                 fileNameText.Text = fi.FullName; 
  38.                 contentText.Text = ""
  39.   
  40.                 //讀取文件內容 
  41.                 StreamReader sr = new StreamReader(openFileDialog.OpenFile(), Encoding.Default); 
  42.                 contentText.Text = sr.ReadToEnd(); 
  43.             } 
  44.         } 
  45.   
  46.         private void speakButton_Click(object sender, EventArgs e) 
  47.         { 
  48.             try 
  49.             { 
  50.                 if (isXml) 
  51.                 { 
  52.                     talker.SpeakXml(contentText.Text); 
  53.                 } 
  54.                 else 
  55.                 { 
  56.                     talker.Volume = volumeBar.Value; 
  57.                     talker.Rate = rateBar.Value; 
  58.                     String readText = Talker.AddXmlLangTag(contentText.Text); 
  59.                     readText = Talker.AddXmlPitchTag(readText, pitchBar.Value); 
  60.                     talker.SpeakXml(readText); 
  61.                 } 
  62.             } 
  63.             catch (Exception ex) 
  64.             { 
  65.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  66.             } 
  67.         } 
  68.   
  69.         private void StopButton_Click(object sender, EventArgs e) 
  70.         { 
  71.             try 
  72.             { 
  73.                 talker.Stop(); 
  74.             } 
  75.             catch (Exception ex) 
  76.             { 
  77.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  78.             } 
  79.         } 
  80.   
  81.         private void pauseButton_Click(object sender, EventArgs e) 
  82.         { 
  83.             try 
  84.             { 
  85.                 if (pauseButton.Text == "暫停"
  86.                 { 
  87.                     talker.Pause(); 
  88.                     pauseButton.Text = "繼續"
  89.                 } 
  90.                 else 
  91.                 { 
  92.                     talker.Resume(); 
  93.                     pauseButton.Text = "暫停"
  94.                 } 
  95.             } 
  96.             catch (Exception ex) 
  97.             { 
  98.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  99.             } 
  100.         } 
  101.   
  102.         private void saveButton_Click(object sender, EventArgs e) 
  103.         { 
  104.             try 
  105.             { 
  106.                 DialogResult dr = saveFileDialog.ShowDialog(); 
  107.                 if (dr == DialogResult.OK) 
  108.                 { 
  109.                     string fileName = saveFileDialog.FileName; 
  110.                     string content = contentText.Text; 
  111.                     if (!isXml) 
  112.                     { 
  113.                         talker.Volume = volumeBar.Value; 
  114.                         talker.Rate = rateBar.Value; 
  115.                         content = Talker.AddXmlLangTag(content); 
  116.                         content = Talker.AddXmlPitchTag(content, pitchBar.Value); 
  117.                     } 
  118.                     talker.Save(content, fileName); 
  119.                 } 
  120.             } 
  121.             catch (Exception ex) 
  122.             { 
  123.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  124.             } 
  125.         } 
  126.   
  127.         private void volumeBar_Scroll(object sender, EventArgs e) 
  128.         { 
  129.             try 
  130.             { 
  131.                 talker.Volume = volumeBar.Value; 
  132.             } 
  133.             catch (Exception ex) 
  134.             { 
  135.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  136.             } 
  137.         } 
  138.   
  139.         private void rateBar_Scroll(object sender, EventArgs e) 
  140.         { 
  141.             try 
  142.             { 
  143.                 talker.Rate = rateBar.Value; 
  144.             } 
  145.             catch (Exception ex) 
  146.             { 
  147.                 MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); 
  148.             } 
  149.         } 
  150.   
  151.         private void aboutButton_Click(object sender, EventArgs e) 
  152.         { 
  153.             new AboutForm().ShowDialog(); 
  154.         } 
  155.   
  156.         private void exitButton_Click(object sender, EventArgs e) 
  157.         { 
  158.             Application.Exit(); 
  159.         } 
  160.     } 

出處:http://tb.blog.csdn.net/TrackBack.aspx?PostId=1529844

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章