//1,生成动态连接库时,要#define USE_SPEECH_DLL,
// 并且#define LANE_SPEECH_EXPORTS
//2,使用动态连接库时,要#define USE_SPEECH_DLL
//3,声称和使用静态连接库时,什么都不需要
//4,另外主程序中静态连接库要调用的方式里要调用CoInitialize( NULL )和CoUninitialize(),
// 动态连接库就不用调用了。
#ifndef LANE_SPEECH_H
#define LANE_SPEECH_H
#include <windows.h>
#define _ATL_APARTMENT_THREADED
#include <atlbase.h>
extern CComModule _Module; //You may derive a class from CComModule and use it. if you want to override something,but do not change the name of _Module
#include <atlcom.h>
#include <sphelper.h> //sapi需要的头文件
//-----生成动态连接库和静态库的处理----------------
#ifdef USE_SPEECH_DLL //定义了USE_SPEECH_DLL,就按生成DLL,声明导出导入类
#ifdef LANE_SPEECH_EXPORTS
#define LANE_SPEECH_DLL __declspec(dllexport)
#else
#define LANE_SPEECH_DLL __declspec(dllimport)
#endif
//这个警告我现在还没闹清楚是怎么回事了,估计是DLL和com或atl有关
//暂时只能屏蔽掉它,在静态库里就不会出现这个警告。
#pragma warning( disable : 4251 )
#else //没定义USE_SPEECH_DLL,则不声明导出或导入类(LANE_SPEECH_DLL就为空)
#define LANE_SPEECH_DLL
#endif //USE_SPEECH_DLL
//***************************常量***********************
/公共常量-----------------
const DWORD SP_CHINESE = 0x0000; //简体中文.
const DWORD SP_ENGLISH = 0x0001; //英语.
/CTTS常量-----------------
const UINT WM_SPEAK = WM_USER + 4444; //触发事件产生的消息。
/SR常量-------------------
const UINT WM_RECOEVENT = WM_USER + 3333; //触发事件产生的消息。
const DWORD SR_INPROC = 0x0000; //独享类型的SR.
const DWORD SR_SHARE = 0x0001; //共享类型的SR.
//以下常量仅作例子用。
#define VID_TopLevelRule 9000 //顶级规则ID
#define VID_SubLevelRule1 9001 //子规则ID
#define VID_SubLevelRule2 9002 //子规则ID
#define VID_SubLevelRule3 9003 //子规则ID
//*************************类声明************************
class CSR;
///
///
//
// CTTS
//
///
///
class LANE_SPEECH_DLL CTTS
{
protected:
HWND m_hWnd; // 关联的窗口句柄。
CComPtr<ISpVoice> m_pVoice; // 声音对象的指针。
CComPtr<ISpObjectToken> m_pToken; // token对象的指针。
CComPtr<ISpAudio> m_pAudio; // 音频对象的指针。(用来保存原来默认的输入流)
CComPtr<ISpStream> m_pOutputStream; // 输出到文件的流对象。
public:
//********************************初始化部分********************
//功能: 保存与识别引擎关联的窗口句柄。
//参数: hWnd:要关联的窗口句柄。
//返回值: 无。
CTTS ( const HWND hWnd );
//功能: 释放所有的对象。
//参数: 无。
//返回值: 无。
~CTTS ();
//功能: 建立一个voice对象。设置要是别的语言种类,消息,通知事件。
//参数: dwLanguage:要朗读的语言种类,SP_CHINESE为中文,
// SP_ENGLISH为英文。
//返回值: HRESULT类型。
HRESULT Create( const DWORD dwLanguage = SP_CHINESE );
//功能: 从一个SR引擎建立一个voice对象。设置要是别的语言种类,消息,
// 通知事件。
//参数: pSRContext:SR引擎的指针。dwLanguage:要朗读的语言种类,
// SP_CHINESE为中文,SP_ENGLISH为英文。
//返回值: HRESULT类型。
HRESULT Create ( const CSR * pSR,
const DWORD dwLanguage = SP_CHINESE );
//********************************设置部分***************************************
//功能: 设置朗读声音的语言种类。
//参数: dwLanguage:语言种类。SP_CHINESE为中文,SP_ENGLISH为英文。
//返回值: HRESULT类型。
HRESULT SetLanguage ( const DWORD dwLanguage );
//功能: 设置要处理的的事件。
//参数: ullInterest:来自enum SPEVENTENUM,要用SPFEI()转化为64bit的,
// 设置多个事件用运算符" | "。 用SPFEI_ALL_SR_EVENTS表示全部事
// 件都会收到通知。
//返回值: HRESULT。
HRESULT SetInterest ( const ULONGLONG ullInterest );
//功能: 设置朗读声音的音量。
//参数: usVolume:音量数值应该从0到100
//返回值: 无。
void SetVolume ( USHORT usVolume );
//功能: 得到朗读声音的音量。
//参数: 无。
//返回值: 音量数值,应该从0到100。
USHORT GetVolume ( );
//功能: 设置朗读声音的音速。
//参数: RateAdjust:音速,参数范围从-10到10。
//返回值: 无。
void SetRate ( LONG RateAdjust );
//功能: 得到朗读声音的音速。
//参数: 无。
//返回值: 音速,参数范围从-10到10。
LONG GetRate ( );
//功能: 设置朗读的声音流到.wav文件,如果不调用此函数则默认从音箱输出。
//参数: pszFileName:.wav文件的文件名。要用" L"" "转换。
//返回值: HRESULT。
HRESULT SetOutputWithWav ( const WCHAR *pszFileName = L"TtsOut.wav");
//功能: 设置朗读的声音从音箱输出。
//参数: 无。
//返回值: HRESULT。
HRESULT UnSetOutputWithWav ();
//**********************播放语音,文本到语音转换部分*****************************
//功能: 停止朗读。如果朗读为同步方式,则不能停止。
//参数: pwcs:要朗读的字符串,需用" L"" "转换,可以是包含xml标记
// 的字符串。dwFlags:朗读方式。SPF_ASYNC为异步,SVSFDefault为同步,
// SVSFIsXML为朗读带xml标记的文本。
//返回值: HRESULT。
HRESULT Speak ( const WCHAR *pwcs, const DWORD dwFlags = SPF_ASYNC );
//功能: 停止朗读。如果朗读为同步方式,则不能停止。
//参数: 无。
//返回值: 无。
void Stop ( );
//功能: 暂停朗读。如果朗读为同步方式,则不能暂停。
//参数: 无。
//返回值: 无。
void Pause ();
//功能: 从暂停的地方继续朗读。
//参数: 无。
//返回值: 无。
void Resume ();
//********************************处理事件部分***********************************
public:
//功能: 处理发生的事件。系统自动调用,不需要用户自己处理。
//参数: 无。
//返回值: 无。
void ProcessTTSEvent ();
//功能: 为虚函数。当输出流结束时要触发的动作,需要在派生类重载。
//参数: 无。
//返回值: 无。
virtual void OnStreamStart ();
//功能: 为虚函数。当输出流结束时要触发的动作,需要在派生类重载。
//参数: 无。
//返回值: 无。
virtual void OnStreamEnd ();
};
///
///
// CSR
//
///
///
class LANE_SPEECH_DLL CSR
{
protected:
HWND m_hWnd;
public:
CComPtr<ISpRecognizer> m_pSREngine; // 语音识别引擎(recognition)的接口。
CComPtr<ISpRecoContext> m_pSRContext; // 识别引擎上下文(context)的接口。
CComPtr<ISpRecoGrammar> m_pSRGrammar; // 识别文法(grammar)的接口。
CComPtr<ISpStream> m_pInputStream; // 流()的接口。
CComPtr<ISpObjectToken> m_pToken; // 语音特征的(token)接口。
CComPtr<ISpAudio> m_pAudio; // 音频(Audio)的接口。(用来保存原来默认的输入流)
public:
static ULONGLONG ullGrammerID; // Grammer的标识符, 64位无符号整型 每建立一个Grammar,加一。
protected:
//***************************辅助功能部分****************************************
//GrammarID加一,每个GrammerID必须不同。
static void UpdateGrammerID ( );
public:
//功能: 友员。TTS中的从SR引擎中建立voice对象。
//参数: pSRContext:SR上下文对象的指针。
//返回值: HRESULT类型。
friend HRESULT CTTS::Create ( const CSR * pSR,
const DWORD dwLanguage );
//****************************初始化部分*****************************************
//功能: 保存与识别引擎关联的窗口句柄,更新GrammarID。
//参数: hWnd:要关联的窗口句柄。
//返回值: 无。
CSR ( HWND hWnd );
//功能: 释放所有的对象。
//参数: 无。
//返回值: 无。
~CSR ( );
//功能: 建立各个接口的对象。设置要是别的语言种类,消息,通知事件,
// 加载文法文件。
//参数: SRType:识别引擎的类型,SR_INPROC为独享类型,SR_SHARE共享类型。
// pwcGramFileName:文法文件的文件名,要用" L"" "转换为WCHAR型。
// dwLanguage:要是别的语言种类,SP_CHINESE为中文,SP_ENGLISH为英文。
//返回值: HRESULT类型。
HRESULT Create (const DWORD SRType,
const WCHAR *pwcGramFileName = L"grammar.xml",
const DWORD dwLanguage = SP_CHINESE );
//**********************************设置部分*************************************
//功能: 设置要处理的上下文接受的事件。
//参数: ullInterest:来自enum SPEVENTENUM,要用SPFEI()转化为64bit的,
// 设置多个事件用运算符" | "。 用SPFEI_ALL_SR_EVENTS表示全部事
// 件都会收到通知。
//返回值: HRESULT。
HRESULT SetInterest ( const ULONGLONG ullInterest );
//功能: 设置某个规则的状态(激活或者取消激活)。
//参数: pszName:规则名,要用" L"" "转换。bFlag:TRUE表示激活,
// FALSE表示取消激活。
//返回值: HRESULT。
HRESULT SetRuleState ( const WCHAR *pszName, const BOOL bFlag );
//功能: 设置识别引擎从.wav文件识别语音,如果不调用此函数则默认从麦克
// 风输入。
//参数: pszFileName:.wav文件的文件名。要用" L"" "转换。
//返回值: HRESULT。
HRESULT SetInputWithWav ( const WCHAR *pszFileName = L"sr.wav" );
//功能: 取消从.wav文件识别。恢复从麦克风识别。
//参数: 无。
//返回值: HRESULT。
HRESULT UnSetInputWithWav ( );
//***********************识别开始,结束,识别结果的处理**************************
//功能: 识别开始(将所有规则激活)。
//参数: 无
//返回值: 无。
void StartRecognize ( );
//功能: 识别结束(将所有规则取消激活)。
//参数: 无。
//返回值: 无。
void EndRecognize ( );
public:
//功能: 处理发生的事件。系统自动调用,不需要用户自己处理。
//参数: 无。
//返回值: 无。
void ProcessRecoEvent ( );
protected:
//功能: 识别成功时要调用的函数。系统自动调用,不需要用户自己处理。
//参数: pPhrase:ISpPhrase类型。
//返回值: 无。
void OnRecoSuccess ( ISpPhrase *pPhrase );
public:
//功能: 识别成功后,根据规则的ID决定动作。系统自动调用。虚函数,
// 需要在派生类重载。规则ID必须以常量形式预先定义。
//参数: ulRuleID:顶级规则的ID。ulVal:子规则的ID。
//返回值: 无。
virtual void ExecuteCommand ( const ULONG ulRuleID,
const ULONG ulVal );
//功能: 识别失败时的动作,系统自动调用。虚函数,需要在派生类重载。
//参数: 无。
//返回值: 无。
virtual void OnRecoFail ();
//功能: 为虚函数。当输入流开始时要触发的动作,需要在派生类重载。
//参数: 无。
//返回值: 无。
virtual void OnStreamStart ();
//功能: 为虚函数。当输入流结束时要触发的动作,需要在派生类重载。
//参数: 无。
//返回值: 无。
virtual void OnStreamEnd ();
};
#endif //LANE_SPEECH_DLL_H
///
//
// 文件:LaneSpeech.cpp
// 功能:封装的speech sdk5.1 的文本语音合成(TTS)和语音识别(SR)功能
// 语音识别只支持命令模式,不支持连续模式
// 作者:吕宝虹(Lane), msn: lkjx82@msn.com, qq: 3619908
// 日期: 2004.10
// 版本:1.2
//
//
#include "LaneSpeech.h"
//-----生成动态连接库和静态库的处理----------------
#ifdef USE_SPEECH_DLL //定义了USE_SPEECH_DLL,就按生成DLL,声明导出导入类
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CoInitialize(NULL);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
CoUninitialize();
break;
}
return TRUE;
}
#endif //USE_SPEECH_DLL
///
//功能: 弹出一个信息框。
//参数: lpText:是对话框信息。lpCaption:对话框标题。
//返回值: 无。
inline void ShowError ( const LPCTSTR lpText = "ERROR",
const LPCTSTR lpCaption = "ERROR" )
{
::MessageBox( NULL, lpText, lpCaption, MB_OK | MB_ICONERROR );
}
//功能: 检查一个HRESULT类型的值,如果是错误的值则,弹出信息框提示错误。
//参数: hr:要检查的HRESULT的引用。 lpText:是对话框信息。
// lpCaption:对话框标题。
//返回值: 有错则为FALSE,没错则返回TRUE。
inline BOOL CheckHr ( const HRESULT &hr,
const LPCTSTR lpText = "ERROR",
const LPCTSTR lpCaption = "ERROR" )
{
if ( FAILED( hr ) ) {
ShowError ( lpText, lpCaption );
return FALSE;
}
return TRUE;
}
///
///
//
// CTTS
//
///
///
//保存关联窗口句柄。初始化COM。
CTTS::CTTS ( const HWND hWnd )
{
m_hWnd = hWnd;
m_pVoice = NULL;
m_pToken = NULL;
m_pOutputStream = NULL;
m_pAudio = NULL;
}
//释放所有对象。
CTTS::~CTTS ()
{
if( m_pToken) {
m_pToken.Release();
m_pToken = NULL;
}
if( m_pAudio ) {
m_pAudio.Release();
m_pAudio = NULL;
}
if ( m_pOutputStream ) {
m_pOutputStream.Release();
m_pOutputStream = NULL;
}
if( m_pVoice ) {
m_pVoice.Release();
m_pVoice = NULL;
}
}
//从SR的上下文中得到voice对象。此函数在CSR中被声明一个友员。
HRESULT CTTS::Create( const CSR * pSR,
const DWORD dwLanguage )
{
HRESULT hr;
hr = pSR->m_pSRContext->GetVoice ( &m_pVoice );
if ( !::CheckHr ( hr, "pSRContext->GetVoice()" ) ) {
return hr;
}
SetLanguage ( dwLanguage );
hr = SpCreateDefaultObjectFromCategoryId ( SPCAT_AUDIOIN, &m_pAudio );//建立一个默认音频流
if ( !::CheckHr ( hr, "CreateDefaultObjectFodd()" ) ) {
return hr;
}
//SPEI_START_INPUT_STREAM表示输出对象开始接受流输出SPEI_START_INPUT_STREAM
//SPEI_END_INPUT_STREAM 表示完成流输出。
hr = m_pVoice->SetInterest( SPFEI( SPEI_START_INPUT_STREAM ) |
SPFEI( SPEI_END_INPUT_STREAM ),
SPFEI( SPEI_START_INPUT_STREAM ) |
SPFEI( SPEI_END_INPUT_STREAM ) );
if ( !::CheckHr ( hr, "m_pVoice->SetInterest()" ) ) {
return hr;
}
//设置通知消息
hr = m_pVoice->SetNotifyWindowMessage( m_hWnd, WM_SPEAK, 0, 0 );
if ( !::CheckHr ( hr, "m_pVoice->SetNotifyWindowMessage()" ) ) {
return hr;
}
return hr;
}
//单独(相对于从SR的上下文中得到voice对象)建立一个voice对象。
//并设置兴趣,设置通知事件。
HRESULT CTTS::Create( const DWORD dwLanguage )
{
HRESULT hr;
hr = m_pVoice.CoCreateInstance ( CLSID_SpVoice );
if ( !::CheckHr ( hr, "m_pVoice.CoCreateInstance()" ) ) {
return hr;
}
SetLanguage ( dwLanguage );
hr = SpCreateDefaultObjectFromCategoryId ( SPCAT_AUDIOIN, &m_pAudio );//建立一个默认音频流
if ( !::CheckHr ( hr, "CreateDefaultObjectFodd()" ) ) {
return hr;
}
//SPEI_START_INPUT_STREAM表示输出对象开始接受流输出SPEI_START_INPUT_STREAM
//SPEI_END_INPUT_STREAM 表示完成流输出。
hr = m_pVoice->SetInterest( SPFEI( SPEI_START_INPUT_STREAM ) |
SPFEI( SPEI_END_INPUT_STREAM ),
SPFEI( SPEI_START_INPUT_STREAM ) |
SPFEI( SPEI_END_INPUT_STREAM ) );
if ( !::CheckHr ( hr, "m_pVoice->SetInterest()" ) ) {
return hr;
}
//设置通知消息
hr = m_pVoice->SetNotifyWindowMessage( m_hWnd, WM_SPEAK, 0, 0 );
if ( !::CheckHr ( hr, "m_pVoice->SetNotifyWindowMessage()" ) ) {
return hr;
}
return hr;
}
//设置语言,默认的语言为中文。SP_CHINESE为中文,SP_ENGLISH为英文
HRESULT CTTS::SetLanguage ( const DWORD dwLanguage )
{
HRESULT hr;
switch ( dwLanguage )
{
case SP_CHINESE:
hr = SpFindBestToken( SPCAT_VOICES, L"language=804",
NULL, &m_pToken );
if ( !::CheckHr ( hr, "SpFindBestToken()错误",
"CTTS::SetLanguage" ) ) {
return hr;
}
hr = m_pVoice->SetVoice( m_pToken );
if ( !::CheckHr ( hr, "SpFindBestToken()错误",
"CTTS::SetLanguage" ) ) {
return hr;
}
break;
case SP_ENGLISH:
hr = SpFindBestToken( SPCAT_VOICES, L"language=409",
NULL, &m_pToken );
if ( !::CheckHr ( hr, "SpFindBestToken()错误",
"CTTS::SetLanguage" ) ) {
return hr;
}
hr = m_pVoice->SetVoice( m_pToken );
if ( !::CheckHr ( hr, "SpFindBestToken()错误",
"CTTS::SetLanguage" ) ) {
return hr;
}
break;
default:
::ShowError ( "设置中文请用SP_CHINESE,设置英文请用SP_ENGLISH",
"CTTS::SetLanguage()参数错误" );
}
return hr;
}
//设置要处理的事件。
HRESULT CTTS::SetInterest ( const ULONGLONG ullInterest )
{
HRESULT hr;
// 设置pvoice感兴趣的事件。
hr = m_pVoice->SetInterest( ullInterest, ullInterest );
if ( !::CheckHr ( hr, "m_pVoice->SetInterest()", "CTTS::SetInterest()" ) ) {
return hr;
}
/*//设置通知消息
hr = m_pVoice->SetNotifyWindowMessage( hWnd, WM_SPEAK, 0, 0 );
if ( !::CheckHr ( hr, "m_pVoice->SetNotifyWindowMessage()",
"CTTS::SetInterest()" ) ) {
return hr;
}*/
return hr;
}
//设置朗读音量。音量数值应该从0到100
void CTTS::SetVolume ( USHORT usVolume )
{
if ( (usVolume > 100) || (usVolume<0) ) {
::ShowError ( "CTTS::SetVolume音量范围应该从0到100" );
return;
}
m_pVoice->SetVolume( usVolume );
}
//取得音量的值。
USHORT CTTS::GetVolume ( )
{
USHORT usVolume;
m_pVoice->GetVolume ( &usVolume );
return usVolume;
}
//设置语速。参数范围从-10到10
void CTTS::SetRate ( LONG RateAdjust )
{
if ( (RateAdjust < -10) || (RateAdjust > 10) ) {
::ShowError ( "SetRate()设置的数值必须在 -10到10之间" );
return;
}
m_pVoice->SetRate ( RateAdjust );
}
//得到当前语速的数值。
LONG CTTS::GetRate ( )
{
LONG RateAdjust;
m_pVoice->GetRate( &RateAdjust );
return RateAdjust;
}
//设置朗读的声音流到.wav文件,如果不调用此函数则默认从音箱输出。
//并设置要处理的事件,应该包含对流的开始和结束。在Create()中已经设置好了。
HRESULT CTTS::SetOutputWithWav ( const WCHAR *pszFileName )
//,LONG Format = PSF_22kHz16BitStereo)
{
HRESULT hr;
CSpStreamFormat sOutputFormat;
CComPtr<ISpStreamFormat> cpOldStream;
m_pVoice->GetOutputStream( &cpOldStream );
sOutputFormat.AssignFormat( SPSF_22kHz16BitStereo );
//sOutputFormat.AssignFormat(cpOldStream);
hr = SPBindToFile( pszFileName,
SPFM_CREATE_ALWAYS,
//SPFM_CREATE,
//SPFM_OPEN_READWRITE,
&m_pOutputStream,
&sOutputFormat.FormatId(),
sOutputFormat.WaveFormatExPtr() );
if ( !::CheckHr ( hr, "SPBindToFile" ) ) {
return hr;
}
hr = m_pVoice->SetOutput ( m_pOutputStream, TRUE );
if ( !::CheckHr ( hr, "m_pVoice->SetOutput()" ) ) {
return hr;
}
return hr;
}
//设置朗读的声音从音箱输出。
HRESULT CTTS::UnSetOutputWithWav ()
{
HRESULT hr;
hr = m_pVoice->SetOutput ( m_pAudio, FALSE );
::CheckHr ( hr, "UnSetOutputWithWav ()" );
m_pOutputStream->Close ();
return hr;
}
//朗读。dwFlags为SPF_ASYNC时,指异步朗读。SPF_DEFAULT为同步。
//SPF_IS_XML指pwcs中包含xml标签。按xml标签的设置朗读。
HRESULT CTTS::Speak ( const WCHAR *pwcs, const DWORD dwFlags )
{
//WCHAR WTX[] = L"<VOICE REQUIRED=''NAME=Microsoft Mary''/>text to wave";
HRESULT hr;
hr = m_pVoice->Speak ( pwcs, dwFlags, NULL);
::CheckHr ( hr, "CTTS::Speak" );
return hr;
}
//停止播放。Speak()为同步朗读时不能停止。
void CTTS::Stop ( )
{
m_pVoice->Speak ( NULL, SPF_ASYNC, NULL );
}
//暂停朗读。
void CTTS::Pause ()
{
m_pVoice->Pause ();
}
//从暂停处继续朗读
void CTTS::Resume ()
{
m_pVoice->Resume ();
}
//从事件队列取得事件并处理。根据相应的事件调用相应的函数。
void CTTS::ProcessTTSEvent ()
{
CSpEvent event; // 事件助手类
// 循环处理事件当事件队列里有事件的时候。
while ( event.GetFrom(m_pVoice) == S_OK )
{
// 察看识别成功事件和识别失败事件
switch (event.eEventId)
{
case SPEI_START_INPUT_STREAM:
OnStreamStart ();
break;
case SPEI_END_INPUT_STREAM:
OnStreamEnd ();
break;
case SPEI_VOICE_CHANGE:
//OnVoiceChange ();
break;
case SPEI_TTS_BOOKMARK:
//OnBookMark ();
break;
case SPEI_WORD_BOUNDARY:
//OnWordBoundAry ();
break;
}
}
}
//流输出到文件开始时要触发的事件。
void CTTS::OnStreamStart ()
{
ShowError ("朗读开始");
}
//流输出到文件结束时要触发的事件。
void CTTS::OnStreamEnd ()
{
ShowError ("朗读完毕");
}
///
///
//
// CSR
//
///
///
/
//保存窗口句柄,初始化接口指针,调用UpdateGrammerID()。
//如果要建立多个Grammar,Grammar必须不同。
/
CSR::CSR ( HWND hWnd )
{
m_hWnd = hWnd;
m_pSREngine = NULL;
m_pSRContext = NULL;
m_pSRGrammar = NULL;
m_pInputStream = NULL;
m_pToken = NULL;
m_pAudio = NULL;
UpdateGrammerID ( );
}
/
//更新GrammerID:GrammerID+1。为static函数。
/
void CSR::UpdateGrammerID ( )
{
ullGrammerID ++;
}
/
//建立引擎ISpRecognizer,设置识别语言。
//建立引擎上下文ISpRecoContext,设置感兴趣的事件及发生事件要通知窗口的消息。
//建立上下文的文法ISpRecoGrammar。从.xml文件加载文法。
/
HRESULT CSR::Create ( const DWORD dwSRType,
const WCHAR *pwcGramFileName,
const DWORD dwLanguage )
{
HRESULT hr = S_OK;
//容错.
if ( (dwSRType != SR_INPROC) && (dwSRType != SR_SHARE) ) {
::ShowError ( "指定要建立的SR类型错误,请用SR_INPROC或SR_SHARE调用" );
return 0x3333;
}
if ( (dwLanguage != SP_CHINESE) && (dwLanguage != SP_ENGLISH) ) {
::ShowError ( "请选择要建立的SR的语言的种类:SP_CHINESE,SP_ENGLISH" );
return 0x3434;
}
if ( dwSRType == SR_INPROC ) { //建立一个独享的识别引擎,这里必须调用SetInput()
hr = m_pSREngine.CoCreateInstance ( CLSID_SpInprocRecognizer );
if ( !::CheckHr ( hr, "m_pSREngine.CoCreateInstance()" ) ) {
return hr;
}
hr = SpCreateDefaultObjectFromCategoryId ( SPCAT_AUDIOIN, &m_pAudio );//建立一个默认音频流
if ( !::CheckHr ( hr, "CreateDefaultObjectFodd()" ) ) {
return hr;
}
hr = m_pSREngine->SetInput ( m_pAudio, TRUE );//为识别引擎设置音频输入
if ( !::CheckHr ( hr, "m_pSREngine->SetInput()" ) ) {
return hr;
}
}
else {
hr = m_pSREngine.CoCreateInstance ( CLSID_SpSharedRecognizer );//建立一个共享的SR引擎。
if ( !::CheckHr ( hr, "m_pSREngine.CoCreateInstance()" ) ) {
return hr;
}
}
//设置识别的语言。
if ( dwLanguage == SP_CHINESE ) {
hr = SpFindBestToken ( SPCAT_RECOGNIZERS, L"language=804",
NULL, &m_pToken );
}
else {
hr = SpFindBestToken ( SPCAT_RECOGNIZERS, L"language=409",
NULL, &m_pToken );
}
if ( !::CheckHr ( hr, "SpFindBestToken()" ) ) {
return hr;
}
hr = m_pSREngine->SetRecognizer ( m_pToken );
if ( !::CheckHr ( hr, "m_pSREngine->SetRecognizerI()" ) ) {
return hr;
}
//为引擎建立一个上下文接口
hr = m_pSREngine->CreateRecoContext ( &m_pSRContext );
if ( !::CheckHr ( hr, "m_pSREngine->CreateRecoContext()" ) ) {
return hr;
}
//设置通知窗口的消息
hr = m_pSRContext->SetNotifyWindowMessage ( m_hWnd, WM_RECOEVENT, 0, 0 );
if ( !::CheckHr ( hr, "m_pSRContext->SetNotifyWindowMessage()" ) ) {
return hr;
}
//设置兴趣。
//hr = m_pSRContext->SetInterest ( SPFEI_ALL_SR_EVENTS, SPFEI_ALL_SR_EVENTS );
hr = m_pSRContext->SetInterest ( SPFEI( SPEI_RECOGNITION ) | SPFEI( SPEI_FALSE_RECOGNITION ),
SPFEI( SPEI_RECOGNITION ) | SPFEI( SPEI_FALSE_RECOGNITION ) );
if ( !::CheckHr ( hr, "m_pSRContext->SetInterest()" ) ) {
return hr;
}
//建立一个Grammar
hr = m_pSRContext->CreateGrammar ( ullGrammerID, &m_pSRGrammar );
if ( !::CheckHr ( hr, "m_pSRContext->CreateGrammar()" ) ) {
return hr;
}
//从文件加载识别文法.文件名为WCHAR类型。
hr = m_pSRGrammar->LoadCmdFromFile ( pwcGramFileName, SPLO_DYNAMIC );
if ( !::CheckHr ( hr, "m_pSRGrammar->LoadCmdFromFile" ) ) {
return hr;
}
return hr;
}
/
//释放所有对象。
/
CSR::~CSR( )
{
if ( m_pSRGrammar ) {
m_pSRGrammar.Release();
m_pSRGrammar = NULL;
}
if ( m_pSRContext ) {
m_pSRContext->SetNotifySink(NULL);
m_pSRContext.Release();
m_pSRContext = NULL;
}
if ( m_pToken) {
m_pToken.Release();
m_pToken = NULL;
}
if ( m_pAudio ) {
m_pAudio.Release();
m_pAudio = NULL;
}
if ( m_pInputStream ) {
m_pInputStream.Release();
m_pInputStream = NULL;
}
if ( m_pSREngine ) {
m_pSREngine.Release();
m_pSREngine = NULL;
}
}
/
//设置要处理的事件。即上下文要捕捉的事件。
//ullInterest 必须是用SPFEI()转化每个事件,然后用"|"运算得来。
//事件的类型是 enum SPEVENTENUM
/
HRESULT CSR::SetInterest ( const ULONGLONG ullInterest )
{
HRESULT hr;
hr = m_pSRContext->SetInterest ( ullInterest, ullInterest );
::CheckHr ( hr, "CSR::SetInterest" );
//设置通知窗口的消息
/*hr = m_pSRContext->SetNotifyWindowMessage ( m_hWnd, WM_RECOEVENT, 0, 0 );
if ( !::CheckHr ( hr, "m_pSRContext->SetNotifyWindowMessage()" ) ) {
return hr;
}*/
return hr;
}
/
//激活或者取消某个激活规则。
//m_pSRGrammar->SetRuleState的第三个参数为一个enum SPRULESTATE类型,
//常用的为SPRS_INACTIVE,SPRS_ACTIVE。
/
HRESULT CSR::SetRuleState ( const WCHAR *pszName, const BOOL bFlag )
{
HRESULT hr;
if ( bFlag ){
hr = m_pSRGrammar->SetRuleState ( pszName, NULL, SPRS_ACTIVE );
}
else {
hr = m_pSRGrammar->SetRuleState ( pszName, NULL, SPRS_INACTIVE );
}
::CheckHr ( hr, "CSR::SetRuleState()" );
return hr;
}
/
//设置.wav文件的声音采样格式要用到enum SPSTREAMFORMAT类型
/
HRESULT CSR::SetInputWithWav ( const WCHAR *pszFileName )
{
HRESULT hr;
//取消规则激活先
hr = m_pSRGrammar->SetRuleState ( NULL, NULL, SPRS_INACTIVE );
if ( !::CheckHr ( hr, "m_pSRGrammar->SetRuleState" ) ) {
return hr;
}
// 建立基本的sapi流对象。用SpBindToFile绑定到文件
hr = m_pInputStream.CoCreateInstance(CLSID_SpStream);
if ( !::CheckHr ( hr, "m_pInputStream.CoCreateInstance" ) ) {
return hr;
}
//建立WaveFormatEx结构,wav格式是22kHz, 16-bit, Stereo
CSpStreamFormat sInputFormat;
hr = sInputFormat.AssignFormat(SPSF_22kHz16BitStereo);
if ( !::CheckHr ( hr, "sInputFormat.AssignFormat" ) ) {
return hr;
}
//用wav文件pszFileName设置流对象,只读
hr = m_pInputStream->BindToFile ( pszFileName,
SPFM_OPEN_READONLY,
&sInputFormat.FormatId(),
sInputFormat.WaveFormatExPtr(),
SPFEI_ALL_EVENTS );
if ( !::CheckHr ( hr, "m_pInputStream->BindToFile" ) ) {
return hr;
}
// 连接wav输入到SR.
hr = m_pSREngine->SetInput(m_pInputStream, TRUE);
if ( !::CheckHr ( hr, "m_pSREngine->SetInput()" ) ) {
return hr;
}
//检查 识别和流结束 事件
hr = m_pSRContext->SetInterest (SPFEI(SPEI_RECOGNITION) | SPFEI( SPEI_RECOGNITION ) |
SPFEI(SPEI_END_SR_STREAM) | SPFEI(SPEI_START_SR_STREAM),
SPFEI(SPEI_RECOGNITION) | SPFEI( SPEI_RECOGNITION ) |
SPFEI(SPEI_END_SR_STREAM) | SPFEI(SPEI_START_SR_STREAM) );
//设置通知消息。
hr = m_pSRContext->SetNotifyWindowMessage ( m_hWnd, WM_RECOEVENT, 0, 0 );
if ( !::CheckHr ( hr, "m_pSRContext->SetNotifyWindowMessage()" ) ) {
return hr;
}
return hr;
}
/
//取消从.wav文件识别,恢复从麦克风识别。
/
HRESULT CSR::UnSetInputWithWav ( )
{
HRESULT hr;
//取消规则激活先
hr = m_pSRGrammar->SetRuleState ( NULL, NULL, SPRS_INACTIVE );
if ( !::CheckHr ( hr, "m_pSRGrammar->SetRuleState" ) ) {
return hr;
}
hr = m_pSREngine->SetInput ( m_pAudio, TRUE );//为识别引擎设置音频输入
if ( !::CheckHr ( hr, "m_pSREngine->SetInput()" ) ) {
return hr;
}
//更新上下文
hr = m_pSRContext->GetRecognizer ( &m_pSREngine );
if ( !::CheckHr ( hr, "m_pSRContext->GetRecognizer()" ) ) {
return hr;
}
//设置通知窗口的消息
hr = m_pSRContext->SetNotifyWindowMessage ( m_hWnd, WM_RECOEVENT, 0, 0 );
if ( !::CheckHr ( hr, "m_pSRContext->SetNotifyWindowMessage()" ) ) {
return hr;
}
//设置兴趣。
hr = m_pSRContext->SetInterest ( SPFEI( SPEI_RECOGNITION ) | SPFEI( SPEI_FALSE_RECOGNITION ),
SPFEI( SPEI_RECOGNITION ) | SPFEI( SPEI_FALSE_RECOGNITION ) );
if ( !::CheckHr ( hr, "m_pSRContext->SetInterest()" ) ) {
return hr;
}
//释放流
hr = m_pInputStream->Close();
if ( !::CheckHr ( hr, "m_pInputStream->Close" ) ) {
return hr;
}
//规则全部激活。
hr = m_pSRGrammar->SetRuleState ( NULL, NULL, SPRS_INACTIVE );
if ( !::CheckHr ( hr, "m_pSRGrammar->SetRuleState" ) ) {
return hr;
}
return hr;
}
/
//开始识别(激活所有规则)。
/
void CSR::StartRecognize ( )
{
HRESULT hr;
//激活文法规则,第一个NULL说明激活全部规则。
hr = m_pSRGrammar->SetRuleState ( NULL, NULL, SPRS_ACTIVE );
::CheckHr ( hr, "m_pSRGrammar->SetRuleState" );
}
/
//识别结束(取消激活所有规则)。
/
void CSR::EndRecognize ( )
{
HRESULT hr;
//取消激活文法规则,第一个NULL说明取消激活全部规则。
hr = m_pSRGrammar->SetRuleState ( NULL, NULL, SPRS_INACTIVE );
::CheckHr ( hr, "m_pSRGrammar->SetRuleState" );
return ;
}
/
//从事件队列取得事件并处理。根据相应的事件调用相应的函数。
/
void CSR::ProcessRecoEvent( )
{
CSpEvent event; // 事件助手类
// 循环处理事件当事件队列里有事件的时候。
while ( event.GetFrom(m_pSRContext) == S_OK )
{
// 察看识别成功事件和识别失败事件
switch (event.eEventId)
{
case SPEI_RECOGNITION: //识别成功
OnRecoSuccess ( event.RecoResult() );
break;
case SPEI_FALSE_RECOGNITION://识别失败
OnRecoFail ();
break;
case SPEI_START_SR_STREAM:
OnStreamStart ();
break;
case SPEI_END_SR_STREAM:
OnStreamEnd ();
break;
}
}
}
/
//识别成功,把识别成功的短语的所属的规则顶级ID和子规则的ID传递给
//ExecuteCommand()。
/
void CSR::OnRecoSuccess ( ISpPhrase *pPhrase )
{
SPPHRASE *pElements;
//得到短语元素,如果其中一个规则id (rule id )是我们在文法中指定的,
//在switch中判断出是哪一个命令被识别。
if (SUCCEEDED(pPhrase->GetPhrase(&pElements)))
{
ExecuteCommand ( pElements->Rule.ulId,
pElements->pProperties->vValue.ulVal );
//释放我们分配的pElements内存空间
::CoTaskMemFree(pElements);
}
}
/
//根据识别的短语执行相应的动作。规则ID必须以常量形式预先定义。
/
void CSR::ExecuteCommand ( const ULONG ulRuleID, const ULONG ulVal )
{
switch ( ulRuleID ) //Rule.ulID类型是: ULONG
{
case VID_TopLevelRule://这里为顶级规则。
switch ( ulVal )
{
case VID_SubLevelRule1://这里为VID_TopLevelRule规则下的子规则。
::ShowError ( "子规则1" );
break;
case VID_SubLevelRule2:
::ShowError ( "子规则2" );
break;
case VID_SubLevelRule3:
::ShowError ( "子规则3" );
break;
}
break;
default:
break;
}
}
/
//识别失败时触发的动作。
/
void CSR::OnRecoFail ( )
{
::ShowError ( "识别失败", "识别失败" );
}
/
//开始从文件读入流开始时要触发的事件。
/
void CSR::OnStreamStart ()
{
//::ShowError("识别开始");
}
/
//开始从文件读入流结束时要触发的事件。
/
void CSR::OnStreamEnd ()
{
//::ShowError("识别结束");
}
ULONGLONG CSR::ullGrammerID = 1000;