// // sptask.cpp // // implements a notification callback ISpTask // // created: 12/1/99 // // #include "private.h" #include "globals.h" #include "sptask.h" #include "candui.h" #include "ids.h" #include "computil.h" // // ctor // // CSpTask::CSpTask(CCandidateUI *pcui) { // CSpTask is initialized with an TFX instance // so store the pointer to the TFX // init data members here m_pcui = pcui; m_fSapiInitialized = FALSE; m_fActive = FALSE; m_fInCallback = FALSE; } CSpTask::~CSpTask() { _ReleaseGrammars(); } // // CSpTask::_InitializeSAPIObjects // // initialize SAPI objects for SR // later we'll get other objects initialized here // (TTS, audio etc) // HRESULT CSpTask::InitializeSAPIObjects(void) { #ifdef _WIN64 return E_NOTIMPL; #else if (m_fSapiInitialized == TRUE) return m_cpRecoCtxt ? S_OK : E_FAIL; // do not try again even in failure m_fSapiInitialized = TRUE; // m_xxx are CComPtrs from ATL // HRESULT hr = _GetSapilayrEngineInstance(&m_cpRecoEngine); // create the recognition context if( S_OK == hr ) { hr = m_cpRecoEngine->CreateRecoContext( &m_cpRecoCtxt ); } if ( hr == S_OK ) { SPRECOGNIZERSTATUS stat; if (S_OK == m_cpRecoEngine->GetStatus(&stat)) { m_langid = stat.aLangID[0]; } } return hr; #endif // _WIN64 } // // CSpTask::NotifyCallback // // INotifyControl object calls back here // returns S_OK when it handles notifications // // HRESULT CSpTask::NotifyCallback( WPARAM wParam, LPARAM lParam ) { USES_CONVERSION; // we can't delete reco context while we're in this callback // m_fInCallback = TRUE; // also we can't terminate candidate UI object while in the callback m_pcui->AddRef(); { CSpEvent event; while ( m_cpRecoCtxt && event.GetFrom(m_cpRecoCtxt) == S_OK ) { switch (event.eEventId) { case SPEI_RECOGNITION: _OnSpEventRecognition(event); break; default: break; } } } m_fInCallback = FALSE; m_pcui->Release(); return S_OK; } HRESULT CSpTask::_OnSpEventRecognition(CSpEvent &event) { HRESULT hr = S_OK; ISpRecoResult *pResult = event.RecoResult(); if (pResult) { static const WCHAR szUnrecognized[] = L""; SPPHRASE *pPhrase; hr = pResult->GetPhrase(&pPhrase); if (S_OK == hr) { if (pPhrase->ullGrammarID == GRAM_ID_CANDCC) { if (SUCCEEDED(hr) && pPhrase) { // retrieve LANGID from phrase LANGID langid = pPhrase->LangID; hr = _DoCommand(pPhrase, langid); } } else if(pPhrase->ullGrammarID == GRAM_ID_DICT) { if (m_pcui->_ptim != NULL) { // Windows bug#508709 // Ignore dictation event during SPTip is in commanding mode DWORD dwSpeechGlobalState; GetCompartmentDWORD(m_pcui->_ptim, GUID_COMPARTMENT_SPEECH_GLOBALSTATE, &dwSpeechGlobalState, TRUE); if (dwSpeechGlobalState & TF_DICTATION_ON) { hr = _DoDictation(pResult); } } } ::CoTaskMemFree( pPhrase ); } } return hr; } const WCHAR c_szRuleName[] = L"ID_Candidate"; // // CSpTask::_DoCommand // // review: the rulename may need to be localizable? // HRESULT CSpTask::_DoCommand(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; if ( wcscmp(pPhrase->Rule.pszName, c_szRuleName) == 0) { if (m_pcui) { hr = m_pcui->NotifySpeechCmd(pPhrase, pPhrase->pProperties[0].pszValue, pPhrase->pProperties[0].ulId); } } return hr; } // // CSpTask::_DoDictation // // support spelling // HRESULT CSpTask::_DoDictation(ISpRecoResult *pResult) { HRESULT hr = E_FAIL; BYTE bAttr; // no need? Assert(pResult); // this cotaskmemfree's text we get CSpDynamicString dstr; hr = pResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstr, &bAttr); if (S_OK == hr) { WCHAR sz[2]={0}; StringCchCopyW(sz, ARRAYSIZE(sz), dstr); Assert(m_pcui); hr = m_pcui->FHandleSpellingChar(sz[0]); } return hr; } // // CSpTask::InitializeCallback // // HRESULT CSpTask::InitializeCallback() { #ifdef _WIN64 return E_NOTIMPL; #else // set recognition notification CComPtr cpNotify; HRESULT hr = cpNotify.CoCreateInstance(CLSID_SpNotifyTranslator); // set this class instance to notify control object if (SUCCEEDED(hr)) { hr = cpNotify->InitSpNotifyCallback( (ISpNotifyCallback *)this, 0, 0 ); } if (SUCCEEDED(hr)) { hr = m_cpRecoCtxt->SetNotifySink(cpNotify); } // set the events we're interested in if( SUCCEEDED( hr ) ) { const ULONGLONG ulInterest = SPFEI(SPEI_RECOGNITION); hr = m_cpRecoCtxt->SetInterest(ulInterest, ulInterest); } else { m_cpRecoCtxt.Release(); } if( SUCCEEDED( hr ) ) { hr = _LoadGrammars(); } return hr; #endif // _WIN64 } // // _LoadGrammars // // synopsis - load CFG for dictation and commands available during dictation // HRESULT CSpTask::_LoadGrammars() { // do not initialize grammars more than once // if (m_cpDictGrammar || m_cpCmdGrammar) return S_OK; HRESULT hr = E_FAIL; if (m_cpRecoCtxt) { // // create grammar object // if ( m_langid != 0x0804 ) // Chinese Engine doesn't support spelling grammar. { hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_DICT, &m_cpDictGrammar); if (S_OK == hr) { // specify spelling mode hr = m_cpDictGrammar->LoadDictation(L"Spelling", SPLO_STATIC); } if (SUCCEEDED(hr)) { hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_CANDCC, &m_cpCmdGrammar); } } else hr = m_cpRecoCtxt->CreateGrammar(GRAM_ID_CANDCC, &m_cpCmdGrammar); // load the command grammar // if (SUCCEEDED(hr) ) { // Load it from the resource first to speed up the initialization. if (m_langid == 0x409 || // English m_langid == 0x411 || // Japanese m_langid == 0x804 ) // Simplified Chinese { hr = m_cpCmdGrammar->LoadCmdFromResource( g_hInst, (const WCHAR*)MAKEINTRESOURCE(ID_DICTATION_COMMAND_CFG), L"SRGRAMMAR", m_langid, SPLO_DYNAMIC); } // in case LoadCmdFromResource returns wrong. if (!SUCCEEDED(hr)) { if(!_GetCmdFileName(m_langid)) { hr = E_FAIL; } if (m_szCmdFile[0]) { hr = m_cpCmdGrammar->LoadCmdFromFile(_GetCmdFileName(m_langid), SPLO_DYNAMIC); } if (!SUCCEEDED(hr)) { m_cpCmdGrammar.Release(); } } hr = S_OK; } } return hr; } void CSpTask::_ReleaseGrammars(void) { if (!m_fInCallback) { m_cpDictGrammar.Release(); m_cpCmdGrammar.Release(); if (m_cpRecoCtxt) { m_cpRecoCtxt->SetNotifySink(NULL); m_cpRecoCtxt.Release(); } } } WCHAR * CSpTask::_GetCmdFileName(LANGID langid) { if (!m_szCmdFile[0]) { // now we only have a command file for English/Japanese // when cfgs are available, we'll get the name of cmd file // and the rule names from resources using findresourceex // if (PRIMARYLANGID(langid) == LANG_ENGLISH || PRIMARYLANGID(langid) == LANG_JAPANESE || PRIMARYLANGID(langid) == LANG_CHINESE) { char szFilePath[MAX_PATH]; char *pszExt; char szCp[MAX_PATH]; int ilen; if (!GetModuleFileName(g_hInst, szFilePath, ARRAYSIZE(szFilePath))) return NULL; // find extension // is this dbcs safe? pszExt = strrchr(szFilePath, (int)'.'); if (pszExt) { *pszExt = '\0'; } ilen = lstrlen(szFilePath); if (!pszExt) { pszExt = szFilePath+ilen; } LoadStringA(g_hInst, IDS_CMD_EXT, pszExt, ARRAYSIZE(szFilePath)-ilen); if (GetLocaleInfo(langid, LOCALE_IDEFAULTANSICODEPAGE, szCp, ARRAYSIZE(szCp))>0) { int iACP = atoi(szCp); if (MultiByteToWideChar(iACP, NULL, szFilePath, -1, m_szCmdFile, ARRAYSIZE(m_szCmdFile)) == 0) { m_szCmdFile[0] = 0; return NULL; } } } } return m_szCmdFile; } HRESULT CSpTask::_Activate(BOOL fActive) { HRESULT hr = E_FAIL; if (m_cpRecoCtxt) { // Need SAPI bug# for this workaround. // m_fActive = fActive; // // Is the NULL rulename fine? // if (m_cpCmdGrammar) hr = m_cpCmdGrammar->SetRuleState(NULL, NULL, m_fActive ? SPRS_ACTIVE : SPRS_INACTIVE); if (m_cpDictGrammar) hr = m_cpDictGrammar->SetDictationState(m_fActive? SPRS_ACTIVE : SPRS_INACTIVE); } return hr; } HRESULT CSpTask::InitializeSpeech() { HRESULT hr = E_FAIL; #ifdef _WIN64 hr = E_NOTIMPL; #else hr = InitializeSAPIObjects(); // set callback if (hr == S_OK) hr = InitializeCallback(); // activate grammars if (hr == S_OK) hr = _Activate(TRUE); #endif // _WIN64 return hr; } // // _GetSapilayrEngineInstance // // // HRESULT CSpTask::_GetSapilayrEngineInstance(ISpRecognizer **ppRecoEngine) { #ifdef _WIN64 return E_NOTIMPL; #else HRESULT hr = E_FAIL; CComPtr cpFuncPrv; CComPtr cpGetSAPI; // we shouldn't release this until we terminate ourselves // so we don't use comptr here if (m_pcui->_ptim != NULL) { hr = m_pcui->_ptim->GetFunctionProvider(CLSID_SapiLayr, &cpFuncPrv); if (S_OK == hr) { hr = cpFuncPrv->GetFunction(GUID_NULL, IID_ITfFnGetSAPIObject, (IUnknown **)&cpGetSAPI); } if (S_OK == hr) { hr = cpGetSAPI->Get(GETIF_RECOGNIZERNOINIT, (IUnknown **)ppRecoEngine); } } return hr; #endif }