2025-04-27 07:49:33 -04:00

878 lines
26 KiB
C++

// BaseSAPI.cpp: implementation of the CBaseSAPI class.
//
// All speech related classes derive from this.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "BaseSAPI.h"
#include "filestream.h"
#include "debug.h"
#include "utils.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
LONG CBaseSAPI::m_bInited=0;
CComPtr<ISpRecoInstance> CBaseSAPI::g_cpEngine=NULL; // Pointer to reco engine interface
CComPtr<ISpVoice> CBaseSAPI::g_cpVoice=NULL; // Global voice for failure/success
CTunaClient CBaseSAPI::g_Notifications;
CRITICAL_SECTION g_CritSec={0};
CBaseSAPI::CBaseSAPI()
{
m_bActive=NULL;
m_cpCmdGrammar=NULL;
m_cpNumberGrammar=NULL;
if( InterlockedIncrement( &m_bInited ) == 1 )
{
InitializeCriticalSection( & g_CritSec );
}
EnterCriticalSection( & g_CritSec );
if( g_cpEngine == NULL )
{
g_Notifications.SetPropertyName("Command Prompts");
g_Notifications.SetText(L"Please wait ...");
if( SUCCEEDED( InitSAPI() ))
g_Notifications.SetText(L"You can now voice command Windows");
else
g_Notifications.SetText(L"Please upgrade your system");
}
LeaveCriticalSection( & g_CritSec );
if( g_cpEngine )
{
ISpRecoInstance * pReco=g_cpEngine;
if(pReco)
pReco->AddRef();
}
}
CBaseSAPI::~CBaseSAPI()
{
ResetGrammar();
g_cpEngine.Release();
if( InterlockedDecrement( &m_bInited ) == 0 )
{
g_cpVoice=NULL;
DeleteCriticalSection( &g_CritSec );
}
if( g_cpEngine == NULL )
CoUninitialize();
}
HRESULT CBaseSAPI::ExitNode(IRCMLNode * parent, LONG lresult)
{
ResetGrammar();
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// The base sapi node has provisions for a FAILURE element, and list of SYNONYMs
//
HRESULT STDMETHODCALLTYPE CBaseSAPI::AcceptChild(
IRCMLNode __RPC_FAR *pChild)
{
LPWSTR pType;
LPWSTR pChildType;
get_StringType( &pType );
pChild->get_StringType( &pChildType );
if( SUCCEEDED( pChild->IsType( L"CICERO:FAILURE" )))
{
m_cpFailure=pChild;
return S_OK;
}
else if( SUCCEEDED( pChild->IsType( L"CICERO:SYNONYM" )))
{
m_Synonyms.Append((CXMLSynonym*)pChild);
return S_OK;
}
else if( SUCCEEDED( pChild->IsType( L"CICERO:SUCCESS" )))
{
m_cpSuccess=pChild;
return S_OK;
}
return E_INVALIDARG; // we don't take children.
}
//
// Taken from the SimpleCC.cpp file in the sapi sdk.
//
HRESULT CBaseSAPI::InitSAPI( void )
{
HRESULT hr = S_OK;
CoInitialize(NULL);
//
// create a recognition engine
// CLSID_SpInprocRecoInstance or
// CLSID_SpSharedRecoInstance
//
if ( SUCCEEDED( hr = g_cpEngine.CoCreateInstance(CLSID_SpSharedRecoInstance) ) )
{
#ifdef _INPROC
//
// We should be able to remove this REVIEW 6/12/00
//
CComPtr<ISpAudio> cpAudio;
// create default audio object
if ( SUCCEEDED( hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio) ) )
{
hr = g_cpEngine->SetInput(cpAudio, TRUE);
}
#endif
}
return hr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// This loads a memory based grammar.
// this will be replaced once SAPI loads from memory.
//
HRESULT CBaseSAPI::LoadCFGFromString( LPCTSTR szRuntimeCFG, LPCTSTR prefix )
{
HRESULT hr=S_OK;
// #ifdef DEBUG
// Need to go via files because of the M2 to M3 changes.
if( prefix == NULL )
prefix = TEXT("temp");
if( prefix )
{
//
// This creates a file for us to look at
//
int i=lstrlen( szRuntimeCFG );
TCHAR szFile[MAX_PATH];
wsprintf(szFile,TEXT("c:\\cicerorcml\\%s.xml"),prefix);
HANDLE hFile=CreateFile( szFile,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
NULL);
if( hFile != INVALID_HANDLE_VALUE)
{
DWORD dwWritten;
WriteFile( hFile, szRuntimeCFG, i*sizeof(TCHAR),&dwWritten,NULL);
CloseHandle(hFile);
}
LoadCFG( szFile );
}
// #endif
#if 0
//
// From RalphL (SAPI).
//
CComPtr<ISpStream> cpSrcStream;
CComPtr<IStream> cpDestMemStream;
CComPtr<ISpGrammarCompiler> m_cpCompiler;
_MemoryStream * pMemStream= new _MemoryStream((LPBYTE)szRuntimeCFG, lstrlen(szRuntimeCFG)*sizeof(TCHAR));
if (SUCCEEDED(hr))
{
hr = ::CreateStreamOnHGlobal(NULL, TRUE, &cpDestMemStream);
}
if (SUCCEEDED(hr))
{ // ISpGramCompBackend
hr = m_cpCompiler.CoCreateInstance(CLSID_SpGrammarCompiler);
}
if (SUCCEEDED(hr))
{
hr = m_cpCompiler->CompileStream(pMemStream, cpDestMemStream, NULL, NULL, NULL, 0);
}
if (SUCCEEDED(hr))
{
HGLOBAL hGlobal;
hr = ::GetHGlobalFromStream(cpDestMemStream, &hGlobal);
if (SUCCEEDED(hr))
{
SPCFGSERIALIZEDHEADER * pBinaryData = (SPCFGSERIALIZEDHEADER * )::GlobalLock(hGlobal);
if (pBinaryData)
{
// hr = LoadCmdFromMemory(pBinaryData, Options);
// create the command recognition context
if( SUCCEEDED( hr = GetRecoContext() ))
{
// we use the 'shared' context here.
if( SUCCEEDED( hr = m_cpRecoCtxt->CreateGrammar( (DWORD)this, &m_cpCmdGrammar) ))
{
if( SUCCEEDED( hr = m_cpCmdGrammar->LoadCmdFromMemory( pBinaryData, FALSE ) )) // is TRUE needed?
{
// Set rules to active, we are now listening for commands
if( SUCCEEDED( hr = SetRuleState(TRUE) ))
{
}
else
{
// If we failed here, it is OK, we can recover later
m_bActive = false;
hr = S_OK;
}
}
}
}
::GlobalUnlock(hGlobal);
}
}
}
delete pMemStream;
#endif
return hr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// This loads a local grammar.
//
HRESULT CBaseSAPI::LoadCFG( LPCTSTR pszFileName )
{
HRESULT hr=E_FAIL;
if( !g_cpEngine )
return NULL;
m_bActive=FALSE;
if(m_cpCmdGrammar )
{
TRACE(TEXT("This is NOT supposed to happen"));
m_cpCmdGrammar=NULL;
#ifdef _DEBUG
_asm int 3;
#endif
}
if( SUCCEEDED( hr = GetRecoContext() ))
{
if( SUCCEEDED( hr = m_cpRecoCtxt->CreateGrammar( (DWORD)this, &m_cpCmdGrammar) ))
{
if( SUCCEEDED( hr = m_cpCmdGrammar->LoadCmdFromFile( pszFileName, SPLO_STATIC ) ))
{
// Set rules to active, we are now listening for commands
if( SUCCEEDED( hr = SetRuleState(TRUE) ))
{
}
else
{
// If we failed here, it is OK, we can recover later
m_bActive = false;
hr = S_OK;
}
}
}
}
if ( FAILED( hr ) )
{
// Since we couldn't initialize, we will completely shut down
// ResetGrammar();
}
return ( hr );
}
HRESULT CBaseSAPI::SetRuleState(BOOL bOnOff)
{
// Set rules to active, we are now listening for commands
if(m_bActive==bOnOff)
return S_OK;
HRESULT hr=S_OK;
m_bActive=bOnOff;
if( m_cpCmdGrammar )
{
TRACE(TEXT("Turning %s grammars ..."),bOnOff?TEXT("ON "):TEXT("OFF"));
#ifdef _WITH_DICTATION
m_cpCmdGrammar->SetDictationState(bOnOff?SPRS_ACTIVE:SPRS_INACTIVE);
#endif
if( m_cpNumberGrammar )
m_cpNumberGrammar->SetRuleState(
NULL,
NULL,
bOnOff?SPRS_ACTIVE:SPRS_INACTIVE);
hr=m_cpCmdGrammar->SetRuleState(
NULL,
NULL,
bOnOff?SPRS_ACTIVE:SPRS_INACTIVE); // , // enable the rule
// FALSE );
TRACE(TEXT("done\n"));
}
return hr;
}
/******************************************************************************
* ResetGrammar *
*----------------*
* Description:
* Called to close down SAPI COM objects we have stored away.
*
******************************************************************************/
void CBaseSAPI::ResetGrammar( void )
{
if( m_cpCmdGrammar )
m_cpCmdGrammar.Release();
if( m_cpRecoCtxt )
{
// m_cpRecoCtxt->SetNotifySink(NULL);
m_cpRecoCtxt.Release(); // this will release us.
}
}
/******************************************************************************
* ProcessRecoEvent *
*------------------*
* Description:
* Called to when reco event message is sent to main window procedure.
* In the case of a recognition, it extracts result and calls ExecuteCommand.
*
******************************************************************************/
void CBaseSAPI::ProcessRecoEvent( void )
{
CSpEvent event; // Event helper class
// Loop processing events while there are any in the queue
while ( S_OK == event.GetFrom(m_cpRecoCtxt) )
{
// Look at recognition event only
if ( SPEI_RECOGNITION == event.eEventId )
{
// m_cCmdManager.ExecuteCommand(event.RecoResult());
}
}
}
//
// Returne true if it managed to set the text, false otherwise.
//
BOOL CBaseSAPI::SetControlText( LPCWSTR dstrText )
{
BOOL bRet=TRUE;
IRCMLNode * pParent;
if( SUCCEEDED(DetachParent( & pParent )))
{
IRCMLControl * pControl;
if( SUCCEEDED( pParent->QueryInterface( __uuidof( IRCMLControl ) , (LPVOID*)&pControl )))
{
HWND hWnd;
if( SUCCEEDED( pControl->get_Window( &hWnd )))
{
if( SUCCEEDED(pControl->IsType(L"EDIT") ))
{
// SetWindowText( hWnd, dstrText );
SendMessage( hWnd, WM_SETTEXT, NULL,(LPARAM)dstrText );
}
else if ( SUCCEEDED(pControl->IsType(L"COMBO") ))
{
if( SendMessage( hWnd, CB_SELECTSTRING, -1, (LPARAM)dstrText ) == CB_ERR )
{
bRet=FALSE;
if( SendMessage( hWnd, WM_SETTEXT, NULL,(LPARAM)dstrText ) )
bRet=TRUE;
}
else
{
// now fake up the change of selection messages!
SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), CBN_SELENDOK ), (LPARAM)hWnd );
SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), CBN_SELCHANGE ), (LPARAM)hWnd );
SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), CBN_SETFOCUS ), (LPARAM)hWnd );
}
}
else if ( SUCCEEDED(pControl->IsType(L"LISTBOX") ))
{
if( SendMessage( hWnd, LB_SELECTSTRING, -1, (LPARAM)dstrText ) == CB_ERR )
bRet=FALSE;
else
{
// now fake up the change of selection messages - CHECK THESE!
// SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), LBN_SELENDOK ), (LPARAM)hWnd );
SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), LBN_SELCHANGE ), (LPARAM)hWnd );
SendMessage( GetParent( hWnd), WM_COMMAND, MAKEWPARAM( GetDlgCtrlID(hWnd), LBN_SETFOCUS ), (LPARAM)hWnd );
}
}
}
pControl->Release();
}
}
return bRet;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This will take a string like "&About ..." and return "About"
// or "Try to R&einvest" and return "Reinvest"
//
LPWSTR CBaseSAPI::FindNiceText(LPCWSTR text)
{
if( text==NULL )
return NULL;
LPCWSTR pszLastSpace=text;
BOOL bFoundAccelorator=FALSE;
LPCWSTR pszCurrentChar=text;
while(*pszCurrentChar)
{
if( *pszCurrentChar == L'&' )
{
bFoundAccelorator=TRUE;
break;
}
if( *pszCurrentChar == L' ' )
pszLastSpace=pszCurrentChar+1;
pszCurrentChar++;
}
//
// If we found an &, then we know which word it is on.
//
pszCurrentChar = pszLastSpace;
BOOL bFindingEnd=TRUE;
UINT iStrLen=0;
while( bFindingEnd )
{
switch (*pszCurrentChar )
{
case L' ':
case L'.':
case 0:
bFindingEnd=FALSE; // we're left pointing at the terminating char.
break;
default:
pszCurrentChar++;
iStrLen++;
break;
}
};
LPWSTR pszNewString = new TCHAR[iStrLen+1];
if( bFoundAccelorator==FALSE )
{
ZeroMemory( pszNewString, (iStrLen+1)*sizeof(WCHAR) );
CopyMemory( pszNewString, pszLastSpace, iStrLen*sizeof(WCHAR));
}
else
{
// copy, skipping over the & and the ... if present (though they shouldn't be).
LPWSTR pszDest=pszNewString;
LPCWSTR pszLastChar=pszCurrentChar;
pszCurrentChar = pszLastSpace;
while( pszCurrentChar != pszLastChar )
{
if( *pszCurrentChar==L'&' || *pszCurrentChar==L'.' )
{
}
else
*pszDest++=*pszCurrentChar;
pszCurrentChar++;
};
*pszDest=0;
}
return pszNewString;
}
LPWSTR CBaseSAPI::GetRecognizedText(ISpPhrase *pPhrase)
{
SPPHRASE *pElements;
LPTSTR pszPropertyName;
// Get the phrase elements, one of which is the rule id we specified in
// the grammar. Switch on it to figure out which command was recognized.
if (SUCCEEDED(pPhrase->GetPhrase(&pElements)))
{
CSpDynamicString dstrText;
BOOL bPropFound=FALSE;
if( SUCCEEDED( get_Attr(L"PROPNAME", & pszPropertyName) ))
{
// find the property name we were looking for.
if( pElements )
{
const SPPHRASEPROPERTY * pProperty=pElements->pProperties;
while(pProperty)
{
if( lstrcmpi( pProperty->pszName, pszPropertyName ) == 0 )
{
ULONG FirstElement=pProperty->ulFirstElement;
if( FirstElement < pElements->Rule.ulCountOfElements )
{
dstrText=pElements->pElements[FirstElement].pszDisplayText;
pProperty=NULL;
bPropFound=TRUE;
continue;
}
}
pProperty= pProperty->pNextSibling;
}
}
}
//
// Even if they specified a property name, perhaps we ignore it??
//
if(bPropFound==FALSE)
pPhrase->GetText( SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
LPWSTR pszText = new WCHAR[ lstrlen(dstrText)+1];
lstrcpy(pszText, dstrText );
return pszText;
}
return NULL;
}
void CBaseSAPI::SayFailure()
{
if( m_cpFailure )
{
LPWSTR pszText;
if( SUCCEEDED( m_cpFailure->get_Attr( L"TEXT", &pszText )))
{
if( GetVoice() )
g_cpVoice->Speak( pszText, 0, NULL);
}
}
}
void CBaseSAPI::SaySuccess()
{
if( m_cpSuccess )
{
LPWSTR pszText;
if( SUCCEEDED( m_cpSuccess->get_Attr( L"TEXT", &pszText )))
{
if( GetVoice() )
g_cpVoice->Speak( pszText, 0, NULL);
}
}
}
LPWSTR CBaseSAPI::GetRecognizedRule(ISpPhrase *pPhrase)
{
SPPHRASE *pElements;
LPTSTR pszPropertyName=NULL;
// Get the phrase elements, one of which is the rule id we specified in
// the grammar. Switch on it to figure out which command was recognized.
if (SUCCEEDED(pPhrase->GetPhrase(&pElements)))
{
#ifdef DEBUG
CSpDynamicString dstrText;
pPhrase->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
TRACE(TEXT("I heard %s\n"),dstrText);
#endif
LPCWSTR pszRuleName;
//
// find the property name we were looking for.
//
if( pElements )
pszRuleName = pElements->Rule.pszName;
LPWSTR pszText = new WCHAR[ lstrlen(pszRuleName)+1];
lstrcpy(pszText, pszRuleName );
return pszText;
}
return NULL;
}
//
// Non - visible too?
//
BOOL CBaseSAPI::IsEnabled(HWND hWnd)
{
LONG style=GetWindowLong( hWnd, GWL_STYLE );
if( style & WS_DISABLED )
return FALSE;
if( style & WS_VISIBLE)
return TRUE;
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// Adds a string to a buffer, allocates the buffer if null.
// and frees the buffer if you append NULL - wow, is that hacky!
//
PSTRINGBUFFER CBaseSAPI::AppendText(PSTRINGBUFFER buffer, LPTSTR pszText)
{
PSTRINGBUFFER pResult=buffer;
if(buffer==NULL)
{
pResult=new STRINGBUFFER;
pResult->size=512;
pResult->pszString = new TCHAR[pResult->size];
pResult->pszStringEnd = pResult->pszString;
pResult->used = 0;
}
if(pszText)
{
UINT cbNewText=lstrlen(pszText);
// Make sure we the space
if( pResult->size < pResult->used + cbNewText + 4 )
{
LPTSTR pszNewBuffer=new TCHAR[pResult->size * 2];
CopyMemory( pszNewBuffer, pResult->pszString, pResult->size * sizeof(TCHAR) );
delete pResult->pszString;
pResult->pszString = pszNewBuffer;
pResult->size*=2;
pResult->pszStringEnd = (pResult->pszString)+pResult->used;
}
// append the string.
CopyMemory( pResult->pszStringEnd, pszText, cbNewText*sizeof(TCHAR));
pResult->used +=cbNewText;
pResult->pszStringEnd = (pResult->pszString)+pResult->used;
*(pResult->pszStringEnd)=NULL;
}
else
{
delete pResult->pszString;
delete pResult;
pResult=NULL;
}
return pResult;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
//
// Returns a good piece of text to use (the ID, the pControl's Text or our Text);
// don't really like all the alloc'ing and freeing here.
//
LPTSTR CBaseSAPI::GetControlText(IRCMLControl *pControl)
{
TCHAR id[MAX_PATH];
LPWSTR pszButtonText;
get_Attr( L"TEXT", &pszButtonText );
LPWSTR pszShortButtonText=NULL;
if(pszButtonText==NULL)
{
pControl->get_Attr( L"TEXT", &pszButtonText );
pszShortButtonText=FindNiceText(pszButtonText);
pszButtonText=pszShortButtonText;
}
if( pszButtonText == NULL )
{
LPWSTR pszID;
LPWSTR pszControlType;
pControl->get_Attr(L"ID",&pszID);
pControl->get_StringType( &pszControlType );
wsprintf(id,TEXT("Control [%s] %s"),pszControlType, pszID);
pszButtonText = id;
}
if( pszButtonText )
{
LPTSTR pszText=new TCHAR[lstrlen(pszButtonText)+1];
lstrcpy(pszText, pszButtonText );
if(pszShortButtonText)
delete pszShortButtonText;
return pszText;
}
return NULL;
}
//
// We keep a property on the RCML root node pointing to the context
//
HRESULT CBaseSAPI::GetRecoContext()
{
if( g_cpEngine == NULL )
return E_FAIL;
HRESULT hr=S_OK;
// get the FORM node - we add information here.
IRCMLNode * pForm;
DetachParent(&pForm);
BOOL bFound=FALSE;
do
{
if( SUCCEEDED( hr=pForm->DetachParent( &pForm )))
{
bFound=SUCCEEDED(pForm->IsType( TEXT("FORM" )));
}
} while( (bFound== FALSE) && pForm );
if(pForm == NULL )
return E_FAIL;
LPWSTR pszType;
pForm->get_StringType(&pszType);
//
// BUGBUG - this is VERY bad.
// add a VT_VARIANT property mechanism for containers.
//
BOOL bGot=FALSE;
#ifdef _ONE_CONTEXT
// We do this ONLY ONCE per dialog.
LPWSTR pszContext;
if( SUCCEEDED( pForm->get_Attr( TEXT("SAPI:CONTEXT"), &pszContext )))
{
ISpRecoContext *pContext=(ISpRecoContext *)StringToIntDef( pszContext, 0 );
if( pContext )
{
m_cpRecoCtxt=pContext;
bGot=TRUE;
}
}
#endif
if( bGot == FALSE )
{
if( SUCCEEDED( hr=g_cpEngine->CreateRecoContext( &m_cpRecoCtxt.p ) ))
{
// REVIEW - BIG BIG HACK!
TCHAR szNumString[128];
wsprintf( szNumString, TEXT("%d"), m_cpRecoCtxt );
pForm->put_Attr( TEXT("SAPI:CONTEXT"), szNumString);
// end hack.
if( SUCCEEDED(hr = m_cpRecoCtxt->SetNotifySink( this ))) // This add ref's US.
{
// Tell SR what types of events interest us. Here we only care about command
// recognition.
const ULONGLONG ullInterest =
// SPFEI(SPEI_SOUND_START) | SPFEI(SPEI_SOUND_END) |
// SPFEI(SPEI_PHRASE_START) |
SPFEI(SPEI_RECOGNITION) |
// SPFEI(SPEI_FALSE_RECOGNITION) |
// SPFEI(SPEI_HYPOTHESIS) |
// SPFEI(SPEI_INTERFERENCE) |
// SPFEI(SPEI_REQUEST_UI) | SPFEI(SPEI_RECO_STATE_CHANGE) |
// SPFEI(SPEI_PROPERTY_NUM_CHANGE) | SPFEI(SPEI_PROPERTY_STRING_CHANGE)
0;
hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);
}
}
}
return hr;
}
//
// Called for ALL RULES (as we only have one context)
//
HRESULT STDMETHODCALLTYPE CBaseSAPI::Notify( void)
{
#ifdef _ONE_CONTEXT
// work out which rule caused this thing to fire.
CSpEvent event;
if (m_cpRecoCtxt)
{
while (event.GetFrom(m_cpRecoCtxt) == S_OK)
{
switch (event.eEventId)
{
case SPEI_RECOGNITION:
{
ISpRecoResult * pResult = event.RecoResult();
{
CBaseSAPI * pBase;
if( SUCCEEDED( pResult->GetGrammarId((ULONG*)&pBase)))
{
ISpPhrase * pPhrase = event.RecoResult();
pBase->ExecuteCommand(pPhrase);
}
}
}
break;
case SPEI_INTERFERENCE:
{
g_Notifications.SetText(L"I can't quite hear you");
}
break;
case SPEI_FALSE_RECOGNITION:
{
g_Notifications.SetText(L"I mis-understood you");
}
break;
}
}
}
#else
Callback();
#endif
return S_OK;
}
BOOL CBaseSAPI::NeedsTip()
{
LPWSTR pszTip;
if( SUCCEEDED( get_Attr(L"TOOLTIP", &pszTip )))
return FALSE;
return TRUE;
}
void CBaseSAPI::LoadNumberGrammar(LPCWSTR pszFile)
{
// load a specific grammar.
if( pszFile == NULL )
pszFile = L"c:\\cicerorcml\\numbers.xml";
HRESULT hr;
if( SUCCEEDED( hr = GetRecoContext() ))
{
// we use the 'shared' context here.
if( SUCCEEDED( hr = m_cpRecoCtxt->CreateGrammar( (DWORD)this, &m_cpNumberGrammar) ))
{
if( SUCCEEDED( hr = m_cpNumberGrammar->LoadCmdFromFile( pszFile, SPLO_STATIC ) )) // is TRUE needed?
{
m_cpNumberGrammar->SetRuleState(
NULL,
NULL,
SPRS_ACTIVE);
}
}
}
}
BOOL CBaseSAPI::GetVoice()
{
if( g_cpVoice )
return TRUE;
if( SUCCEEDED( g_cpVoice.CoCreateInstance( CLSID_SpVoice ) ))
{
return TRUE;
}
return FALSE;
}