diff options
author | Jaromir Wysoglad | 2019-07-15 21:09:36 -0700 |
---|---|---|
committer | Filippos Karapetis | 2019-09-01 22:47:55 +0300 |
commit | 8357e8e6bf909353c3db720ba8fda9cdb0a41933 (patch) | |
tree | c8a5735d16f9b82249d4877cb3985238bc4beadf /backends/text-to-speech/windows | |
parent | 6303f522e180f1474434c75c0e5a4d44b141fe3a (diff) | |
download | scummvm-rg350-8357e8e6bf909353c3db720ba8fda9cdb0a41933.tar.gz scummvm-rg350-8357e8e6bf909353c3db720ba8fda9cdb0a41933.tar.bz2 scummvm-rg350-8357e8e6bf909353c3db720ba8fda9cdb0a41933.zip |
TTS: Prepare for windows TTS
Add windows configuration in configure
Add basic skeleton to backends
Check if ttsMan is initialized in GUI
Diffstat (limited to 'backends/text-to-speech/windows')
-rw-r--r-- | backends/text-to-speech/windows/sphelper-scummvm.h | 2648 | ||||
-rw-r--r-- | backends/text-to-speech/windows/windows-text-to-speech.cpp | 109 | ||||
-rw-r--r-- | backends/text-to-speech/windows/windows-text-to-speech.h | 67 |
3 files changed, 2824 insertions, 0 deletions
diff --git a/backends/text-to-speech/windows/sphelper-scummvm.h b/backends/text-to-speech/windows/sphelper-scummvm.h new file mode 100644 index 0000000000..d910a65f67 --- /dev/null +++ b/backends/text-to-speech/windows/sphelper-scummvm.h @@ -0,0 +1,2648 @@ +/******************************************************************************* +* SPHelper.h * +*------------* +* Description: +* This is the header file for core helper functions implementation. +*------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +*******************************************************************************/ +#ifndef SPHelper_h +#define SPHelper_h + +#include <iostream> +#ifndef _INC_MALLOC +#include <malloc.h> +#endif + +#ifndef __sapi_h__ +#include <sapi.h> +#endif + +#ifndef __sapiddk_h__ +#include <sapiddk.h> +#endif + +#ifndef SPError_h +#include <SPError.h> +#endif + +#ifndef _INC_LIMITS +#include <limits.h> +#endif + +#ifndef _INC_MMSYSTEM +#include <mmsystem.h> +#endif + +#ifndef __comcat_h__ +#include <comcat.h> +#endif + +#ifndef _INC_MMREG +#include <mmreg.h> +#endif + +#include <cwctype> +//=== Constants ============================================================== +#define sp_countof(x) ((sizeof(x) / sizeof(*(x)))) + +/*** CSpDynamicString helper class +* +*/ +class CSpDynamicString +{ +public: + + WCHAR * m_psz; + CSpDynamicString() + { + m_psz = NULL; + } + CSpDynamicString(ULONG cchReserve) + { + m_psz = (WCHAR *)::CoTaskMemAlloc(cchReserve * sizeof(WCHAR)); + } + WCHAR * operator=(const CSpDynamicString& src) + { + if (m_psz != src.m_psz) + { + ::CoTaskMemFree(m_psz); + m_psz = src.Copy(); + } + return m_psz; + } + WCHAR * operator=(const WCHAR * pSrc) + { + Clear(); + if (pSrc) + { + ULONG cbNeeded = (wcslen(pSrc) + 1) * sizeof(WCHAR); + m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded); + if (m_psz) + { + memcpy(m_psz, pSrc, cbNeeded); + } + } + return m_psz; + } + + WCHAR * operator=(const char * pSrc) + { + Clear(); + if (pSrc) + { + ULONG cbNeeded = (lstrlenA(pSrc) + 1) * sizeof(WCHAR); + m_psz = (WCHAR *)::CoTaskMemAlloc(cbNeeded); + if (m_psz) + { + ::MultiByteToWideChar(CP_ACP, 0, pSrc, -1, m_psz, cbNeeded/sizeof(WCHAR)); + } + } + return m_psz; + } + + WCHAR * operator=(REFGUID rguid) + { + Clear(); + ::StringFromCLSID(rguid, &m_psz); + return m_psz; + } + + + /*explicit*/ CSpDynamicString(const WCHAR * pSrc) + { + m_psz = NULL; + operator=(pSrc); + } + /*explicit*/ CSpDynamicString(const char * pSrc) + { + m_psz = NULL; + operator=(pSrc); + } + /*explicit*/ CSpDynamicString(const CSpDynamicString& src) + { + m_psz = src.Copy(); + } + /*explicit*/ CSpDynamicString(REFGUID rguid) + { + ::StringFromCLSID(rguid, &m_psz); + } + + + ~CSpDynamicString() + { + ::CoTaskMemFree(m_psz); + } + unsigned int Length() const + { + return (m_psz == NULL)? 0 : wcslen(m_psz); + } + + operator WCHAR * () const + { + return m_psz; + } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the m_psz member explicitly. + WCHAR ** operator&() + { + return &m_psz; + } + + WCHAR * Append(const WCHAR * pszSrc) + { + if (pszSrc) + { + ULONG lenSrc = wcslen(pszSrc); + if (lenSrc) + { + ULONG lenMe = Length(); + WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc + 1) * sizeof(WCHAR)); + if (pszNew) + { + if (m_psz) // Could append to an empty string so check... + { + if (lenMe) + { + memcpy(pszNew, m_psz, lenMe * sizeof(WCHAR)); + } + ::CoTaskMemFree(m_psz); + } + memcpy(pszNew + lenMe, pszSrc, (lenSrc + 1) * sizeof(WCHAR)); + m_psz = pszNew; + } + else + { + } + } + } + return m_psz; + } + + WCHAR * Append(const WCHAR * pszSrc, const ULONG lenSrc) + { + if (pszSrc && lenSrc) + { + ULONG lenMe = Length(); + WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc + 1) * sizeof(WCHAR)); + if (pszNew) + { + if (m_psz) // Could append to an empty string so check... + { + if (lenMe) + { + memcpy(pszNew, m_psz, lenMe * sizeof(WCHAR)); + } + ::CoTaskMemFree(m_psz); + } + memcpy(pszNew + lenMe, pszSrc, lenSrc * sizeof(WCHAR)); + *(pszNew + lenMe + lenSrc) = L'\0'; + m_psz = pszNew; + } + else + { + } + } + return m_psz; + } + + WCHAR * Append2(const WCHAR * pszSrc1, const WCHAR * pszSrc2) + { + ULONG lenSrc1 = pszSrc1 ? wcslen(pszSrc1) : 0; + ULONG lenSrc2 = pszSrc2 ? wcslen(pszSrc2) : 0; + + if (lenSrc1 || lenSrc2) + { + ULONG lenMe = Length(); + WCHAR *pszNew = (WCHAR *)::CoTaskMemAlloc((lenMe + lenSrc1 + lenSrc2 + 1) * sizeof(WCHAR)); + if (pszNew) + { + if (m_psz) // Could append to an empty string so check... + { + if (lenMe) + { + memcpy(pszNew, m_psz, lenMe * sizeof(WCHAR)); + } + ::CoTaskMemFree(m_psz); + } + // In both of these cases, we copy the trailing NULL so that we're sure it gets + // there (if lenSrc2 is 0 then we better copy it from pszSrc1). + if (lenSrc1) + { + memcpy(pszNew + lenMe, pszSrc1, (lenSrc1 + 1) * sizeof(WCHAR)); + } + if (lenSrc2) + { + memcpy(pszNew + lenMe + lenSrc1, pszSrc2, (lenSrc2 + 1) * sizeof(WCHAR)); + } + m_psz = pszNew; + } + else + { + } + } + return m_psz; + } + WCHAR * Copy() const + { + if (m_psz) + { + CSpDynamicString szNew(m_psz); + return szNew.Detach(); + } + return NULL; + } + CHAR * CopyToChar() const + { + if (m_psz) + { + CHAR* psz; + ULONG cbNeeded = ::WideCharToMultiByte(CP_ACP, 0, m_psz, -1, NULL, NULL, NULL, NULL); + psz = (CHAR *)::CoTaskMemAlloc(cbNeeded); + if (psz) + { + ::WideCharToMultiByte(CP_ACP, 0, m_psz, -1, psz, cbNeeded/sizeof(CHAR), NULL, NULL); + } + return psz; + } + return NULL; + } + void Attach(WCHAR * pszSrc) + { + m_psz = pszSrc; + } + WCHAR * Detach() + { + WCHAR * s = m_psz; + m_psz = NULL; + return s; + } + void Clear() + { + ::CoTaskMemFree(m_psz); + m_psz = NULL; + } + bool operator!() const + { + return (m_psz == NULL); + } + HRESULT CopyToBSTR(BSTR * pbstr) + { + if (m_psz) + { + *pbstr = ::SysAllocString(m_psz); + if (*pbstr == NULL) + { + return E_OUTOFMEMORY; + } + } + else + { + *pbstr = NULL; + } + return S_OK; + } + void TrimToSize(ULONG ulNumChars) + { + if (m_psz && ulNumChars < Length()) + { + m_psz[ulNumChars] = 0; + } + } + WCHAR * Compact() + { + if (m_psz) + { + ULONG cch = wcslen(m_psz); + m_psz = (WCHAR *)::CoTaskMemRealloc(m_psz, (cch + 1) * sizeof(WCHAR)); + } + return m_psz; + } + WCHAR * ClearAndGrowTo(ULONG cch) + { + if (m_psz) + { + Clear(); + } + m_psz = (WCHAR *)::CoTaskMemAlloc(cch * sizeof(WCHAR)); + return m_psz; + } + WCHAR * LTrim() + { + if (m_psz) + { + WCHAR * pszRead = m_psz; + while (iswspace(*pszRead)) + { + pszRead++; + } + if (pszRead != m_psz) + { + WCHAR * pszWrite = m_psz; + while (*pszRead) + { + *pszWrite++ = *pszRead++; + } + *pszWrite = '\0'; + } + } + return m_psz; + } + WCHAR * RTrim() + { + if (m_psz) + { + WCHAR * pszTail = m_psz + wcslen(m_psz); + WCHAR * pszZeroTerm = pszTail; + while (pszZeroTerm > m_psz && iswspace(pszZeroTerm[-1])) + { + pszZeroTerm--; + } + if (pszZeroTerm != pszTail) + { + *pszZeroTerm = '\0'; + } + } + return m_psz; + } + WCHAR * TrimBoth() + { + RTrim(); + return LTrim(); + } +}; + + + +// +// Simple inline function converts a ulong to a hex string. +// +inline void SpHexFromUlong(WCHAR * psz, ULONG ul) +{ + const static WCHAR szHexChars[] = L"0123456789ABCDEF"; + if (ul == 0) + { + psz[0] = L'0'; + psz[1] = 0; + } + else + { + ULONG ulChars = 1; + psz[0] = 0; + while (ul) + { + memmove(psz + 1, psz, ulChars * sizeof(WCHAR)); + psz[0] = szHexChars[ul % 16]; + ul /= 16; + ulChars++; + } + } +} + + +//=== Token helpers + +inline HRESULT SpGetTokenFromId( + const WCHAR * pszTokenId, + ISpObjectToken ** ppToken, + BOOL fCreateIfNotExist = FALSE) +{ + + LPUNKNOWN pUnkOuter = nullptr; + HRESULT hr = ::CoCreateInstance(CLSID_SpObjectToken, pUnkOuter, CLSCTX_ALL, IID_ISpObjectToken,reinterpret_cast<void **>(ppToken)); + + if (SUCCEEDED(hr)) + { + (*ppToken)->SetId(NULL, pszTokenId, fCreateIfNotExist); + } + + return hr; +} + +inline HRESULT SpGetCategoryFromId( + const WCHAR * pszCategoryId, + ISpObjectTokenCategory ** ppCategory, + BOOL fCreateIfNotExist = FALSE) +{ + LPUNKNOWN pUnkOuter = nullptr; + HRESULT hr = ::CoCreateInstance(CLSID_SpObjectTokenCategory, pUnkOuter, CLSCTX_ALL, IID_ISpObjectTokenCategory, reinterpret_cast<void **>(ppCategory)); + + if (SUCCEEDED(hr)) + { + hr = (*ppCategory)->SetId(pszCategoryId, fCreateIfNotExist); + } + + return hr; +} + +inline HRESULT SpGetDefaultTokenIdFromCategoryId( + const WCHAR * pszCategoryId, + WCHAR ** ppszTokenId) +{ + HRESULT hr; + + ISpObjectTokenCategory *cpCategory; + hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); + + if (SUCCEEDED(hr)) + { + hr = cpCategory->GetDefaultTokenId(ppszTokenId); + } + + return hr; +} + +inline HRESULT SpSetDefaultTokenIdForCategoryId( + const WCHAR * pszCategoryId, + const WCHAR * pszTokenId) +{ + HRESULT hr; + + ISpObjectTokenCategory *cpCategory; + hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); + + if (SUCCEEDED(hr)) + { + hr = cpCategory->SetDefaultTokenId(pszTokenId); + } + + return hr; +} + +inline HRESULT SpGetDefaultTokenFromCategoryId( + const WCHAR * pszCategoryId, + ISpObjectToken ** ppToken, + BOOL fCreateCategoryIfNotExist = TRUE) +{ + HRESULT hr; + + ISpObjectTokenCategory *cpCategory; + hr = SpGetCategoryFromId(pszCategoryId, &cpCategory, fCreateCategoryIfNotExist); + + if (SUCCEEDED(hr)) + { + WCHAR * pszTokenId; + hr = cpCategory->GetDefaultTokenId(&pszTokenId); + if (SUCCEEDED(hr)) + { + hr = SpGetTokenFromId(pszTokenId, ppToken); + ::CoTaskMemFree(pszTokenId); + } + } + + return hr; +} + +inline HRESULT SpSetDefaultTokenForCategoryId( + const WCHAR * pszCategoryId, + ISpObjectToken * pToken) +{ + HRESULT hr; + + WCHAR * pszTokenId; + hr = pToken->GetId(&pszTokenId); + + if (SUCCEEDED(hr)) + { + hr = SpSetDefaultTokenIdForCategoryId(pszCategoryId, pszTokenId); + ::CoTaskMemFree(pszTokenId); + } + + return hr; +} + +inline HRESULT SpSetCommonTokenData( + ISpObjectToken * pToken, + const CLSID * pclsid, + const WCHAR * pszLangIndependentName, + LANGID langid, + const WCHAR * pszLangDependentName, + ISpDataKey ** ppDataKeyAttribs) +{ + HRESULT hr = S_OK; + + // Set the new token's CLSID (if specified) + if (SUCCEEDED(hr) && pclsid) + { + CSpDynamicString dstrClsid; + hr = StringFromCLSID(*pclsid, &dstrClsid); + + if (SUCCEEDED(hr)) + { + hr = pToken->SetStringValue(SPTOKENVALUE_CLSID, dstrClsid); + } + } + + // Set the token's lang independent name + if (SUCCEEDED(hr) && pszLangIndependentName) + { + hr = pToken->SetStringValue(NULL, pszLangIndependentName); + } + + // Set the token's lang dependent name + if (SUCCEEDED(hr) && pszLangDependentName) + { + WCHAR szLangId[10]; + SpHexFromUlong(szLangId, langid); + + hr = pToken->SetStringValue(szLangId, pszLangDependentName); + } + + // Open the attributes key if requested + if (SUCCEEDED(hr) && ppDataKeyAttribs) + { + hr = pToken->CreateKey(L"Attributes", ppDataKeyAttribs); + } + + return hr; +} + +inline HRESULT SpCreateNewToken( + const WCHAR * pszTokenId, + ISpObjectToken ** ppToken) +{ + HRESULT hr; + + // Forcefully create the token + hr = SpGetTokenFromId(pszTokenId, ppToken, TRUE); + + return hr; +} + +inline HRESULT SpCreateNewToken( + const WCHAR * pszCategoryId, + const WCHAR * pszTokenKeyName, + ISpObjectToken ** ppToken) +{ + HRESULT hr; + + // Forcefully create the category + ISpObjectTokenCategory *cpCategory; + hr = SpGetCategoryFromId(pszCategoryId, &cpCategory, TRUE); + + // Come up with a token key name if one wasn't specified + CSpDynamicString dstrTokenKeyName; + if (SUCCEEDED(hr)) + { + if (pszTokenKeyName == NULL) + { + GUID guidTokenKeyName; + hr = CoCreateGuid(&guidTokenKeyName); + + if (SUCCEEDED(hr)) + { + hr = StringFromCLSID(guidTokenKeyName, &dstrTokenKeyName); + } + + if (SUCCEEDED(hr)) + { + pszTokenKeyName = dstrTokenKeyName; + } + } + } + + // Build the token id + CSpDynamicString dstrTokenId; + if (SUCCEEDED(hr)) + { + dstrTokenId = pszCategoryId; + dstrTokenId.Append2(L"\\Tokens\\", pszTokenKeyName); + } + + // Forcefully create the token + if (SUCCEEDED(hr)) + { + hr = SpGetTokenFromId(dstrTokenId, ppToken, TRUE); + } + + return hr; +} + +inline HRESULT SpCreateNewTokenEx( + const WCHAR * pszCategoryId, + const WCHAR * pszTokenKeyName, + const CLSID * pclsid, + const WCHAR * pszLangIndependentName, + LANGID langid, + const WCHAR * pszLangDependentName, + ISpObjectToken ** ppToken, + ISpDataKey ** ppDataKeyAttribs) +{ + HRESULT hr; + + // Create the new token + hr = SpCreateNewToken(pszCategoryId, pszTokenKeyName, ppToken); + + // Now set the extra data + if (SUCCEEDED(hr)) + { + hr = SpSetCommonTokenData( + *ppToken, + pclsid, + pszLangIndependentName, + langid, + pszLangDependentName, + ppDataKeyAttribs); + } + + return hr; +} + +inline HRESULT SpCreateNewTokenEx( + const WCHAR * pszTokenId, + const CLSID * pclsid, + const WCHAR * pszLangIndependentName, + LANGID langid, + const WCHAR * pszLangDependentName, + ISpObjectToken ** ppToken, + ISpDataKey ** ppDataKeyAttribs) +{ + HRESULT hr; + + // Create the new token + hr = SpCreateNewToken(pszTokenId, ppToken); + + // Now set the extra data + if (SUCCEEDED(hr)) + { + hr = SpSetCommonTokenData( + *ppToken, + pclsid, + pszLangIndependentName, + langid, + pszLangDependentName, + ppDataKeyAttribs); + } + + return hr; +} + +inline HRESULT SpEnumTokens( + const WCHAR * pszCategoryId, + const WCHAR * pszReqAttribs, + const WCHAR * pszOptAttribs, + IEnumSpObjectTokens ** ppEnum) +{ + HRESULT hr = S_OK; + + ISpObjectTokenCategory *cpCategory; + hr = SpGetCategoryFromId(pszCategoryId, &cpCategory); + + if (SUCCEEDED(hr)) + { + hr = cpCategory->EnumTokens( + pszReqAttribs, + pszOptAttribs, + ppEnum); + } + + return hr; +} + +inline HRESULT SpFindBestToken( + const WCHAR * pszCategoryId, + const WCHAR * pszReqAttribs, + const WCHAR * pszOptAttribs, + ISpObjectToken **ppObjectToken) +{ + HRESULT hr = S_OK; + + const WCHAR *pszVendorPreferred = L"VendorPreferred"; + const unsigned long LenVendorPreferred = wcslen(pszVendorPreferred); + + // append VendorPreferred to the end of pszOptAttribs to force this preference + ULONG ulLen = pszOptAttribs ? wcslen(pszOptAttribs) + LenVendorPreferred + 1 : LenVendorPreferred; + WCHAR *pszOptAttribsVendorPref = (WCHAR*)_alloca((ulLen+1)*sizeof(WCHAR)); + if (pszOptAttribsVendorPref) + { + if (pszOptAttribs) + { + wcscpy(pszOptAttribsVendorPref, pszOptAttribs); + wcscat(pszOptAttribsVendorPref, L";"); + wcscat(pszOptAttribsVendorPref, pszVendorPreferred); + } + else + { + wcscpy(pszOptAttribsVendorPref, pszVendorPreferred); + } + } + else + { + hr = E_OUTOFMEMORY; + } + + IEnumSpObjectTokens *cpEnum; + if (SUCCEEDED(hr)) + { + hr = SpEnumTokens(pszCategoryId, pszReqAttribs, pszOptAttribsVendorPref, &cpEnum); + } + + if (SUCCEEDED(hr)) + { + hr = cpEnum->Next(1, ppObjectToken, NULL); + if (hr == S_FALSE) + { + *ppObjectToken = NULL; + hr = SPERR_NOT_FOUND; + } + } + + if (hr != SPERR_NOT_FOUND) + { + } + + return hr; +} + +/*template<class T> +HRESULT SpCreateObjectFromToken(ISpObjectToken * pToken, T ** ppObject, + IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) +{ + HRESULT hr; + + hr = pToken->CreateInstance(pUnkOuter, dwClsCtxt, __uuidof(T), (void **)ppObject); + + return hr; +} + +template<class T> +HRESULT SpCreateObjectFromTokenId(const WCHAR * pszTokenId, T ** ppObject, + IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) +{ + + ISpObjectToken * pToken; + HRESULT hr = SpGetTokenFromId(pszTokenId, &pToken); + if (SUCCEEDED(hr)) + { + hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt); + pToken->Release(); + } + + return hr; +}*/ +/* +template<class T> +HRESULT SpCreateDefaultObjectFromCategoryId(const WCHAR * pszCategoryId, T ** ppObject, + IUnknown * pUnkOuter = NULL, DWORD dwClsCtxt = CLSCTX_ALL) +{ + + ISpObjectToken * pToken; + HRESULT hr = SpGetDefaultTokenFromCategoryId(pszCategoryId, &pToken); + if (SUCCEEDED(hr)) + { + hr = SpCreateObjectFromToken(pToken, ppObject, pUnkOuter, dwClsCtxt); + pToken->Release(); + } + + return hr; +} + +template<class T> +HRESULT SpCreateBestObject( + const WCHAR * pszCategoryId, + const WCHAR * pszReqAttribs, + const WCHAR * pszOptAttribs, + T ** ppObject, + IUnknown * pUnkOuter = NULL, + DWORD dwClsCtxt = CLSCTX_ALL) +{ + HRESULT hr; + + ISpObjectToken *cpToken; + hr = SpFindBestToken(pszCategoryId, pszReqAttribs, pszOptAttribs, &cpToken); + + if (SUCCEEDED(hr)) + { + hr = SpCreateObjectFromToken(cpToken, ppObject, pUnkOuter, dwClsCtxt); + } + + if (hr != SPERR_NOT_FOUND) + { + } + + return hr; +}*/ + +/*HRESULT SpCreateBestObject( + const WCHAR * pszCategoryId, + const WCHAR * pszReqAttribs, + const WCHAR * pszOptAttribs, + ISpPhoneConverter ** ** ppObject, + IUnknown * pUnkOuter = NULL, + DWORD dwClsCtxt = CLSCTX_ALL) +{ + HRESULT hr; + + ISpObjectToken *cpToken; + hr = SpFindBestToken(pszCategoryId, pszReqAttribs, pszOptAttribs, &cpToken); + + if (SUCCEEDED(hr)) + { + hr = SpCreateObjectFromToken(cpToken, ppObject, pUnkOuter, dwClsCtxt); + } + + if (hr != SPERR_NOT_FOUND) + { + } + + return hr; +}*/ + +/*inline HRESULT SpCreatePhoneConverter( + LANGID LangID, + const WCHAR * pszReqAttribs, + const WCHAR * pszOptAttribs, + ISpPhoneConverter ** ppPhoneConverter) +{ + HRESULT hr; + + if (LangID == 0) + { + hr = E_INVALIDARG; + } + else + { + CSpDynamicString dstrReqAttribs; + if (pszReqAttribs) + { + dstrReqAttribs = pszReqAttribs; + dstrReqAttribs.Append(L";"); + } + + WCHAR szLang[MAX_PATH]; + + SpHexFromUlong(szLang, LangID); + + WCHAR szLangCondition[MAX_PATH]; + wcscpy(szLangCondition, L"Language="); + wcscat(szLangCondition, szLang); + + dstrReqAttribs.Append(szLangCondition); + + hr = SpCreateBestObject(SPCAT_PHONECONVERTERS, dstrReqAttribs, pszOptAttribs, ppPhoneConverter); + } + + if (hr != SPERR_NOT_FOUND) + { + } + + return hr; +}*/ + +/**************************************************************************** +* SpHrFromWin32 * +*---------------* +* Description: +* This inline function works around a basic problem with the macro +* HRESULT_FROM_WIN32. The macro forces the expresion in ( ) to be evaluated +* two times. By using this inline function, the expression will only be +* evaluated once. +* +* Returns: +* HRESULT of converted Win32 error code +* +*****************************************************************************/ + +inline HRESULT SpHrFromWin32(DWORD dwErr) +{ + return HRESULT_FROM_WIN32(dwErr); +} + + +/**************************************************************************** +* SpHrFromLastWin32Error * +*------------------------* +* Description: +* This simple inline function is used to return a converted HRESULT +* from the Win32 function ::GetLastError. Note that using HRESULT_FROM_WIN32 +* will evaluate the error code twice so we don't want to use: +* +* HRESULT_FROM_WIN32(::GetLastError()) +* +* since that will call GetLastError twice. +* On Win98 and WinMe ::GetLastError() returns 0 for some functions (see MSDN). +* We therefore check for that and return E_FAIL. This function should only be +* called in an error case since it will always return an error code! +* +* Returns: +* HRESULT for ::GetLastError() +* +*****************************************************************************/ + +inline HRESULT SpHrFromLastWin32Error() +{ + DWORD dw = ::GetLastError(); + return (dw == 0) ? E_FAIL : SpHrFromWin32(dw); +} + + +/**************************************************************************** +* SpGetUserDefaultUILanguage * +*----------------------------* +* Description: +* Returns the default user interface language, using a method +* appropriate to the platform (Windows 9x, Windows NT, or Windows 2000) +* +* Returns: +* Default UI language +* +*****************************************************************************/ + +inline LANGID SpGetUserDefaultUILanguage(void) +{ + HRESULT hr = S_OK; + LANGID wUILang = 0; + + OSVERSIONINFO Osv ; + Osv.dwOSVersionInfoSize = sizeof(Osv) ; + if(!GetVersionEx(&Osv)) + { + hr = SpHrFromLastWin32Error(); + } + // Get the UI language by one of three methods, depending on the system + else if(Osv.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + // Case 1: Running on Windows 9x. Get the system UI language from registry: + CHAR szData[32]; + DWORD dwSize = sizeof(szData) ; + HKEY hKey; + + long lRet = RegOpenKeyEx( + HKEY_USERS, + ".Default\\Control Panel\\desktop\\ResourceLocale", + 0, + KEY_READ, + &hKey); + +#ifdef _WIN32_WCE_BUG_10655 + if (lRet == ERROR_INVALID_PARAMETER) + { + lRet = ERROR_FILE_NOT_FOUND; + } +#endif // _WIN32_WCE_BUG_10655 + + hr = SpHrFromWin32(lRet); + + if (SUCCEEDED(hr)) + { + lRet = RegQueryValueEx( + hKey, + "", + NULL, + NULL, + (BYTE *)szData, + &dwSize); + +#ifdef _WIN32_WCE_BUG_10655 + if(lRet == ERROR_INVALID_PARAMETER) + { + lRet = ERROR_FILE_NOT_FOUND; + } +#endif //_WIN32_WCE_BUG_10655 + + hr = SpHrFromWin32(lRet); + ::RegCloseKey(hKey) ; + } + if (SUCCEEDED(hr)) + { + // Convert string to number + wUILang = (LANGID) strtol(szData, NULL, 16) ; + } + } + else if (Osv.dwMajorVersion >= 5.0) + { + // Case 2: Running on Windows 2000 or later. Use GetUserDefaultUILanguage to find + // the user's prefered UI language + + + HMODULE hMKernel32 = ::LoadLibraryW(L"kernel32.dll") ; + if (hMKernel32 == NULL) + { + hr = SpHrFromLastWin32Error(); + } + else + { + + LANGID (WINAPI *pfnGetUserDefaultUILanguage) () = + (LANGID (WINAPI *)(void)) +#ifdef _WIN32_WCE + GetProcAddress(hMKernel32, L"GetUserDefaultUILanguage") ; +#else + GetProcAddress(hMKernel32, "GetUserDefaultUILanguage") ; +#endif + + if(NULL != pfnGetUserDefaultUILanguage) + { + wUILang = pfnGetUserDefaultUILanguage() ; + } + else + { // GetProcAddress failed + hr = SpHrFromLastWin32Error(); + } + ::FreeLibrary(hMKernel32); + } + } + else { + // Case 3: Running on Windows NT 4.0 or earlier. Get UI language + // from locale of .default user in registry: + // HKEY_USERS\.DEFAULT\Control Panel\International\Locale + + WCHAR szData[32] ; + DWORD dwSize = sizeof(szData) ; + HKEY hKey ; + + LONG lRet = RegOpenKeyEx(HKEY_USERS, + ".DEFAULT\\Control Panel\\International", + 0, + KEY_READ, + &hKey); +#ifdef _WIN32_WCE_BUG_10655 + if(lRet == ERROR_INVALID_PARAMETER) + { + lRet = ERROR_FILE_NOT_FOUND; + } +#endif //_WIN32_WCE_BUG_10655 + + hr = SpHrFromWin32(lRet); + + if (SUCCEEDED(hr)) + { + lRet = RegQueryValueEx( + hKey, + "Locale", + NULL, + NULL, + (BYTE *)szData, + &dwSize); + +#ifdef _WIN32_WCE_BUG_10655 + if(lRet == ERROR_INVALID_PARAMETER) + { + lRet = ERROR_FILE_NOT_FOUND; + } +#endif //_WIN32_WCE_BUG_10655 + + hr = SpHrFromWin32(lRet); + ::RegCloseKey(hKey); + } + + if (SUCCEEDED(hr)) + { + // Convert string to number + wUILang = (LANGID) wcstol(szData, NULL, 16) ; + + if(0x0401 == wUILang || // Arabic + 0x040d == wUILang || // Hebrew + 0x041e == wUILang // Thai + ) + { + // Special case these to the English UI. + // These versions of Windows NT 4.0 were enabled only, i.e., the + // UI was English. However, the registry setting + // HKEY_USERS\.DEFAULT\Control Panel\International\Locale was set + // to the respective locale for application compatibility. + wUILang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) ; + } + } + } + + return (wUILang ? wUILang : ::GetUserDefaultLangID()); // In failure case, try our best! +} + + +inline HRESULT SpGetDescription(ISpObjectToken * pObjToken, WCHAR ** ppszDescription, LANGID Language = SpGetUserDefaultUILanguage()) +{ + WCHAR szLangId[10]; + SpHexFromUlong(szLangId, Language); + HRESULT hr = pObjToken->GetStringValue(szLangId, ppszDescription); + if (hr == SPERR_NOT_FOUND) + { + hr = pObjToken->GetStringValue(NULL, ppszDescription); + } + return hr; +} + + +inline HRESULT SpSetDescription(ISpObjectToken * pObjToken, const WCHAR * pszDescription, LANGID Language = SpGetUserDefaultUILanguage(), BOOL fSetLangIndependentId = TRUE) +{ + WCHAR szLangId[10]; + SpHexFromUlong(szLangId, Language); + HRESULT hr = pObjToken->SetStringValue(szLangId, pszDescription); + if (SUCCEEDED(hr) && fSetLangIndependentId) + { + hr = pObjToken->SetStringValue(NULL, pszDescription); + } + return hr; +} + +/**************************************************************************** +* SpConvertStreamFormatEnum * +*---------------------------* +* Description: +* This method converts the specified stream format into a wave format +* structure. +* +*****************************************************************************/ +inline HRESULT SpConvertStreamFormatEnum(SPSTREAMFORMAT eFormat, GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx) +{ + HRESULT hr = S_OK; + + if(pFormatId==NULL || ::IsBadWritePtr(pFormatId, sizeof(*pFormatId)) + || ppCoMemWaveFormatEx==NULL || ::IsBadWritePtr(ppCoMemWaveFormatEx, sizeof(*ppCoMemWaveFormatEx))) + { + return E_INVALIDARG; + } + + const GUID * pFmtGuid = &GUID_NULL; // Assume failure case + if( eFormat >= SPSF_8kHz8BitMono && eFormat <= SPSF_48kHz16BitStereo ) + { + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc(sizeof(WAVEFORMATEX)); + *ppCoMemWaveFormatEx = pwfex; + if (pwfex) + { + DWORD dwIndex = eFormat - SPSF_8kHz8BitMono; + BOOL bIsStereo = dwIndex & 0x1; + BOOL bIs16 = dwIndex & 0x2; + DWORD dwKHZ = (dwIndex & 0x3c) >> 2; + static const DWORD adwKHZ[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 }; + pwfex->wFormatTag = WAVE_FORMAT_PCM; + pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); + pwfex->nSamplesPerSec = adwKHZ[dwKHZ]; + pwfex->wBitsPerSample = 8; + if (bIs16) + { + pwfex->wBitsPerSample *= 2; + pwfex->nBlockAlign *= 2; + } + pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; + pwfex->cbSize = 0; + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else if( eFormat == SPSF_TrueSpeech_8kHz1BitMono ) + { + int NumBytes = sizeof( WAVEFORMATEX ) + 32; + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); + *ppCoMemWaveFormatEx = pwfex; + if( pwfex ) + { + memset( pwfex, 0, NumBytes ); + pwfex->wFormatTag = WAVE_FORMAT_DSPGROUP_TRUESPEECH; + pwfex->nChannels = 1; + pwfex->nSamplesPerSec = 8000; + pwfex->nAvgBytesPerSec = 1067; + pwfex->nBlockAlign = 32; + pwfex->wBitsPerSample = 1; + pwfex->cbSize = 32; + BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); + pExtra[0] = 1; + pExtra[2] = 0xF0; + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else if( (eFormat >= SPSF_CCITT_ALaw_8kHzMono ) && + (eFormat <= SPSF_CCITT_ALaw_44kHzStereo ) ) + { + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) ); + *ppCoMemWaveFormatEx = pwfex; + if( pwfex ) + { + memset( pwfex, 0, sizeof(WAVEFORMATEX) ); + DWORD dwIndex = eFormat - SPSF_CCITT_ALaw_8kHzMono; + DWORD dwKHZ = dwIndex / 2; + static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; + BOOL bIsStereo = dwIndex & 0x1; + pwfex->wFormatTag = WAVE_FORMAT_ALAW; + pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); + pwfex->nSamplesPerSec = adwKHZ[dwKHZ]; + pwfex->wBitsPerSample = 8; + pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; + pwfex->cbSize = 0; + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else if( (eFormat >= SPSF_CCITT_uLaw_8kHzMono ) && + (eFormat <= SPSF_CCITT_uLaw_44kHzStereo ) ) + { + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) ); + *ppCoMemWaveFormatEx = pwfex; + if( pwfex ) + { + memset( pwfex, 0, sizeof(WAVEFORMATEX) ); + DWORD dwIndex = eFormat - SPSF_CCITT_uLaw_8kHzMono; + DWORD dwKHZ = dwIndex / 2; + static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; + BOOL bIsStereo = dwIndex & 0x1; + pwfex->wFormatTag = WAVE_FORMAT_MULAW; + pwfex->nChannels = pwfex->nBlockAlign = (WORD)(bIsStereo ? 2 : 1); + pwfex->nSamplesPerSec = adwKHZ[dwKHZ]; + pwfex->wBitsPerSample = 8; + pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; + pwfex->cbSize = 0; + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else if( (eFormat >= SPSF_ADPCM_8kHzMono ) && + (eFormat <= SPSF_ADPCM_44kHzStereo ) ) + { + int NumBytes = sizeof( WAVEFORMATEX ) + 32; + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); + *ppCoMemWaveFormatEx = pwfex; + if( pwfex ) + { + //--- Some of these values seem odd. We used what the codec told us. + static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; + static const DWORD BytesPerSec[] = { 4096, 8192, 5644, 11289, 11155, 22311, 22179, 44359 }; + static const DWORD BlockAlign[] = { 256, 256, 512, 1024 }; + static const BYTE Extra811[32] = + { + 0xF4, 0x01, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF + }; + + static const BYTE Extra22[32] = + { + 0xF4, 0x03, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF + }; + + static const BYTE Extra44[32] = + { + 0xF4, 0x07, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x02, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0x00, 0x00, + 0xCC, 0x01, 0x30, 0xFF, 0x88, 0x01, 0x18, 0xFF + }; + + static const BYTE* Extra[4] = { Extra811, Extra811, Extra22, Extra44 }; + memset( pwfex, 0, NumBytes ); + DWORD dwIndex = eFormat - SPSF_ADPCM_8kHzMono; + DWORD dwKHZ = dwIndex / 2; + BOOL bIsStereo = dwIndex & 0x1; + pwfex->wFormatTag = WAVE_FORMAT_ADPCM; + pwfex->nChannels = (WORD)(bIsStereo ? 2 : 1); + pwfex->nSamplesPerSec = adwKHZ[dwKHZ]; + pwfex->nAvgBytesPerSec = BytesPerSec[dwIndex]; + pwfex->nBlockAlign = (WORD)(BlockAlign[dwKHZ] * pwfex->nChannels); + pwfex->wBitsPerSample = 4; + pwfex->cbSize = 32; + BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); + memcpy( pExtra, Extra[dwKHZ], 32 ); + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else if( (eFormat >= SPSF_GSM610_8kHzMono ) && + (eFormat <= SPSF_GSM610_44kHzMono ) ) + { + int NumBytes = sizeof( WAVEFORMATEX ) + 2; + WAVEFORMATEX * pwfex = (WAVEFORMATEX *)::CoTaskMemAlloc( NumBytes ); + *ppCoMemWaveFormatEx = pwfex; + if( pwfex ) + { + //--- Some of these values seem odd. We used what the codec told us. + static const DWORD adwKHZ[] = { 8000, 11025, 22050, 44100 }; + static const DWORD BytesPerSec[] = { 1625, 2239, 4478, 8957 }; + memset( pwfex, 0, NumBytes ); + DWORD dwIndex = eFormat - SPSF_GSM610_8kHzMono; + pwfex->wFormatTag = WAVE_FORMAT_GSM610; + pwfex->nChannels = 1; + pwfex->nSamplesPerSec = adwKHZ[dwIndex]; + pwfex->nAvgBytesPerSec = BytesPerSec[dwIndex]; + pwfex->nBlockAlign = 65; + pwfex->wBitsPerSample = 0; + pwfex->cbSize = 2; + BYTE* pExtra = ((BYTE*)pwfex) + sizeof( WAVEFORMATEX ); + pExtra[0] = 0x40; + pExtra[1] = 0x01; + pFmtGuid = &SPDFID_WaveFormatEx; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else + { + *ppCoMemWaveFormatEx = NULL; + switch (eFormat) + { + case SPSF_NoAssignedFormat: + break; + case SPSF_Text: + pFmtGuid = &SPDFID_Text; + break; + default: + hr = E_INVALIDARG; + break; + } + } + *pFormatId = *pFmtGuid; + return hr; +} + +class CSpStreamFormat +{ +public: + GUID m_guidFormatId; + WAVEFORMATEX * m_pCoMemWaveFormatEx; + + + static CoMemCopyWFEX(const WAVEFORMATEX * pSrc, WAVEFORMATEX ** ppCoMemWFEX) + { + ULONG cb = sizeof(WAVEFORMATEX) + pSrc->cbSize; + *ppCoMemWFEX = (WAVEFORMATEX *)::CoTaskMemAlloc(cb); + if (*ppCoMemWFEX) + { + memcpy(*ppCoMemWFEX, pSrc, cb); + return S_OK; + } + else + { + return E_OUTOFMEMORY; + } + } + + + CSpStreamFormat() + { + m_guidFormatId = GUID_NULL; + m_pCoMemWaveFormatEx = NULL; + } + + CSpStreamFormat(SPSTREAMFORMAT eFormat, HRESULT * phr) + { + *phr = SpConvertStreamFormatEnum(eFormat, &m_guidFormatId, &m_pCoMemWaveFormatEx); + } + + CSpStreamFormat(const WAVEFORMATEX * pWaveFormatEx, HRESULT * phr) + { + *phr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); + m_guidFormatId = SUCCEEDED(*phr) ? SPDFID_WaveFormatEx : GUID_NULL; + } + + ~CSpStreamFormat() + { + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + } + + void Clear() + { + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + m_pCoMemWaveFormatEx = NULL; + memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); + } + + const GUID & FormatId() const + { + return m_guidFormatId; + } + + const WAVEFORMATEX * WaveFormatExPtr() const + { + return m_pCoMemWaveFormatEx; + } + + + HRESULT AssignFormat(SPSTREAMFORMAT eFormat) + { + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + return SpConvertStreamFormatEnum(eFormat, &m_guidFormatId, &m_pCoMemWaveFormatEx); + } + + HRESULT AssignFormat(ISpStreamFormat * pStream) + { + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + m_pCoMemWaveFormatEx = NULL; + return pStream->GetFormat(&m_guidFormatId, &m_pCoMemWaveFormatEx); + } + + HRESULT AssignFormat(const WAVEFORMATEX * pWaveFormatEx) + { + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + HRESULT hr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); + m_guidFormatId = SUCCEEDED(hr) ? SPDFID_WaveFormatEx : GUID_NULL; + return hr; + } + + HRESULT AssignFormat(REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx) + { + HRESULT hr = S_OK; + + m_guidFormatId = rguidFormatId; + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + m_pCoMemWaveFormatEx = NULL; + + if (rguidFormatId == SPDFID_WaveFormatEx) + { + if (::IsBadReadPtr(pWaveFormatEx, sizeof(*pWaveFormatEx))) + { + hr = E_INVALIDARG; + } + else + { + hr = CoMemCopyWFEX(pWaveFormatEx, &m_pCoMemWaveFormatEx); + } + + if (FAILED(hr)) + { + m_guidFormatId = GUID_NULL; + } + } + + return hr; + } + + + BOOL IsEqual(REFGUID rguidFormatId, const WAVEFORMATEX * pwfex) const + { + if (rguidFormatId == m_guidFormatId) + { + if (m_pCoMemWaveFormatEx) + { + if (pwfex && + pwfex->cbSize == m_pCoMemWaveFormatEx->cbSize && + memcmp(m_pCoMemWaveFormatEx, pwfex, sizeof(WAVEFORMATEX) + pwfex->cbSize) == 0) + { + return TRUE; + } + } + else + { + return (pwfex == NULL); + } + } + return FALSE; + } + + + + HRESULT ParamValidateAssignFormat(REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx, BOOL fRequireWaveFormat = FALSE) + { + if ((pWaveFormatEx && (::IsBadReadPtr(pWaveFormatEx, sizeof(*pWaveFormatEx)) || rguidFormatId != SPDFID_WaveFormatEx)) || + (fRequireWaveFormat && pWaveFormatEx == NULL)) + { + return E_INVALIDARG; + } + return AssignFormat(rguidFormatId, pWaveFormatEx); + } + + SPSTREAMFORMAT ComputeFormatEnum() + { + if (m_guidFormatId == GUID_NULL) + { + return SPSF_NoAssignedFormat; + } + if (m_guidFormatId == SPDFID_Text) + { + return SPSF_Text; + } + if (m_guidFormatId != SPDFID_WaveFormatEx) + { + return SPSF_NonStandardFormat; + } + // + // It is a WAVEFORMATEX. Now determine which type it is and convert. + // + DWORD dwIndex = 0; + switch (m_pCoMemWaveFormatEx->wFormatTag) + { + case WAVE_FORMAT_PCM: + { + switch (m_pCoMemWaveFormatEx->nChannels) + { + case 1: + break; + case 2: + dwIndex |= 1; + break; + default: + return SPSF_ExtendedAudioFormat; + } + + switch (m_pCoMemWaveFormatEx->wBitsPerSample) + { + case 8: + break; + case 16: + dwIndex |= 2; + break; + default: + return SPSF_ExtendedAudioFormat; + } + + switch (m_pCoMemWaveFormatEx->nSamplesPerSec) + { + case 48000: + dwIndex += 4; // Fall through + case 44100: + dwIndex += 4; // Fall through + case 32000: + dwIndex += 4; // Fall through + case 24000: + dwIndex += 4; // Fall through + case 22050: + dwIndex += 4; // Fall through + case 16000: + dwIndex += 4; // Fall through + case 12000: + dwIndex += 4; // Fall through + case 11025: + dwIndex += 4; // Fall through + case 8000: + break; + default: + return SPSF_ExtendedAudioFormat; + } + + return static_cast<SPSTREAMFORMAT>(SPSF_8kHz8BitMono + dwIndex); + } + + case WAVE_FORMAT_DSPGROUP_TRUESPEECH: + { + return SPSF_TrueSpeech_8kHz1BitMono; + } + + case WAVE_FORMAT_ALAW: // fall through + case WAVE_FORMAT_MULAW: + case WAVE_FORMAT_ADPCM: + { + switch (m_pCoMemWaveFormatEx->nChannels) + { + case 1: + break; + case 2: + dwIndex |= 1; + break; + default: + return SPSF_ExtendedAudioFormat; + } + + if(m_pCoMemWaveFormatEx->wFormatTag == WAVE_FORMAT_ADPCM) + { + if(m_pCoMemWaveFormatEx->wBitsPerSample != 4) + { + return SPSF_ExtendedAudioFormat; + } + } + else if(m_pCoMemWaveFormatEx->wBitsPerSample != 8) + { + return SPSF_ExtendedAudioFormat; + } + + switch (m_pCoMemWaveFormatEx->nSamplesPerSec) + { + case 44100: + dwIndex += 2; // Fall through + case 22050: + dwIndex += 2; // Fall through + case 11025: + dwIndex += 2; // Fall through + case 8000: + break; + default: + return SPSF_ExtendedAudioFormat; + } + + switch( m_pCoMemWaveFormatEx->wFormatTag ) + { + case WAVE_FORMAT_ALAW: + return static_cast<SPSTREAMFORMAT>(SPSF_CCITT_ALaw_8kHzMono + dwIndex); + case WAVE_FORMAT_MULAW: + return static_cast<SPSTREAMFORMAT>(SPSF_CCITT_uLaw_8kHzMono + dwIndex); + case WAVE_FORMAT_ADPCM: + return static_cast<SPSTREAMFORMAT>(SPSF_ADPCM_8kHzMono + dwIndex); + } + } + + case WAVE_FORMAT_GSM610: + { + if( m_pCoMemWaveFormatEx->nChannels != 1 ) + { + return SPSF_ExtendedAudioFormat; + } + + switch (m_pCoMemWaveFormatEx->nSamplesPerSec) + { + case 44100: + dwIndex = 3; + break; + case 22050: + dwIndex = 2; + break; + case 11025: + dwIndex = 1; + break; + case 8000: + dwIndex = 0; + break; + default: + return SPSF_ExtendedAudioFormat; + } + + return static_cast<SPSTREAMFORMAT>(SPSF_GSM610_8kHzMono + dwIndex); + } + + default: + return SPSF_ExtendedAudioFormat; + break; + } + } + + void DetachTo(CSpStreamFormat & Other) + { + ::CoTaskMemFree(Other.m_pCoMemWaveFormatEx); + Other.m_guidFormatId = m_guidFormatId; + Other.m_pCoMemWaveFormatEx = m_pCoMemWaveFormatEx; + m_pCoMemWaveFormatEx = NULL; + memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); + } + + void DetachTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWaveFormatEx) + { + *pFormatId = m_guidFormatId; + *ppCoMemWaveFormatEx = m_pCoMemWaveFormatEx; + m_pCoMemWaveFormatEx = NULL; + memset(&m_guidFormatId, 0, sizeof(m_guidFormatId)); + } + + HRESULT CopyTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWFEX) const + { + HRESULT hr = S_OK; + *pFormatId = m_guidFormatId; + if (m_pCoMemWaveFormatEx) + { + hr = CoMemCopyWFEX(m_pCoMemWaveFormatEx, ppCoMemWFEX); + if (FAILED(hr)) + { + memset(pFormatId, 0, sizeof(*pFormatId)); + } + } + else + { + *ppCoMemWFEX = NULL; + } + return hr; + } + + HRESULT CopyTo(CSpStreamFormat & Other) const + { + ::CoTaskMemFree(Other.m_pCoMemWaveFormatEx); + return CopyTo(&Other.m_guidFormatId, &Other.m_pCoMemWaveFormatEx); + } + + HRESULT AssignFormat(const CSpStreamFormat & Src) + { + return Src.CopyTo(*this); + } + + + HRESULT ParamValidateCopyTo(GUID * pFormatId, WAVEFORMATEX ** ppCoMemWFEX) const + { + if (::IsBadWritePtr(pFormatId, sizeof(*pFormatId)) || + ::IsBadWritePtr(ppCoMemWFEX, sizeof(*ppCoMemWFEX))) + { + return E_POINTER; + } + return CopyTo(pFormatId, ppCoMemWFEX); + } + + BOOL operator==(const CSpStreamFormat & Other) const + { + return IsEqual(Other.m_guidFormatId, Other.m_pCoMemWaveFormatEx); + } + BOOL operator!=(const CSpStreamFormat & Other) const + { + return !IsEqual(Other.m_guidFormatId, Other.m_pCoMemWaveFormatEx); + } + + ULONG SerializeSize() const + { + ULONG cb = sizeof(ULONG) + sizeof(m_guidFormatId); + if (m_pCoMemWaveFormatEx) + { + cb += sizeof(WAVEFORMATEX) + m_pCoMemWaveFormatEx->cbSize + 3; // Add 3 to round up + cb -= cb % 4; // Round to DWORD + } + return cb; + } + + ULONG Serialize(BYTE * pBuffer) const + { + ULONG cb = SerializeSize(); + *((UNALIGNED ULONG *)pBuffer) = cb; + pBuffer += sizeof(ULONG); + *((UNALIGNED GUID *)pBuffer) = m_guidFormatId; + if (m_pCoMemWaveFormatEx) + { + pBuffer += sizeof(m_guidFormatId); + memcpy(pBuffer, m_pCoMemWaveFormatEx, sizeof(WAVEFORMATEX) + m_pCoMemWaveFormatEx->cbSize); + } + return cb; + } + + HRESULT Deserialize(const BYTE * pBuffer, ULONG * pcbUsed) + { + HRESULT hr = S_OK; + ::CoTaskMemFree(m_pCoMemWaveFormatEx); + m_pCoMemWaveFormatEx = NULL; + *pcbUsed = *((UNALIGNED ULONG *)pBuffer); + pBuffer += sizeof(ULONG); + // Misaligment exception is generated for SHx platform. + // Marking pointer as UNALIGNED does not help. +#ifndef _WIN32_WCE + m_guidFormatId = *((UNALIGNED GUID *)pBuffer); +#else + memcpy(&m_guidFormatId, pBuffer, sizeof(GUID)); +#endif + if (*pcbUsed > sizeof(GUID) + sizeof(ULONG)) + { + pBuffer += sizeof(m_guidFormatId); + hr = CoMemCopyWFEX((const WAVEFORMATEX *)pBuffer, &m_pCoMemWaveFormatEx); + if (FAILED(hr)) + { + m_guidFormatId = GUID_NULL; + } + } + return hr; + } + +}; + + + +// Return the default codepage given a LCID. +// Note some of the newer locales do not have associated Windows codepages. For these, we return UTF-8. + +inline UINT SpCodePageFromLcid(LCID lcid) +{ + char achCodePage[6]; + + return (0 != GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, achCodePage, sizeof(achCodePage))) ? atoi(achCodePage) : 65001; +} + + +/**************************************************************************** +* SpClearEvent * +*--------------* +* Description: +* Helper function that can be used by clients that do not use the CSpEvent +* class. +* +* Returns: +* +*****************************************************************************/ + +inline void SpClearEvent(SPEVENT * pe) +{ + if( pe->elParamType != SPEI_UNDEFINED) + { + if( pe->elParamType == SPET_LPARAM_IS_POINTER || + pe->elParamType == SPET_LPARAM_IS_STRING) + { + ::CoTaskMemFree((void *)pe->lParam); + } + else if (pe->elParamType == SPET_LPARAM_IS_TOKEN || + pe->elParamType == SPET_LPARAM_IS_OBJECT) + { + ((IUnknown*)pe->lParam)->Release(); + } + } + memset(pe, 0, sizeof(*pe)); +} + +/**************************************************************************** +* SpInitEvent * +*-------------* +* Description: +* +* Returns: +* +*****************************************************************************/ + +inline void SpInitEvent(SPEVENT * pe) +{ + memset(pe, 0, sizeof(*pe)); +} + +/**************************************************************************** +* SpEventSerializeSize * +*----------------------* +* Description: +* Computes the required size of a buffer to serialize an event. The caller +* must specify which type of serialized event is desired -- either SPSERIALIZEDEVENT +* or SPSERIALIZEDEVENT64. +* +* Returns: +* Size in bytes required to seriailze the event. +* +****************************************************************************/ + +// WCE compiler does not work propertly with template +#ifndef _WIN32_WCE +template <class T> +inline ULONG SpEventSerializeSize(const SPEVENT * pEvent) + +{ + ULONG ulSize = sizeof(T); + +#else + +inline ULONG SpEventSerializeSize(const SPEVENT * pEvent, ULONG ulSize) +{ +#endif //_WIN32_WCE + + if( ( pEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pEvent->lParam ) + { + ulSize += ULONG(pEvent->wParam); + } + else if ((pEvent->elParamType == SPET_LPARAM_IS_STRING) && pEvent->lParam != NULL) + { + ulSize += (wcslen((WCHAR*)pEvent->lParam) + 1) * sizeof( WCHAR ); + } + else if( pEvent->elParamType == SPET_LPARAM_IS_TOKEN ) + { + CSpDynamicString dstrObjectId; + if( ((ISpObjectToken*)(pEvent->lParam))->GetId( &dstrObjectId ) == S_OK ) + { + ulSize += (dstrObjectId.Length() + 1) * sizeof( WCHAR ); + } + } + // Round up to nearest DWORD + ulSize += 3; + ulSize -= ulSize % 4; + return ulSize; +} + +/**************************************************************************** +* SpSerializedEventSize * +*-----------------------* +* Description: +* Returns the size, in bytes, used by a serialized event. The caller can +* pass a pointer to either a SPSERIAILZEDEVENT or SPSERIALIZEDEVENT64 structure. +* +* Returns: +* Number of bytes used by serizlied event +* +********************************************************************* RAL ***/ + +// WCE compiler does not work propertly with template +#ifndef _WIN32_WCE +template <class T> +inline ULONG SpSerializedEventSize(const T * pSerEvent) +{ + ULONG ulSize = sizeof(T); + + if( ( pSerEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pSerEvent->SerializedlParam ) + { + ulSize += ULONG(pSerEvent->SerializedwParam); + } + else if ((pSerEvent->elParamType == SPET_LPARAM_IS_STRING || pSerEvent->elParamType == SPET_LPARAM_IS_TOKEN) && + pSerEvent->SerializedlParam != NULL) + { + ulSize += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR ); + } + // Round up to nearest DWORD + ulSize += 3; + ulSize -= ulSize % 4; + return ulSize; +} + +#else //_WIN32_WCE + +inline ULONG SpSerializedEventSize(const SPSERIALIZEDEVENT * pSerEvent, ULONG ulSize) +{ + if( ( pSerEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pSerEvent->SerializedlParam ) + { + ulSize += ULONG(pSerEvent->SerializedwParam); + } + else if ((pSerEvent->elParamType == SPET_LPARAM_IS_STRING || pSerEvent->elParamType == SPET_LPARAM_IS_TOKEN) && + pSerEvent->SerializedlParam != NULL) + { + ulSize += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR ); + } + // Round up to nearest DWORD + ulSize += 3; + ulSize -= ulSize % 4; + return ulSize; +} + +inline ULONG SpSerializedEventSize(const SPSERIALIZEDEVENT64 * pSerEvent, ULONG ulSize) +{ + if( ( pSerEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pSerEvent->SerializedlParam ) + { + ulSize += ULONG(pSerEvent->SerializedwParam); + } + else if ((pSerEvent->elParamType == SPET_LPARAM_IS_STRING || pSerEvent->elParamType == SPET_LPARAM_IS_TOKEN) && + pSerEvent->SerializedlParam != NULL) + { + ulSize += (wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR ); + } + // Round up to nearest DWORD + ulSize += 3; + ulSize -= ulSize % 4; + return ulSize; +} + +#endif //_WIN32_WCE + +/*** CSpEvent helper class +* +*/ +class CSpEvent : public SPEVENT +{ +public: + CSpEvent() + { + SpInitEvent(this); + } + ~CSpEvent() + { + SpClearEvent(this); + } + // If you need to take the address of a CSpEvent that is not const, use the AddrOf() method + // which will do debug checking of parameters. If you encounter this problem when calling + // GetEvents from an event source, you may want to use the GetFrom() method of this class. + const SPEVENT * operator&() + { + return this; + } + CSpEvent * AddrOf() + { + // Note: This method does not ASSERT since we assume the caller knows what they are doing. + return this; + } + void Clear() + { + SpClearEvent(this); + } + HRESULT CopyTo(SPEVENT * pDestEvent) const + { + memcpy(pDestEvent, this, sizeof(*pDestEvent)); + if ((elParamType == SPET_LPARAM_IS_POINTER) && lParam) + { + pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc(wParam); + if (pDestEvent->lParam) + { + memcpy((void *)pDestEvent->lParam, (void *)lParam, wParam); + } + else + { + pDestEvent->eEventId = SPEI_UNDEFINED; + return E_OUTOFMEMORY; + } + } + else if (elParamType == SPET_LPARAM_IS_STRING && lParam != NULL) + { + pDestEvent->lParam = (LPARAM)::CoTaskMemAlloc((wcslen((WCHAR*)lParam) + 1) * sizeof(WCHAR)); + if (pDestEvent->lParam) + { + wcscpy((WCHAR*)pDestEvent->lParam, (WCHAR*)lParam); + } + else + { + pDestEvent->eEventId = SPEI_UNDEFINED; + return E_OUTOFMEMORY; + } + } + else if (elParamType == SPET_LPARAM_IS_TOKEN || + elParamType == SPET_LPARAM_IS_OBJECT) + { + ((IUnknown*)lParam)->AddRef(); + } + return S_OK; + } + + HRESULT GetFrom(ISpEventSource * pEventSrc) + { + SpClearEvent(this); + return pEventSrc->GetEvents(1, this, NULL); + } + HRESULT CopyFrom(const SPEVENT * pSrcEvent) + { + SpClearEvent(this); + return static_cast<const CSpEvent *>(pSrcEvent)->CopyTo(this); + } + void Detach(SPEVENT * pDestEvent = NULL) + { + if (pDestEvent) + { + memcpy(pDestEvent, this, sizeof(*pDestEvent)); + } + memset(this, 0, sizeof(*this)); + } + + template <class T> + ULONG SerializeSize() const + { + return SpEventSerializeSize<T>(this); + } + + // Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64 + template <class T> + void Serialize(T * pSerEvent) const + { + pSerEvent->eEventId = this->eEventId; + pSerEvent->elParamType = this->elParamType; + pSerEvent->ulStreamNum = this->ulStreamNum; + pSerEvent->ullAudioStreamOffset = this->ullAudioStreamOffset; + pSerEvent->SerializedwParam = static_cast<ULONG>(this->wParam); + pSerEvent->SerializedlParam = static_cast<LONG>(this->lParam); + if (lParam) + { + switch(elParamType) + { + case SPET_LPARAM_IS_POINTER: + memcpy(pSerEvent + 1, (void *)lParam, wParam); + pSerEvent->SerializedlParam = sizeof(T); + break; + + case SPET_LPARAM_IS_STRING: + wcscpy((WCHAR *)(pSerEvent + 1), (WCHAR*)lParam); + pSerEvent->SerializedlParam = sizeof(T); + break; + + case SPET_LPARAM_IS_TOKEN: + { + CSpDynamicString dstrObjectId; + if( SUCCEEDED( ((ISpObjectToken*)lParam)->GetId( &dstrObjectId ) ) ) + { + pSerEvent->SerializedwParam = (dstrObjectId.Length() + 1) * sizeof( WCHAR );; + memcpy( pSerEvent + 1, (void *)dstrObjectId.m_psz, static_cast<ULONG>(pSerEvent->SerializedwParam) ); + } + pSerEvent->SerializedlParam = sizeof(T); + } + break; + + default: + break; + } + } + } + + template <class T> + HRESULT Serialize(T ** ppCoMemSerEvent, ULONG * pcbSerEvent) const + { +// WCE compiler does not work propertly with template +#ifndef _WIN32_WCE + *pcbSerEvent = SpEventSerializeSize<T>(this); +#else + *pcbSerEvent = SpEventSerializeSize(this, sizeof(** ppCoMemSerEvent)); +#endif + *ppCoMemSerEvent = (T *)::CoTaskMemAlloc(*pcbSerEvent); + if (*ppCoMemSerEvent) + { + Serialize(*ppCoMemSerEvent); + return S_OK; + } + else + { + *pcbSerEvent = 0; + return E_OUTOFMEMORY; + } + } + + + // Call this method with either SPSERIALIZEDEVENT or SPSERIALIZEDEVENT64 + template <class T> + HRESULT Deserialize(const T * pSerEvent, ULONG * pcbUsed = NULL) + { + Clear(); + HRESULT hr = S_OK; + const UNALIGNED T * pTemp = pSerEvent; + this->eEventId = pTemp->eEventId; + this->elParamType = pTemp->elParamType; + this->ulStreamNum = pTemp->ulStreamNum; + this->ullAudioStreamOffset = pTemp->ullAudioStreamOffset; + this->wParam = static_cast<WPARAM>(pTemp->SerializedwParam); + this->lParam = static_cast<LPARAM>(pTemp->SerializedlParam); + if (pTemp->SerializedlParam) + { + ULONG cbAlloc = 0; + switch (pTemp->elParamType) + { + case SPET_LPARAM_IS_POINTER: + cbAlloc = static_cast<ULONG>(wParam); + break; + + case SPET_LPARAM_IS_STRING: + cbAlloc = sizeof(WCHAR) * (1 + wcslen((const WCHAR *)(pTemp + 1))); + break; + + case SPET_LPARAM_IS_TOKEN: + { + ULONG ulDataOffset = ULONG(lParam); + hr = SpGetTokenFromId( (const WCHAR*)(pTemp + 1), + (ISpObjectToken **)&lParam ); + wParam = 0; + } + break; + } + if (cbAlloc) + { + void * pvBuff = ::CoTaskMemAlloc(cbAlloc); + this->lParam = (LPARAM)pvBuff; + if (pvBuff) + { + memcpy(pvBuff, pTemp + 1, cbAlloc); + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + + if( SUCCEEDED( hr ) && pcbUsed ) + { +// WCE compiler does not work propertly with template +#ifndef _WIN32_WCE + *pcbUsed = SpEventSerializeSize<T>(this); +#else + *pcbUsed = SpEventSerializeSize(this, sizeof(*pTemp)); +#endif + } + return hr; + } + + // + // Helpers for access to events. Performs run-time checks in debug and casts + // data to the appropriate types + // + SPPHONEID Phoneme() const + { + return (SPPHONEID)LOWORD(lParam); + } + SPVISEMES Viseme() const + { + return (SPVISEMES)LOWORD(lParam); + } + ULONG InputWordPos() const + { + return ULONG(lParam); + } + ULONG InputWordLen() const + { + return ULONG(wParam); + } + ULONG InputSentPos() const + { + return ULONG(lParam); + } + ULONG InputSentLen() const + { + return ULONG(wParam); + } + ISpObjectToken * ObjectToken() const + { + return (ISpObjectToken *)lParam; + } + ISpObjectToken * VoiceToken() const // More explicit check than ObjectToken() + { + return ObjectToken(); + } + BOOL PersistVoiceChange() const + { + return (BOOL)wParam; + } + IUnknown * Object() const + { + return (IUnknown*)lParam; + } + ISpRecoResult * RecoResult() const + { + return (ISpRecoResult *)Object(); + } + BOOL IsPaused() + { + return (BOOL)(wParam & SPREF_AutoPause); + } + BOOL IsEmulated() + { + return (BOOL)(wParam & SPREF_Emulated); + } + const WCHAR * String() const + { + return (const WCHAR*)lParam; + } + const WCHAR * BookmarkName() const + { + return String(); + } + const WCHAR * RequestTypeOfUI() const + { + return String(); + } + SPRECOSTATE RecoState() const + { + return static_cast<SPRECOSTATE>(wParam); + } + const WCHAR * PropertyName() const + { + // Note: Don't use String() method here since in the case of string attributes, the elParamType + // field specifies LPARAM_IS_POINTER, but the attribute name IS the first string in this buffer + return (const WCHAR*)lParam; + } + const LONG PropertyNumValue() const + { + return static_cast<LONG>(wParam); + } + const WCHAR * PropertyStringValue() const + { + // Search for the first NULL and return pointer to the char past it. + const WCHAR *psz; + for (psz = (const WCHAR *)lParam; *psz; psz++) {} + return psz + 1; + } + SPINTERFERENCE Interference() const + { + return static_cast<SPINTERFERENCE>(lParam); + } + HRESULT EndStreamResult() const + { + return static_cast<HRESULT>(lParam); + } + BOOL InputStreamReleased() const + { + return (wParam & SPESF_STREAM_RELEASED) ? TRUE : FALSE; + } +}; + +class CSpPhrasePtr +{ +public: + SPPHRASE * m_pPhrase; + CSpPhrasePtr() : m_pPhrase(NULL) {} + CSpPhrasePtr(ISpPhrase * pPhraseObj, HRESULT * phr) + { + *phr = pPhraseObj->GetPhrase(&m_pPhrase); + } + ~CSpPhrasePtr() + { + ::CoTaskMemFree(m_pPhrase); + } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the m_pPhrase member explicitly. + SPPHRASE ** operator&() + { + return &m_pPhrase; + } + operator SPPHRASE *() const + { + return m_pPhrase; + } + SPPHRASE & operator*() const + { + return *m_pPhrase; + } + SPPHRASE * operator->() const + { + return m_pPhrase; + } + bool operator!() const + { + return (m_pPhrase == NULL); + } + void Clear() + { + if (m_pPhrase) + { + ::CoTaskMemFree(m_pPhrase); + m_pPhrase = NULL; + } + } + HRESULT GetFrom(ISpPhrase * pPhraseObj) + { + Clear(); + return pPhraseObj->GetPhrase(&m_pPhrase); + } +}; + + +template <class T> +class CSpCoTaskMemPtr +{ +public: + T * m_pT; + CSpCoTaskMemPtr() : m_pT(NULL) {} + CSpCoTaskMemPtr(void * pv) : m_pT((T *)pv) {} + CSpCoTaskMemPtr(ULONG cElements, HRESULT * phr) + { + m_pT = (T *)::CoTaskMemAlloc(cElements * sizeof(T)); + *phr = m_pT ? S_OK : E_OUTOFMEMORY; + } + ~CSpCoTaskMemPtr() + { + ::CoTaskMemFree(m_pT); + } + void Clear() + { + if (m_pT) + { + ::CoTaskMemFree(m_pT); + m_pT = NULL; + } + } + HRESULT Alloc(ULONG cArrayElements = 1) + { + m_pT = (T *)::CoTaskMemRealloc(m_pT, sizeof(T) * cArrayElements); + return (m_pT ? S_OK : E_OUTOFMEMORY); + } + void Attach(void * pv) + { + Clear(); + m_pT = (T *)pv; + } + T * Detatch() + { + T * pT = m_pT; + m_pT = NULL; + return pT; + } + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the m_pT member explicitly. + T ** operator&() + { + return &m_pT; + } + T * operator->() + { + return m_pT; + } + operator T *() + { + return m_pT; + } + bool operator!() const + { + return (m_pT == NULL); + } +}; + +/**** Helper function used to create a new phrase object from an array of + test words. Each word in the string is converted to a phrase element. + This is useful to create a phrase to pass to the EmulateRecognition method. + The method can convert standard words as well as words with the + "/display_text/lexical_form/pronounciation;" word format. + You can also specify the DisplayAttributes for each element if desired. + If prgDispAttribs is NULL then the DisplayAttribs for each element default to + SPAF_ONE_TRAILING_SPACE. ****/ +/*inline HRESULT CreatePhraseFromWordArray(const WCHAR ** ppWords, ULONG cWords, + SPDISPLYATTRIBUTES * prgDispAttribs, + ISpPhraseBuilder **ppResultPhrase, + LANGID LangId = 0, + ISpPhoneConverter *cpPhoneConv = NULL) +{ + HRESULT hr = S_OK; + + if ( cWords == 0 || ppWords == NULL || ::IsBadReadPtr(ppWords, sizeof(*ppWords) * cWords ) ) + { + return E_INVALIDARG; + } + + if ( prgDispAttribs != NULL && ::IsBadReadPtr(prgDispAttribs, sizeof(*prgDispAttribs) * cWords ) ) + { + return E_INVALIDARG; + } + + ULONG cTotalChars = 0; + ULONG i; + WCHAR** pStringPtrArray = (WCHAR**)::CoTaskMemAlloc( cWords * sizeof(WCHAR *)); + if ( !pStringPtrArray ) + { + return E_OUTOFMEMORY; + } + for (i = 0; i < cWords; i++) + { + cTotalChars += wcslen(ppWords[i])+1; + } + + CSpDynamicString dsText(cTotalChars); + if(dsText.m_psz == NULL) + { + ::CoTaskMemFree(pStringPtrArray); + return E_OUTOFMEMORY; + } + CSpDynamicString dsPhoneId(cTotalChars); + if(dsPhoneId.m_psz == NULL) + { + ::CoTaskMemFree(pStringPtrArray); + return E_OUTOFMEMORY; + } + SPPHONEID* pphoneId = dsPhoneId; + + SPPHRASE Phrase; + memset(&Phrase, 0, sizeof(Phrase)); + Phrase.cbSize = sizeof(Phrase); + + if(LangId == 0) + { + LangId = SpGetUserDefaultUILanguage(); + } + + if(cpPhoneConv == NULL) + { + hr = SpCreatePhoneConverter(LangId, NULL, NULL, &cpPhoneConv); + if(FAILED(hr)) + { + ::CoTaskMemFree(pStringPtrArray); + return hr; + } + } + + SPPHRASEELEMENT *pPhraseElement = new SPPHRASEELEMENT[cWords]; + if(pPhraseElement == NULL) + { + ::CoTaskMemFree(pStringPtrArray); + return E_OUTOFMEMORY; + } + memset(pPhraseElement, 0, sizeof(SPPHRASEELEMENT) * cWords); // !!! + + WCHAR * pText = dsText; + for (i = 0; SUCCEEDED(hr) && i < cWords; i++) + { + WCHAR *p = pText; + pStringPtrArray[i] = pText; + wcscpy( pText, ppWords[i] ); + pText += wcslen( p ) + 1; + + if (*p == L'/') + { + //This is a compound word + WCHAR* pszFirstPart = ++p; + WCHAR* pszSecondPart = NULL; + WCHAR* pszThirdPart = NULL; + + while (*p && *p != L'/') + { + p++; + } + if (*p == L'/') + { + //It means we stop at the second '/' + *p = L'\0'; + pszSecondPart = ++p; + while (*p && *p != L'/') + { + p++; + } + if (*p == L'/') + { + //It means we stop at the third '/' + *p = L'\0'; + pszThirdPart = ++p; + } + } + + pPhraseElement[i].pszDisplayText = pszFirstPart; + pPhraseElement[i].pszLexicalForm = pszSecondPart ? pszSecondPart : pszFirstPart; + + if ( pszThirdPart) + { + hr = cpPhoneConv->PhoneToId(pszThirdPart, pphoneId); + if (SUCCEEDED(hr)) + { + pPhraseElement[i].pszPronunciation = pphoneId; + pphoneId += wcslen(pphoneId) + 1; + } + } + } + else + { + //It is the simple format, only have one form, use it for everything. + pPhraseElement[i].pszDisplayText = NULL; + pPhraseElement[i].pszLexicalForm = p; + pPhraseElement[i].pszPronunciation = NULL; + } + + pPhraseElement[i].bDisplayAttributes = (BYTE)(prgDispAttribs ? prgDispAttribs[i] : SPAF_ONE_TRAILING_SPACE); + pPhraseElement[i].RequiredConfidence = SP_NORMAL_CONFIDENCE; + pPhraseElement[i].ActualConfidence = SP_NORMAL_CONFIDENCE; + } + + Phrase.Rule.ulCountOfElements = cWords; + Phrase.pElements = pPhraseElement; + Phrase.LangID = LangId; + + ISpPhraseBuilder *cpPhrase; + if (SUCCEEDED(hr)) + { + hr = cpPhrase.CoCreateInstance(CLSID_SpPhraseBuilder); + } + + if (SUCCEEDED(hr)) + { + hr = cpPhrase->InitFromPhrase(&Phrase); + } + if (SUCCEEDED(hr)) + { + *ppResultPhrase = cpPhrase.Detach(); + } + + delete pPhraseElement; + ::CoTaskMemFree(pStringPtrArray); + + return hr; +}*/ + +/**** Helper function used to create a new phrase object from a + test string. Each word in the string is converted to a phrase element. + This is useful to create a phrase to pass to the EmulateRecognition method. + The method can convert standard words as well as words with the + "/display_text/lexical_form/pronounciation;" word format ****/ +/*inline HRESULT CreatePhraseFromText(const WCHAR *pszOriginalText, + ISpPhraseBuilder **ppResultPhrase, + LANGID LangId = 0, + ISpPhoneConverter *cpPhoneConv = NULL) +{ + HRESULT hr = S_OK; + + //We first trim the input text + CSpDynamicString dsText(pszOriginalText); + if(dsText.m_psz == NULL) + { + return E_OUTOFMEMORY; + } + dsText.TrimBoth(); + + ULONG cWords = 0; + BOOL fInCompoundword = FALSE; + + // Set first array pointer (if *p). + WCHAR *p = dsText; + while (*p) + { + if( iswspace(*p) && !fInCompoundword) + { + cWords++; + *p++ = L'\0'; + while (*p && iswspace(*p)) + { + *p++ = L'\0'; + } + // Add new array pointer. Use vector. + } + else if (*p == L'/' && !fInCompoundword) + { + fInCompoundword = TRUE; + } + else if (*p == L';' && fInCompoundword) + { + fInCompoundword = FALSE; + *p++ = L'\0'; + // Add new array element. + } + else + { + p++; + } + } + + cWords++; + + WCHAR** pStringPtrArray = (WCHAR**)::CoTaskMemAlloc( cWords * sizeof(WCHAR *)); + if ( !pStringPtrArray ) + { + hr = E_OUTOFMEMORY; + } + + if ( SUCCEEDED( hr ) ) + { + p = dsText; + for (ULONG i=0; i<cWords; i++) + { + pStringPtrArray[i] = p; + p += wcslen(p)+1; + } + + hr = CreatePhraseFromWordArray((const WCHAR **)pStringPtrArray, cWords, NULL, ppResultPhrase, LangId, cpPhoneConv); + + ::CoTaskMemFree(pStringPtrArray); + } + return hr; +}*/ + +#endif /* This must be the last line in the file */ diff --git a/backends/text-to-speech/windows/windows-text-to-speech.cpp b/backends/text-to-speech/windows/windows-text-to-speech.cpp new file mode 100644 index 0000000000..cae1e6be78 --- /dev/null +++ b/backends/text-to-speech/windows/windows-text-to-speech.cpp @@ -0,0 +1,109 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if defined(USE_WINDOWS_TTS) +#include <basetyps.h> +#include <windows.h> +#include <Servprov.h> +#include <sapi.h> +#include "backends/text-to-speech/windows/sphelper-scummvm.h" + +#include "backends/text-to-speech/windows/windows-text-to-speech.h" + + +#include "common/translation.h" +#include "common/debug.h" +#include "common/system.h" +#include "common/ustr.h" +#include "common/config-manager.h" + +WindowsTextToSpeechManager::WindowsTextToSpeechManager() { + debug("hi"); + init(); +} + +void WindowsTextToSpeechManager::init() { +} + +WindowsTextToSpeechManager::~WindowsTextToSpeechManager() { +} + +bool WindowsTextToSpeechManager::say(Common::String str) { + return true; +} + +bool WindowsTextToSpeechManager::stop() { + return true; +} + +bool WindowsTextToSpeechManager::pause() { + return true; +} + +bool WindowsTextToSpeechManager::resume() { + return true; +} + +bool WindowsTextToSpeechManager::isSpeaking() { + return true; +} + +bool WindowsTextToSpeechManager::isPaused() { + return true; +} + +bool WindowsTextToSpeechManager::isReady() { + return true; +} + +void WindowsTextToSpeechManager::setVoice(unsigned index) { +} + +void WindowsTextToSpeechManager::setRate(int rate) { +} + +void WindowsTextToSpeechManager::setPitch(int pitch) { +} + +void WindowsTextToSpeechManager::setVolume(unsigned volume) { +} + +int WindowsTextToSpeechManager::getVolume() { + return 0; +} + +void WindowsTextToSpeechManager::setLanguage(Common::String language) { +} + +void WindowsTextToSpeechManager::createVoice(int typeNumber, Common::TTSVoice::Gender gender, char *description) { +} + +void WindowsTextToSpeechManager::updateVoices() { + +} + +#endif diff --git a/backends/text-to-speech/windows/windows-text-to-speech.h b/backends/text-to-speech/windows/windows-text-to-speech.h new file mode 100644 index 0000000000..a00ed58b8b --- /dev/null +++ b/backends/text-to-speech/windows/windows-text-to-speech.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef BACKENDS_TEXT_TO_SPEECH_WINDOWS_H +#define BACKENDS_TEXT_TO_SPEECH_WINDOWS_H + +#include "common/scummsys.h" + +#if defined(USE_WINDOWS_TTS) + +#include "common/text-to-speech.h" +#include "common/str.h" + +class WindowsTextToSpeechManager : public Common::TextToSpeechManager { +public: + WindowsTextToSpeechManager(); + virtual ~WindowsTextToSpeechManager(); + + virtual bool say(Common::String str); + + virtual bool stop(); + virtual bool pause(); + virtual bool resume(); + + virtual bool isSpeaking(); + virtual bool isPaused(); + virtual bool isReady(); + + virtual void setVoice(unsigned index); + + virtual void setRate(int rate); + + virtual void setPitch(int pitch); + + virtual void setVolume(unsigned volume); + virtual int getVolume(); + + virtual void setLanguage(Common::String language); + +private: + void init(); + virtual void updateVoices(); + void createVoice(int typeNumber, Common::TTSVoice::Gender, char *description); +}; + +#endif + +#endif // BACKENDS_UPDATES_WINDOWS_H |