2025-11-28 00:35:46 +09:00

2057 lines
76 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright © Microsoft Corporation. All rights reserved
/****************************************************************************
* Reco.Cpp *
*----------*
* Description:
* This sample program is a basic speech recognition test tool. It is a
* dialog based Win32 application that uses ATL to help manage COM interface
* pointers. A basic understanding of the ATL CComPtr object will be useful
* when looking at this code.
*
* This tool allows the user to load and activate command and control
* grammars, activate a dictation grammar, turn the microphone and audio
* retention on and off, and examine the results of recognitions.
****************************************************************************/
#include "stdafx.h"
#define FT64(filetime) (*((LONGLONG*)&(filetime)))
/****************************************************************************
* WinMain *
*---------*
* Description:
* Main entry point for a Win32 application. This program simply initializes
* COM, constructs a CRecoDlg class object on the stack, and displays the dialog
* by calling DialogBoxParam.
*
* Returns:
* Always returns 0
*
****************************************************************************/
int APIENTRY WinMain(__in HINSTANCE hInstance,
__in_opt HINSTANCE hPrevInstance,
__in_opt LPSTR lpCmdLine,
__in int nCmdShow)
{
#ifdef _WIN32_WCE
if (SUCCEEDED(::CoInitializeEx(NULL,COINIT_MULTITHREADED)))
#else
if (SUCCEEDED(::CoInitialize(NULL)))
#endif
{
// NOTE: Extra scope provided so DlgClass is destroyed before CoUninitialize is called.
{
CRecoDlgClass DlgClass(hInstance);
::DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_RECODLG), NULL, (DLGPROC)CRecoDlgClass::DlgProc, (LPARAM)&DlgClass);
}
::CoUninitialize();
}
return 0;
}
/****************************************************************************
* Implementation of CRecoDlgClass
****************************************************************************/
/****************************************************************************
* CRecoDlgClass::DlgProc *
*------------------------*
* Description:
* This static member function is the message handler for the main appliation
* dialog. When the dialog is initialized via WM_INITDIALOG, the pointer to the
* CRecoDlgClass object is passed to this function in the lParam. This is stored
* in the USERDATA window long and used for subsequent message processing.
*
* Returns:
* Appropriate LRESULT for give window message
*
****************************************************************************/
LRESULT CALLBACK CRecoDlgClass::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
CRecoDlgClass * pThis = (CRecoDlgClass *)(LONG_PTR)::GetWindowLongPtr(hDlg, GWLP_USERDATA);
LRESULT lr = FALSE;
switch (message)
{
case WM_INITDIALOG:
::SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)lParam);
pThis = (CRecoDlgClass *)lParam;
lr = pThis->InitDialog(hDlg);
break;
case WM_RECOEVENT:
pThis->RecoEvent();
lr = TRUE;
break;
case WM_HELP:
::DialogBoxParam(pThis->m_hInstance, (LPCTSTR)IDD_DIALOG_BETAHELP, hDlg, (DLGPROC)BetaHelpDlgProc, NULL);
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_CHECK_CREATE)
{
pThis->UpdateRecoCtxtState();
}
else if (LOWORD(wParam) == IDC_CHECK_MIC)
{
BOOL bIsChecked = (::SendDlgItemMessage( hDlg, IDC_CHECK_MIC, BM_GETCHECK, 0, 0 ) == BST_CHECKED);
if (bIsChecked)
{
pThis->m_cpRecognizer->SetRecoState( SPRST_ACTIVE );
}
else
{
pThis->m_cpRecognizer->SetRecoState( SPRST_INACTIVE );
}
}
else if (LOWORD(wParam) == IDM_FILE_EXIT || LOWORD(wParam) == IDCANCEL)
{
pThis->Cleanup();
EndDialog(hDlg, 0);
lr = TRUE;
}
else if (LOWORD(wParam) == IDM_HELP)
{
::DialogBoxParam(pThis->m_hInstance, (LPCTSTR)IDD_DIALOG_BETAHELP, hDlg, (DLGPROC)BetaHelpDlgProc, NULL);
}
else if (LOWORD(wParam) == IDM_CFG_LOAD_GRAMMAR)
{
pThis->SpecifyCAndCGrammar();
}
else if (LOWORD(wParam) == IDM_CFG_SET_WORD_SEQUENCE_DATA)
{
pThis->SetWordSequenceData();
}
else if (LOWORD(wParam) == IDC_EDIT_PARSETEXT)
{
BOOL bEnableSubmit = (::SendDlgItemMessage(hDlg, IDC_EDIT_PARSETEXT, WM_GETTEXTLENGTH, 0, 0) != 0);
::EnableWindow(::GetDlgItem(hDlg, IDC_BUTTON_SUBMIT), bEnableSubmit);
}
else if (LOWORD(wParam) == IDC_BUTTON_SUBMIT)
{
TCHAR tszText[MAX_PATH];
::SendDlgItemMessage(hDlg, IDC_EDIT_PARSETEXT, WM_GETTEXT, sp_countof(tszText), (LPARAM)tszText);
if (tszText[0] != 0)
{
pThis->EmulateRecognition(CT2W(tszText));
}
}
else if( LOWORD(wParam) == IDC_BUTTON_ALTERNATES )
{
LRESULT item = ::SendDlgItemMessage(hDlg, IDC_LIST_PHRASES, LB_GETCURSEL, 0, 0);
CRecoDlgListItem * pli = NULL;
if (item != LB_ERR)
{
pli = (CRecoDlgListItem*)::SendDlgItemMessage(hDlg, IDC_LIST_PHRASES, LB_GETITEMDATA, item, 0);
}
if( pli )
{
CAlternatesDlgClass AltDlg;
AltDlg.m_pResult = pli->GetRecoResult();
if (AltDlg.m_pResult)
{
LPARAM RetVal = ::DialogBoxParam( pThis->m_hInstance, (LPCTSTR)IDD_ALTS_DIALOG, hDlg,
(DLGPROC)CAlternatesDlgClass::AlternatesDlgProc,
(LPARAM)&AltDlg );
//--- Draw the new item
if( RetVal == IDOK )
{
CSpDynamicString dstrText;
AltDlg.m_pResult->GetText( SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE,
TRUE, &dstrText, NULL );
CRecoDlgListItem * pNewItem =
new CRecoDlgListItem( AltDlg.m_pResult, dstrText, pli->IsHypothesis() );
LPARAM iNewPhrase = ::SendDlgItemMessage( hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pNewItem);
::SendDlgItemMessage( hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
::SendDlgItemMessage( hDlg, IDC_LIST_PHRASES, LB_DELETESTRING, iNewPhrase-1, 0);
pThis->UpdatePropWindow( pNewItem );
}
}
}
lr = TRUE;
}
else if (LOWORD(wParam) == IDM_CFG_ADD_DYNAMIC_RULE && pThis->m_cpCFGGrammar)
{
CDynGrammarDlgClass DlgClass(pThis, &pThis->m_ItemList);
::DialogBoxParam(pThis->m_hInstance, (LPCTSTR)IDD_DYNGRAMMARDLG, hDlg, (DLGPROC)CDynGrammarDlgClass::DlgProc, (LPARAM)&DlgClass);
lr = TRUE;
}
else if (LOWORD(wParam) == IDM_SLM_ADAPT_FROM_FILE)
{
lr = pThis->FeedDocumentFromFile();
}
else if (LOWORD(wParam) == IDM_SLM_ADAPT_FROM_CLIPBOARD)
{
lr = pThis->FeedDocumentFromClipboard();
}
else if (LOWORD(wParam) == IDM_SLM_TRAIN_FROM_FILE)
{
lr = pThis->TrainFromFile();
}
else if (LOWORD(wParam) == IDC_RADIO_SHARED ||
LOWORD(wParam) == IDC_RADIO_INPROC)
{
pThis->UpdateRecoCtxtState();
}
else if (pThis->m_cpRecoCtxt)
{
if (HIWORD(wParam) == BN_CLICKED &&
(LOWORD(wParam) == IDC_CHECK_CFG ||
LOWORD(wParam) == IDC_CHECK_DICTATION ||
LOWORD(wParam) == IDC_CHECK_DICTATION_ACTIVE ||
LOWORD(wParam) == IDC_CHECK_CFG_ACTIVE ||
LOWORD(wParam) == IDC_CHECK_SPELLING ||
LOWORD(wParam) == IDC_CHECK_SPELLING_ACTIVE))
{
pThis->UpdateGrammarState(LOWORD(wParam));
}
else if (LOWORD(wParam) == IDC_CHECK_RETAIN_AUDIO)
{
SPAUDIOOPTIONS Opts = SPAO_NONE;
if (::SendDlgItemMessage( hDlg, IDC_CHECK_RETAIN_AUDIO, BM_GETCHECK, 0, 0 ) == BST_CHECKED)
{
Opts = SPAO_RETAIN_AUDIO;
}
pThis->m_cpRecoCtxt->SetAudioOptions(Opts, NULL, NULL);
}
else if (LOWORD(wParam) == IDC_LIST_PHRASES)
{
WORD wNotify = HIWORD(wParam);
LPARAM item = ::SendDlgItemMessage(hDlg, IDC_LIST_PHRASES, LB_GETCURSEL, 0, 0);
CRecoDlgListItem * pli = NULL;
if (item != LB_ERR)
{
pli = (CRecoDlgListItem*)::SendDlgItemMessage(hDlg, IDC_LIST_PHRASES, LB_GETITEMDATA, item, 0);
}
if (wNotify == LBN_SELCHANGE)
{
pThis->UpdatePropWindow(pli);
}
else if (wNotify == LBN_DBLCLK && pli)
{
ISpRecoResult * pResult = pli->GetRecoResult();
if (pResult)
{
if (FAILED(pli->GetRecoResult()->SpeakAudio(NULL, 0, SPF_ASYNC, NULL)))
{
CComPtr<ISpVoice> cpVoice;
pThis->m_cpRecoCtxt->GetVoice(&cpVoice);
if (cpVoice)
{
cpVoice->Speak(pli->GetText(), SPF_ASYNC, NULL);
}
}
}
}
}
}
break;
case WM_DRAWITEM:
if (wParam == IDC_LIST_PHRASES)
{
LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT)lParam;
const CRecoDlgListItem * pli = (const CRecoDlgListItem *)pdis->itemData;
if (pli && pli->GetText())
{
HGDIOBJ hfontOld = pThis->m_hfont ? SelectObject( pdis->hDC, pThis->m_hfont ) : NULL;
CComPtr<ISpRecoResult> cpResult;
SPRECORESULTTIMES times;
UINT oldTextAlign = GetTextAlign(pdis->hDC);
UINT options = ETO_OPAQUE | ETO_CLIPPED;
cpResult = pli->GetRecoResult();
if (cpResult && cpResult->GetResultTimes(&times) == S_OK)
{
FILETIME localtime;
SYSTEMTIME systime;
if (FileTimeToLocalFileTime(&times.ftStreamTime, &localtime) &&
FileTimeToSystemTime(&localtime, &systime))
{
TCHAR sztime[18];
_stprintf_s(sztime, _countof(sztime), _T("[%2d:%02d:%02d.%03d] -"),
systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
SetTextAlign(pdis->hDC, TA_UPDATECP);
MoveToEx(pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, NULL);
ExtTextOut(pdis->hDC,
pdis->rcItem.left, pdis->rcItem.top,
options,
&pdis->rcItem,
sztime, 16,
NULL);
options = ETO_CLIPPED;
}
}
ExtTextOutW(pdis->hDC,
pdis->rcItem.left, pdis->rcItem.top,
options,
&pdis->rcItem,
pli->GetText(), pli->GetTextLength(),
NULL);
if (pli->IsHypothesis())
{
const WCHAR *pszHypothesis = L" [Hypothesis]";
ExtTextOutW(pdis->hDC,
pdis->rcItem.left, pdis->rcItem.top,
options,
&pdis->rcItem,
pszHypothesis, (UINT)wcslen(pszHypothesis),
NULL);
}
SetTextAlign(pdis->hDC, oldTextAlign);
if (hfontOld)
{
SelectObject(pdis->hDC, hfontOld);
}
}
}
break;
case WM_DELETEITEM:
if (wParam == IDC_LIST_PHRASES)
{
CRecoDlgListItem * pli = (CRecoDlgListItem *)((DELETEITEMSTRUCT *)lParam)->itemData;
delete pli;
lr = TRUE;
}
break;
}
return lr;
}
/****************************************************************************
* CRecoDlgClass::ConstructRuleDisplay *
*-------------------------------------*
* Description:
* Given the SPPHRASERULE obtained from the parsed result object,
* writes the tree for the rules into dstr.
* Calls itself recursively for sub-rules.
* Returns:
* S_OK
****************************************************************************/
HRESULT CRecoDlgClass::ConstructRuleDisplay(const SPPHRASERULE *pRule, CSpDynamicString &dstr, ULONG ulLevel)
{
HRESULT hr = S_OK;
WCHAR szText[256];
// construct indent
while(SUCCEEDED(hr) && pRule)
{
swprintf_s(szText, _countof(szText), L" [%2d, %2d] ", pRule->ulFirstElement, pRule->ulFirstElement + pRule->ulCountOfElements);
for (ULONG i = 0; i < ulLevel; i++)
{
dstr.Append(L"\t");
}
if (pRule->pszName)
{
if (wcslen(pRule->pszName) > 240)
{
dstr.Append(L"\"(Rule name too long)\" = \"");
}
else
{
WCHAR *pwszRuleName = _wcsdup( pRule->pszName );
if ( pwszRuleName )
{
swprintf_s(szText, _countof(szText), L"%s \"%s\" (%d)", szText, pwszRuleName, pRule->ulId);
dstr.Append(szText);
free( pwszRuleName );
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
else
{
dstr.Append(L"\"(UNK)\" = \"");
}
swprintf_s(szText, _countof(szText), L"{Confidence (%i) %f}", pRule->Confidence, pRule->SREngineConfidence);
dstr.Append(szText);
dstr.Append(L"\r\n");
if (pRule->pFirstChild && SUCCEEDED(hr))
{
hr = ConstructRuleDisplay(pRule->pFirstChild, dstr, ulLevel + 1);
}
pRule = pRule->pNextSibling;
}
return hr;
}
/****************************************************************************
* CRecoDlgClass::ConstructPropertyDisplay *
*-----------------------------------------*
* Description:
* Given the SPPHRASEPROPERTY obtained from the parsed result object,
* writes the property tree into dstr.
* Calls itself recursively for sub-properties.
* Returns:
* S_OK
****************************************************************************/
HRESULT CRecoDlgClass::ConstructPropertyDisplay(const SPPHRASEELEMENT *pElem, const SPPHRASEPROPERTY *pProp,
CSpDynamicString & dstr, ULONG ulLevel)
{
HRESULT hr = S_OK;
WCHAR szText[256];
// construct indent
while(SUCCEEDED(hr) && pProp)
{
swprintf_s(szText, _countof(szText), L" [%2d, %2d] ", pProp->ulFirstElement, pProp->ulCountOfElements ? pProp->ulFirstElement + pProp->ulCountOfElements-1 : 0);
dstr.Append(szText);
for (ULONG i = 0; i < ulLevel; i++)
{
dstr.Append(L"\t");
}
if (pProp->pszName)
{
if (wcslen(pProp->pszName) > 240)
{
dstr.Append(L"\"(Rule name too long)\" = \"");
}
else
{
WCHAR *pwszPropName = _wcsdup( pProp->pszName );
if ( pwszPropName )
{
swprintf_s(szText, _countof(szText), L"\"%s\" = \"", pwszPropName);
dstr.Append(szText);
free( pwszPropName );
}
else
{
dstr.Append(L"Out of memory" );
}
}
}
else
{
dstr.Append(L"\"(UNK)\" = \"");
}
if (!pProp->pszValue)
{
// construct the value from the elements!
ULONG ulEndElement = pProp->ulFirstElement + pProp->ulCountOfElements;
for (ULONG j = pProp->ulFirstElement; j < ulEndElement; j++)
{
if (j+1 < ulEndElement)
{
dstr.Append2(pElem[j].pszDisplayText, L" ");
}
else
{
dstr.Append(pElem[j].pszDisplayText);
}
}
}
else
{
dstr.Append(pProp->pszValue);
}
if (pProp->vValue.vt != VT_EMPTY)
{
CComVariant cv = pProp->vValue;
cv.ChangeType(VT_BSTR);
swprintf_s(szText, _countof(szText), L"\" (%d = %s)", pProp->ulId, cv.bstrVal);
}
else
{
swprintf_s(szText, _countof(szText), L"\" (%d)", pProp->ulId);
}
swprintf_s(szText, _countof(szText), L"{Confidence (%i) %f}", pProp->Confidence, pProp->SREngineConfidence);
dstr.Append(szText);
dstr.Append(L"\r\n");
if (pProp->pFirstChild)
{
hr = ConstructPropertyDisplay(pElem, pProp->pFirstChild, dstr, ulLevel + 1);
}
pProp = pProp->pNextSibling;
}
return hr;
}
/****************************************************************************
* CRecoDlgClass::UpdatePropWindow *
*---------------------------------*
* Description:
* Updates the contents of the property status window to contain a full
* text dump of the result list item pointed to by pli. If a NULL is passed
* to this method, the property window will be cleared.
*
* Returns:
* TRUE if property window was updated
* FALSE if it was not
*
****************************************************************************/
BOOL CRecoDlgClass::UpdatePropWindow(const CRecoDlgListItem * pli)
{
BOOL fOK = FALSE;
HWND hwndStatus = ::GetDlgItem(m_hDlg, IDC_STATUS);
ISpPhrase * pPhrase = pli ? pli->GetRecoResult() : NULL;
SPPHRASE *pElements;
if (pli && pPhrase && SUCCEEDED(pPhrase->GetPhrase(&pElements)))
{
WCHAR szText[256];
CSpDynamicString dstr;
if (pElements->Rule.pszName || pElements->Rule.ulId)
{
WCHAR *pwszRuleName = NULL;
if ( pElements->Rule.pszName )
{
pwszRuleName = _wcsdup( pElements->Rule.pszName );
}
else
{
pwszRuleName = _wcsdup( L"<no name>" );
}
if ( pwszRuleName )
{
swprintf_s(szText, _countof(szText), L"RULE=\"%s\" (%05d)\r\n\r\n", pwszRuleName, pElements->Rule.ulId);
dstr.Append(szText);
dstr.Append(pli->GetText());
free( pwszRuleName );
}
else
{
dstr.Append( L"Out of memory" );
}
}
else
{
dstr = L"DICTATION\r\n\r\n";
CSpDynamicString dstrText;
pPhrase->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
dstr.Append(dstrText);
}
dstr.Append(L"\r\n");
// Display the overall confidence. This is specified on a per-rule basis.
// However, the top rule contains everything and hence is the phrase confidence.
swprintf_s(szText, _countof(szText), L"\r\n{Overall Confidence (%i) %f}\r\n", pElements->Rule.Confidence, pElements->Rule.SREngineConfidence);
dstr.Append(szText);
CComQIPtr<ISpXMLRecoResult> cpSML(pli->GetRecoResult());
if (cpSML)
{
CSpDynamicString dstrSML;
HRESULT hr = cpSML->GetXMLResult(&dstrSML, SPXRO_SML);
if (hr == S_OK)
{
dstr.Append(L"\r\nSML:\r\n");
dstr.Append(dstrSML);
dstr.Append(L"\r\n");
}
else if (hr == SPERR_SML_GENERATION_FAIL)
{
SPSEMANTICERRORINFO err;
if(SUCCEEDED(cpSML->GetXMLErrorInfo(&err)))
{
ULONG ulBufSize = 1024;
dstrSML.ClearAndGrowTo(ulBufSize);
_snwprintf_s(dstrSML, ulBufSize, ulBufSize - 1, L"\r\nSML Error: [line: %d, HRESULT: %x] %s: %s\r\n",
err.ulLineNumber, err.hrResultCode, err.pszSource ? err.pszSource : L"", err.pszDescription ? err.pszDescription : L"");
dstrSML.m_psz[ulBufSize - 1] = L'\0';
dstr.Append(dstrSML);
if(err.pszScriptLine)
{
_snwprintf_s(dstrSML, ulBufSize, ulBufSize - 1, L" Error in this line: \"%s\"\r\n", err.pszScriptLine);
dstrSML.m_psz[ulBufSize - 1] = L'\0';
}
dstr.Append(dstrSML);
CoTaskMemFree(err.pszSource);
CoTaskMemFree(err.pszDescription);
CoTaskMemFree(err.pszScriptLine);
}
}
else if(FAILED(hr))
{
dstr.Append(L"\r\nUnknown error processing result.\r\n");
}
}
if (pElements->pProperties)
{
dstr.Append(L"\r\nPROPERTIES:\r\n");
ConstructPropertyDisplay(pElements->pElements, pElements->pProperties, dstr, 0);
dstr.Append(L"\r\n");
}
dstr.Append(L"\r\nPARSE TREE:\r\n");
ConstructRuleDisplay(&pElements->Rule, dstr, 0);
dstr.Append(L"\r\nELEMENTS:\r\n");
for (ULONG i = 0; i < pElements->Rule.ulCountOfElements; i++)
{
WCHAR wszIntPhone[SP_MAX_PRON_LENGTH * 7 + 1];
wszIntPhone[0] = L'\0';
if ( pElements->pElements[i].pszPronunciation )
{
m_cpPhoneConv->IdToPhone(pElements->pElements[i].pszPronunciation, wszIntPhone);
}
WCHAR *pwszDisplayText = _wcsdup( pElements->pElements[i].pszDisplayText );
if (pwszDisplayText && wcslen(pwszDisplayText) + wcslen(wszIntPhone) <= 200)
{
swprintf_s(szText, _countof(szText), L" <%u - %u> \"%s\" {%s} {Confidence (%i) %f}\r\n",
pElements->pElements[i].ulAudioStreamOffset,
pElements->pElements[i].ulAudioStreamOffset + pElements->pElements[i].ulAudioSizeBytes,
pwszDisplayText,
wszIntPhone, pElements->pElements[i].ActualConfidence, pElements->pElements[i].SREngineConfidence);
}
else if (!pwszDisplayText || wcslen(pwszDisplayText) <= 200)
{
swprintf_s(szText, _countof(szText), L" <%u - %u> \"%s\" {} {Confidence (%i) %f}\r\n",
pElements->pElements[i].ulAudioStreamOffset,
pElements->pElements[i].ulAudioStreamOffset + pElements->pElements[i].ulAudioSizeBytes,
pwszDisplayText ? pwszDisplayText : L"(out of memory)",
pElements->pElements[i].ActualConfidence, pElements->pElements[i].SREngineConfidence);
}
else
{
swprintf_s(szText, _countof(szText), L" <%u - %u> \"()\" {} {Confidence (%i) %f}\r\n",
pElements->pElements[i].ulAudioStreamOffset,
pElements->pElements[i].ulAudioStreamOffset + pElements->pElements[i].ulAudioSizeBytes,
pElements->pElements[i].ActualConfidence, pElements->pElements[i].SREngineConfidence);
}
if ( pwszDisplayText )
{
free( pwszDisplayText );
}
dstr.Append(szText);
}
::CoTaskMemFree(pElements);
SPRECORESULTTIMES times;
pli->GetRecoResult()->GetResultTimes(&times);
DWORD dwTickEnd = times.dwTickCount + (DWORD)(times.ullLength / 10000);
DWORD dwTickNow = GetTickCount();
swprintf_s(szText, _countof(szText), L"GetTickCount latency = (%u - %u) = %dms\r\n", dwTickNow, dwTickEnd, dwTickNow - dwTickEnd);
//ATLTRACE(szText);
dstr.Append(szText);
//ATLTRACE(L"\r\n");
{
::SendMessage(hwndStatus, WM_SETTEXT, 0, (LPARAM)(LPTSTR)CW2T(dstr));
}
fOK = TRUE;
}
else
{
{
::SendMessage(hwndStatus, WM_SETTEXT, 0, (LPARAM)TEXT(""));
}
}
return fOK;
}
/****************************************************************************
* CRecoDlgClass::UpdateGrammarState *
*-----------------------------------*
* Description:
* This method is called whenever a check box state has changed to update
* the state of grammars. It will create or release m_cpCFGGrammar and
* m_cpDictationGrammar and activate or deactivate either of them.
*
* If at any point during this function, a SAPI call fails, the state of
* the application will be reset.
*
* Returns:
* S_OK, or failed HRESULT from SAPI calls made below
*
****************************************************************************/
HRESULT CRecoDlgClass::UpdateGrammarState(WORD wIdChanged)
{
#ifdef _DEBUG
static BOOL fInUpdate = FALSE;
_ASSERTE(!fInUpdate);
fInUpdate = TRUE;
#endif // _DEBUG
HRESULT hr = S_OK;
if (m_cpRecoCtxt) // only if there is a recognition context
{
BOOL fActivateCFG = (::SendDlgItemMessage(m_hDlg, IDC_CHECK_CFG_ACTIVE, BM_GETCHECK, 0, 0) != 0);
BOOL fActivateDictation = (::SendDlgItemMessage(m_hDlg, IDC_CHECK_DICTATION_ACTIVE, BM_GETCHECK, 0, 0) != 0);
BOOL fLoadDictation = (::SendDlgItemMessage( m_hDlg, IDC_CHECK_DICTATION, BM_GETCHECK, 0, 0 ) != 0);
BOOL fLoadCFG = (::SendDlgItemMessage( m_hDlg, IDC_CHECK_CFG, BM_GETCHECK, 0, 0 ) != 0);
BOOL fActivateSpelling = (::SendDlgItemMessage(m_hDlg, IDC_CHECK_SPELLING_ACTIVE, BM_GETCHECK, 0, 0) != 0);
BOOL fLoadSpelling = (::SendDlgItemMessage( m_hDlg, IDC_CHECK_SPELLING, BM_GETCHECK, 0, 0 ) != 0);
SPSTATEHANDLE hDynRule = NULL;
// Force the pair of checks to be in sync. If the ID is 0 then no checkbox
// has changed state, so we don't have to do anything.
WORD wIdToUpdate = 0;
WPARAM NewState;
switch (wIdChanged)
{
case IDC_CHECK_CFG:
if ((!fLoadCFG) && fActivateCFG)
{
fActivateCFG = FALSE;
wIdToUpdate = IDC_CHECK_CFG_ACTIVE;
NewState = BST_UNCHECKED;
}
break;
case IDC_CHECK_DICTATION:
if ((!fLoadDictation) && fActivateDictation)
{
fActivateDictation = FALSE;
wIdToUpdate = IDC_CHECK_DICTATION_ACTIVE;
NewState = BST_UNCHECKED;
}
break;
case IDC_CHECK_SPELLING:
if ((!fLoadSpelling) && fActivateSpelling)
{
fActivateSpelling = FALSE;
wIdToUpdate = IDC_CHECK_SPELLING_ACTIVE;
NewState = BST_UNCHECKED;
}
break;
case IDC_CHECK_CFG_ACTIVE:
if (fActivateCFG && (!fLoadCFG))
{
fLoadCFG = TRUE;
wIdToUpdate = IDC_CHECK_CFG;
NewState = BST_CHECKED;
}
break;
case IDC_CHECK_DICTATION_ACTIVE:
if (fActivateDictation && (!fLoadDictation))
{
fLoadDictation = TRUE;
wIdToUpdate = IDC_CHECK_DICTATION;
NewState = BST_CHECKED;
}
break;
case IDC_CHECK_SPELLING_ACTIVE:
if (fActivateSpelling && (!fLoadSpelling))
{
fLoadSpelling = TRUE;
wIdToUpdate = IDC_CHECK_SPELLING;
NewState = BST_CHECKED;
}
break;
}
if (wIdToUpdate)
{
::SendDlgItemMessage(m_hDlg, wIdToUpdate, BM_SETCHECK, NewState, 0);
}
//
// Now load the appropriate grammar(s)...
//
UINT uiErrorMessageID = 0;
if (fLoadCFG)
{
if (!m_cpCFGGrammar)
{
//
// If the first char of m_szGrammarFile is 0 then load the solitaire grammar
// from our resource. Otherwise, load the grammar from the file.
//
hr = m_cpRecoCtxt->CreateGrammar(MYGRAMMARID, &m_cpCFGGrammar);
if (SUCCEEDED(hr))
{
if (*m_szGrammarFile)
{
hr = m_cpCFGGrammar->LoadCmdFromFile(CT2W(m_szGrammarFile), SPLO_DYNAMIC);
}
else
{
// Need a method of determining the language id of the grammar resource we
// want to load. This is dependent solely on the primary language of the engine the
// user has selected. Hence, the resources are labelled 'Primary', SUBLANG_NEUTRAL
// and we use the primary language id of the engine to do the LoadCmdFromResource.
// NOTE: we now have an ENU vs. ENG system, so we need to make a sublanguage distinction
hr = m_cpCFGGrammar->LoadCmdFromResource(NULL, MAKEINTRESOURCEW(IDR_SOL_CFG),
L"SRGRAMMAR", MAKELANGID(PRIMARYLANGID(m_langid), SUBLANGID(m_langid)), SPLO_DYNAMIC);
}
}
// Load dynamic rules, if any.
if (SUCCEEDED(hr) && m_ItemList.GetHead())
{
hr = m_cpCFGGrammar->GetRule(g_szDynRuleName, NULL, SPRAF_TopLevel | SPRAF_Dynamic, TRUE, &hDynRule);
if (SUCCEEDED(hr))
{
CDynItem *pItem = m_ItemList.GetHead();
while (pItem && SUCCEEDED(hr))
{
hr = m_cpCFGGrammar->AddWordTransition(hDynRule, NULL, pItem->m_dstr, L" ", SPWT_LEXICAL, 1.0, NULL);
pItem = m_ItemList.GetNext(pItem);
}
if (SUCCEEDED(hr))
hr = m_cpCFGGrammar->Commit(0);
}
}
}
}
else
{
::SendDlgItemMessage(m_hDlg, IDC_CHECK_CFG_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0);
m_cpCFGGrammar.Release();
m_cpCFGVoice.Release();
}
if (SUCCEEDED(hr))
{
if (fLoadDictation)
{
if (!m_cpDictationGrammar)
{
hr = m_cpRecoCtxt->CreateGrammar(MYGRAMMARID, &m_cpDictationGrammar);
if (SUCCEEDED(hr))
{
hr = m_cpDictationGrammar->LoadDictation(NULL, SPLO_STATIC);
}
}
}
else
{
::SendDlgItemMessage(m_hDlg, IDC_CHECK_DICTATION_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0);
m_cpDictationGrammar.Release();
}
}
if (SUCCEEDED(hr))
{
if (fLoadSpelling)
{
if (!m_cpSpellingGrammar)
{
hr = m_cpRecoCtxt->CreateGrammar(MYGRAMMARID, &m_cpSpellingGrammar);
if (SUCCEEDED(hr))
{
hr = m_cpSpellingGrammar->LoadDictation(SPTOPIC_SPELLING, SPLO_STATIC);
if (FAILED(hr))
{
m_cpSpellingGrammar.Release();
}
}
}
}
else
{
::SendDlgItemMessage(m_hDlg, IDC_CHECK_SPELLING_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0);
m_cpSpellingGrammar.Release();
}
}
// If we've failed by now, it's a grammar load failure
if ( FAILED( hr ) )
{
uiErrorMessageID = IDS_GRAMMAR_LOAD_FAIL;
}
if (SUCCEEDED(hr) && m_cpCFGGrammar)
{
hr = m_cpCFGGrammar->SetRuleState(NULL, NULL, fActivateCFG ? SPRS_ACTIVE : SPRS_INACTIVE);
if (SUCCEEDED(hr) && hDynRule)
{
hr = m_cpCFGGrammar->SetRuleState(g_szDynRuleName, NULL, fActivateCFG ? SPRS_ACTIVE : SPRS_INACTIVE);
}
}
if (SUCCEEDED(hr) && m_cpDictationGrammar)
{
hr = m_cpDictationGrammar->SetDictationState(fActivateDictation ? SPRS_ACTIVE : SPRS_INACTIVE);
}
if (SUCCEEDED(hr) && m_cpSpellingGrammar)
{
hr = m_cpSpellingGrammar->SetDictationState(fActivateSpelling ? SPRS_ACTIVE : SPRS_INACTIVE);
}
// If we have failed here, but the grammar load succeeded, then it's a
// SetRuleState/SetDictationState failure
if ( FAILED( hr ) && (0 == uiErrorMessageID) )
{
if ( SPERR_DEVICE_BUSY == hr )
{
uiErrorMessageID = IDS_DEVICE_BUSY;
}
else
{
uiErrorMessageID = IDS_RULESTATE_FAIL;
}
}
if (SUCCEEDED(hr))
{
HMENU hMenu = ::GetMenu(m_hDlg);
::EnableMenuItem(hMenu, IDM_CFG_LOAD_GRAMMAR, fLoadCFG ? MF_GRAYED : MF_ENABLED);
::EnableMenuItem(hMenu, IDM_CFG_ADD_DYNAMIC_RULE, fLoadCFG ? MF_ENABLED : MF_GRAYED);
::EnableMenuItem(hMenu, IDM_CFG_SET_WORD_SEQUENCE_DATA, fLoadCFG ? MF_ENABLED : MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_TRAIN_FROM_FILE, m_cpRecognizer ? MF_ENABLED : MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_ADAPT_FROM_FILE, m_cpDictationGrammar ? MF_ENABLED : MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_ADAPT_FROM_CLIPBOARD, m_cpDictationGrammar ? MF_ENABLED : MF_GRAYED);
}
else
{
MessageBoxFromResource( uiErrorMessageID );
Reset();
}
}
#ifdef _DEBUG
fInUpdate = FALSE;
#endif // _DEBUG
return hr;
}
/****************************************************************************
* CRecoDlgClass::UpdateRecoCtxtState *
*------------------------------------*
* Description:
* Creates or releases the m_cpRecoContext member based on the state of the
* ICD_CHECK_CREATE checkbox. This method also enables or disables various controls
* based on weather or not there is a m_cpRecoContext object. If for any reason
* one of the SAPI calls fails, the application state will be reset.
*
* Returns:
* S_OK
* Return value of CRecoDlgClass::CreateRecoCtxt()
* Return value of CRecoDlgClass::UpdateGrammarState()
*
****************************************************************************/
HRESULT CRecoDlgClass::UpdateRecoCtxtState()
{
HRESULT hr = S_OK;
BOOL bCreateChecked = (::SendDlgItemMessage(m_hDlg, IDC_CHECK_CREATE, BM_GETCHECK, 0, 0 ) == BST_CHECKED);
if (bCreateChecked)
{
if (!m_cpRecoCtxt)
{
BOOL bShared = (::SendDlgItemMessage(m_hDlg, IDC_RADIO_SHARED, BM_GETCHECK, 0, 0) == BST_CHECKED);
if (bShared)
{
hr = CreateRecoCtxt(0);
}
else
{
HWND hwndList = ::GetDlgItem(m_hDlg, IDC_COMBO_ENGINES);
LPARAM i = ::SendMessage(hwndList, CB_GETCURSEL, 0, 0);
hr = CreateRecoCtxt(::SendMessage(hwndList, CB_GETITEMDATA, i, 0));
}
}
if( SUCCEEDED(hr) )
{
hr = UpdateGrammarState(0);
if (FAILED(hr))
{
Reset();
}
}
}
else
{
Reset();
}
BOOL bEnable = (m_cpRecoCtxt != NULL);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_LIST_PHRASES), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_CFG), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_CFG_ACTIVE), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_MIC), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_DICTATION), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_DICTATION_ACTIVE), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_SPELLING), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_SPELLING_ACTIVE), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_RETAIN_AUDIO), bEnable);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_RADIO_SHARED), m_cpRecoCtxt == NULL);
::EnableWindow(::GetDlgItem(m_hDlg, IDC_RADIO_INPROC), m_cpRecoCtxt == NULL);
BOOL bEnableCombo = (m_cpRecoCtxt == NULL);
if (bEnableCombo)
{
bEnableCombo = (::SendDlgItemMessage(m_hDlg, IDC_RADIO_INPROC, BM_GETCHECK, 0, 0) == BST_CHECKED);
}
::EnableWindow(::GetDlgItem(m_hDlg, IDC_COMBO_ENGINES), bEnableCombo);
if (!bEnable)
{
UpdatePropWindow(NULL);
}
return hr;
}
/****************************************************************************
* CRecoDlgClass::InitDialog *
*---------------------------*
* Description:
* Enumerates the available SR engines and populates the combo box.
* Initializes the application to the default starting state.
*
* Returns:
* TRUE if successful, else FALSE
*
****************************************************************************/
BOOL CRecoDlgClass::InitDialog(HWND hDlg)
{
HRESULT hr = S_OK;
m_hDlg = hDlg;
//
// Initialize the list of engines. The shared context will be indicated by a NULL
// GUID pointer (a 0 item data). The default inproc engine will be GUID_NULL, and all
// other engines will have a GUID allocated for them.
//
CComPtr<IEnumSpObjectTokens> cpEnum;
if (SUCCEEDED(hr))
{
hr = SpEnumTokens(SPCAT_RECOGNIZERS, NULL, NULL, &cpEnum);
}
if (FAILED(hr))
{
return FALSE;
}
CComPtr<ISpObjectToken> cpDefaultRecoToken;
hr = SpGetDefaultTokenFromCategoryId(SPCAT_RECOGNIZERS, &cpDefaultRecoToken);
if (SUCCEEDED(hr))
{
CSpDynamicString dstrDefaultName;
if (SUCCEEDED(SpGetDescription(cpDefaultRecoToken, &dstrDefaultName)))
{
CSpDynamicString dstrDesc(L"Shar&ed: ");
dstrDesc.Append(dstrDefaultName);
::SendDlgItemMessage(hDlg, IDC_RADIO_SHARED, WM_SETTEXT, 0, (LPARAM)(LPTSTR)CW2T(dstrDesc));
}
}
//
// Now set up the list.
//
HWND hwndList = ::GetDlgItem(hDlg, IDC_COMBO_ENGINES);
HDC hdcList = GetDC(hwndList);
HGDIOBJ hFont = (HGDIOBJ)SendMessage(hwndList, WM_GETFONT, 0, 0);
HGDIOBJ hOldFont = SelectObject(hdcList, hFont);
int iListWidth = 0;
ISpObjectToken * pEngineToken;
while (cpEnum->Next(1, &pEngineToken, NULL) == S_OK)
{
CSpDynamicString dstrName;
if (SUCCEEDED(SpGetDescription(pEngineToken, &dstrName)))
{
SIZE itemSize;
CW2T tszName(dstrName);
LRESULT NewItem = ::SendMessage(hwndList, CB_ADDSTRING, 0, (LPARAM)(LPTSTR)tszName);
::SendMessage(hwndList, CB_SETITEMDATA, NewItem, (LPARAM)pEngineToken);
if (GetTextExtentPoint32(hdcList, tszName, (int)_tcslen(tszName), &itemSize) && itemSize.cx > iListWidth)
{
iListWidth = itemSize.cx;
}
}
else
{
pEngineToken->Release();
}
}
if (iListWidth)
{
iListWidth += (GetSystemMetrics(SM_CXVSCROLL) + 2*GetSystemMetrics(SM_CXEDGE));
::SendMessage(hwndList, CB_SETDROPPEDWIDTH, (WPARAM)iListWidth, 0);
}
SelectObject(hdcList, hOldFont);
ReleaseDC(hwndList, hdcList);
::SendMessage(hwndList, CB_SETCURSEL, 0, 0);
::SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)::LoadIconA(m_hInstance, MAKEINTRESOURCEA(IDI_RECO)));
::SendDlgItemMessage( m_hDlg, IDC_CHECK_MIC, BM_SETCHECK, BST_CHECKED, 0 );
::SendDlgItemMessage( m_hDlg, IDC_RADIO_SHARED, BM_SETCHECK, BST_CHECKED, 0 );
UpdateRecoCtxtState();
UpdateGrammarStatusWindow();
//
// Phrase window is owner draw, in order to prevent dataloss in thunking
// We keep the Unicode string in the CRecoDlgListItem and call ExtTextOutW.
//
HWND hwndPhrase = GetDlgItem(hDlg, IDC_LIST_PHRASES);
LPARAM l = GetWindowLongPtr(hwndPhrase, GWL_STYLE);
SetWindowLongPtr(hwndPhrase, GWL_STYLE, (LONG_PTR)(l & ~LBS_HASSTRINGS));
return TRUE;
}
/****************************************************************************
* CRecoDlgClass::Cleanup *
*------------------------*
* Description:
* Called when the application is shutting down.
*
* Returns:
* void
*
****************************************************************************/
void CRecoDlgClass::Cleanup()
{
//
// Release all the engine tokens in the combo box
//
HWND hwndList = ::GetDlgItem(m_hDlg, IDC_COMBO_ENGINES);
LPARAM NumEntries = ::SendMessage(hwndList, CB_GETCOUNT, 0, 0);
for (LONG i = 0; i < NumEntries; i++)
{
LRESULT ItemData = ::SendMessage(hwndList, CB_GETITEMDATA, i, 0);
if (ItemData)
{
((IUnknown *)ItemData)->Release();
}
}
Reset();
if (m_hfont)
{
::DeleteObject(m_hfont);
}
}
/****************************************************************************
* CRecoDlgClass::CreateRecoCtxt *
*-------------------------------*
* Description:
* This method is only called from UpdateRecoCtxtState. It is passed the
* item data from selected item in the IDC_COMBO_ENGINES combo box. If the item
* data is NULL then the "Shared default engine" is selected. If it is non-NULL
* then the item data points to an SpObjectToken for the selected engine to
* load inproc.
*
* Returns:
* S_OK, or failed HRESULT of SAPI initialization calls made below
*
****************************************************************************/
HRESULT CRecoDlgClass::CreateRecoCtxt(LRESULT ItemData)
{
HRESULT hr = S_OK;
// Need to QI for this such that grammar options can be set to SPGO_ALL
CComPtr<ISpRecoContext2> cpRecoCtxt2;
//
// ItemData is NULL, for shared case. In the inproc case, we are REQUIRED to set the
// input so we create the default audio object.
//
if (ItemData)
{
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);
if (SUCCEEDED(hr))
{
hr = m_cpRecognizer->SetRecognizer((ISpObjectToken *)ItemData);
}
if (SUCCEEDED(hr))
{
CComPtr<ISpObjectToken> cpAudioToken;
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpAudioToken);
if (SUCCEEDED(hr))
{
hr = m_cpRecognizer->SetInput(cpAudioToken, TRUE);
}
}
}
else
{
hr = m_cpRecognizer.CoCreateInstance(CLSID_SpSharedRecognizer);
}
if (SUCCEEDED(hr))
{
hr = m_cpRecognizer->CreateRecoContext(&m_cpRecoCtxt);
}
if (SUCCEEDED(hr))
{
HRESULT hr2;
hr2 = m_cpRecoCtxt.QueryInterface(&cpRecoCtxt2);
if (SUCCEEDED(hr2))
{
hr = cpRecoCtxt2->SetGrammarOptions(SPGO_ALL);
}
}
if (SUCCEEDED(hr))
{
hr = m_cpRecoCtxt->SetNotifyWindowMessage(m_hDlg, WM_RECOEVENT, 0, 0);
}
if (SUCCEEDED(hr))
{
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_RECO_OTHER_CONTEXT) |
SPFEI(SPEI_REQUEST_UI) | SPFEI(SPEI_RECO_STATE_CHANGE) |
SPFEI(SPEI_PROPERTY_NUM_CHANGE) | SPFEI(SPEI_PROPERTY_STRING_CHANGE);
hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);
}
if( SUCCEEDED(hr) )
{
//--- Set the max number of desired alternates
hr = m_cpRecoCtxt->SetMaxAlternates( 3 );
}
SPRECOGNIZERSTATUS stat;
// Get locale/font settings
if (SUCCEEDED(hr))
{
ZeroMemory(&stat, sizeof(stat));
hr = m_cpRecognizer->GetStatus(&stat);
}
if (SUCCEEDED(hr))
{
m_langid = stat.aLangID[0];
// Pick an appropriate font. On Windows 2000, let the system fontlink.
DWORD dwVersion = GetVersion();
if ( dwVersion >= 0x80000000
|| LOBYTE(LOWORD(dwVersion)) < 5
|| m_langid != MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US))
{
TCHAR achCodePage[6];
UINT uiCodePage;
if (0 != GetLocaleInfo(MAKELCID(m_langid, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, achCodePage, 6))
{
uiCodePage = _ttoi(achCodePage);
}
else
{
uiCodePage = GetACP();
}
CComPtr<IMultiLanguage> cpMultiLanguage;
MIMECPINFO MimeCpInfo;
if ( SUCCEEDED(cpMultiLanguage.CoCreateInstance(CLSID_CMultiLanguage))
&& SUCCEEDED(cpMultiLanguage->GetCodePageInfo(uiCodePage, &MimeCpInfo)))
{
if (m_hfont)
{
DeleteObject(m_hfont);
}
m_hfont = CreateFont(0, 0, 0, 0, FW_NORMAL, 0, 0, 0,
MimeCpInfo.bGDICharset,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH,
CW2T(MimeCpInfo.wszProportionalFont));
}
}
}
if (SUCCEEDED(hr))
{
m_cpPhoneConv.Release();
hr = SpCreatePhoneConverter(m_langid, NULL, NULL, &m_cpPhoneConv);
}
if (FAILED(hr))
{
MessageBoxFromResource( IDS_RECOCONTEXT_FAIL );
Reset();
}
return hr;
}
/****************************************************************************
* CRecoDlgClass::UpdateGrammarStatusWindow *
*------------------------------------------*
* Description:
* Called whenever the grammar file has changed to set the text of the
* grammar status control (a line in the dialog box below the property window).
*
* Returns:
* void
*
****************************************************************************/
void CRecoDlgClass::UpdateGrammarStatusWindow()
{
const static TCHAR szPrefixText[] = _T("Current C&&C Grammar: ");
TCHAR szDesc[sp_countof(m_szGrammarFileTitle) + sp_countof(szPrefixText)];
_tcscpy_s(szDesc, _countof(szDesc), szPrefixText);
_tcscat_s(szDesc, _countof(szDesc), *m_szGrammarFile ? m_szGrammarFileTitle : _T("Solitaire (built-in demo grammar)"));
::SendDlgItemMessage(m_hDlg, IDC_GRAMMAR_STATUS, WM_SETTEXT, 0, (LPARAM)szDesc);
}
/****************************************************************************
* CRecoDlgClass::Reset *
*----------------------*
* Description:
* Shutdown and release all of the objects associated with an active
* recognition context and reset UI elements to their initial state.
*
* Returns:
* void
*
****************************************************************************/
void CRecoDlgClass::Reset()
{
UpdatePropWindow(NULL);
::SendDlgItemMessage (m_hDlg, IDC_CHECK_CFG_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_CFG, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_DICTATION_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_DICTATION, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_SPELLING_ACTIVE, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_SPELLING, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_CREATE, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_RETAIN_AUDIO, BM_SETCHECK, BST_UNCHECKED, 0 );
::SendDlgItemMessage (m_hDlg, IDC_CHECK_MIC, BM_SETCHECK, BST_CHECKED, 0 );
::EnableWindow(::GetDlgItem(m_hDlg, IDC_CHECK_MIC), 0);
HMENU hMenu = ::GetMenu(m_hDlg);
::EnableMenuItem(hMenu, IDM_CFG_LOAD_GRAMMAR, MF_ENABLED);
::EnableMenuItem(hMenu, IDM_CFG_ADD_DYNAMIC_RULE, MF_GRAYED);
::EnableMenuItem(hMenu, IDM_CFG_SET_WORD_SEQUENCE_DATA, MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_TRAIN_FROM_FILE, MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_ADAPT_FROM_FILE, MF_GRAYED);
::EnableMenuItem(hMenu, IDM_SLM_ADAPT_FROM_CLIPBOARD, MF_GRAYED);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_RESETCONTENT, 0, 0);
m_cpCFGGrammar.Release();
m_cpCFGVoice.Release();
m_cpRecoCtxt.Release();
m_cpDictationGrammar.Release();
m_cpSpellingGrammar.Release();
m_cpRecognizer.Release();
m_bInSound = FALSE;
m_bGotReco = FALSE;
}
/****************************************************************************
* CRecoDlgClass::RecoEvent *
*--------------------------*
* Description:
* Whenever a notification is sent from the m_cpRecoCtxt object, this
* method is called. When a recognition event occurs, it adds an item to
* the listbox. If a sound start / sound end occurs without a recogntion
* then it inserts a "<noise>" string in the listbox.
*
* Returns:
* void
*
****************************************************************************/
void CRecoDlgClass::RecoEvent()
{
CSpEvent event;
LPARAM iNewPhrase;
if (m_cpRecoCtxt)
{
while (event.GetFrom(m_cpRecoCtxt) == S_OK)
{
switch (event.eEventId)
{
case SPEI_REQUEST_UI:
if (event.RequestTypeOfUI() != NULL)
{
#ifdef _DEBUG
SPRECOCONTEXTSTATUS recostatus;
m_cpRecoCtxt->GetStatus(&recostatus);
_ASSERTE(wcscmp(recostatus.szRequestTypeOfUI, event.RequestTypeOfUI()) == 0);
#endif // _DEBUG
m_cpRecognizer->DisplayUI(m_hDlg, L"Basic Speech Recognition", event.RequestTypeOfUI() , NULL, 0);
}
else
{
#ifdef _DEBUG
SPRECOCONTEXTSTATUS recostatus;
m_cpRecoCtxt->GetStatus(&recostatus);
_ASSERTE(recostatus.szRequestTypeOfUI[0] == '\0');
#endif // _DEBUG
}
break;
case SPEI_INTERFERENCE:
{
CSpDynamicString dstr;
switch(event.Interference())
{
case SPINTERFERENCE_NONE:
dstr = L"Interference - None";
break;
case SPINTERFERENCE_NOISE:
dstr = L"Interference - Noise";
break;
case SPINTERFERENCE_NOSIGNAL:
dstr = L"Interference - No signal";
break;
case SPINTERFERENCE_TOOLOUD:
dstr = L"Interference - Too loud";
break;
case SPINTERFERENCE_TOOQUIET:
dstr = L"Interference - Too quiet";
break;
case SPINTERFERENCE_TOOFAST:
dstr = L"Dictation mode: Interference - Too fast";
break;
case SPINTERFERENCE_TOOSLOW:
dstr = L"Dictation mode: Interference - Too slow";
break;
default:
dstr = L"Unrecognized Interference Event";
}
CRecoDlgListItem * pli = new CRecoDlgListItem(NULL, dstr, FALSE);
if (pli)
{
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
}
}
break;
case SPEI_PROPERTY_NUM_CHANGE:
{
TCHAR sz[MAX_PATH * 2];
WCHAR *pwszPropertyName = NULL;
if ( event.PropertyName() )
{
pwszPropertyName = _wcsdup( event.PropertyName() );
}
else
{
pwszPropertyName = _wcsdup( L"<no name>" );
}
_stprintf_s(sz, _countof(sz), _T("Attrib change: %s=%d"),
pwszPropertyName ? (LPTSTR)CW2T(pwszPropertyName) : _T("(out of memory)"),
event.PropertyNumValue());
if ( pwszPropertyName )
{
free( pwszPropertyName );
}
CRecoDlgListItem * pli = new CRecoDlgListItem(NULL, CT2W(sz), FALSE);
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
}
break;
case SPEI_PROPERTY_STRING_CHANGE:
{
TCHAR sz[MAX_PATH * 2];
WCHAR *pwszPropertyName = NULL;
if ( event.PropertyName() )
{
pwszPropertyName = _wcsdup( event.PropertyName() );
}
else
{
pwszPropertyName = _wcsdup( L"<no name>" );
}
WCHAR *pwszPropertyStringValue = NULL;
if ( event.PropertyStringValue() )
{
pwszPropertyStringValue = _wcsdup( event.PropertyStringValue() );
}
else
{
pwszPropertyStringValue = _wcsdup( L"<no string value>" );
}
_stprintf_s(sz, _countof(sz), _T("Attrib change: %s=%s"),
pwszPropertyName ? (LPTSTR)CW2T(pwszPropertyName) : _T("(out of memory)"),
pwszPropertyStringValue ? (LPTSTR)CW2T(pwszPropertyStringValue): _T("(out of memory)") );
if ( pwszPropertyName )
{
free( pwszPropertyName );
}
if ( pwszPropertyStringValue )
{
free( pwszPropertyStringValue );
}
CRecoDlgListItem * pli = new CRecoDlgListItem(NULL, CT2W(sz), FALSE);
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
}
break;
case SPEI_RECO_STATE_CHANGE:
::SendDlgItemMessage( m_hDlg, IDC_CHECK_MIC, BM_SETCHECK,
(event.RecoState() == SPRST_INACTIVE) ? BST_UNCHECKED : BST_CHECKED, 0 );
break;
case SPEI_SOUND_START:
m_bInSound = TRUE;
break;
case SPEI_SOUND_END:
if (m_bInSound)
{
m_bInSound = FALSE;
if (!m_bGotReco)
{
const WCHAR wszNoise[] = L"(Noise without speech)";
CRecoDlgListItem * pli = new CRecoDlgListItem(NULL, wszNoise, FALSE);
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
}
m_bGotReco = FALSE;
}
break;
case SPEI_RECO_OTHER_CONTEXT:
{
m_bGotReco = TRUE;
CRecoDlgListItem * pli = new CRecoDlgListItem(NULL, L"(Recognition for other client)", FALSE);
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
}
break;
case SPEI_FALSE_RECOGNITION:
case SPEI_HYPOTHESIS:
case SPEI_RECOGNITION:
{
CComPtr<ISpRecoResult> cpResult;
cpResult = event.RecoResult();
m_bGotReco = TRUE;
CRecoDlgListItem * pli;
CSpDynamicString dstrText;
cpResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
if (event.eEventId == SPEI_FALSE_RECOGNITION)
dstrText.Append(L"<FALSERECO>");
pli = new CRecoDlgListItem(cpResult, dstrText, event.eEventId == SPEI_HYPOTHESIS);
iNewPhrase = ::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_ADDSTRING, 0, (LPARAM)pli);
::SendDlgItemMessage(m_hDlg, IDC_LIST_PHRASES, LB_SETCURSEL, iNewPhrase, 0);
UpdatePropWindow(pli);
cpResult.Release();
}
break;
}
}
}
}
/****************************************************************************
* CRecoDlgClass::EmulateRecognition *
*-----------------------------------*
* Description:
* Calls ISpRecognizer::EmulateRecognition to parse the phrase
* built out of the text in pszText
* Returns:
* void
****************************************************************************/
void CRecoDlgClass::EmulateRecognition(__in WCHAR *pszText)
{
CComPtr<ISpPhraseBuilder> cpPhrase;
if (SUCCEEDED(CreatePhraseFromText(pszText, &cpPhrase, m_langid, m_cpPhoneConv)) && m_cpRecognizer)
{
m_cpRecognizer->EmulateRecognition(cpPhrase);
}
}
/****************************************************************************
* CRecoDlgClass::SpecifyCAndCGrammar *
*------------------------------------*
* Description:
* This method is called when the "C&C Grammar" button is pressed on the
* main application dialog. It opens the standard file dialog so that the user
* can specify a command and control grammar to be used by the application. If
* the user cancels out of the dialog, then this method resets the m_szGrammarFile
* member so that the built-in grammar will be used.
*
* Returns:
* void
*
****************************************************************************/
void CRecoDlgClass::SpecifyCAndCGrammar()
{
OPENFILENAME OpenFileName;
const static TCHAR szFilter[] = _T("Grammar files\0*.cfg;*.xml;*.grxml\0All files\0*.*\0");
size_t ofnsize = (BYTE*)&OpenFileName.lpTemplateName + sizeof(OpenFileName.lpTemplateName) - (BYTE*)&OpenFileName;
// Fill in the OPENFILENAME structure to support a template and hook.
OpenFileName.lStructSize = (DWORD)ofnsize;
OpenFileName.hwndOwner = m_hDlg;
OpenFileName.hInstance = m_hInstance;
OpenFileName.lpstrFilter = szFilter;
OpenFileName.lpstrCustomFilter = NULL;
OpenFileName.nMaxCustFilter = 0;
OpenFileName.nFilterIndex = 0;
OpenFileName.lpstrFile = m_szGrammarFile;
OpenFileName.nMaxFile = sp_countof(m_szGrammarFile);
OpenFileName.lpstrFileTitle = m_szGrammarFileTitle;
OpenFileName.nMaxFileTitle = sp_countof(m_szGrammarFileTitle);
OpenFileName.lpstrInitialDir = NULL;
OpenFileName.lpstrTitle = _T("Select a grammar file");
OpenFileName.nFileOffset = 0;
OpenFileName.nFileExtension = 0;
OpenFileName.lpstrDefExt = _T("cfg");
OpenFileName.lCustData = 0;
OpenFileName.lpfnHook = NULL;
OpenFileName.lpTemplateName = NULL;
OpenFileName.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
// Call the common dialog function.
if (!GetOpenFileName(&OpenFileName))
{
// If for any reason, GetOpenFileName returns false, revert to our internal grammar
m_szGrammarFile[0] = 0;
}
UpdateGrammarStatusWindow();
}
/****************************************************************************
* CRecoDlgClass::MessageBoxFromResource *
*---------------------------------------*
* Description:
* Display a MessageBox whose text is the requested string resource ID
*
* Returns:
* void
*
****************************************************************************/
int CRecoDlgClass::MessageBoxFromResource( UINT uiResource )
{
TCHAR szMessage[ MAX_LOADSTRING ];
::LoadString( m_hInstance, uiResource, szMessage, _countof( szMessage ) );
return ::MessageBox( m_hDlg, szMessage, NULL, MB_ICONEXCLAMATION );
}
/****************************************************************************
* CRecoDlgClass::SetWordSequenceData *
*------------------------------------*
* Description:
* This method is called when the "Set Word Sequence Data" menu item is selected from the
* C&C menu. It opens the standard file dialog so that the user
* can specify a document to be used by the application.
*
****************************************************************************/
void CRecoDlgClass::SetWordSequenceData()
{
WCHAR * pwszCoMem = 0;
WCHAR * pwszCoMem2 = 0;
ULONG cch = 0;
HRESULT hr = GetTextFile(&pwszCoMem, &cch);
if (SUCCEEDED(hr))
{
SPTEXTSELECTIONINFO tsi;
tsi.ulStartActiveOffset = 0;
tsi.cchActiveChars = cch;
tsi.ulStartSelection = 0;
tsi.cchSelection = cch;
pwszCoMem2 = (WCHAR *)CoTaskMemAlloc(sizeof(WCHAR) * (cch + 2));
if (pwszCoMem2 == NULL)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
// SetWordSequenceData requires double NULL terminator.
memcpy(pwszCoMem2, pwszCoMem, sizeof(WCHAR) * cch);
pwszCoMem2[cch] = L'\0';
pwszCoMem2[cch+1] = L'\0';
m_cpCFGGrammar->SetWordSequenceData(pwszCoMem2, cch + 2, &tsi);
CoTaskMemFree(pwszCoMem2);
}
CoTaskMemFree(pwszCoMem);
}
}
/****************************************************************************
* Implementation of CDynGrammarDlgClass
****************************************************************************/
/****************************************************************************
* CDynGrammarDlgClass::DlgProc *
*------------------------------*
* Description:
* This static member function is the message handler for the dynamic rule
* dialog. When the dialog is initialized via WM_INITDIALOG, the pointer to the
* CDynGrammarDlgClass object is passed to this function in the lParam. This is stored
* in the USERDATA window long and used for subsequent message processing.
*
* Returns:
* Appropriate LRESULT for give window message
*
****************************************************************************/
LRESULT CALLBACK CDynGrammarDlgClass::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
CDynGrammarDlgClass * pThis = (CDynGrammarDlgClass *)(LONG_PTR)::GetWindowLongPtr(hDlg, GWLP_USERDATA);
switch (message)
{
case WM_INITDIALOG:
::SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)lParam);
pThis = (CDynGrammarDlgClass *)lParam;
return pThis->InitDialog(hDlg, pThis->m_pItemList);
case WM_HELP:
::DialogBoxParam(pThis->m_pParent->m_hInstance, (LPCTSTR)IDD_DIALOG_BETAHELP, hDlg, (DLGPROC)BetaHelpDlgProc, NULL);
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
pThis->Cleanup();
EndDialog(hDlg, 0);
return TRUE;
}
else if (LOWORD(wParam) == IDC_BUTTON_ADDITEM)
{
pThis->AddItem();
}
else if (LOWORD(wParam) == IDC_BUTTON_CLEARALL)
{
pThis->ClearAll();
}
else if (HIWORD(wParam) == EN_UPDATE && LOWORD(wParam) == IDC_EDIT_NEWITEM)
{
//
// Only enable the "Add" button if there is some text in the edit control
//
BOOL fEnable = (::SendMessage((HWND)lParam, WM_GETTEXTLENGTH, 0, 0) != 0);
::EnableWindow(::GetDlgItem(hDlg, IDC_BUTTON_ADDITEM), fEnable);
}
break;
}
return FALSE;
}
/****************************************************************************
* CDynGrammarDlgClass::InitDialog *
*---------------------------------*
* Description:
* This method is called from the WM_INITDIALOG message. It initialzes
* the m_hDlg member and populates the listbox with any items that have already
* been added to the "DynRule".
*
* Returns:
* TRUE if successful, else FALSE
*
****************************************************************************/
BOOL CDynGrammarDlgClass::InitDialog(HWND hDlg, CSpBasicQueue<CDynItem> *pItemList)
{
HRESULT hr = S_OK;
m_hDlg = hDlg;
m_pItemList = pItemList;
ISpRecoGrammar * pGram = m_pParent->m_cpCFGGrammar;
hr = pGram->GetRule(g_szDynRuleName, NULL, SPRAF_TopLevel | SPRAF_Dynamic | SPRAF_Active, TRUE, &m_hDynRule);
if (SUCCEEDED(hr))
{
HWND hwndList = ::GetDlgItem(hDlg, IDC_LIST_ITEMS);
CDynItem *pItem = m_pItemList->GetHead();
while (pItem)
{
LRESULT Index = ::SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)CW2T(pItem->m_dstr));
::SendMessage(hwndList, LB_SETITEMDATA, Index, (LPARAM)(LPTSTR)CW2T(pItem->m_dstr));
pItem = m_pItemList->GetNext(pItem);
}
}
return TRUE;
}
/****************************************************************************
* CDynGrammarDlgClass::AddItem *
*------------------------------*
* Description:
* Called when the "Add" button is pressed. It calls AddItem to add the
* text in the edit control to the "DynRule" in the CFG grammar.
*
* Returns:
* void
*
****************************************************************************/
void CDynGrammarDlgClass::AddItem()
{
HRESULT hr = S_OK;
WCHAR wszItem[MAX_PATH];
GETTEXTEX gtex = { sp_countof(wszItem), GT_DEFAULT, 1200, NULL, NULL };
::SendDlgItemMessage(m_hDlg, IDC_EDIT_NEWITEM, EM_GETTEXTEX, (WPARAM)&gtex, (LPARAM)wszItem);
hr = m_pParent->m_cpCFGGrammar->AddWordTransition(m_hDynRule, NULL, wszItem, L" ", SPWT_LEXICAL, 1.0, NULL);
if (SUCCEEDED(hr))
{
CDynItem *pItem = new CDynItem(wszItem);
if (pItem)
{
m_pItemList->InsertTail(pItem);
::SendDlgItemMessage(m_hDlg, IDC_LIST_ITEMS, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)CW2T(wszItem));
}
::SendDlgItemMessage(m_hDlg, IDC_EDIT_NEWITEM, WM_SETTEXT, 0, (LPARAM)_T(""));
::SetFocus(::GetDlgItem(m_hDlg, IDC_EDIT_NEWITEM));
}
}
/****************************************************************************
* CDynGrammarDlgClass::ClearAll *
*-------------------------------*
* Description:
* Called when the "Clear" button is pressed. Clears the contents of the
* listbox and clears the "DynRule" in the CFG grammar.
*
* Returns:
* void
*
****************************************************************************/
void CDynGrammarDlgClass::ClearAll()
{
::SendDlgItemMessage(m_hDlg, IDC_LIST_ITEMS, LB_RESETCONTENT, 0, 0);
m_pParent->m_cpCFGGrammar->ClearRule(m_hDynRule);
m_pItemList->Purge();
}
/****************************************************************************
* CDynGrammarDlgClass::Cleanup *
*------------------------------*
* Description:
* Called when the dialog is being closed. Commits the changes to the
* "DynRule" and sets the activation state of the DynRule to the same state
* as the current setting of the checkbox in the main dialog.
*
* Returns:
* void
*
****************************************************************************/
void CDynGrammarDlgClass::Cleanup()
{
m_pParent->m_cpCFGGrammar->Commit(0);
BOOL fActive = (::SendDlgItemMessage(m_pParent->m_hDlg, IDC_CHECK_CFG_ACTIVE, BM_GETCHECK, 0, 0) != 0);
m_pParent->m_cpCFGGrammar->SetRuleState(g_szDynRuleName, NULL, fActive ? SPRS_ACTIVE : SPRS_INACTIVE);
}
/****************************************************************************
* Implemenation of help dialog for the tool
****************************************************************************/
const TCHAR g_szBetaHelpText[] =
_T("INTRODUCTION:\r\n")
_T("This tool can be used by engine vendors and CFG grammar developers to perform basic functionality ")
_T("testing for recognition. The tool can select a specific engine, load a specified command and control grammar, add items to ")
_T("a dynamic rule, enable a dictation grammar, and turn the microphone state on and off.\r\n\r\n")
_T("BASIC USE:\r\n")
_T("Select the speech recognition engine you want to use and then check Create Recognition Context. ")
_T("Once the engine has loaded, you can enable a dictation grammar by checking Activate Dictation. ")
_T("For debugging purposes, the operations of loading and then activating a specific grammar have been separated so that ")
_T("engine developers can debug the process of loading a grammar prior to the grammar activation.\r\n\r\n")
_T("COMMAND AND CONTROL GRAMMARS\r\n")
_T("Recognition has a built-in grammar containing the basic commands for solitaire. Try saying \"Play the King of Diamonds\" or ")
_T("\"Put the Jack of Clubs on the Queen please.\" If you want to load your own grammar, click Load C&&C. Use GramComp.Exe ")
_T("to compile the grammar for use with this program. If you wish to test dynamic lists, author a grammar with a rule named \"DynRule\" ")
_T("and then reference that rule from a top-level rule in your grammar. When you add items to the grammar, click Dynamic Rules. ")
_T("The items will be added to the dynamic rule in your grammar. If the grammar does not contain a rule named \"DynRule\", then the ")
_T("items added in the Dynamic Rules dialog will become top-level rules.\r\n\r\n")
_T("OTHER FEATURES:\r\n")
_T("When a command is recognized, the text is displayed in the list box at the top of the dialog box, and a dump of the full result is displayed ")
_T("in the status window towards the bottom of the dialog. To examine the result of a particular utterance, simply click it in the list box, and ")
_T("the result status window will be updated. To play back the audio from an utterance, double click the item. Note that if you have not ")
_T("checked Retain Reco Audio, the text will be played back using synthesized speech. If you have enabled audio retention, the ")
_T("original utterance will be played back. To emulate recognition, you can type a phrase you want parsed into the edit box ")
_T("at the bottom of the dialog and click \"Submit\"");
LRESULT CALLBACK BetaHelpDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
::SendDlgItemMessage(hDlg, IDC_BETA_HELP, WM_SETTEXT, 0, (LPARAM)g_szBetaHelpText);
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, 0);
return TRUE;
}
}
return FALSE;
}
/****************************************************************************
* AlternatesDlgProc *
*-------------------*
* Description:
* Dlgproc for the alternates dialog, which displays the
* alternates for the current phrase and allows the user to
* choose (commit) one of them.
****************************************************************************/
LRESULT CALLBACK CAlternatesDlgClass::
AlternatesDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
HRESULT hr = S_OK;
CAlternatesDlgClass* pThis = (CAlternatesDlgClass *)(LONG_PTR)::GetWindowLongPtr(hDlg, GWLP_USERDATA);
switch( message )
{
case WM_INITDIALOG:
{
::SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)lParam);
pThis = (CAlternatesDlgClass *)lParam;
//--- Ask for up to 5 alts
hr = pThis->m_pResult->GetAlternates( 0, SPRR_ALL_ELEMENTS, NUM_ALTS,
pThis->m_Alts, &pThis->m_ulNumAltsReturned );
//--- Show user choices
if( SUCCEEDED( hr ) && pThis->m_ulNumAltsReturned )
{
for( ULONG i = 0; i < pThis->m_ulNumAltsReturned; ++i )
{
WCHAR* pComemText = NULL;
BYTE Attrs;
hr = pThis->m_Alts[i]->GetText( SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, true,
&pComemText, &Attrs );
if( SUCCEEDED( hr ) )
{
::SendDlgItemMessage( hDlg, IDC_ALTS_LIST, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)CW2T(pComemText) );
::CoTaskMemFree( pComemText );
}
}
//--- Pick the first alternate by default
::SendDlgItemMessage( hDlg, IDC_ALTS_LIST, LB_SETCURSEL, 0, 0 );
}
break;
}
case WM_COMMAND:
{
if( LOWORD(wParam) == IDOK )
{
LPARAM Index = ::SendDlgItemMessage( hDlg, IDC_ALTS_LIST, LB_GETCURSEL, 0, 0 );
if( Index >= 0 && Index < (LPARAM) pThis->m_ulNumAltsReturned && Index < NUM_ALTS)
{
pThis->m_Alts[Index]->Commit();
}
}
if( ( LOWORD(wParam) == IDOK ) || LOWORD(wParam) == IDCANCEL )
{
EndDialog(hDlg, LOWORD(wParam) );
return TRUE;
}
break;
}
}
return FALSE;
}