微軟SAPI讓你的軟件能說會道
2011/01/06
“沒聲音,再好的戲也出不來!边@雖然是一句廣告,但是也說出了一個道理,我們所開發(fā)的軟件,特別是一些多媒體軟件,要是能夠發(fā)出聲音,能說會道,將為我們的軟件增添不少光彩。同時,我們面臨的是一個老齡化的社會,將會有越來越多的視力不太好的老年人成為我們的用戶,開始使用我們的軟件,如果我們的軟件能說會道,可以用語音的方式提示用戶進行操作,這將大大增加軟件的可用性,從而獲得用戶的喜愛。// SpVoice對象,我們將使用這個對象來朗讀文本 private SpVoice m_spVoice; private void Init() { // 創(chuàng)建SpVoice對象 m_spVoice = new SpVoice(); // 枚舉出系統中已經安裝的語音,并將其填充到Combo Box控件中 foreach (ISpeechObjectToken Token in m_spVoice.GetVoices(string.Empty, string.Empty)) { this.cmbVoices.Items.Add(Token.GetDescription(49)); } // 默認選中第一個語音 cmbVoices.SelectedIndex = 0; } |
朗讀文本
完成窗體的初始化,創(chuàng)建SpVoice對象之后,接下來我們就可以利用這個對象的Speak()方法來閱讀Text Box控件中的文本了。
private void btnSpeak_Click(object sender, EventArgs
e) { // 獲取用戶在Combo Box中選擇的語音索引 int nVoiceIndex = this.cmbVoices.SelectedIndex; // 根據語音索引指定SpVoice的Voice屬性,也就是指定使用何種語音 m_spVoice.Voice = m_spVoice.GetVoices(string.Empty, string.Empty).Item(nVoiceIndex); // 使用SpVoice的Speak()方法閱讀Text Box中文本 m_spVoice.Speak(this.textPreview.Text, SpeechVoiceSpeakFlags.SVSFlagsAsync); } |
在這里我們使用了SpVoice對象的一個最重要的函數Speak(),它的第一個參數就是我們要朗讀的文本,而第二個參數則是朗讀的方式,有同步,異步,XML文件等等。
這樣,通過SpVoice對象的一個簡單函數,我們就可以朗讀Text Box控件中的文本內容了。
朗讀文本文件
更多時候,我們不是閱讀Text Box控件中輸入的文本,而是閱讀某些文本文件中的文字,這樣,讀取文本文件并將文字填充到Text Box控件中就成為一種必要了。
private void btnFileSelect_Click(object sender, EventArgs
e) { // 使用打開文件對話框選擇文本文件 OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.InitialDirectory = "e:\\"; openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"; openFileDialog1.FilterIndex = 2; openFileDialog1.RestoreDirectory = true; if (openFileDialog1.ShowDialog() == DialogResult.OK) { // 讀取文本文件并將其填充到Text Box控件 StreamReader objReader = new StreamReader( openFileDialog1.FileName); string sLine = ""; string sPreview = ""; while (sLine != null) { sLine = objReader.ReadLine(); if (sLine != null) { // 這里需要添加Environment.NewLine表示換行 sPreview += sLine + Environment.NewLine; } } // 將文本文件中的內容顯示到Text Box控件 this.textPreview.Text = sPreview; // 關閉文件讀取器 objReader.Close(); } } 、 |
這樣,我們就可以通過讀取文本文件中的內容,將其顯示到Text Box控件中,然后SpVoice就可以閱讀Text Box控件中的內容,也就是間接地朗讀了文本文件。
將文本轉換成聲音文件
除了直接朗讀文本之外,更多的時候,我們還需要將文本轉換成聲音文件。這樣我們可以將這些聲音文件隨身攜帶,想聽就聽。要將文本轉換為聲音文件,我們需要用到SpVoice的另外一個重要的函數SetOutput(),我們可以利用它將SpVoice的語音輸出某個WAV文件,從而實現將文本文件轉換為聲音文件。
因為 將一段比較長的文本轉換成聲音文件,通常是一個比較長的過程,所以在這里我們創(chuàng)建一個專門的工作者線程來負責文本的轉換,而界面線程則負責顯示轉換的進度。
// 工作者線程類 public class WorkerThread { // 用戶選擇的語音 private int nVoiceIndex; // 保存的文件名 private string strFileName; // 需要轉換的文本 private ArrayList arrText; // 構造函數,利用構造函數向線程傳遞參數 public WorkerThread(int nIndex, ArrayList aText, string sFileName ) { nVoiceIndex = nIndex; arrText = aText; strFileName = sFileName; } // 線程開始事件 public event EventHandler threadStartEvent; // 線程執(zhí)行時的事件 public event EventHandler threadEvent; // 線程結束事件 public event EventHandler threadEndEvent; // 線程函數 public void runMethod() { // 創(chuàng)建SpVoice對象,并選擇用戶選中的語音 SpVoice voice = new SpVoice(); voice.Voice = voice.GetVoices(string.Empty, string.Empty).Item(nVoiceIndex); try { // 創(chuàng)建流媒體文件 SpeechStreamFileMode SpFileMode = SpeechStreamFileMode.SSFMCreateForWrite; SpFileStream SpFileStream = new SpFileStream(); // 這里我們設置輸出的頻率,這樣可以決定輸出文件的大小 SpFileStream.Format.Type = SpeechAudioFormatType.SAFTCCITT_ALaw_8kHzMono; // 還可以選擇更高品質的格式,不過產生的文件體積更大 // SpFileStream.Format.Type = SpeechAudioFormatType.SAFT11kHz16BitMono; // 創(chuàng)建文件,并將SpVoice的輸出流指定為當前文件 SpFileStream.Open(strFileName, SpFileMode, false); voice.AudioOutputStream = SpFileStream; // 發(fā)送線程開始事件,通知主界面,設定進度條的最大值為Count threadStartEvent.Invoke(arrText.Count, new EventArgs()); // 開始將文本輸出到音頻文件 int nCount = 0; foreach (string sOutput in arrText) { voice.Speak(sOutput, SpeechVoiceSpeakFlags.SVSFlagsAsync); // 發(fā)送線程運行時事件,移動進度條的位置 threadEvent.Invoke(nCount, new EventArgs()); voice.WaitUntilDone(-1); ++nCount; } // 關閉音頻文件 SpFileStream.Close(); } catch { } // 發(fā)送線程結束事件,通知主界面關閉進度條 threadEndEvent.Invoke(new object(), new EventArgs()); } } |
跟直接朗讀文本相似,我們仍舊使用SpVoice的Speak()函數朗讀文本,只是我們通過指定SpVoice的AudioOutputStream屬性,將語音輸出到一個音頻文件,這樣就完成了文本文件到音頻文件的轉換。
完成轉換工作者線程的創(chuàng)建后,我們就可以利用它來完成具體的轉換工作。在窗體的保存按鈕的單擊響應函數中,我們創(chuàng)建相應的工作者線程來進行文本的轉換。
private void btnSavetoWAV_Click(object sender, EventArgs
e) { string strWAVFile = ""; try { // 使用保存文件對話框,選擇保存的文件 SaveFileDialog sfd = new SaveFileDialog(); sfd.Filter = "All files (*.*)|*.*|wav files (*.wav)|*.wav"; sfd.Title = "Save to a wave file"; sfd.FilterIndex = 2; sfd.RestoreDirectory = true; if (sfd.ShowDialog() == DialogResult.OK) { // 獲取用戶輸入的文件名 strWAVFile = sfd.FileName; // 從Text Box控件獲取要轉換的文本 ArrayList arrText = new ArrayList(); foreach (String sLine in this.textPreview.Lines) arrText.Add(sLine); // 顯示進度條 progressForm = new Form2(); progressForm.Show(); // 創(chuàng)建工作者線程,并向工作者線程傳遞要轉換的文本 WorkerThread myThreadFun = new WorkerThread( this.cmbVoices.SelectedIndex, arrText, strWAVFile); // 注冊線程事件 myThreadFun.threadStartEvent += new EventHandler(method_threadStartEvent); myThreadFun.threadEvent += new EventHandler(method_threadEvent); myThreadFun.threadEndEvent += new EventHandler(method_threadEndEvent); // 創(chuàng)建線程,執(zhí)行工作者線程 Thread thread = new Thread(new ThreadStart(myThreadFun.runMethod)); // 啟動線程 thread.Start(); } } catch { } } |
除了創(chuàng)建線程進行文本的轉換之外,為了讓我們的軟件更加易用,更加人性化,我們還需要響應線程事件,移動進度條的位置以反映轉換的進度,免得用戶以為軟件在比較長的轉換過程中死掉了。
// 線程開始的時候調用的委托 private delegate void maxValueDelegate(int maxValue); // 線程執(zhí)行中調用的委托 private delegate void nowValueDelegate(int nowValue); // 線程結束的時候調用的委托 private delegate void hideProgressDelegate(int n); /// 線程完成事件,隱藏進度條窗口 /// 但是我們不能直接操作進度條,需要一個委托來替我們完成 void method_threadEndEvent(object sender, EventArgs e) { hideProgressDelegate hide = new hideProgressDelegate(hideProgress); this.Invoke(hide, 0); } /// 線程執(zhí)行中的事件,設置進度條當前進度 /// 這里的sender,是WorkerThread 函數中傳過來的當前值 void method_threadEvent(object sender, EventArgs e) { int nowValue = Convert.ToInt32(sender); nowValueDelegate now = new nowValueDelegate(setNow); this.Invoke(now, nowValue); } /// 線程開始事件,設置進度條最大值 /// 但是我不能直接操作進度條,需要一個委托來替我完成 /// 這里的sender,是WorkerThread 函數中傳過來的最大值 void method_threadStartEvent(object sender, EventArgs e) { int maxValue = Convert.ToInt32(sender); maxValueDelegate max = new maxValueDelegate(setMax); this.Invoke(max, maxValue); } /// 被委托調用的函數,專門操作進度條 private void setMax(int maxValue) { progressForm.progressBar1.Maximum = maxValue; } private void setNow(int nowValue) { progressForm.progressBar1.Value = nowValue; } private void hideProgress(int n) { progressForm.Hide(); } |
控制SpVoice的閱讀
到這里,一個能說會道的軟件基本上已經完成了,但是,為了讓我們的軟件更加易用,我們還可以通過SpVoice提供的函數對SpVoice的行為進行控制,讓她更加符合我們的心意。例如,我們可以控制SpVoice的暫停和繼續(xù)。
private void btnPause_Click(object sender, EventArgs
e) { if (this.btnPause.Text == "暫停") { // 讓SpVoice暫停朗讀 m_spVoice.Pause(); this.btnPause.Text ="繼續(xù)"; } else { // 讓SpVoice繼續(xù)朗讀 m_spVoice.Resume(); this.btnPause.Text = "暫停"; } } |
IT168
微軟Mediaroom平臺:實現電信級的電視服務 2010-12-29 |
徐工集團重構CRM提升企業(yè)客戶關系管理 2010-12-22 |
微軟發(fā)布新版iPhone客戶端——必應2.0 2010-12-17 |
微軟Lync整合統一通信 和辰信息順勢而為 2010-12-15 |
2011年:微軟計劃在CRM領域超越Salesforce 2010-12-14 |