1. 程式人生 > >VC++基於微軟語音引擎開發語音識別總結

VC++基於微軟語音引擎開發語音識別總結

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

關於SAPI的簡介

API 概述

SAPI API在一個應用程式和語音引擎之間提供一個高級別的介面。SAPI 實現了所有必需的對各種語音引擎的實時的控制和管理等低級別的細節。

SAPI

引擎的兩個基本型別是文字語音轉換系統(TTS)和語音識別系統。TTS系統使用合成語音合成文字字串和檔案到聲音音訊流。語音識別技術轉換人類的聲音語音流到可讀的文字字串或者檔案。

 

 

文字語音轉換API

應用程式能通過IspVoice的物件組建模型(COM)介面控制文字語音轉換。一旦一個應用程式有一個已建立的IspVoice物件(見Text-to-Speech指南),這個應用程式就只需要呼叫ISpVoice::Speak 就可以從文字資料得到發音。另外,ISpVoice介面也提供一些方法來改變聲音和合成屬性,如語速ISpVoice::SetRate,輸出音量ISpVoice::SetVolume,改變當前講話的聲音ISpVoice::SetVoice等。

特定的SAPI控制器也可以嵌入輸入文字使用來實時的改變語音合成器的屬性,如聲音,音調,強調字,語速和音量。這些合成標記在sapi.xsd中,使用標準的XML格式,這是一個簡單但很強大定製TTS語音的方法,不依賴於特定的引擎和當前使用的聲音。

    ISpVoice::Speak方法能夠用於同步的(當完全的完成朗讀後才返回)或非同步的(立即返回,朗讀在後臺處理)操作。當同步朗讀(SPF_ASYNC)時,實時的狀態資訊如朗讀狀態和當前文字位置可以通過ISpVoice::GetStatus得到。當非同步朗讀時,可以打斷當前的朗讀輸出以朗讀一個新文字或者把新文字自動附加在當前朗讀輸出的文字的末尾。

    除了ISpVoice介面之外SAPI也為高階TTS應用程式提供許多有用的COM介面。

 

事件

SAPI用標準的回撥機制(Window訊息, 回撥函式 or Win32 事件)來發送事件來和應用程式通訊。對於TTS,事件大多用於同步地輸出語音。應用程式能夠與它們發生的實時行為例如單詞邊界,音素,口型或者應用程式定製的書籤等同步。應用程式能夠用ISpNotifySource, ISpNotifySink, ISpNotifyTranslator, ISpEventSink, ISpEventSource, 和 ISpNotifyCallback初始化和處理這些實時事件。

 

字典

應用程式通過使用ISpContainerLexicon, ISpLexicon 和IspPhoneConverter提供的方法能為語音合成引擎提供定製的單詞發音。

 

資源

查詢和選擇SAPI語音資料如聲音檔案及發音字典可以被下列COM介面控制:ISpDataKey, ISpRegDataKey, ISpObjectTokenInit, ISpObjectTokenCategory, ISpObjectToken, IEnumSpObjectTokens, ISpObjectWithToken, ISpResourceManager 和 IspTask。

 

音訊

最後,有一個介面能把聲音輸出到一些指定目標如電話和自定硬體 (ISpAudio, ISpMMSysAudio, ISpStream, ISpStreamFormat, ISpStreamFormatConverter)

語音識別 API

就像ISpVoice是語音合成的主介面,IspRecoContext是語音識別的主介面。像ISpVoice一樣,它是一個IspEventSource介面,這意味著它是語音程式接收被請求的語音識別事件通知的媒介。

一個應用程式必須從兩個不同型別的語音識別引擎(ISpRecognizer)中選擇一種。一種是可以與其它語音識別程式共享識別器的語音識別引擎,這在大多數識別程式中被推薦使用。為了為IspRecognizer建立一個共享的ISpRecoContext介面,一個應用程式只需要用CLSID_SpSharedRecoContext呼叫COM的 CoCreateInstance方法。這種方案中,SAPI將建立一個音訊輸入流,把它設定為SAPI預設的音訊輸入流。對於大型伺服器程式,它可能在單獨在一個系統上執行,效能是關鍵,一個InProc語音識別引擎更適合。

為了為InProc ISpRecognizer建立一個IspRecoContext,程式必須首先用CLSID_SpInprocRecoInstance呼叫CoCreateInstance來建立屬於它自己的InProc IspRecognizer。然後程式必須呼叫ISpRecognizer::SetInput(見 also ISpObjectToken)來建立一個音訊輸入流。最後程式可以呼叫ISpRecognizer::CreateRecoContext來得到一個IspRecoContext。

下一步是建立程式感興趣的事件通知,因為IspRecognizer也是一個IspEventSource,IspEventSource實際上是IspNotifySource,程式從它的ISpRecoContext可以呼叫IspNotifySource的一個方法來指出IspRecoContext的哪裡的事件應該被報告。然後它應該呼叫ISpEventSource::SetInterest來指出哪些事件應該通報。最重要的事件是SPEI_RECOGNITION,指出和IspRecoContext相關的IspRecognizer已經識別了一些語音。其他可用到的語音識別事件的詳細資料參見SPEVENTENUM。

最後,一個語音程式必須建立,載入,並且啟用一個IspRecoGrammar,本質上就是指出哪些型別的發言被識別,例如口述或一個命令和控制文法。首先,程式用ISpRecoContext::CreateGrammar建立一個IspRecoGrammar,然後程式載入適合的文法,下面兩個方法中呼叫其中一個:口述模式的呼叫方法ISpRecoGrammar::LoadDictation,命令和控制模式的則呼叫方法ISpRecoGrammar::LoadCmdxxx。最後為了啟用這些文法以開始進行識別,程式為口述模式呼叫ISpRecoGrammar::SetDictationState或者為命令和控制模式呼叫呼叫ISpRecoGrammar::SetRuleState或者ISpRecoGrammar::SetRuleIdState。

當識別依靠通知機制返回到程式,SPEVENT結構的成員lParam將是一個IspRecoResult,程式可以確定什麼被識別和使用了IspRecoContext的哪個IspRecoGrammar。

一個IspRecognizer,無論是否是共享的還是InProc的,都可以有多個IspRecoContexts和它關聯,並且每個都可以通過它自己的事件通知方法通知IspRecognizer。從一個IspRecoContext可以建立多個IspRecoGrammars,以便於識別不同型別的發言。

利用微軟Speech SDK 5.1在MFC中進行語音識別開發時的主要步驟,以Speech API 5.1+VC6為例:

1、初始化COM埠
一般在CWinApp的子類中,呼叫CoInitializeEx函式進行COM初始化,程式碼如下:
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED); // 初始化COM
注意:呼叫這個函式時,要在工程設定(project settings)->C/C++標籤,Category中選Preprocessor,在Preprocessor definitions:下的文字框中加上“,_WIN32_DCOM”。否則編譯不能通過。

2、建立識別引擎
微軟Speech SDK 5.1 支援兩種模式的:共享(Share)和獨享(InProc)。一般情況下可以使用共享型,大的服務型程式使用InProc。如下:
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpSharedRecognizer);//Share
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);//InProc
如果是Share型,可直接進到步驟3;如果是InProc型,必須使用 ISpRecognizer::SetInput 設定語音輸入。如下:
CComPtr<ISpObjectToken> cpAudioToken;  //定義一個token
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpAudioToken); //建立預設的音訊輸入物件
if (SUCCEEDED(hr)) { hr = m_cpRecognizer->SetInput(cpAudioToken, TRUE);}
或者:
CComPtr<ISpAudio> cpAudio;  //定義一個音訊物件
hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);//建立預設的音訊輸入物件
hr = m_cpRecoEngine->SetInput(cpAudio, TRUE);//設定識別引擎輸入源

3、建立識別上下文介面
呼叫 ISpRecognizer::CreateRecoContext 建立識別上下文介面(ISpRecoContext),如下:
hr = m_cpRecoEngine->CreateRecoContext( &m_cpRecoCtxt );

4、設定識別訊息
呼叫 SetNotifyWindowMessage 告訴Windows哪個是我們的識別訊息,需要進行處理。如下:
hr = m_cpRecoCtxt->SetNotifyWindowMessage(m_hWnd, WM_RECOEVENT, 0, 0);
SetNotifyWindowMessage 定義在 ISpNotifySource 中。

5、設定我們感興趣的事件
其中最重要的事件是”SPEI_RECOGNITION“。參照 SPEVENTENUM。程式碼如下:
const ULONGLONG ullInterest = SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) | SPFEI(SPEI_RECOGNITION) ;
hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);

6、建立語法規則
語法規則是識別的靈魂,必須要設定。分為兩種,一種是聽說式(dictation),一種是命令式(command and control---C&C)。首先 利用ISpRecoContext::CreateGrammar 建立語法物件,然後載入不同的語法規則,如下:

//dictation
hr = m_cpRecoCtxt->CreateGrammar( GIDDICTATION, &m_cpDictationGrammar );
if  (SUCCEEDED(hr))
{
hr = m_cpDictationGrammar->LoadDictation(NULL, SPLO_STATIC);//載入詞典
}

//C&C
hr = m_cpRecoCtxt->CreateGrammar( GIDCMDCTRL, &m_cpCmdGrammar);
然後利用ISpRecoGrammar::LoadCmdxxx 載入語法,例如從CmdCtrl.xml中載入:
WCHAR wszXMLFile[20]=L"";
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)"CmdCtrl.xml"  , -1, wszXMLFile, 256);//ANSI轉UNINCODE
hr = m_cpCmdGrammar->LoadCmdFromFile(wszXMLFile,SPLO_DYNAMIC);
注意:C&C時,語法檔案使用xml格式,參見Speech SDK 5.1 中的 Designing Grammar Rules。簡單例子:
<GRAMMAR LANGID="804">
   <DEFINE>
      <ID NAME="CMD" VAL="10"/>
   </DEFINE>
   <RULE NAME="COMMAND" ID="CMD" TOPLEVEL="ACTIVE">
     <L>
<p>尹成</P>
<p>山東大學</p>
<p>中科院</p>
     </L>
   </RULE>
</GRAMMAR>
LANGI*="804"代表簡體中文,在<*>...</*>中增加命令。

7、在開始識別時,啟用語法進行識別
hr = m_cpDictationGrammar->SetDictationState( SPRS_ACTIVE );//dictation
hr = m_cpCmdGrammar->SetRuleState( NULL,NULL,SPRS_ACTIVE );//C&C

8、獲取識別訊息,進行處理
截獲識別訊息(WM_RECOEVENT),然後處理。識別的結果放在CSpEvent的ISpRecoResult 中。如下:

USES_CONVERSION;
CSpEvent event;

switch (event.eEventId)
{
        case SPEI_RECOGNITION:
{
//識別出了語音輸入
m_bGotReco = TRUE;
static const WCHAR wszUnrecognized[] = L"<Unrecognized>";

CSpDynamicString dstrText;

//取得識別結果
if (FAILED(event.RecoResult()->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE ,&dstrText, NULL)))
{
dstrText = wszUnrecognized;
}

BSTR SRout;
dstrText.CopyToBSTR(&SRout);

CString Recstring;
Recstring.Empty();
Recstring = SRout;

//進一步處理
......
}
break;
}

9、釋放建立的引擎、識別上下文物件、語法等。呼叫相應的Release函式即可。

需要原始碼請在本人CSDN留下email!

本文作者專著《Visual C++2010開發權威指南》即將推出,敬請關注,Visual C++2010最近技術,Windows7開發最新技術!

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述