//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;