aboutsummaryrefslogtreecommitdiff
path: root/backends/text-to-speech
diff options
context:
space:
mode:
authorJaromir Wysoglad2019-07-15 21:09:36 -0700
committerFilippos Karapetis2019-09-01 22:47:55 +0300
commit8357e8e6bf909353c3db720ba8fda9cdb0a41933 (patch)
treec8a5735d16f9b82249d4877cb3985238bc4beadf /backends/text-to-speech
parent6303f522e180f1474434c75c0e5a4d44b141fe3a (diff)
downloadscummvm-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')
-rw-r--r--backends/text-to-speech/windows/sphelper-scummvm.h2648
-rw-r--r--backends/text-to-speech/windows/windows-text-to-speech.cpp109
-rw-r--r--backends/text-to-speech/windows/windows-text-to-speech.h67
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