基于Windows Sdk 与visual C++2008 在微软平台上构架自己的语音识别引擎(适用于windows 2000/xp2003/vista windows CE /mobile),本项目开源,源码请留下你们的Email,我给大家发
本人闲来无事,自行开发了一个小型的语音识别引擎,搭建起在微软平台上的语音识别框架服务体系,
鉴于本人个人力量有限,为了将语音识别引擎做的功能更加强悍,强大,
现在将该系统开源,需要源码的请在本人CSDN博客下留下EMail,
本系统属于系统框架,搭建起一个语音识别的引擎服务框架,
在微软平台上畅通无阻,
现在将本系统构架公布一下,
并贴出相关核心源码,源码体积为37M,编译后为3M,
适用于windows 2000/xp2003/vista windows CE /mobile
框架头文件简介:
srengalt.h文件 此文件包含的语音CSrEngineAlternates类。 这实现了接口ISpSRAlternates , 当一个应用程序GetAlternates或识别的结果, 将 寻找AlternatesCLSID的识别引擎对象,并 创建此对象。 并返回结果 #pragma once #include "stdafx.h" #include "SampleSrEngine.h" #include "resource.h" class ATL_NO_VTABLE CSrEngineAlternates : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CSrEngineAlternates, &CLSID_SampleSREngineAlternates>, public ISpSRAlternates { public: DECLARE_REGISTRY_RESOURCEID(IDR_SRENGALT) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSrEngineAlternates) COM_INTERFACE_ENTRY(ISpSRAlternates) END_COM_MAP() public: STDMETHODIMP GetAlternates( SPPHRASEALTREQUEST *pAltRequest, SPPHRASEALT **ppAlts, ULONG *pcAlts); STDMETHODIMP Commit( SPPHRASEALTREQUEST *pAltRequest, SPPHRASEALT *pAlt, void **ppvResultExtra, ULONG *pcbResultExtra); }; srengext.h 文件 此文件包含CSampleSRExtension类。 这实现了自定义接口ISampleSRExtension 当一个应用程序开始识别, SAPI的将 寻找ExtensionCLSID领域中的引擎对象的指针,并 创建该对象,然后创建语音识别的要求。 #pragma once #include "stdafx.h" #include "SampleSrEngine.h" #include "resource.h" class ATL_NO_VTABLE CSampleSRExtension : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CSampleSRExtension, &CLSID_SampleSRExtension>, public ISampleSRExtension, public ISpDisplayAlternates, public ISpEnginePronunciation { public: DECLARE_REGISTRY_RESOURCEID(IDR_SRENGEXT) DECLARE_GET_CONTROLLING_UNKNOWN() DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSampleSRExtension) COM_INTERFACE_ENTRY(ISampleSRExtension) COM_INTERFACE_ENTRY(ISpDisplayAlternates) COM_INTERFACE_ENTRY(ISpEnginePronunciation) END_COM_MAP() HRESULT FinalConstruct() { / /失败CRecoExt作为一个非累计对象。 / /创建CRecoExt的SAPI if(GetControllingUnknown() == dynamic_cast<ISampleSRExtension *>(this) ) { return E_FAIL; } / /这个接口的处理的SAPI 5.1 。 / /必须QI'd中的FinalConstruct对象并没有释放。 return OuterQueryInterface(IID__ISpPrivateEngineCall, (void **)&m_pEngineCall); } void FinalRelease() { // 不释放IID__ISpPrivateEngineCall这里 } STDMETHODIMP ExamplePrivateEngineCall(void); // 测试方法 // ISpDisplayAlternates 方法 STDMETHODIMP GetDisplayAlternates( const SPDISPLAYPHRASE *pPhrase, ULONG cRequestCount, SPDISPLAYPHRASE **ppCoMemPhrases, ULONG *pcPhrasesReturned); STDMETHODIMP SetFullStopTrailSpace(ULONG ulTrailSpace); // ISpEnginePronunciation 方法 STDMETHODIMP Normalize( LPCWSTR pszWord, LPCWSTR pszLeftContext, LPCWSTR pszRightContext, WORD LangID, SPNORMALIZATIONLIST *pNormalizationList); STDMETHODIMP GetPronunciations( LPCWSTR pszWord, LPCWSTR pszLeftContext, LPCWSTR pszRightContext, WORD LangID, SPWORDPRONUNCIATIONLIST *pEnginePronunciationList); private: _ISpPrivateEngineCall *m_pEngineCall; }; /****************************************************************************** * srengobj.h *此文件包含的宣言CSrEngine类。 *本实施ISpSREngine , ISpSREngine2和ISpObjectWithToken 。 *这是主要识别引擎对象 ******************************************************************************/ #pragma once #include "stdafx.h" #include "SampleSrEngine.h" #include "resource.h" //语音识别对象。每个条目的列表中的一个实例这个类。 class CContext { public: CContext * m_pNext; BOOL operator==(SPRECOCONTEXTHANDLE hContext) { return (m_hSapiContext == hContext); } CContext(SPRECOCONTEXTHANDLE hSapiContext) : m_hSapiContext(hSapiContext) {} SPRECOCONTEXTHANDLE m_hSapiContext; };//reco语法存储。每个条目的列表中的一个实例这个类。 class CDrvGrammar { public: CDrvGrammar * m_pNext; SPGRAMMARHANDLE m_hSapiGrammar; // 根据 SAPI 创建语法 BOOL m_SLMLoaded; // 语法是否与听写相关 BOOL m_SLMActive; // 词典是否被激活 WCHAR* m_pWordSequenceText; // 词典词表放在缓冲区 ULONG m_cchText; // 字序缓冲区大小 SPTEXTSELECTIONINFO* m_pInfo; // 文字选择字序缓冲区 CDrvGrammar(SPGRAMMARHANDLE hSapiGrammar) : m_hSapiGrammar(hSapiGrammar), m_SLMLoaded(FALSE), m_SLMActive(FALSE), m_pWordSequenceText(NULL), m_cchText(0), m_pInfo(NULL) { } ~CDrvGrammar() { / /释放资源 / /对于每个语法对象将被释放 SetWordSequenceData(NULL, 0, NULL). / / SetWordSequenceData和SetTextSelection将释放的内存 / /在这里没有必要释放内存的m_pWordSequenceText和m_pInfo . } #ifdef _WIN32_WCE CDrvGrammar() { } static LONG Compare(const CDrvGrammar *, const CDrvGrammar *) { return 0; } #endif }; / /读取的RecognizeStream线程中的音频数据块。每一组 / /决定如果数据讲话或沉默和价值补充说,此队列。 / /解码器读取这些线程和进程。 / /关键部分是用来使队列线程安全的,和一个事件是用来 / /显示如果缓冲区已空或没有。 / /这非常象roughtly模拟,这样做的特征提取 / /一个线程,并通过功能流的解码器。 class CFrameQueue { public: BOOL m_aFrames[100]; // 语音识别返回值 ULONG m_cFrames; ULONG m_ulHeadIndex; HANDLE m_hSpaceAvailEvent; CRITICAL_SECTION m_cs; CFrameQueue() { m_cFrames = 0; m_ulHeadIndex = 0; m_hSpaceAvailEvent = NULL; InitializeCriticalSection(&m_cs); } ~CFrameQueue() { DeleteCriticalSection(&m_cs); } void SetSpaceAvailEvent(HANDLE h) { m_hSpaceAvailEvent = h; } void InsertTail(BOOL b) { EnterCriticalSection(&m_cs); ULONG ulTailIndex = (m_ulHeadIndex + m_cFrames) % sp_countof(m_aFrames); m_aFrames[ulTailIndex] = b; m_cFrames++; if (m_cFrames == sp_countof(m_aFrames)) { ResetEvent(m_hSpaceAvailEvent); } LeaveCriticalSection(&m_cs); } BOOL IsFull() { EnterCriticalSection(&m_cs); BOOL b = (m_cFrames == sp_countof(m_aFrames)); LeaveCriticalSection(&m_cs); return b; } BOOL RemoveHead() { EnterCriticalSection(&m_cs); BOOL b = m_aFrames[m_ulHeadIndex]; m_ulHeadIndex = (m_ulHeadIndex + 1) % sp_countof(m_aFrames); m_cFrames--; SetEvent(m_hSpaceAvailEvent); LeaveCriticalSection(&m_cs); return b; } BOOL HasData() { EnterCriticalSection(&m_cs); ULONG cFrames = m_cFrames; LeaveCriticalSection(&m_cs); return cFrames; } }; //我们可以使用CSpBasicQueue信息存储规则 class CRuleEntry { public: BOOL operator==(SPRULEHANDLE rh) { return (m_hRule == rh); } CRuleEntry * m_pNext; SPRULEHANDLE m_hRule; // SAPI 规则句柄 BOOL m_fTopLevel; // 显示规则是否被激活 BOOL m_fActive; // 显示识别引擎的设置 }; // 语音识别类 class ATL_NO_VTABLE CSrEngine : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CSrEngine, &CLSID_SampleSREngine>, public ISpSREngine2, public ISpObjectWithToken, public ISpThreadTask { public: CSrEngine() : m_ulNextGrammarIndex(0), m_cActive(0), m_bPhraseStarted(FALSE), m_bSoundStarted(FALSE), m_hQueueHasRoom(NULL), m_hRequestSync(NULL), m_LangID(0) {} DECLARE_REGISTRY_RESOURCEID(IDR_SRENG) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSrEngine) COM_INTERFACE_ENTRY(ISpSREngine) COM_INTERFACE_ENTRY(ISpSREngine2) COM_INTERFACE_ENTRY(ISpObjectWithToken) END_COM_MAP() private: HANDLE m_hRequestSync; CFrameQueue m_FrameQueue; ULONG m_cBlahBlah; CSpBasicQueue<CDrvGrammar> m_GrammarList; CSpBasicQueue<CContext> m_ContextList; ULONG m_ulNextGrammarIndex; ULONG m_cActive; ULONGLONG m_ullStart; ULONGLONG m_ullEnd; BOOL m_bSoundStarted:1; BOOL m_bPhraseStarted:1; CComPtr<ISpSREngineSite> m_cpSite; CComPtr<ISpThreadControl> m_cpDecoderThread; HANDLE m_hQueueHasRoom; CSpBasicQueue<CRuleEntry> m_RuleList; CComPtr<ISpLexicon> m_cpLexicon; CComPtr<ISpObjectToken> m_cpEngineObjectToken; CComPtr<ISpObjectToken> m_cpUserObjectToken; LANGID m_LangID; public: HRESULT RandomlyWalkRule(SPRECORESULTINFO * pResult, ULONG nWords, ULONGLONG ullAudioPos, ULONG ulAudioSize); HRESULT RecurseWalk(SPSTATEHANDLE hState, SPPATHENTRY * pPath, ULONG * pcTrans); HRESULT WalkCFGRule(SPRECORESULTINFO * pResult, ULONG cRulesActive, BOOL fHypothesis, ULONG nWords, ULONGLONG ullAudioPos, ULONG ulAudioSize); HRESULT WalkSLM(SPRECORESULTINFO * pResult, ULONG cSLMActive, ULONG nWords, ULONGLONG ullAudioPos, ULONG ulAudioSize); HRESULT WalkTextBuffer(void* pvGrammarCookie, SPPATHENTRY * pPath, SPTRANSITIONID hId, ULONG * pcTrans); HRESULT AddEvent(SPEVENTENUM eEvent, ULONGLONG ullStreamPos, WPARAM wParam = 0, LPARAM lParam = 0); HRESULT AddEventString(SPEVENTENUM eEvent, ULONGLONG ulLStreamPos, const WCHAR * psz, WPARAM = 0); HRESULT CreatePhraseFromRule( CRuleEntry * pRule, BOOL fHypothesis, ULONGLONG ullAudioPos, ULONG ulAudioSize, ISpPhraseBuilder** ppPhrase ); CRuleEntry* FindRule( ULONG ulRuleIndex ); CRuleEntry* NextRuleAlt( CRuleEntry * pPriRule, CRuleEntry * pLastRule ); void _CheckRecognition(); void _NotifyRecognition(BOOL fHypothesis, ULONG nWords); HRESULT FinalConstruct(); HRESULT FinalRelease(); STDMETHODIMP SetObjectToken(ISpObjectToken * pToken); STDMETHODIMP GetObjectToken(ISpObjectToken ** ppToken); STDMETHODIMP SetRecoProfile(ISpObjectToken * pProfileToken); STDMETHODIMP SetSite(ISpSREngineSite *pSite); STDMETHODIMP GetInputAudioFormat(const GUID * pSrcFormatId, const WAVEFORMATEX * pSrcWFEX, GUID * pDesiredFormatId, WAVEFORMATEX ** ppCoMemDesiredWFEX); STDMETHODIMP OnCreateRecoContext(SPRECOCONTEXTHANDLE hSAPIRecoContext, void ** ppvDrvCtxt); STDMETHODIMP OnDeleteRecoContext(void * pvDrvCtxt); STDMETHODIMP OnCreateGrammar(void * pvEngineRecoContext, SPGRAMMARHANDLE hSAPIGrammar, void ** ppvEngineGrammar); STDMETHODIMP OnDeleteGrammar(void * pvEngineGrammar); STDMETHODIMP WordNotify(SPCFGNOTIFY Action, ULONG cWords, const SPWORDENTRY * pWords); STDMETHODIMP RuleNotify(SPCFGNOTIFY Action, ULONG cRules, const SPRULEENTRY * pRules); STDMETHODIMP LoadProprietaryGrammar(void * pvEngineGrammar, REFGUID rguidParam, const WCHAR * pszStringParam, const void * pvDataParam, ULONG ulDataSize, SPLOADOPTIONS Options) { return E_NOTIMPL; } STDMETHODIMP UnloadProprietaryGrammar(void * pvEngineGrammar) { return E_NOTIMPL; } STDMETHODIMP SetProprietaryRuleState(void * pvEngineGrammar, const WCHAR * pszName, void * pvReserved, SPRULESTATE NewState, ULONG * pcRulesChanged) { return E_NOTIMPL; } STDMETHODIMP SetProprietaryRuleIdState(void * pvEngineGrammar, DWORD dwRuleId, SPRULESTATE NewState) { return E_NOTIMPL; } /由于这个引擎不支持专有的语法,我们并不需要执行 / /此方法不仅仅是返回S_OK 。注意执行不返回 E_NOTIMPL 。 / /仅仅返回S_OK ,并忽略这个数据如果您不需要它执行专有语法。 STDMETHODIMP SetGrammarState(void * pvEngineGrammar, SPGRAMMARSTATE eGrammarState) { return S_OK; } STDMETHODIMP SetContextState(void * pvEngineContxt, SPCONTEXTSTATE eCtxtState) { return S_OK; } // 字典方法 STDMETHODIMP LoadSLM(void * pvEngineGrammar, const WCHAR * pszTopicName); STDMETHODIMP UnloadSLM(void * pvEngineGrammar); STDMETHODIMP SetSLMState(void * pvEngineGrammar, SPRULESTATE NewState); STDMETHODIMP IsPronounceable(void *pDrvGrammar, const WCHAR *pszWord, SPWORDPRONOUNCEABLE * pWordPronounceable); STDMETHODIMP SetWordSequenceData(void * pvEngineGrammar, const WCHAR * pText, ULONG cchText, const SPTEXTSELECTIONINFO * pInfo); STDMETHODIMP SetTextSelection(void * pvEngineGrammar, const SPTEXTSELECTIONINFO * pInfo); STDMETHODIMP SetAdaptationData(void * pvEngineCtxtCookie, const WCHAR * pText, const ULONG cch); STDMETHODIMP SetPropertyNum( SPPROPSRC eSrc, void* pvSrcObj, const WCHAR* pName, LONG lValue ); STDMETHODIMP GetPropertyNum( SPPROPSRC eSrc, void* pvSrcObj, const WCHAR* pName, LONG * plValue ); STDMETHODIMP SetPropertyString( SPPROPSRC eSrc, void* pvSrcObj, const WCHAR* pName, const WCHAR* pValue ); STDMETHODIMP GetPropertyString( SPPROPSRC eSrc, void* pvSrcObj, const WCHAR* pName, __deref_out_opt WCHAR** ppCoMemValue ); // 语音识别方法 STDMETHODIMP RecognizeStream(REFGUID rguidFmtId, const WAVEFORMATEX * pWaveFormatEx, HANDLE hRequestSync, HANDLE hDataAvailable, HANDLE hExit, BOOL fNewAudioStream, BOOL fRealTimeAudio, ISpObjectToken * pAudioObjectToken); STDMETHODIMP PrivateCall(void * pvEngineContext, void * pCallFrame, ULONG ulCallFrameSize); STDMETHODIMP PrivateCallEx(void * pvEngineContext, const void * pInCallFrame, ULONG ulCallFrameSize, void ** ppvCoMemResponse, ULONG * pcbResponse); // 语音识别线程 STDMETHODIMP InitThread( void * pvTaskData, HWND hwnd ) { return S_OK; } LRESULT STDMETHODCALLTYPE WindowMessage( void *pvTaskData, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) { return E_UNEXPECTED; } STDMETHODIMP ThreadProc( void *pvTaskData, HANDLE hExitThreadEvent, HANDLE hNotifyEvent, HWND hwndWorker, volatile const BOOL * pfContinueProcessing ); // 语音引擎方法 STDMETHODIMP PrivateCallImmediate( void *pvEngineContext, const void *pInCallFrame, ULONG ulInCallFrameSize, void **ppvCoMemResponse, ULONG *pulResponseSize); STDMETHODIMP SetAdaptationData2( void *pvEngineContext, __in_ecount(cch) const WCHAR *pAdaptationData, const ULONG cch, LPCWSTR pTopicName, SPADAPTATIONSETTINGS eSettings, SPADAPTATIONRELEVANCE eRelevance); STDMETHODIMP SetGrammarPrefix( void *pvEngineGrammar, __in_opt LPCWSTR pszPrefix, BOOL fIsPrefixRequired); STDMETHODIMP SetRulePriority( SPRULEHANDLE hRule, void *pvClientRuleContext, int nRulePriority); STDMETHODIMP EmulateRecognition( ISpPhrase *pPhrase, DWORD dwCompareFlags); STDMETHODIMP SetSLMWeight( void *pvEngineGrammar, float flWeight); STDMETHODIMP SetRuleWeight( SPRULEHANDLE hRule, void *pvClientRuleContext, float flWeight); STDMETHODIMP SetTrainingState( BOOL fDoingTraining, BOOL fAdaptFromTrainingData); STDMETHODIMP ResetAcousticModelAdaptation( void); STDMETHODIMP OnLoadCFG( void *pvEngineGrammar, const SPBINARYGRAMMAR *pGrammarData, ULONG ulGrammarID); STDMETHODIMP OnUnloadCFG( void *pvEngineGrammar, ULONG ulGrammarID); }; /****************************************************************************** * srengui.h *此文件包含的语音识别界面CSrEngineUI类。 *这里的方法可以是所谓的应用程序直接从ISpObjectToken获取识别结果 ******************************************************************************/ #pragma once #include "resource.h" class ATL_NO_VTABLE CSrEngineUI : public CComObjectRootEx<CComMultiThreadModel>, public CComCoClass<CSrEngineUI, &CLSID_SampleSREngineUI>, public ISpTokenUI { public: DECLARE_REGISTRY_RESOURCEID(IDR_SRENGUI) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSrEngineUI) COM_INTERFACE_ENTRY(ISpTokenUI) END_COM_MAP() public: STDMETHODIMP IsUISupported( const WCHAR * pszTypeOfUI, void * pvExtraData, ULONG cbExtraData, IUnknown * punkObject, BOOL *pfSupported); STDMETHODIMP DisplayUI( HWND hwndParent, const WCHAR * pszTitle, const WCHAR * pszTypeOfUI, void * pvExtraData, ULONG cbExtraData, ISpObjectToken * pToken, IUnknown * punkObject); }; #ifndef VER_H /* ver.h 定义用到的常用值 */ //#include <winver.h> #endif #include <windows.h> #define VER_FILETYPE VFT_APP #define VER_FILESUBTYPE VFT2_UNKNOWN #define VER_FILEDESCRIPTION_STR "SR SAMPLE ENGINE 5" #define VER_INTERNALNAME_STR "SRSAMPLEENG5" #define VERSION "5.0" #define VER_FILEVERSION_STR "5.0" #define VER_FILEVERSION 5,0 #define VER_PRODUCTVERSION_STR "5.0" #define VER_PRODUCTVERSION 5,0 #define OFFICIAL 1 #define FINAL 1 #if _DEBUG #define VER_DEBUG VS_FF_DEBUG #else #define VER_DEBUG 0 #endif #ifndef OFFICIAL #define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD #else #define VER_PRIVATEBUILD 0 #endif #ifndef FINAL #define VER_PRERELEASE VS_FF_PRERELEASE #else #define VER_PRERELEASE 0 #endif #define VER_FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #define VER_FILEOS VOS_DOS_WINDOWS32 #define VER_FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG) #define VER_COMPANYNAME_STR "Microsoft Corporation/0" #define VER_PRODUCTNAME_STR "Microsoft/256 Windows(TM) Operating System/0" #define VER_LEGALTRADEMARKS_STR / SampleSrEngine.cpp语音识别引擎实例 创建一个语音识别服务 #include "stdafx.h" #include "resource.h" #include <initguid.h> #include "SampleSrEngine.h" #include "SampleSrEngine_i.c" #include "SampleSrEngine.h" #include "srengobj.h" #include "srengui.h" #include "srengext.h" #include "srengalt.h" CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_SampleSREngine, CSrEngine) OBJECT_ENTRY(CLSID_SampleSREngineUI, CSrEngineUI) OBJECT_ENTRY(CLSID_SampleSRExtension, CSampleSRExtension) OBJECT_ENTRY(CLSID_SampleSREngineAlternates, CSrEngineAlternates) END_OBJECT_MAP() ///////////////////////////////////////////////////////////////////////////// // DLL Entry Point #ifdef _WIN32_WCE extern "C" BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, (HINSTANCE)hInstance, &LIBID_SRENGLib); } else if (dwReason == DLL_PROCESS_DETACH) _Module.Term(); return TRUE; // ok } #else extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, hInstance, &LIBID_SRENGLib); DisableThreadLibraryCalls(hInstance); } else if (dwReason == DLL_PROCESS_DETACH) _Module.Term(); return TRUE; // ok } #endif STDAPI DllCanUnloadNow(void) { return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; } STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { return _Module.GetClassObject(rclsid, riid, ppv); } STDAPI DllRegisterServer(void) { return _Module.RegisterServer(TRUE); } STDAPI DllUnregisterServer(void) { return _Module.UnregisterServer(TRUE); } SampleSrEngine.def引擎公开 LIBRARY "SampleSrEngine.DLL" EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE / / SampleSrEngine.idl : IDL编译器源SampleSrEngine.dll / /此文件将由MIDL工具 / /产生的类型库( SampleSrEngine.tlb )和编组代码 import "oaidl.idl"; import "ocidl.idl"; import "sapiddk.idl"; typedef [restricted, hidden] struct SPDISPLAYTOKEN { const WCHAR *pszLexical; const WCHAR *pszDisplay; BYTE bDisplayAttributes; } SPDISPLAYTOKEN; typedef [restricted, hidden] struct SPDISPLAYPHRASE { ULONG ulNumTokens; SPDISPLAYTOKEN *pTokens; } SPDISPLAYPHRASE; [ object, uuid(BBC18F3B-CF35-4f7c-99E8-D1F803AB4851), helpstring("ISampleSRExtension Interface"), pointer_default(unique) ] interface ISampleSRExtension : IUnknown { HRESULT ExamplePrivateEngineCall(void); }; [ object, uuid(C8D7C7E2-0DDE-44b7-AFE3-B0C991FBEB5E), helpstring("ISpDisplayAlternates Interface"), pointer_default(unique), local ] interface ISpDisplayAlternates : IUnknown { HRESULT GetDisplayAlternates( [in] const SPDISPLAYPHRASE *pPhrase, [in] ULONG cRequestCount, [annotation("__out_ecount_part(cRequestCount, *pcPhrasesReturned)")][out] SPDISPLAYPHRASE **ppCoMemPhrases, [out] ULONG *pcPhrasesReturned); HRESULT SetFullStopTrailSpace([in] ULONG ulTrailSpace); }; [ uuid(41B89B6C-9399-11D2-9623-00C04F8EE628), version(1.0), helpstring("SampleSrEngine 1.0 Type Library") ] library SRENGLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(41B89B79-9399-11D2-9623-00C04F8EE628), helpstring("Sample SR Engine Class") ] coclass SampleSREngine { [default] interface ISpSREngine; }; [ uuid(B84714C0-3BFD-405D-83C5-E9C486826AD5), helpstring("Sample SR Engine UI Class") ] coclass SampleSREngineUI { [default] interface ISpTokenUI; }; [ uuid(78771A48-CE55-46a5-B78C-B813E3403F82), helpstring("Sample SR Engine Extension Class") ] coclass SampleSRExtension { [default] interface ISampleSRExtension; interface ISpDisplayAlternates; }; [ uuid(882CAE4A-99BA-490b-BF80-CF69A60454A7), helpstring("Sample SR Engine Alternates Class") ] coclass SampleSREngineAlternates { [default] interface ISpSRAlternates; }; }; * srengui.cpp *此文件包含执行CSrEngineUI界面。 *本实施ISpTokenUI 。这是使用的应用程序,以显示用户界面。 *这里的方法可以是所谓的应用程序直接从ISpObjectToken获取电话 *能作为一个电话识别服务 ******************************************************************************/ #include "stdafx.h" #include "SampleSrEngine.h" #include "srengui.h" /************************************************* *************************** * CSrEngineUI : : IsUISupported * *----------------------------* *描述: *确定是否对用户界面的支持。提到主要引擎 *对象(如果已建立) ,可从punkObject 。 *如果没有空,这可能是一个ISpRecoContext ,其中一个引擎 *扩展接口可以得到。 * *返回: * S_OK成功 * E_INVALIDARG无效论点 ************************************************** ***************************/ STDMETHODIMP CSrEngineUI::IsUISupported(const WCHAR * pszTypeOfUI, void * pvExtraData, ULONG cbExtraData, IUnknown * punkObject, BOOL *pfSupported) { *pfSupported = FALSE; if (wcscmp(pszTypeOfUI, SPDUI_EngineProperties) == 0) *pfSupported = TRUE; if (wcscmp(pszTypeOfUI, SPDUI_UserTraining) == 0 && punkObject != NULL) *pfSupported = TRUE; if (wcscmp(pszTypeOfUI, SPDUI_MicTraining) == 0 && punkObject != NULL) *pfSupported = TRUE; return S_OK; } STDMETHODIMP CSrEngineUI::DisplayUI(HWND hwndParent, const WCHAR * pszTitle, const WCHAR * pszTypeOfUI, void * pvExtraData, ULONG cbExtraData, ISpObjectToken * pToken, IUnknown * punkObject) { if (wcscmp(pszTypeOfUI, SPDUI_EngineProperties) == 0) { if (punkObject) { MessageBoxW(hwndParent, L"Developer Sample Engine: Replace this with real engine properties dialog.", pszTitle, MB_OK); } } if (wcscmp(pszTypeOfUI, SPDUI_UserTraining) == 0) { MessageBoxW(hwndParent, L"Developer Sample Engine: Replace this with real user training wizard / dialog.", pszTitle, MB_OK); } if (wcscmp(pszTypeOfUI, SPDUI_MicTraining) == 0) { MessageBoxW(hwndParent, L"Developer Sample Engine: Replace this with real microphone training wizard / dialog.", pszTitle, MB_OK); } return S_OK; } /****************************************************************************** * srengobj.cpp *此文件包含执行CSrEngine级。 *本实施ISpSREngine , ISpSREngine2和ISpObjectWithToken 。 *这是主要识别对象 ******************************************************************************/ #include "stdafx.h" #include "SampleSrEngine.h" #include "srengobj.h" #include "SpHelper.h" #ifndef _WIN32_WCE #include "shfolder.h" #endif static const WCHAR DICT_WORD[] = L"Blah"; static const WCHAR ALT_WORD[] = L"Alt"; HRESULT CSrEngine::FinalConstruct() { HRESULT hr = S_OK; m_hQueueHasRoom = ::CreateEvent(NULL, TRUE, TRUE, NULL); m_FrameQueue.SetSpaceAvailEvent(m_hQueueHasRoom); CComPtr<ISpTaskManager> cpTaskMgr; hr = cpTaskMgr.CoCreateInstance(CLSID_SpResourceManager); if (SUCCEEDED(hr)) { hr = cpTaskMgr->CreateThreadControl(this, this, THREAD_PRIORITY_NORMAL, &m_cpDecoderThread); } if(SUCCEEDED(hr)) { hr = m_cpLexicon.CoCreateInstance(CLSID_SpLexicon); } return hr; } HRESULT CSrEngine::FinalRelease() { ::CloseHandle(m_hQueueHasRoom); return S_OK; } /**************************************************************************** * CSrEngine : : SetObjectToken * *---------------------------* *描述: *这种方法被称为后立即通过的SAPI引擎创建。 *它可以用来获得特定的注册表信息引擎。 *新的扫描引擎能恢复过来的凭证文件路径存储在安装过程中。 *该引擎还可以恢复用户设置为默认值的准确性,排斥等 *也可以有不同的发动机共享同一代码基础(的CLSID ) ,但具有不同登录信息 *例如:如果引擎支持不同的语言 *返回: * S_OK *失败(小时) *****************************************************************************/ STDMETHODIMP CSrEngine::SetObjectToken(ISpObjectToken * pToken) { HRESULT hr = S_OK; hr = SpGenericSetObjectToken(pToken, m_cpEngineObjectToken); if(FAILED(hr)) { return hr; } CComPtr<ISpDataKey> cpAttribKey; hr = pToken->OpenKey(L"Attributes", &cpAttribKey); if(SUCCEEDED(hr)) { WCHAR *psz = NULL; hr = cpAttribKey->GetStringValue(L"Desktop", &psz); ::CoTaskMemFree(psz); if(SUCCEEDED(hr)) { } else if(hr == SPERR_NOT_FOUND) { hr = cpAttribKey->GetStringValue(L"Telephony", &psz); ::CoTaskMemFree(psz); if(SUCCEEDED(hr)) { } } } if(SUCCEEDED(hr)) { WCHAR *pszLangID = NULL; hr = cpAttribKey->GetStringValue(L"Language", &pszLangID); if(SUCCEEDED(hr)) { m_LangID = (unsigned short)wcstol(pszLangID, NULL, 16); ::CoTaskMemFree(pszLangID); } else { // Default language (US English) m_LangID = 0x409; } } WCHAR *pszPath = NULL; hr = pToken->GetStorageFileName(CLSID_SampleSREngine, L"SampleEngDataFile", NULL, 0, &pszPath); ::CoTaskMemFree(pszPath); return hr; } /**************************************************************************** * CSrEngine : : GetObjectToken * *---------------------------* *描述: *这种方法被称为的SAPI令牌,如果找到这些对象令牌,该引擎使用 *返回: * S_OK *失败(小时) *****************************************************************************/ STDMETHODIMP CSrEngine::GetObjectToken(ISpObjectToken ** ppToken) { return SpGenericGetObjectToken(ppToken, m_cpEngineObjectToken); } /**************************************************************************** * CSrEngine : : SetSite * *---------------------------* *描述: *这就是所谓的让引擎的参考ISpSREngineSite 。 *引擎使用此呼吁回的SAPI 。 *返回: * S_OK * S_OK *失败(小时) *****************************************************************************/ STDMETHODIMP CSrEngine::SetSite(ISpSREngineSite *pSite) { m_cpSite = pSite; return S_OK; } /**************************************************************************** * CSrEngine : : SetRecoProfile * *---------------------------* *描述: *在RecoProfile是一个对象令牌举行关于当前 *用户和注册会议。该引擎可以存储在这里的任何信息 *它喜欢。它应存放在一个关键的RecoProfile主要命名引擎类ID 。 *返回: * S_OK *失败(小时) *****************************************************************************/ STDMETHODIMP CSrEngine::SetRecoProfile(ISpObjectToken *pProfile) { HRESULT hr = S_OK; WCHAR *pszCLSID, *pszPath = NULL; CComPtr<ISpDataKey> dataKey; m_cpUserObjectToken = pProfile; hr = ::StringFromCLSID(CLSID_SampleSREngine, &pszCLSID); if(FAILED(hr)) { return hr; } hr = pProfile->OpenKey(pszCLSID, &dataKey); if(hr == SPERR_NOT_FOUND) { hr = pProfile->CreateKey(pszCLSID, &dataKey); if(SUCCEEDED(hr)) { hr = dataKey->SetStringValue(L"GENDER", L"UNKNOWN"); } if(SUCCEEDED(hr)) { hr = dataKey->SetStringValue(L"AGE", L"UNKNOWN"); } if(SUCCEEDED(hr)) { pProfile->GetStorageFileName(CLSID_SampleSREngine, L"SampleEngTrainingFile", NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, &pszPath); } hr = AddEventString(SPEI_REQUEST_UI, 0, SPDUI_UserTraining); } else if(SUCCEEDED(hr)) { WCHAR *pszGender = NULL, *pszAge = NULL; hr = dataKey->GetStringValue(L"GENDER", &pszGender); if(SUCCEEDED(hr)) { hr = dataKey->GetStringValue(L"AGE", &pszAge); } if(SUCCEEDED(hr)) { hr = pProfile->GetStorageFileName(CLSID_SampleSREngine, L"SampleEngTrainingFile", NULL, 0, &pszPath); } ::CoTaskMemFree(pszGender); ::CoTaskMemFree(pszAge); } ::CoTaskMemFree(pszPath); ::CoTaskMemFree(pszCLSID); return hr; } /**************************************************************************** * CSrEngine : : OnCreateRecoContext * *---------------------------* *描述: *这种方法被称为每当有新reco方面是建立在 *申请使用这个引擎。 *本示例引擎不严格需要信息reco背景 *但是,以供参考,我们将继续清单一人。 *返回: * S_OK *失败(小时) *****************************************************************************/ STDMETHODIMP CSrEngine::OnCreateRecoContext(SPRECOCONTEXTHANDLE hSapiContext, void ** ppvDrvCtxt) { CContext * pContext = new CContext(hSapiContext); // Store a reference to the CContext structure *ppvDrvCtxt = pContext; m_ContextList.InsertHead(pContext); return S_OK; } /**************************************************************************** * CSrEngine : : OnDeleteRecoContext * *---------------------------* *描述: *这种方法被称为每次reco方面被删除。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::OnDeleteRecoContext(void * pvDrvCtxt) { CContext * pContext = (CContext *) pvDrvCtxt; m_ContextList.Remove(pContext); delete pContext; return S_OK; } /**************************************************************************** * CSrEngine : : OnCreateGrammar * *---------------------------* *描述: *这种方法被称为每当有新reco语法中创建 *申请使用这个引擎。 *我们保留名单语法-存储一个指针列表条目ppvEngineGrammar 。 *返回: * S_OK * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::OnCreateGrammar(void * pvEngineRecoContext, SPGRAMMARHANDLE hSapiGrammar, void ** ppvEngineGrammar) { CContext * pContext = (CContext *) pvEngineRecoContext; _ASSERT(m_ContextList.Find(pContext->m_hSapiContext)); CDrvGrammar * pGrammar = new CDrvGrammar(hSapiGrammar); *ppvEngineGrammar = pGrammar; m_GrammarList.InsertHead(pGrammar); return S_OK; } /**************************************************************************** * CSrEngine : : OnDeleteGrammar * *---------------------------* *描述: *这种方法被称为每次reco语法被删除。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::OnDeleteGrammar(void * pvDrvGrammar) { CDrvGrammar * pGrammar = (CDrvGrammar *)pvDrvGrammar; m_GrammarList.Remove(pGrammar); delete pGrammar; return S_OK; } STDMETHODIMP CSrEngine::WordNotify(SPCFGNOTIFY Action, ULONG cWords, const SPWORDENTRY * pWords) { HRESULT hr = S_OK; ULONG i; WCHAR *wordPron; switch(Action){ case SPCFGN_ADD: SPWORDENTRY WordEntry; for(i = 0; SUCCEEDED(hr) && i < cWords; i++) { WordEntry = pWords[i]; hr = m_cpSite->GetWordInfo(&WordEntry, SPWIO_WANT_TEXT); if(SUCCEEDED(hr) && WordEntry.aPhoneId) { size_t cWordPron = wcslen(WordEntry.aPhoneId) + 1; wordPron = new WCHAR[cWordPron]; wcscpy_s(wordPron, cWordPron, WordEntry.aPhoneId); ::CoTaskMemFree((void*)WordEntry.aPhoneId); } else { SPWORDPRONUNCIATIONLIST PronList; PronList.pFirstWordPronunciation = 0; PronList.pvBuffer = 0; PronList.ulSize = 0; hr = m_cpLexicon->GetPronunciations(WordEntry.pszLexicalForm, eLEXTYPE_APP | eLEXTYPE_USER, pWords[i].LangID, &PronList); if(SUCCEEDED(hr)) { if(PronList.pFirstWordPronunciation != NULL) { size_t cWordPron = wcslen(PronList.pFirstWordPronunciation->szPronunciation) + 1; wordPron = new WCHAR[cWordPron]; wcscpy_s(wordPron, cWordPron, PronList.pFirstWordPronunciation->szPronunciation); ::CoTaskMemFree(PronList.pvBuffer); } else { wordPron = NULL; } } else if(hr == SPERR_NOT_IN_LEX) wordPron = NULL; hr = S_OK; } else { break; } if(SUCCEEDED(hr)) { hr = m_cpSite->SetWordClientContext(WordEntry.hWord, wordPron); } } if (SUCCEEDED(hr)) { ::CoTaskMemFree((void*)WordEntry.pszDisplayText); ::CoTaskMemFree((void*)WordEntry.pszLexicalForm); } } break; case SPCFGN_REMOVE: for(i = 0; i < cWords; i++) { WordEntry = pWords[i]; wordPron = (WCHAR *) WordEntry.pvClientContext; if(wordPron) { delete[] wordPron; } } break; } return hr; } /**************************************************************************** * CSrEngine : : RuleNotify * *---------------------------* *描述: *这种方法被称为由SAPI的通知引擎的规则, *指挥及控制语法。该命令或行动CFG桩语法是这种形式: * WordNotify ( SPCFGN_ADD ) -添加关键词 * RuleNotify ( SPCFGN_ADD ) -添加规则 * RuleNotify ( SPCFGN_ACTIVATE ) -激活规则,以表明它们是用于识别 * RuleNotify ( SPCFGN_INVALIDATE ) -如果规则得到编辑的应用程序那么这就是所谓的 * RuleNotify ( SPCFGN_DEACTIVE ) -停用规则 * RuleNotify ( SPCFGN_REMOVE ) -删除规则 * WordNotify ( SPCFGN_REMOVE ) -删除字词 * *该引擎可致电GetRuleInfo找到初始状态中的规则, *然后GetStateInfo找到有关国家和转型以后的规则。 *如果规则编辑然后SPCFGN_INVALIDATE称为表明规则已经改变,使引擎 *必须重法治的信息。 * *新的扫描引擎能获得所有有关的规则之前或期间承认。 *在此示例中,我们只是保持发动机的清单规则最初然后等待 *直到我们要生成的结果,然后找到一个随机的路径规则。 * *返回: * S_OK * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::RuleNotify(SPCFGNOTIFY Action, ULONG cRules, const SPRULEENTRY * pRules) { ULONG i; CRuleEntry *pRuleEntry; switch (Action) { case SPCFGN_ADD: for (i = 0; i < cRules; i++) { pRuleEntry = new CRuleEntry; pRuleEntry->m_hRule = pRules[i].hRule; pRuleEntry->m_fTopLevel = (pRules[i].Attributes & SPRAF_TopLevel); pRuleEntry->m_fActive = (pRules[i].Attributes & SPRAF_Active); m_RuleList.InsertHead(pRuleEntry); m_cpSite->SetRuleClientContext(pRules[i].hRule, (void *)pRuleEntry); } break; case SPCFGN_REMOVE: for (i = 0; i < cRules; i++) { pRuleEntry = m_RuleList.Find(pRules[i].hRule); _ASSERT(pRuleEntry); // The rule must have been added before being removed m_RuleList.Remove(pRuleEntry); delete pRuleEntry; } break; case SPCFGN_ACTIVATE: for (i = 0; i < cRules; i++) { pRuleEntry = m_RuleList.Find(pRules[i].hRule); _ASSERT(pRuleEntry && !pRuleEntry->m_fActive && pRuleEntry->m_fTopLevel); if (pRuleEntry != NULL) { pRuleEntry->m_fActive = TRUE; } } break; case SPCFGN_DEACTIVATE: for (i = 0; i < cRules; i++) { pRuleEntry = m_RuleList.Find(pRules[i].hRule); _ASSERT(pRuleEntry && pRuleEntry->m_fActive && pRuleEntry->m_fTopLevel); if (pRuleEntry != NULL) { pRuleEntry->m_fActive = FALSE; } } break; case SPCFGN_INVALIDATE: for (i = 0; i < cRules; i++) { pRuleEntry = m_RuleList.Find(pRules[i].hRule); _ASSERT(pRuleEntry); if (pRuleEntry != NULL) { pRuleEntry->m_fTopLevel = (pRules[i].Attributes & SPRAF_TopLevel); pRuleEntry->m_fActive = (pRules[i].Attributes & SPRAF_Active); } } break; } return S_OK; } /**************************************************************************** * CSrEngine : : LoadSLM * *---------------------------* *描述: *时调用的SAPI希望引擎加载dictaion语言模式( SLM ) 。 *对于每一个听写reco gramar以及命令与征服规则可以被装载。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::LoadSLM(void * pvEngineGrammar, const WCHAR * pszTopicName) { if (pszTopicName) { } CDrvGrammar * pGrammar = (CDrvGrammar *)pvEngineGrammar; pGrammar->m_SLMLoaded = TRUE; return S_OK; } /**************************************************************************** * CSrEngine : : UnloadSLM * *---------------------------* *描述: *时调用的SAPI希望引擎删除解运。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::UnloadSLM(void *pvEngineGrammar) { CDrvGrammar * pGrammar = (CDrvGrammar *)pvEngineGrammar; pGrammar->m_SLMLoaded = FALSE; return S_OK; } /**************************************************************************** * CSrEngine : : SetSLMState * *---------------------------* *描述: *调用以激活或停用的语法 * NewState要么SPRS_ACTIVE或SPRS_INACTIVE 。 *返回: * S_OK *****************************************************************************/ HRESULT CSrEngine::SetSLMState(void * pvDrvGrammar, SPRULESTATE NewState) { CDrvGrammar * pGrammar = (CDrvGrammar *)pvDrvGrammar; if (NewState != SPRS_INACTIVE) { pGrammar->m_SLMActive = TRUE; } else { pGrammar->m_SLMActive = FALSE; } return S_OK; } /**************************************************************************** * CSrEngine : : SetWordSequenceData * *---------------------------* *描述: *如果应用程序提交一个文本缓冲区的SAPI这种方法被称为。 *的文本缓冲区这里提供可以用于CFGs的文本缓冲区过渡, *或听写提供资料的发动机事先看见屏幕上的文字。 *本示例引擎就是使用文字的文本缓冲区缓冲区过渡。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::SetWordSequenceData(void *pvEngineGrammar, const WCHAR *pText, ULONG cchText, const SPTEXTSELECTIONINFO *pInfo) { CDrvGrammar * pGrammar = (CDrvGrammar*)pvEngineGrammar; if(pGrammar->m_pWordSequenceText) { delete pGrammar->m_pWordSequenceText; } if(cchText) { pGrammar->m_pWordSequenceText = new WCHAR[cchText]; memcpy((void *)pGrammar->m_pWordSequenceText, pText, sizeof(WCHAR) * cchText); pGrammar->m_cchText = cchText; } else { pGrammar->m_pWordSequenceText = NULL; pGrammar->m_cchText = NULL; } SetTextSelection(pvEngineGrammar, pInfo); return S_OK; } /**************************************************************************** * CSrEngine : : SetTextSelection * *---------------------------* *描述: *这种方法告诉引擎如果SPTEXTSELECTIONINFO结构 *已更新。本示例只使用发动机领域ulStartActiveOffset和cchActiveChars的SPTEXTSELECTIONINFO 。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::SetTextSelection(void * pvEngineGrammar, const SPTEXTSELECTIONINFO * pInfo) { CDrvGrammar * pGrammar = (CDrvGrammar*)pvEngineGrammar; if (pGrammar->m_pInfo) { delete pGrammar->m_pInfo; } if (pInfo) { pGrammar->m_pInfo = new SPTEXTSELECTIONINFO(*pInfo); } else { pGrammar->m_pInfo = NULL; } return S_OK; } /**************************************************************************** * CSrEngine : : IsPronounceable * *---------------------------* *描述: *发动机应该回到它是否已经或将能够 *产生的这个词的发音。 *在此示例中的引擎,这是总是如此。 *返回: * S_OK *****************************************************************************/ STDMETHODIMP CSrEngine::IsPronounceable(void * pDrvGrammar, const WCHAR * pszWord, SPWORDPRONOUNCEABLE * pWordPronounceable) { *pWordPronounceable = SPWP_KNOWN_WORD_PRONOUNCEABLE; return S_OK; } /**************************************************************************** * CSrEngine : : SetAdaptationData * *---------------------------* *描述: *这种方法可以使用的应用程序,使文本数据引擎 *为适应语言模型等方法只能被称为 *的应用程序后,如果收到了SPEI_ADAPTATION活动。 *返回: * E_UNEXPECTED *****************************************************************************/ STDMETHODIMP CSrEngine::SetAdaptationData(void * pvEngineCtxtCookie, const WCHAR *pAdaptationData, const ULONG cch) { _ASSERT(0); return E_UNEXPECTED; } HRESULT CSrEngine::AddEvent(SPEVENTENUM eEventId, ULONGLONG ullStreamPos, WPARAM wParam, LPARAM lParam) { HRESULT hr = S_OK; SPEVENT Event; Event.eEventId = eEventId; Event.elParamType = SPET_LPARAM_IS_UNDEFINED; Event.ulStreamNum = 0; Event.ullAudioStreamOffset = ullStreamPos; Event.wParam = wParam; Event.lParam = lParam; hr = m_cpSite->AddEvent(&Event, NULL); return hr; } HRESULT CSrEngine::AddEventString(SPEVENTENUM eEventId, ULONGLONG ullStreamPos, const WCHAR * psz, WPARAM wParam) { HRESULT hr = S_OK; SPEVENT Event; Event.eEventId = eEventId; Event.elParamType = SPET_LPARAM_IS_STRING; Event.ulStreamNum = 0; Event.ullAudioStreamOffset = ullStreamPos; Event.wParam = wParam; Event.lParam = (LPARAM)psz; hr = m_cpSite->AddEvent(&Event, NULL); return hr; } /************************************************* *************************** * CSrEngine : : RecognizeStream * #定义BLOCKSIZE 220 / / 1 / 100秒 *---------------------------* *描述: *这是方法的SAPI呼吁承认发生。 *发动机只能由这个方法返回后,他们已经阅读所有数据 *并完成所有的承认,他们正在尽在此流。 *因此,这种方法是让一个线程的引擎做承认, *和引擎可能会创造更多的线程。 * *在此示例中,我们不断地读取数据,使用此线程,然后执行 *非常基本的语音检测,并通过数据传输到识别线程,这 *产生的假设和结果。 * *参数: * * - REFGUID rguidFormatId -这是的GUID输入的音频格式 * -常量WAVEFORMATEX * pWaveFormatEx -这是扩大信息WAV格式的音频格式 * -拉手hRequestSync -这是使用的Win32事件表明,有尚未完成的任务 *和发动机应要求同步( )的SAPI的来处理这些。 * -拉手hDataAvailable -本的Win32事件是用来告诉引擎,数据可以被读取。 *频率,这是一套可控制的SetBufferNotifySize方法。 * -拉手hExit -本的Win32事件表示引擎正在关闭,并应立即结束。 * -布尔fNewAudioStream -这表明这是一个新的输入流 *例如:在应用方面做了新的SetInput要求,而不是仅仅重新启动前流。 * -布尔fRealTimeAudio -这表明,从投入的实时ISpAudio流,而不是说,一个文件 * - ISpObjectToken * pAudioObjectToken -这是象征性的对象代表音频输入装置 *引擎可能要查询这一点。 * *返回: * S_OK *失败(小时) ************************************************** ***************************/ STDMETHODIMP CSrEngine::RecognizeStream(REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, HANDLE hRequestSync, HANDLE hDataAvailable, HANDLE hExit, BOOL fNewAudioStream, BOOL fRealTimeAudio, ISpObjectToken * pAudioObjectToken) { HRESULT hr = S_OK; m_hRequestSync = hRequestSync; hr = m_cpDecoderThread->StartThread(0, NULL); if (SUCCEEDED(hr)) { const HANDLE aWait[] = { hExit, m_hQueueHasRoom }; while (TRUE) { ULONG cbRead; hr = m_cpSite->Read(aData, sizeof(aData), &cbRead); if (hr != S_OK || cbRead < sizeof(aData)) { break; } BOOL bNoiseDetected = FALSE; SHORT * pBuffer = (SHORT *)aData; for (ULONG i = 0; i < cbRead; i += 2, pBuffer++) { if (*pBuffer < (SHORT)-3000 || *pBuffer > (SHORT)3000) { bNoiseDetected = TRUE; break; } } BOOL bBlock = m_FrameQueue.IsFull(); if(bBlock) { if (::WaitForMultipleObjects(sp_countof(aWait), aWait, FALSE, INFINITE) == WAIT_OBJECT_0) { break; } } m_FrameQueue.InsertTail(bNoiseDetected); m_cpDecoderThread->Notify(); } m_cpDecoderThread->WaitForThreadDone(TRUE, &hr, 30 * 1000); } m_hRequestSync = NULL; return hr; } STDMETHODIMP CSrEngine::ThreadProc(void *, HANDLE hExitThreadEvent, HANDLE hNotifyEvent, HWND hwndWorker, volatile const BOOL * pfContinueProcessing) { HRESULT hr = S_OK; const HANDLE aWait[] = { hExitThreadEvent, hNotifyEvent, m_hRequestSync }; ULONG block = 0; ULONG silenceafternoise = 0; DWORD waitres; m_bSoundStarted = FALSE; m_bPhraseStarted = FALSE; m_cBlahBlah = 0; m_ullStart = 0; m_ullEnd = 0; while (*pfContinueProcessing) { if(m_bPhraseStarted && (block - m_ullStart / BLOCKSIZE) < 5 * 100) { --cEvents; } waitres = ::WaitForMultipleObjects(cEvents, aWait, FALSE, INFINITE); switch (waitres) { case WAIT_OBJECT_0: break; case WAIT_OBJECT_0 + 1: m_cpSite->UpdateRecoPos((ULONGLONG)block * BLOCKSIZE); if (m_ullStart == 0 && !m_bPhraseStarted) { m_cpSite->Synchronize((ULONGLONG)block * BLOCKSIZE); } while (m_FrameQueue.HasData()) { BOOL bNoise = m_FrameQueue.RemoveHead(); block++; if (bNoise) { silenceafternoise = 0; if (m_ullStart == 0) { m_ullStart = (ULONGLONG)block * BLOCKSIZE; } m_ullEnd = (ULONGLONG)block * BLOCKSIZE; _CheckRecognition(); } else { silenceafternoise++; if (silenceafternoise > 50) { if (m_bSoundStarted) { if (m_bPhraseStarted) { _NotifyRecognition(FALSE, m_cBlahBlah); } AddEvent(SPEI_SOUND_END, m_ullEnd); // send the sound end event m_bSoundStarted = FALSE; m_bPhraseStarted = FALSE; } m_ullStart = 0; m_ullEnd = 0; } } if (block % (100 * 30) == 20 * 100) { const SPINTERFERENCE rgspi[] = { SPINTERFERENCE_NOISE, SPINTERFERENCE_NOSIGNAL, SPINTERFERENCE_TOOLOUD, SPINTERFERENCE_TOOQUIET }; AddEvent(SPEI_INTERFERENCE, block*BLOCKSIZE, 0, rgspi[rand() % 4]); } else if (block % (100 * 30) == 22 * 100) { AddEvent(SPEI_INTERFERENCE, block*BLOCKSIZE, 0, SPINTERFERENCE_NONE); } else if (block == 10 * 100) { AddEventString(SPEI_REQUEST_UI, block * BLOCKSIZE, SPDUI_UserTraining); } else if (block == 11 * 100) { AddEventString(SPEI_REQUEST_UI, block * BLOCKSIZE, NULL); } } break; case WAIT_OBJECT_0 + 2: m_cpSite->Synchronize((ULONGLONG)block * BLOCKSIZE); m_ullStart = block * BLOCKSIZE; if(m_ullEnd < m_ullStart) { m_ullEnd = m_ullStart; } break; default: _ASSERT(FALSE); break; } } if (m_bPhraseStarted) { _NotifyRecognition(FALSE, m_cBlahBlah); } if (m_bSoundStarted) { AddEvent(SPEI_SOUND_END, m_ullEnd); } return S_OK; } void CSrEngine::_CheckRecognition() { ULONG duration, blahs; if (m_ullEnd > m_ullStart) { duration = (ULONG)(m_ullEnd - m_ullStart); if (duration >= BLOCKSIZE * 100 * 1 / 8) { if (!m_bSoundStarted) { AddEvent(SPEI_SOUND_START, m_ullStart); m_bSoundStarted = TRUE; m_cBlahBlah = 0; } if (duration >= BLOCKSIZE * 100 * 1 / 4) { blahs = duration / (BLOCKSIZE * 100 * 1 / 4); if (blahs != m_cBlahBlah) { m_cBlahBlah = blahs; if (!m_bPhraseStarted) { m_bPhraseStarted = TRUE; AddEvent(SPEI_PHRASE_START, m_ullStart); } _NotifyRecognition(TRUE, blahs); } } } } } void CSrEngine::_NotifyRecognition( BOOL fHypothesis, ULONG nWords ) { HRESULT hr = S_OK; ULONG cActiveCFGRules = 0; CRuleEntry * pRule = m_RuleList.GetHead(); for(; pRule; pRule = m_RuleList.GetNext(pRule)) { if( pRule->m_fActive ) { cActiveCFGRules++; } } ULONG cActiveSLM = 0; CDrvGrammar * pGram = m_GrammarList.GetHead(); for(; pGram; pGram = m_GrammarList.GetNext(pGram)) { if(pGram->m_SLMActive) { cActiveSLM++; } } if(cActiveCFGRules && cActiveSLM) { if(rand() % 2) { cActiveSLM = 0; } else { cActiveCFGRules = 0; } } SPRECORESULTINFO Result; memset(&Result, 0, sizeof(SPRECORESULTINFO)); Result.cbSize = sizeof(SPRECORESULTINFO); Result.fHypothesis = fHypothesis; Result.ullStreamPosStart = m_ullStart; Result.ullStreamPosEnd = m_ullEnd; if( cActiveCFGRules ) { hr = WalkCFGRule(&Result, cActiveCFGRules, fHypothesis, nWords, m_ullStart, (ULONG)(m_ullEnd - m_ullStart)); if( SUCCEEDED(hr) ) { m_cpSite->Recognition(&Result); for(ULONG i = 0; i < Result.ulNumAlts; i++) { Result.aPhraseAlts[i].pPhrase->Release(); } Result.pPhrase->Release(); delete[] Result.aPhraseAlts; } } else if(cActiveSLM) { hr = WalkSLM(&Result, cActiveSLM, nWords, m_ullStart, (ULONG)(m_ullEnd - m_ullStart)); if( SUCCEEDED(hr) ) { m_cpSite->Recognition(&Result); Result.pPhrase->Release(); delete[] Result.pvEngineData; } } else if(!fHypothesis) { Result.eResultType = SPRT_FALSE_RECOGNITION; m_cpSite->Recognition(&Result); } } HRESULT CSrEngine::CreatePhraseFromRule( CRuleEntry * pRule, BOOL fHypothesis, ULONGLONG ullAudioPos, ULONG ulAudioSize, ISpPhraseBuilder** ppPhrase ) { HRESULT hr = S_OK; SPRULEENTRY RuleInfo; RuleInfo.hRule = pRule->m_hRule; hr = m_cpSite->GetRuleInfo(&RuleInfo, SPRIO_NONE); if( SUCCEEDED(hr) ) { const ULONG MAXPATH = 200; SPPATHENTRY Path[MAXPATH]; ULONG cTrans; hr = RecurseWalk(RuleInfo.hInitialState, Path, &cTrans); if (cTrans) { ULONG ulInterval = ulAudioSize/cTrans; for (ULONG ul = 0; ul < cTrans && ul < MAXPATH; ul++) { Path[ul].elem.ulAudioStreamOffset = ul * ulInterval; Path[ul].elem.ulAudioSizeBytes = ulInterval/2; } } if (SUCCEEDED(hr)) { SPPARSEINFO ParseInfo; memset(&ParseInfo, 0, sizeof(ParseInfo)); ParseInfo.cbSize = sizeof(SPPARSEINFO); ParseInfo.hRule = pRule->m_hRule; ParseInfo.ullAudioStreamPosition = ullAudioPos; ParseInfo.ulAudioSize = ulAudioSize; ParseInfo.cTransitions = cTrans; ParseInfo.pPath = Path; ParseInfo.fHypothesis = fHypothesis; ParseInfo.SREngineID = CLSID_SampleSREngine; ParseInfo.ulSREnginePrivateDataSize = 0; ParseInfo.pSREnginePrivateData = NULL; hr = m_cpSite->ParseFromTransitions(&ParseInfo, ppPhrase ); if(SUCCEEDED(hr)) { for(ULONG i = 0; i < cTrans; i++) { if(Path[i].elem.pszDisplayText) { delete const_cast<WCHAR*>(Path[i].elem.pszDisplayText); } } } } } return hr; } CRuleEntry* CSrEngine::FindRule( ULONG ulRuleIndex ) { CRuleEntry * pRule = m_RuleList.GetHead(); ULONG ulRule = 0; while( pRule ) { if( pRule->m_fActive && ( ulRule++ == ulRuleIndex ) ) { break; } pRule = m_RuleList.GetNext( pRule ); } _ASSERT(pRule && pRule->m_fActive); return pRule; } CRuleEntry* CSrEngine::NextRuleAlt( CRuleEntry * pPriRule, CRuleEntry * pLastRule ) { CRuleEntry * pRule = (pLastRule)?(pLastRule):(m_RuleList.GetHead()); for(; pRule; pRule = m_RuleList.GetNext(pRule)) { if( pRule->m_fActive && ( m_cpSite->IsAlternate( pPriRule->m_hRule, pRule->m_hRule ) == S_OK ) ) { break; } } return pRule; } HRESULT CSrEngine::WalkCFGRule( SPRECORESULTINFO * pResult, ULONG cRulesActive, BOOL fHypothesis, ULONG nWords, ULONGLONG ullAudioPos, ULONG ulAudioSize) { HRESULT hr = E_FAIL; CRuleEntry * pPriRule = NULL; pResult->ulSizeEngineData = 0; pResult->pvEngineData = NULL; pResult->eResultType = SPRT_CFG; pResult->hGrammar = NULL; while (hr == E_FAIL) { pPriRule = FindRule( rand() % cRulesActive ); hr = CreatePhraseFromRule( pPriRule, fHypothesis, ullAudioPos, ulAudioSize, &pResult->pPhrase ); } if (hr != S_OK) { _ASSERT(FALSE); } SPPHRASE* pPriPhraseInfo = NULL; if( SUCCEEDED( hr ) ) { hr = pResult->pPhrase->GetPhrase( &pPriPhraseInfo ); } ULONG ulNumAlts = 0; if( SUCCEEDED( hr ) ) { hr = m_cpSite->GetMaxAlternates( pPriRule->m_hRule, &ulNumAlts ); } if( SUCCEEDED( hr ) && ulNumAlts ) { pResult->aPhraseAlts = new SPPHRASEALT[ulNumAlts]; if( pResult->aPhraseAlts ) { memset( pResult->aPhraseAlts, 0, ulNumAlts * sizeof(SPPHRASEALT) ); CRuleEntry * pAltRule = NULL; for( ULONG i = 0; SUCCEEDED( hr ) && (i < ulNumAlts); ++i ) { pAltRule = NextRuleAlt( pPriRule, pAltRule ); if( !pAltRule ) { break; } hr = CreatePhraseFromRule( pAltRule, fHypothesis, ullAudioPos, ulAudioSize, &pResult->aPhraseAlts[i].pPhrase ); SPPHRASE* pAltPhraseInfo = NULL; if( SUCCEEDED( hr ) ) { hr = pResult->aPhraseAlts[i].pPhrase->GetPhrase( &pAltPhraseInfo ); } if( SUCCEEDED( hr ) ) { ++pResult->ulNumAlts; pResult->aPhraseAlts[i].cElementsInParent = pPriPhraseInfo->Rule.ulCountOfElements; pResult->aPhraseAlts[i].cElementsInAlternate = pAltPhraseInfo->Rule.ulCountOfElements; static BYTE AltData[] = { 0xED, 0xED, 0xED, 0xED }; pResult->aPhraseAlts[i].pvAltExtra = &AltData; pResult->aPhraseAlts[i].cbAltExtra = sp_countof( AltData ); } if( pAltPhraseInfo ) { ::CoTaskMemFree( pAltPhraseInfo ); } } } else { E_OUTOFMEMORY; } } // Cleanup main phrase information if( pPriPhraseInfo ) { ::CoTaskMemFree( pPriPhraseInfo ); } // Cleanup on failure if( FAILED( hr ) ) { if( pResult->pPhrase ) { pResult->pPhrase->Release(); pResult->pPhrase = NULL; } for( ULONG i = 0; i < pResult->ulNumAlts; ++i ) { pResult->aPhraseAlts[i].pPhrase->Release(); pResult->aPhraseAlts[i].pPhrase = NULL; } pResult->ulNumAlts = 0; delete[] pResult->aPhraseAlts; pResult->aPhraseAlts = NULL; } return hr; } HRESULT CSrEngine::WalkSLM(SPRECORESULTINFO * pResult, ULONG cSLMActive, ULONG nWords, ULONGLONG ullAudioPos, ULONG ulAudioSize) { HRESULT hr = S_OK; ULONG ulGramIndex = rand() % cSLMActive; CDrvGrammar * pGram = m_GrammarList.GetHead(); ULONG nGram = 0; for(; pGram; pGram = m_GrammarList.GetNext(pGram)) { if(pGram->m_SLMActive) { if(nGram == ulGramIndex) { break; } nGram++; } } _ASSERT(pGram && pGram->m_SLMActive); if( pGram == NULL ) { return E_FAIL; } SPPHRASE phrase; memset(&phrase, 0, sizeof(SPPHRASE)); phrase.cbSize = sizeof(SPPHRASE); phrase.LangID = m_LangID; phrase.ullAudioStreamPosition = ullAudioPos; phrase.ulAudioSizeBytes = ulAudioSize; phrase.SREngineID = CLSID_SampleSREngine; ULONG cb = nWords * sizeof(SPPHRASEELEMENT); SPPHRASEELEMENT* pElements = (SPPHRASEELEMENT *)_malloca(cb); memset(pElements, 0, cb); for (ULONG n = 0; n < nWords; n++) { ULONG ulInterval = ulAudioSize/nWords; pElements[n].bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; pElements[n].pszDisplayText = DICT_WORD; pElements[n].ulAudioStreamOffset = n * ulInterval; pElements[n].ulAudioSizeBytes = ulInterval/2; } phrase.Rule.ulCountOfElements = nWords; phrase.pElements = pElements; CComPtr<ISpPhraseBuilder> cpBuilder; hr = cpBuilder.CoCreateInstance(CLSID_SpPhraseBuilder); if (SUCCEEDED(hr)) { hr = cpBuilder->InitFromPhrase(&phrase); } if (SUCCEEDED(hr)) { pResult->ulSizeEngineData = sizeof(ALT_WORD) * nWords; pResult->pvEngineData = new WCHAR[(sizeof(ALT_WORD) / sizeof(WCHAR )) * nWords]; if (pResult->pvEngineData == NULL) { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { WCHAR *pC = (WCHAR *)pResult->pvEngineData; size_t cRemainingSize = (size_t)pResult->ulSizeEngineData / sizeof(WCHAR); for(ULONG i = 0; i < nWords; i++) { wcscpy_s(pC, cRemainingSize, ALT_WORD); size_t cAltWord = wcslen(ALT_WORD); pC += cAltWord; *pC = ' '; pC++; cRemainingSize -= cAltWord + 1; } *(--pC) = '/0'; pResult->eResultType = SPRT_SLM; pResult->hGrammar = pGram->m_hSapiGrammar; } if (SUCCEEDED(hr)) { pResult->pPhrase = cpBuilder.Detach(); } else { delete[] pResult->pvEngineData; pResult->pvEngineData = NULL; pResult->ulSizeEngineData = 0; } _freea(pElements); return hr; } HRESULT CSrEngine::WalkTextBuffer(void* pvGrammarCookie, SPPATHENTRY * pPath, SPTRANSITIONID hId, ULONG * pcTrans) { HRESULT hr = S_OK; CDrvGrammar * pGrammar = (CDrvGrammar *) pvGrammarCookie; _ASSERT(pGrammar->m_pWordSequenceText && pGrammar->m_cchText >= 2); if (!pGrammar->m_pWordSequenceText || pGrammar->m_cchText < 2) { return E_UNEXPECTED; } *pcTrans = 0; ULONG nPhrase = 0; const WCHAR *cPhrase; ULONG ulStartActiveOffset = 0; //The default value with text selection ULONG cchActiveChars = pGrammar->m_cchText - 2; //The default value with text selection ULONG ccChars = 0; if (pGrammar->m_pInfo) { ulStartActiveOffset = pGrammar->m_pInfo->ulStartActiveOffset; cchActiveChars = pGrammar->m_pInfo->cchActiveChars; } for (cPhrase = pGrammar->m_pWordSequenceText + ulStartActiveOffset; ccChars < cchActiveChars && (*cPhrase != L'/0' || *(cPhrase + 1) != '/0'); ccChars++, cPhrase++) { if(*cPhrase != L'/0' && *(cPhrase + 1) == L'/0') { nPhrase++; } } if (nPhrase == 0) { return E_FAIL; } nPhrase = rand() % nPhrase; //nPhrase would be 0 index ULONG nP = 0; for(cPhrase = pGrammar->m_pWordSequenceText + ulStartActiveOffset; nP != nPhrase; cPhrase++) { if(*cPhrase == L'/0') { nP++; } } // Count words in sentence ULONG nWord = 1; const WCHAR *cWord; for(cWord = cPhrase; *cWord != L'/0' && ULONG(cWord - pGrammar->m_pWordSequenceText) < ulStartActiveOffset + cchActiveChars; cWord++) { if(iswspace(*cWord)) { while(*(cWord+1) && iswspace(*(cWord+1)) && ULONG(cWord - pGrammar->m_pWordSequenceText) < ulStartActiveOffset + cchActiveChars - 1) {cWord++;} nWord++; } } ULONG startWord = rand() % nWord; ULONG countWord = rand() % (nWord - startWord) + 1; for(nWord = 0, cWord = cPhrase; nWord != startWord; cWord++) { if(iswspace(*cWord)) { while(*(cWord+1) && iswspace(*(cWord+1)) && ULONG(cWord - pGrammar->m_pWordSequenceText) < ulStartActiveOffset + cchActiveChars - 1) {cWord++;} nWord++; } } const WCHAR *cW = cWord; for(nWord = 0; nWord != countWord; cWord++) { if(*cWord == L'/0' || iswspace(*cWord) || ULONG(cWord - pGrammar->m_pWordSequenceText) == ulStartActiveOffset + cchActiveChars) { pPath->hTransition = hId; memset(&pPath->elem, 0, sizeof(pPath->elem)); pPath->elem.bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; WCHAR *pszWord = new WCHAR[cWord - cW + 1]; wcsncpy_s(pszWord, cWord - cW + 1, cW, cWord - cW); pszWord[cWord - cW] = '/0'; pPath->elem.pszDisplayText = pszWord; pPath++; (*pcTrans)++; while(*(cWord+1) && iswspace(*(cWord+1)) && ULONG(cWord - pGrammar->m_pWordSequenceText) < ulStartActiveOffset + cchActiveChars - 1) {cWord++;} cW = cWord + 1; // first char of next word nWord++; } } return hr; } /**************************************************************************** /************************************************* *************************** * CSrEngine : : RecognizeStream * *---------------------------* *描述: *这是方法的SAPI呼吁承认发生。 *发动机只能由这个方法返回后,他们已经阅读所有数据 *并完成所有的承认,他们正在尽在此流。 *因此,这种方法是让一个线程的引擎做承认, *和引擎可能会创造更多的线程。 * CSrEngine : : RecurseWalk * *----------------------* *描述: *这种方法产生的随机路径通过积极CFG桩。如果路径中包含 *规则参考过渡RecurseWalk是递归要求生产的道路虽然 *分规则。结果是一系列的SPPATHENTRY内容包含过渡。 * *初始状态每个规则中得到致电GetRuleInfo 。然后为每个 *国家GetStateInfo可称为。这使一系列SPTRANSITION条目 *在包含的信息类型的转型,转型的ID和 *在未来国家的过渡去。过渡编号是主要的信息 *包括在SPPATHENTRY 。只适用于Word过渡SPPATHENTRY创建, *因为这是所有所需的ParseFromTransitions 。 * *返回: * S_OK *失败(小时) ****************************************************************************/ HRESULT CSrEngine::RecurseWalk(SPSTATEHANDLE hState, SPPATHENTRY * pPath, ULONG * pcTrans) { HRESULT hr = S_OK; CSpStateInfo StateInfo; *pcTrans = 0; while (SUCCEEDED(hr) && hState) { ULONG cTrans; hr = m_cpSite->GetStateInfo(hState, &StateInfo); if (SUCCEEDED(hr)) { ULONG cTransInState = StateInfo.cEpsilons + StateInfo.cWords + StateInfo.cRules + StateInfo.cSpecialTransitions; if (cTransInState == 0) { hr = E_FAIL; break; } SPTRANSITIONENTRY * pTransEntry = StateInfo.pTransitions + (rand() % cTransInState); switch(pTransEntry->Type) { case SPTRANSEPSILON: break; case SPTRANSRULE: hr = RecurseWalk(pTransEntry->hRuleInitialState, pPath, &cTrans); *pcTrans += cTrans; pPath += cTrans; break; case SPTRANSWORD: case SPTRANSWILDCARD: pPath->hTransition = pTransEntry->ID; memset(&pPath->elem, 0, sizeof(pPath->elem)); pPath->elem.bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; pPath++; (*pcTrans)++; break; case SPTRANSTEXTBUF: hr = WalkTextBuffer(pTransEntry->pvGrammarCookie, pPath, pTransEntry->ID, &cTrans); *pcTrans += cTrans; pPath += cTrans; break; case SPTRANSDICTATION: pPath->hTransition = pTransEntry->ID; memset(&pPath->elem, 0, sizeof(pPath->elem)); pPath->elem.bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; size_t cDictWord = wcslen(DICT_WORD); WCHAR *pszWord = new WCHAR[cDictWord + 1]; wcscpy_s(pszWord, cDictWord + 1, DICT_WORD); pPath->elem.pszDisplayText = pszWord; pPath++; (*pcTrans)++; break; } hState = pTransEntry->hNextState; } } return hr; } STDMETHODIMP CSrEngine::GetInputAudioFormat(const GUID * pSourceFormatId, const WAVEFORMATEX * pSourceWaveFormatEx, GUID * pDesiredFormatId, WAVEFORMATEX ** ppCoMemDesiredWFEX) { ppCoMemDesiredWFEX); } STDMETHODIMP CSrEngine:: SetPropertyNum( SPPROPSRC eSrc, PVOID pvSrcObj, const WCHAR* pName, LONG lValue ) { HRESULT hr = S_OK; hr = S_FALSE; return hr; } STDMETHODIMP CSrEngine:: GetPropertyNum( SPPROPSRC eSrc, PVOID pvSrcObj, const WCHAR* pName, LONG * plValue ) { HRESULT hr = S_OK; hr = S_FALSE; *plValue = 0; return hr; } STDMETHODIMP CSrEngine:: SetPropertyString( SPPROPSRC eSrc, PVOID pvSrcObj, const WCHAR* pName, const WCHAR* pValue ) { HRESULT hr = S_OK; hr = S_FALSE; return hr; } STDMETHODIMP CSrEngine:: GetPropertyString( SPPROPSRC eSrc, PVOID pvSrcObj, const WCHAR* pName, __deref_out_opt WCHAR** ppCoMemValue ) { HRESULT hr = S_OK; hr = S_FALSE; *ppCoMemValue = NULL; return hr; } STDMETHODIMP CSrEngine::PrivateCall(void * pvEngineContext, void * pCallFrame, ULONG ulCallFrameSize) { return S_OK; } STDMETHODIMP CSrEngine::PrivateCallEx(void * pvEngineContext, const void * pInCallFrame, ULONG ulCallFrameSize, void ** ppvCoMemResponse, ULONG * pcbResponse) { HRESULT hr = S_OK; *ppvCoMemResponse = NULL; *pcbResponse = 0; return hr; } STDMETHODIMP CSrEngine::PrivateCallImmediate( void *pvEngineContext, const void *pInCallFrame, ULONG ulInCallFrameSize, void **ppvCoMemResponse, ULONG *pulResponseSize) { *ppvCoMemResponse = NULL; *pulResponseSize = 0; return S_OK; } STDMETHODIMP CSrEngine::SetAdaptationData2( void *pvEngineContext, __in_ecount(cch) const WCHAR *pAdaptationData, const ULONG cch, LPCWSTR pTopicName, SPADAPTATIONSETTINGS eSettings, SPADAPTATIONRELEVANCE eRelevance) { return S_OK; } STDMETHODIMP CSrEngine::SetGrammarPrefix( void *pvEngineGrammar, __in_opt LPCWSTR pszPrefix, BOOL fIsPrefixRequired) { return S_OK; } STDMETHODIMP CSrEngine::SetRulePriority( SPRULEHANDLE hRule, void *pvClientRuleContext, int nRulePriority) { return S_OK; } STDMETHODIMP CSrEngine::EmulateRecognition( ISpPhrase *pPhrase, DWORD dwCompareFlags) { // Let SAPI do its own emulation. return E_NOTIMPL; } STDMETHODIMP CSrEngine::SetSLMWeight( void *pvEngineGrammar, float flWeight) { return S_OK; } STDMETHODIMP CSrEngine::SetRuleWeight( SPRULEHANDLE hRule, void *pvClientRuleContext, float flWeight) { return S_OK; } STDMETHODIMP CSrEngine::SetTrainingState( BOOL fDoingTraining, BOOL fAdaptFromTrainingData) { return S_OK; } STDMETHODIMP CSrEngine::ResetAcousticModelAdaptation( void) { return S_OK; } STDMETHODIMP CSrEngine::OnLoadCFG( void *pvEngineGrammar, const SPBINARYGRAMMAR *pGrammarData, ULONG ulGrammarID) { return S_OK; } STDMETHODIMP CSrEngine::OnUnloadCFG( void *pvEngineGrammar, ULONG ulGrammarID) { return S_OK; } /****************************************************************************** * srengext.cpp *此文件包含执行CSampleSRExtension级。 *这实现了自定义接口ISampleSRExtension 。 *当一个应用程序启这一从reco方面, SAPI的将 *寻找ExtensionCLSID领域中的引擎对象的原因,并 *创建该对象,然后齐界面的要求。 ******************************************************************************/ #include "stdafx.h" #include "srengext.h" STDMETHODIMP CSampleSRExtension::ExamplePrivateEngineCall(void) { static BYTE Data[4] = { 1, 2, 3, 4 }; HRESULT hr = S_OK; CComPtr<ISpPrivateEngineCallEx> cpEngineCallEx; OuterQueryInterface(IID_ISpPrivateEngineCallEx, (void **)&cpEngineCallEx); if (cpEngineCallEx) { void *pCoMemOutFrame = NULL; ULONG ulOutFrameSize; hr = cpEngineCallEx->CallEngineSynchronize( (void*)Data, sp_countof(Data), &pCoMemOutFrame, &ulOutFrameSize ); ::CoTaskMemFree( pCoMemOutFrame ); } else { hr = m_pEngineCall->CallEngine( (void*)Data, sp_countof(Data) ); } return hr; } STDMETHODIMP CSampleSRExtension::GetDisplayAlternates( const SPDISPLAYPHRASE *pPhrase, ULONG cRequestCount, SPDISPLAYPHRASE **ppCoMemPhrases, ULONG *pcPhrasesReturned) { HRESULT hr = S_OK; memset(ppCoMemPhrases, 0, sizeof(*ppCoMemPhrases) * cRequestCount); *pcPhrasesReturned = cRequestCount; for (unsigned int p=0; p<*pcPhrasesReturned; p++) { size_t cbPhraseSize = sizeof(SPDISPLAYPHRASE); cbPhraseSize += pPhrase->ulNumTokens * sizeof(SPDISPLAYTOKEN); for (unsigned int t=0; t<pPhrase->ulNumTokens; t++) { if (pPhrase->pTokens[t].pszDisplay != NULL) { cbPhraseSize += (wcslen(pPhrase->pTokens[t].pszDisplay) + 1) * sizeof(WCHAR); } if (pPhrase->pTokens[t].pszLexical != NULL) { cbPhraseSize += (wcslen(pPhrase->pTokens[t].pszLexical) + 1) * sizeof(WCHAR); } } // Allocate the memory ppCoMemPhrases[p] = (SPDISPLAYPHRASE *)CoTaskMemAlloc(cbPhraseSize); if (ppCoMemPhrases[p] == NULL) { hr = E_OUTOFMEMORY; goto CleanUp; } SPDISPLAYPHRASE *pCoMemPhrase = ppCoMemPhrases[p]; pCoMemPhrase->ulNumTokens = pPhrase->ulNumTokens; pCoMemPhrase->pTokens = (SPDISPLAYTOKEN *)(pCoMemPhrase + 1); LPWSTR pStringTable = (LPWSTR)(pCoMemPhrase->pTokens + pCoMemPhrase->ulNumTokens); LPWSTR pEndOfStringTable = (LPWSTR)(((BYTE*)pCoMemPhrase) + cbPhraseSize); for (unsigned int t=0; t<pPhrase->ulNumTokens; t++) { pCoMemPhrase->pTokens[t].bDisplayAttributes = pPhrase->pTokens[t].bDisplayAttributes; if (t == 0) { if (pPhrase->pTokens[t].pszDisplay != NULL) { pCoMemPhrase->pTokens[t].bDisplayAttributes |= SPAF_ONE_TRAILING_SPACE; } } else { if (t<pPhrase->ulNumTokens-1) { pCoMemPhrase->pTokens[t].bDisplayAttributes |= SPAF_ONE_TRAILING_SPACE; } } if (pPhrase->pTokens[t].pszDisplay != NULL) { size_t length = wcslen(pPhrase->pTokens[t].pszDisplay); if (wcscpy_s(pStringTable, pEndOfStringTable - pStringTable, pPhrase->pTokens[t].pszDisplay)) { hr = E_OUTOFMEMORY; goto CleanUp; } pCoMemPhrase->pTokens[t].pszDisplay = pStringTable; pStringTable += length + 1; } else { ppCoMemPhrases[p]->pTokens[t].pszDisplay = NULL; } if (pPhrase->pTokens[t].pszLexical != NULL) { size_t length = wcslen(pPhrase->pTokens[t].pszLexical); if (wcscpy_s(pStringTable, pEndOfStringTable - pStringTable, pPhrase->pTokens[t].pszLexical)) { hr = E_OUTOFMEMORY; goto CleanUp; } pCoMemPhrase->pTokens[t].pszLexical = pStringTable; pStringTable += length + 1; } else { ppCoMemPhrases[p]->pTokens[t].pszLexical = NULL; } } } CleanUp: if (FAILED(hr)) { for (unsigned int p=0; p<*pcPhrasesReturned; p++) { CoTaskMemFree(ppCoMemPhrases[p]); ppCoMemPhrases[p] = NULL; } *pcPhrasesReturned = 0; } return hr; } STDMETHODIMP CSampleSRExtension::SetFullStopTrailSpace(ULONG ulTrailSpace) { return S_OK; } STDMETHODIMP CSampleSRExtension::Normalize( LPCWSTR pszWord, LPCWSTR pszLeftContext, LPCWSTR pszRightContext, WORD LangID, SPNORMALIZATIONLIST *pNormalizationList) { pNormalizationList = NULL; return S_NOTSUPPORTED; } STDMETHODIMP CSampleSRExtension::GetPronunciations( LPCWSTR pszWord, LPCWSTR pszLeftContext, LPCWSTR pszRightContext, WORD LangID, SPWORDPRONUNCIATIONLIST *pEnginePronunciationList) { pEnginePronunciationList->ulSize = 0; pEnginePronunciationList->pvBuffer = 0; pEnginePronunciationList->pFirstWordPronunciation = 0; return S_NOTSUPPORTED; } /****************************************************************************** * srengalt.h *此文件包含执行CSrEngineAlternates级。 *这实现了接口ISpSRAlternates 。 *当一个应用程序要求GetAlternates或犯下的结果, SAPI的将 *寻找AlternatesCLSID领域中的引擎对象的原因,并 *创建此对象。 *然后,将在这里呼吁的方法,通过相关的结果信息。 *这包括任何序列数据的主要动力已返回 *结果让候补产生离线。 ******************************************************************************/ #include "stdafx.h" #include "srengalt.h" STDMETHODIMP CSrEngineAlternates::GetAlternates(SPPHRASEALTREQUEST *pAltRequest, SPPHRASEALT **ppAlts, ULONG *pcAlts) { HRESULT hr = S_OK; *pcAlts = 1; *ppAlts = (SPPHRASEALT *)::CoTaskMemAlloc(sizeof(SPPHRASEALT)); if( *ppAlts == NULL) { return E_OUTOFMEMORY; } (*ppAlts)[0].ulStartElementInParent = pAltRequest->ulStartElement; (*ppAlts)[0].cElementsInParent = pAltRequest->cElements; (*ppAlts)[0].cElementsInAlternate = pAltRequest->cElements; (*ppAlts)[0].pvAltExtra = NULL; (*ppAlts)[0].cbAltExtra = 0; SPPHRASE phrase; memset(&phrase, 0, sizeof(phrase)); phrase.cbSize = sizeof(phrase); phrase.LangID = 1033; WCHAR *pAlts = (WCHAR *) pAltRequest->pvResultExtra; ULONG nAltChars = pAltRequest->cbResultExtra / sizeof(WCHAR); ULONG nWord = 0; ULONG i; for(i = 0; i < nAltChars; i++) { if(iswspace(pAlts[i]) || pAlts[i] == '/0') { nWord++; } } SPPHRASEELEMENT* pElements = (SPPHRASEELEMENT*)_malloca(sizeof(SPPHRASEELEMENT) * nWord); memset(pElements, 0, sizeof(SPPHRASEELEMENT)*nWord); ULONG cW = 0; ULONG cWord = 0; for(i = 0; i < nAltChars && cWord < nWord; i++) { if(iswspace(pAlts[i]) || pAlts[i] == '/0') { pElements[cWord].bDisplayAttributes = SPAF_ONE_TRAILING_SPACE; WCHAR *pszWord = (WCHAR *)_malloca(sizeof(WCHAR) * (i - cW + 1)); wcsncpy_s(pszWord, i - cW + 1, &pAlts[cW], i - cW); pszWord[i - cW] = '/0'; pElements[cWord].pszDisplayText = pszWord; cW = i + 1; cWord++; } } phrase.Rule.ulCountOfElements = cWord; phrase.pElements = pElements; CComPtr<ISpPhraseBuilder> cpBuilder; hr = cpBuilder.CoCreateInstance(CLSID_SpPhraseBuilder); if(SUCCEEDED(hr)) { hr = cpBuilder->InitFromPhrase(&phrase); } if(SUCCEEDED(hr)) { (*ppAlts)[0].pPhrase = cpBuilder; (*ppAlts)[0].pPhrase->AddRef(); } CComPtr<ISampleSRExtension> m_cpExt; hr = pAltRequest->pRecoContext->QueryInterface(&m_cpExt); if(SUCCEEDED(hr)) { hr = m_cpExt->ExamplePrivateEngineCall(); } for(i = 0; i < cWord; i++) { _freea((void*)pElements[i].pszDisplayText); } _freea(pElements); if (FAILED(hr)) { ::CoTaskMemFree(*ppAlts); } return hr; } STDMETHODIMP CSrEngineAlternates::Commit(SPPHRASEALTREQUEST *pAltRequest, SPPHRASEALT *pAlt, void **ppvResultExtra, ULONG *pcbResultExtra) { return S_OK; }