VC++基于微软语音引擎开发语音识别总结

Posted 尹成

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VC++基于微软语音引擎开发语音识别总结相关的知识,希望对你有一定的参考价值。

关于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开发最新技术!

以上是关于VC++基于微软语音引擎开发语音识别总结的主要内容,如果未能解决你的问题,请参考以下文章

基于GMM-HMM的语音识别系统

基于GMM-HMM的语音识别系统

微软语音技术 Windows 语音编程初步

微软TTS5.1语音引擎(中文).msi怎么用?

微软语音引擎 TTS 最基本使用

[语音识别] kaldi -- aidatatang_200zh脚本解析:音素训练