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

287 lines
11 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
/******************************************************************************
* srengext.cpp
* This file contains the implementation of the CSampleSRExtension class.
* This implements the custom interface ISampleSRExtension.
* When an app QI's for this from the reco context, SAPI will
* look for the ExtensionCLSID field in the engine object token, and
* create this object and then QI for the requested interface.
******************************************************************************/
#include "stdafx.h"
#include "srengext.h"
/****************************************************************************
* CSampleSRExtension::ExamplePrivateEngineCall *
*----------------------------------------------*
* Description:
* This method shows an example of calling back to the main engine object.
* When CallEngine is called, the data supplied will get passed by SAPI
* to the ISpSREngine::PrivateCall method in CSrEngine.
* Return:
* S_OK
* FAILED(hr)
*****************************************************************************/
STDMETHODIMP CSampleSRExtension::ExamplePrivateEngineCall(void)
{
// We can use this method to pass data to and from the actual engine class, via the context
// We can query back to SAPI to find both the reco context, and,
// an IID__ISpPrivateEngineCall interface which can be used to call
// back to the main engine object.
static BYTE Data[4] = { 1, 2, 3, 4 };
HRESULT hr = S_OK;
// Try and query for new interface. Gets released on exit {don't keep persistent pointer}.
CComPtr<ISpPrivateEngineCallEx> cpEngineCallEx;
OuterQueryInterface(IID_ISpPrivateEngineCallEx, (void **)&cpEngineCallEx); // Ignore error
if (cpEngineCallEx)
{
void *pCoMemOutFrame = NULL;
ULONG ulOutFrameSize;
hr = cpEngineCallEx->CallEngineSynchronize( (void*)Data, sp_countof(Data), &pCoMemOutFrame, &ulOutFrameSize );
::CoTaskMemFree( pCoMemOutFrame );
}
else
{
// Else use old interface.
hr = m_pEngineCall->CallEngine( (void*)Data, sp_countof(Data) );
}
return hr;
}
/****************************************************************************
* CSampleSRExtension::GetDisplayAlternates *
*----------------------------------------------*
* Description:
* This method is supposed to generate display alternates for the
* specified phrase. It's called by Windows Vista Speech Recognition
* System before the dictation result is inserted into a document.
*
* The input phrase contains the left context, the phrase recognized by
* the engine and the right context. Left context is the word which is
* right before the cursor at the time of recognition. It's the first
* token. The right context is the word which is right after the cursor,
* and it is the last token. All the other tokens are the phrase that is
* recognized by the engine
*
* This method should return at least one valid alternate in order to
* enable Windows Vista to dictate using this engine.
* Return:
* S_OK
* FAILED(hr)
*****************************************************************************/
STDMETHODIMP CSampleSRExtension::GetDisplayAlternates(
const SPDISPLAYPHRASE *pPhrase,
ULONG cRequestCount,
SPDISPLAYPHRASE **ppCoMemPhrases,
ULONG *pcPhrasesReturned)
{
HRESULT hr = S_OK;
// Initialize pointers to NULL.
memset(ppCoMemPhrases, 0, sizeof(*ppCoMemPhrases) * cRequestCount);
// Return cRequestCount display alternates (though they'll be all same)
*pcPhrasesReturned = cRequestCount;
for (unsigned int p=0; p<*pcPhrasesReturned; p++)
{
// Only one buffer will be allocated (by CoTaskMemAlloc) to store all the
// SPDISPLAYPHRASE, the SPDISPLAYTOKEN structures that are pointed from
// the SPDISPLAYPHRASE, and all the strings. This buffer will be freed
// by the caller.
// First calculate the size of the memory needed
size_t cbPhraseSize = sizeof(SPDISPLAYPHRASE);
// Add the size needed to store the SPDISPLAYTOKEN structures
cbPhraseSize += pPhrase->ulNumTokens * sizeof(SPDISPLAYTOKEN);
// Finally add the size needed to store the strings
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;
// The tokens are appended right after the phrase
pCoMemPhrase->pTokens = (SPDISPLAYTOKEN *)(pCoMemPhrase + 1);
// String table begins right after the tokens
LPWSTR pStringTable = (LPWSTR)(pCoMemPhrase->pTokens + pCoMemPhrase->ulNumTokens);
LPWSTR pEndOfStringTable = (LPWSTR)(((BYTE*)pCoMemPhrase) + cbPhraseSize);
// Now fill the tokens and strings
for (unsigned int t=0; t<pPhrase->ulNumTokens; t++)
{
// Copy the token's display attributes
pCoMemPhrase->pTokens[t].bDisplayAttributes = pPhrase->pTokens[t].bDisplayAttributes;
// If current token is the left context
if (t == 0)
{
// If the left context is not empty (we're not at the beginning of the line or paragraph)
if (pPhrase->pTokens[t].pszDisplay != NULL)
{
// Insert a space after it
pCoMemPhrase->pTokens[t].bDisplayAttributes |= SPAF_ONE_TRAILING_SPACE;
}
}
else
{
// If it's not the right context
if (t<pPhrase->ulNumTokens-1)
{
// Insert a space after it
pCoMemPhrase->pTokens[t].bDisplayAttributes |= SPAF_ONE_TRAILING_SPACE;
}
}
// Copy the token's display string
if (pPhrase->pTokens[t].pszDisplay != NULL)
{
// Get the length of the token's display string
size_t length = wcslen(pPhrase->pTokens[t].pszDisplay);
// Copy it to the string table
if (wcscpy_s(pStringTable, pEndOfStringTable - pStringTable, pPhrase->pTokens[t].pszDisplay))
{
hr = E_OUTOFMEMORY;
goto CleanUp;
}
// Adjust the output token's display string to point to the string in the string table
pCoMemPhrase->pTokens[t].pszDisplay = pStringTable;
// Advance the string table pointer
pStringTable += length + 1;
}
else
{
ppCoMemPhrases[p]->pTokens[t].pszDisplay = NULL;
}
// Copy the token's lexical string
if (pPhrase->pTokens[t].pszLexical != NULL)
{
// Get the length of the token's lexical string
size_t length = wcslen(pPhrase->pTokens[t].pszLexical);
// Copy it to the string table
if (wcscpy_s(pStringTable, pEndOfStringTable - pStringTable, pPhrase->pTokens[t].pszLexical))
{
hr = E_OUTOFMEMORY;
goto CleanUp;
}
// Adjust the output token's lexical string to point to the string in the string table
pCoMemPhrase->pTokens[t].pszLexical = pStringTable;
// Advance the string table pointer
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;
}
/****************************************************************************
* CSampleSRExtension::SetFullStopTrailSpace *
*----------------------------------------------*
* Description:
* This is used by the application to specify the number of spaces after
* full stop puctuations.
* Return:
* S_OK
*****************************************************************************/
STDMETHODIMP CSampleSRExtension::SetFullStopTrailSpace(ULONG ulTrailSpace)
{
// Do nothing.
return S_OK;
}
/****************************************************************************
* CSampleSRExtension::Normalize *
*----------------------------------------------*
* Description:
* This method is supposed to return the list of normalized forms for a
* given word, but it's not supported by this sample engine.
* Return:
* S_NOTSUPPORTED
*****************************************************************************/
STDMETHODIMP CSampleSRExtension::Normalize(
LPCWSTR pszWord,
LPCWSTR pszLeftContext,
LPCWSTR pszRightContext,
WORD LangID,
SPNORMALIZATIONLIST *pNormalizationList)
{
// This functionality is not supported
pNormalizationList = NULL;
return S_NOTSUPPORTED;
}
/****************************************************************************
* CSampleSRExtension::GetPronunciations *
*----------------------------------------------*
* Description:
* This method is supposed to return all the pronunciations the engine
* knows about for a given word, but it's not supported by this sample
* engine.
* Return:
* S_NOTSUPPORTED
*****************************************************************************/
STDMETHODIMP CSampleSRExtension::GetPronunciations(
LPCWSTR pszWord,
LPCWSTR pszLeftContext,
LPCWSTR pszRightContext,
WORD LangID,
SPWORDPRONUNCIATIONLIST *pEnginePronunciationList)
{
// This functionality is not supported
pEnginePronunciationList->ulSize = 0;
pEnginePronunciationList->pvBuffer = 0;
pEnginePronunciationList->pFirstWordPronunciation = 0;
return S_NOTSUPPORTED;
}