mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-02 17:48:11 +03:00
1012 lines
33 KiB
C++
1012 lines
33 KiB
C++
//******************************************************************************
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// ttscomp.h
|
|
//
|
|
//******************************************************************************
|
|
|
|
#ifndef __TTSCOMP_H__
|
|
#define __TTSCOMP_H__
|
|
|
|
#include <windows.h>
|
|
|
|
#include <spdebug.h>
|
|
#define ATLASSERT(expr) SPDBG_ASSERT(expr)
|
|
// Replace ATLASSERT with SPDBG_ASSERT.
|
|
// If the compiler says macro redefinition at this line, you need to remove any
|
|
// #include <atlbase.h> before include this file
|
|
#include <atlbase.h>
|
|
|
|
#include <tchar.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include <kato.h>
|
|
#include <tux.h>
|
|
#include "sapi.h"
|
|
#include "sapiddk.h"
|
|
#include <sperror.h>
|
|
#include <sphelper.h>
|
|
#include "spgeterrormsg.h"
|
|
#include "resource.h"
|
|
#include <wchar.h>
|
|
#include <string>
|
|
|
|
|
|
// Added for logging across .cpp files
|
|
extern CKato *g_pKato;
|
|
extern SPS_SHELL_INFO *g_pShellInfo;
|
|
|
|
//************* Helper function prototypes *************
|
|
HRESULT GetWStrFromRes( UINT id, WCHAR* szwStr, int cchBufferMax = MAX_PATH);
|
|
HRESULT RegRecursiveCopyKey(HKEY hkeySrc, LPCTSTR szSubKeySrc, HKEY hkeyDest, LPCTSTR szSubKeyDest, BOOL fClearDest = FALSE);
|
|
HRESULT RegRecursiveDeleteKey(HKEY hkeySrc, LPCTSTR szSubKeySrc, BOOL fKeepEmptyRoot = FALSE);
|
|
void CleanupVoiceAndEngine();
|
|
HRESULT GetFreqFromSamples (ISpStream * pStream, int sampFreq, ULONG &length, ULONG * pAverage );
|
|
int ComputeF0 (float* data, int nData, int sampFreq, int minF0, int maxF0, int feaSampFreq,
|
|
float** f0, int* nF0, float **pRms, int *nRms);
|
|
HRESULT SpGetLanguageIdFromDefaultVoice(LANGID *plangid);
|
|
HRESULT SPCreateStreamOnHGlobal(HGLOBAL hGlobal, BOOL fDeleteOnRelease, REFGUID rguidFormatId,
|
|
const WAVEFORMATEX * pwfex, ISpStream ** ppStream );
|
|
|
|
#define TTS_WAITTIME 30000 //30 seconds
|
|
#define TTS_WAITTIME_LONG 90000 //90 seconds
|
|
|
|
// Useful macros and definitions
|
|
#define ZeroStruct(structvar) memset(&(structvar),0,sizeof(structvar))
|
|
#define SP_IS_BAD_WRITE_PTR(p) ( SPIsBadWritePtr( p, sizeof(*(p)) ))
|
|
|
|
inline BOOL SPIsBadWritePtr( void* pMem, UINT Size )
|
|
{
|
|
#ifdef _DEBUG
|
|
BOOL bIsBad = ::IsBadWritePtr( pMem, Size );
|
|
SPDBG_ASSERT(!bIsBad);
|
|
return bIsBad;
|
|
#else
|
|
return ::IsBadWritePtr( pMem, Size );
|
|
#endif
|
|
}
|
|
|
|
template <class T>
|
|
class CAutoReleaseComPtr: public CComPtr<T>
|
|
{
|
|
public:
|
|
T ** operator&()
|
|
{
|
|
Release();
|
|
return CComPtr<T>::operator &();
|
|
}
|
|
};
|
|
|
|
#define DOCHECKHRGOTO(statement) statement CHECKHRGOTO(hr, tpr, #statement);
|
|
#define DOCHECKHR(statement) statement CHECKHR(hr, tpr, #statement);
|
|
|
|
inline LPCSTR CatMsg( LPCSTR psz1, LPCSTR psz2 = NULL, LPCSTR psz3 = NULL, LPCSTR psz4 = NULL, LPCSTR psz5 = NULL)
|
|
{
|
|
static std::string s_str;
|
|
|
|
{ // make a new copy so that it works for CatMsg(CatMsg(str1, ...), str2, ...);
|
|
std::string str = psz1;
|
|
if(psz2) str += psz2;
|
|
if(psz3) str += psz3;
|
|
if(psz4) str += psz4;
|
|
if(psz5) str += psz5;
|
|
s_str = str;
|
|
}
|
|
|
|
return s_str.c_str();
|
|
}
|
|
|
|
#define CHECKHRALTId( hr, althr, tpr, id ) \
|
|
{ \
|
|
if( FAILED( hr ) && (hr != althr) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if(SUCCEEDED(GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
g_pKato->Comment(10, SpGetErrorMsg(hr)); \
|
|
} \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRALTIdEx( hr, althr, tpr, id, topic ) \
|
|
{ \
|
|
\
|
|
if( FAILED( hr ) && (hr != althr) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg(SpGetErrorMsg(hr), " in ", topic)); \
|
|
} \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRALTGOTOIdEx( hr, althr, tpr, id, topic ) \
|
|
{ \
|
|
\
|
|
if( FAILED( hr ) && (hr != althr) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg(SpGetErrorMsg(hr), " in ", topic)); \
|
|
} \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
// for more general asserts.
|
|
// will not output error message based on hr
|
|
#define CHECKASSERTGOTOId( cond, tpr, id ) \
|
|
{ \
|
|
\
|
|
if( !(cond) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg("Condition: ", #cond, " Failed")); \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKASSERTGOTOIdEx( cond, tpr, id, topic ) \
|
|
{ \
|
|
\
|
|
if( !(cond) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg("Condition: ", #cond, " Failed in ", topic)); \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKASSERTId( cond, tpr, id ) \
|
|
{ \
|
|
\
|
|
if( !(cond) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg("Condition: ", #cond, " Failed")); \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKISSUPPORTEDId( cond, tpr, id ) \
|
|
{ \
|
|
\
|
|
if( !(cond) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg("Condition: ", #cond, " Failed")); \
|
|
tpr = TPR_UNSUPPORTED; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKASSERTIdEx( cond, tpr, id, topic ) \
|
|
{ \
|
|
\
|
|
if( !(cond) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg("Condition: ", #cond, " Failed in ", topic)); \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
// Useful macros and definitions
|
|
#define CHECKHRId( hr, tpr, id ) \
|
|
{ \
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
g_pKato->Comment(10, SpGetErrorMsg(hr)); \
|
|
} \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRIdEx( hr, tpr, id, topic ) \
|
|
{ \
|
|
\
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg(SpGetErrorMsg(hr), " in ", topic )); \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRGOTOId( hr, tpr, id) \
|
|
{ \
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, SpGetErrorMsg(hr)); \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRGOTOIdEx( hr, tpr, id, topic ) \
|
|
{ \
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
{ \
|
|
WCHAR szwDebug[MAX_PATH]=L""; \
|
|
if (SUCCEEDED (GetWStrFromRes( id, szwDebug ))) \
|
|
{ \
|
|
USES_CONVERSION; \
|
|
g_pKato->Comment(5, W2T(szwDebug)); \
|
|
} \
|
|
} \
|
|
g_pKato->Comment(10, CatMsg (SpGetErrorMsg(hr), " in ", topic)); \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
// Useful macros and definitions
|
|
#define CHECKHR( hr, tpr, szError ) \
|
|
{ \
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
if( szError) \
|
|
{ \
|
|
g_pKato->Comment(5, szError); \
|
|
g_pKato->Comment(10, SpGetErrorMsg(hr)); \
|
|
} \
|
|
tpr = TPR_FAIL; \
|
|
} \
|
|
}
|
|
|
|
#define CHECKHRGOTO( hr, tpr, szError ) \
|
|
{ \
|
|
if( FAILED( hr ) ) \
|
|
{ \
|
|
if( szError ) \
|
|
{ \
|
|
g_pKato->Comment(5, szError); \
|
|
g_pKato->Comment(10, SpGetErrorMsg(hr)); \
|
|
} \
|
|
tpr = TPR_ABORT; \
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
// Get a random number between min and max
|
|
#define GET_RANDOM( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))
|
|
|
|
|
|
/***** Helper Classes *****/
|
|
class CEventingTest : public ISpNotifyCallback
|
|
{
|
|
/*=== Methods =======*/
|
|
public:
|
|
STDMETHODIMP NotifyCallback(WPARAM wParam, LPARAM lParam) { m_bCallbackNotified = true; return S_OK; }
|
|
|
|
BOOL m_bCallbackNotified;
|
|
|
|
CEventingTest() { m_bCallbackNotified = false; }
|
|
};
|
|
|
|
//speak two streams: stream1 and stream2, and return back samples: sample1 and sample2
|
|
inline HRESULT t_SpeakTwoStreams(ISpVoice *cpVoice, WCHAR *stream1,
|
|
WCHAR *stream2, ULONG &sample1, ULONG &sample2)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
CComPtr<ISpStream> cpStream1;
|
|
CComPtr<ISpStream> cpStream2;
|
|
STATSTG Stat;
|
|
|
|
CSpStreamFormat InFormat(SPSF_22kHz16BitMono, &hr);
|
|
|
|
// Create stream #1
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = SPCreateStreamOnHGlobal( NULL, true, InFormat.FormatId(), InFormat.WaveFormatExPtr(), &cpStream1 );
|
|
}
|
|
// Set the default output format
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpVoice->SetOutput( cpStream1, TRUE);
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpVoice->Speak(stream1, 0, 0);
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpStream1->Stat( &Stat, STATFLAG_NONAME );
|
|
sample1 = (ULONG)Stat.cbSize.QuadPart / sizeof(WCHAR);
|
|
}
|
|
|
|
// Create stream #2
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = SPCreateStreamOnHGlobal( NULL, true, InFormat.FormatId(), InFormat.WaveFormatExPtr(), &cpStream2 );
|
|
}
|
|
|
|
// Set the default output format
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpVoice->SetOutput( cpStream2, TRUE);
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpVoice->Speak(stream2, 0, 0);
|
|
}
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = cpStream2->Stat( &Stat, STATFLAG_NONAME );
|
|
sample2 = (ULONG)Stat.cbSize.QuadPart / sizeof(WCHAR);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT GetFreqFromSamples (ISpStream * pStream, int sampFreq, ULONG &length, ULONG * pAverage )
|
|
{
|
|
//this is a help function to get Frequency from the wav stream. The test allocates a
|
|
// buffer with length * sizeof (BUCKET) bytes.
|
|
|
|
//Note: The application uses 16 bits wave stream.
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG cbActuallyRead = 0;
|
|
ULONG cSamples = 0;
|
|
ULONG freq =0;
|
|
BOOL bPositive = FALSE;
|
|
|
|
typedef short BUCKET; //use 16 bits
|
|
BUCKET *buff = NULL, *buffHead = NULL;
|
|
float *afUnfilteredBuffer = NULL, *afUnfilteredBufferHead = NULL;
|
|
|
|
int feaSampFreq = 100; //sample rate
|
|
int minF0 = 30; //low freq Range of frequences interested
|
|
int maxF0 = 500; //high freq
|
|
|
|
float *f0 = NULL;
|
|
int nF0 = 0;
|
|
|
|
float *pRms = NULL;
|
|
int nRms = 0;
|
|
*pAverage = 0;
|
|
float *pAverageRms =0;
|
|
int NumRms =0;
|
|
|
|
buff = (BUCKET *)::CoTaskMemAlloc ( length * sizeof (BUCKET) );
|
|
if(!buff)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
afUnfilteredBuffer = (float *)::CoTaskMemAlloc ( length * sizeof ( float) );
|
|
if(!afUnfilteredBuffer)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pAverageRms = (float *)::CoTaskMemAlloc ( length * sizeof ( float) );
|
|
if(!pAverageRms)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//save the head pointer
|
|
buffHead = buff;
|
|
afUnfilteredBufferHead = afUnfilteredBuffer;
|
|
|
|
// Read stream into a buffer until EOF
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pStream->Read( buff, length * sizeof (BUCKET), &cbActuallyRead);
|
|
}
|
|
|
|
cSamples = cbActuallyRead / sizeof(BUCKET);
|
|
|
|
// Create a low frequency filter and run the samples through
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
for( ULONG i=0; i< cSamples; i++ )
|
|
{
|
|
afUnfilteredBuffer[i] = (float)buff[i];
|
|
}
|
|
}
|
|
|
|
if (!ComputeF0 (afUnfilteredBuffer, cSamples, sampFreq, minF0, maxF0,
|
|
feaSampFreq, &f0, &nF0, &pRms, &nRms))
|
|
{
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
*pAverage = 0;
|
|
for( ULONG i=0; i < nF0; i++ )
|
|
{
|
|
if( f0[i] >0 )
|
|
{
|
|
*pAverage += (ULONG)f0[i];
|
|
freq++;
|
|
}
|
|
}
|
|
|
|
*pAverageRms = 0;
|
|
for( ULONG j=0; j < nRms; j++ )
|
|
{
|
|
if( pRms[j] >0 )
|
|
{
|
|
*pAverageRms += (ULONG)pRms[j];
|
|
NumRms++;
|
|
}
|
|
}
|
|
if (NumRms > 0 )
|
|
*pAverageRms /= NumRms;
|
|
|
|
if (freq > 0 )
|
|
*pAverage /= freq;
|
|
|
|
::CoTaskMemFree (buffHead);
|
|
::CoTaskMemFree (afUnfilteredBufferHead);
|
|
::CoTaskMemFree (pAverageRms);
|
|
if ( f0 )
|
|
{
|
|
free (f0);
|
|
f0 = NULL;
|
|
}
|
|
if ( pRms )
|
|
{
|
|
free (pRms);
|
|
pRms = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT GetAmpFromSamples (ISpStream * pStream, ULONG * pamp )
|
|
{
|
|
//this is a help function to get amplitude from the stream. The test assume that
|
|
// the max of the buffer for Read is 1000 X 2 bytes, i.e. everytime Read gets called, at most
|
|
// 2000 bytes are readed.
|
|
|
|
//Note: The application uses 16 bits wave stream.
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ULONG cbActuallyRead = 1;
|
|
ULONG nRead = 1000;
|
|
ULONG totalAmp = 0, average = 0;
|
|
|
|
typedef short BUCKET;
|
|
BUCKET *buff = NULL, *buffHead = NULL;
|
|
|
|
buff = (BUCKET *)::CoTaskMemAlloc ( nRead * sizeof (BUCKET) );
|
|
if(!buff)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//save the head pointer
|
|
buffHead = buff;
|
|
|
|
// Read stream into a buffer until EOF
|
|
while ( cbActuallyRead && SUCCEEDED( hr ) )
|
|
{
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
hr = pStream->Read( buff, nRead * sizeof (BUCKET), &cbActuallyRead);
|
|
}
|
|
|
|
// Sum abs value of amplitude
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
average += (cbActuallyRead / sizeof(BUCKET));
|
|
|
|
for( ULONG i=0; i < cbActuallyRead / sizeof(BUCKET); i++ )
|
|
{
|
|
|
|
if( buff[i] < 0 )
|
|
{
|
|
buff[i] = -(buff[i]);
|
|
}
|
|
totalAmp += buff[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( average > 0)
|
|
*pamp = totalAmp/average;
|
|
|
|
::CoTaskMemFree (buffHead);
|
|
return hr;
|
|
}
|
|
|
|
|
|
inline HRESULT TTSSymToPhoneId(LANGID langid, WCHAR* pszSym, WCHAR* pszPhoneId)
|
|
{
|
|
CComPtr<ISpPhoneConverter> cpPhoneConv;
|
|
CComPtr<ISpObjectToken> cpToken;
|
|
CComPtr<IEnumSpObjectTokens> cpEnum;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = SpCreatePhoneConverter(langid, NULL, NULL, &cpPhoneConv);
|
|
}
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = cpPhoneConv->PhoneToId(pszSym, pszPhoneId);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT CreateAppLexicon(
|
|
const WCHAR * pszLangIndependentName,
|
|
LANGID langid,
|
|
const WCHAR * pszLangDependentName,
|
|
WCHAR* pwszAttributes,
|
|
ISpLexicon** ppLexicon)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<ISpObjectToken> cpToken;
|
|
CComPtr<ISpDataKey> cpDataKeyAttribs;
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = SpCreateNewTokenEx(SPCAT_APPLEXICONS, pszLangIndependentName, &CLSID_SpUnCompressedLexicon, pszLangIndependentName, langid, pszLangDependentName, &cpToken, &cpDataKeyAttribs);
|
|
}
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
CSpDynamicString str(pwszAttributes); // make a copy of the pwszAttributes
|
|
WCHAR* p = wcstok(str, L";");
|
|
while(SUCCEEDED(hr) && p)
|
|
{
|
|
if(WCHAR* pe = wcschr(p, L'='))
|
|
{
|
|
*(pe++) = L'\0';
|
|
hr = cpDataKeyAttribs->SetStringValue(p, pe);
|
|
}
|
|
else
|
|
{
|
|
hr = cpDataKeyAttribs->SetStringValue(p, L"");
|
|
}
|
|
p = wcstok(NULL, L";");
|
|
}
|
|
}
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = SpCreateObjectFromToken(cpToken, ppLexicon);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT RegRecursiveDeleteKey(HKEY hkeySrc, LPCTSTR szSubKeySrc, BOOL fKeepEmptyRoot)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONG l;
|
|
HKEY hkey;
|
|
|
|
// try to use RegDeleteKey. On Win95/98 this will be enough.
|
|
l = ::RegDeleteKey(hkeySrc, szSubKeySrc);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(fKeepEmptyRoot)
|
|
{
|
|
HKEY hkey;
|
|
l = ::RegCreateKeyEx(hkeySrc, szSubKeySrc, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hkey, NULL);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
::RegCloseKey(hkey);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
l = ::RegOpenKeyEx(hkeySrc, szSubKeySrc, 0, KEY_ALL_ACCESS, &hkey);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
if(SUCCEEDED(hr)) // open hkey
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
DWORD cbName;
|
|
|
|
// recursively copy the subkeys
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
for(DWORD i = 0; SUCCEEDED(hr); i++)
|
|
{
|
|
cbName = MAX_PATH;
|
|
|
|
// always enum the first subkey because the old one has been deleted
|
|
l = ::RegEnumKeyEx(hkey, 0, szName, &cbName, NULL, NULL, NULL, NULL);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = RegRecursiveDeleteKey(hkey, szName);
|
|
}
|
|
}
|
|
if(hr == SpHrFromWin32(ERROR_NO_MORE_ITEMS)) hr = S_OK;
|
|
}
|
|
|
|
::RegCloseKey(hkey);
|
|
}
|
|
|
|
if(SUCCEEDED(hr) && !fKeepEmptyRoot)
|
|
{
|
|
l = ::RegDeleteKey(hkeySrc, szSubKeySrc);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
}
|
|
|
|
if(hr == SpHrFromWin32(ERROR_NO_MORE_ITEMS)) hr = SPERR_NO_MORE_ITEMS;
|
|
if(hr == SpHrFromWin32(ERROR_FILE_NOT_FOUND)) hr = SPERR_NOT_FOUND;
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT RegRecursiveCopyKey(HKEY hkeySrc, LPCTSTR szSubKeySrc, HKEY hkeyDest, LPCTSTR szSubKeyDest, BOOL fClearDest)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LONG l;
|
|
HKEY hkey1, hkey2;
|
|
|
|
l = ::RegOpenKeyEx(hkeySrc, szSubKeySrc, 0, KEY_ALL_ACCESS, &hkey1);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(fClearDest)
|
|
{
|
|
hr = RegRecursiveDeleteKey(hkeyDest, szSubKeyDest);
|
|
if(hr == SPERR_NOT_FOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
l = ::RegCreateKeyEx(hkeyDest, szSubKeyDest, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey2, NULL);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
BYTE data[MAX_PATH];
|
|
DWORD cbName, cbData, dwType;
|
|
|
|
// copy the values
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
for(DWORD i = 0; SUCCEEDED(hr); i++)
|
|
{
|
|
BYTE* _data = data;
|
|
cbName = cbData = MAX_PATH;
|
|
l = ::RegEnumValue(hkey1, i, szName, &cbName, NULL, &dwType, data, &cbData);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
if(hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) // buffer is not large enough to hold the data
|
|
{
|
|
_data = new BYTE[cbData];
|
|
if(_data)
|
|
{
|
|
l = ::RegEnumValue(hkey1, i, szName, &cbName, NULL, &dwType, _data, &cbData);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
}
|
|
}
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
l = ::RegSetValueEx(hkey2, szName, NULL, dwType, _data, cbData);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
}
|
|
if(_data && _data != data)
|
|
{
|
|
delete[] _data;
|
|
}
|
|
}
|
|
if(hr == SpHrFromWin32(ERROR_NO_MORE_ITEMS)) hr = S_OK;
|
|
}
|
|
|
|
// recursively copy the subkeys
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
for(DWORD i = 0; SUCCEEDED(hr); i++)
|
|
{
|
|
cbName = MAX_PATH;
|
|
l = ::RegEnumKeyEx(hkey1, i, szName, &cbName, NULL, NULL, NULL, NULL);
|
|
hr = (l == ERROR_SUCCESS) ? S_OK : HRESULT_FROM_WIN32(l);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = RegRecursiveCopyKey(hkey1, szName, hkey2, szName);
|
|
}
|
|
}
|
|
if(hr == SpHrFromWin32(ERROR_NO_MORE_ITEMS)) hr = S_OK;
|
|
}
|
|
|
|
::RegCloseKey(hkey2);
|
|
}
|
|
|
|
::RegCloseKey(hkey1);
|
|
}
|
|
|
|
if(hr == SpHrFromWin32(ERROR_NO_MORE_ITEMS)) hr = SPERR_NO_MORE_ITEMS;
|
|
if(hr == SpHrFromWin32(ERROR_FILE_NOT_FOUND)) hr = SPERR_NOT_FOUND;
|
|
return hr;
|
|
}
|
|
|
|
inline HRESULT RemoveTestAppLexicon()
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IEnumSpObjectTokens> cpEnum;
|
|
CComPtr<ISpObjectToken> cpSpObjectToken;
|
|
|
|
if( SUCCEEDED( hr ) )
|
|
{
|
|
// Get tts test engine voice
|
|
hr = SpEnumTokens(SPCAT_APPLEXICONS, L"TTSCompliance", NULL, &cpEnum);
|
|
}
|
|
|
|
if( hr == S_OK )
|
|
{
|
|
CAutoReleaseComPtr<ISpObjectToken> cpToken;
|
|
hr = cpEnum->Next(1, &cpToken, NULL);
|
|
while(hr == S_OK)
|
|
{
|
|
hr = cpToken->RemoveStorageFileName(CLSID_SpUnCompressedLexicon, L"DataFile", TRUE);
|
|
if(( hr == S_OK ) || (hr == SPERR_NOT_FOUND))
|
|
{
|
|
hr = cpToken->Remove(NULL); // remove the token itself
|
|
}
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = cpEnum->Next(1, &cpToken, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//*****************************************************************************/
|
|
//Similar to CreateStreamOnHGlobal Win32 API, but allows a stream to be created
|
|
inline HRESULT SPCreateStreamOnHGlobal(
|
|
HGLOBAL hGlobal, //Memory handle for the stream object
|
|
BOOL fDeleteOnRelease, //Whether to free memory when the object is released
|
|
REFGUID rguidFormatId, //Format ID for stream
|
|
const WAVEFORMATEX * pwfex, //WaveFormatEx for stream
|
|
ISpStream ** ppStream) //Address of variable to receive ISpStream pointer
|
|
{
|
|
HRESULT hr;
|
|
IStream * pMemStream;
|
|
*ppStream = NULL;
|
|
hr = ::CreateStreamOnHGlobal(hGlobal, fDeleteOnRelease, &pMemStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ::CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = (*ppStream)->SetBaseStream(pMemStream, rguidFormatId, pwfex);
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppStream)->Release();
|
|
*ppStream = NULL;
|
|
}
|
|
}
|
|
pMemStream->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
/************* Test function prototypes (TestProc's) *************/
|
|
// ISpTTSEngine Tests
|
|
TESTPROCAPI t_ISpTTSEngine_Speak (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_ISpTTSEngine_Skip (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_ISpTTSEngine_GetOutputFormat (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_ISpTTSEngine_SetRate (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_ISpTTSEngine_SetVolume (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
// Eventing Tests
|
|
TESTPROCAPI t_CheckEventsSAPI (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_CheckEventsNotRequire (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
// TTS Markup Tests
|
|
TESTPROCAPI t_XMLBookmark (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLSilence (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLSpell (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLPronounce (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLRate (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLVolume (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLPitch (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLNonSapiTagsTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLEmphTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLPartOfSpTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_XMLContext (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
// Real Time Rate/Vol Tests
|
|
TESTPROCAPI t_RealTimeRateChange (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_RealTimeVolumeChange (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
//Multiple Instances test
|
|
TESTPROCAPI t_MultiInstanceTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
// Audio State Tests
|
|
TESTPROCAPI t_SpeakStop (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_SpeakDestroy (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
// Lexicon test
|
|
TESTPROCAPI t_UserLexiconTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
TESTPROCAPI t_AppLexiconTest (UINT, TPPARAM, LPFUNCTION_TABLE_ENTRY);
|
|
|
|
//the following are used in Multiple instance tests
|
|
|
|
TESTPROCAPI t_ISpTTSEngine_Skip_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti=false);
|
|
TESTPROCAPI t_ISpTTSEngine_Speak_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti=false);
|
|
TESTPROCAPI t_ISpTTSEngine_GetOutputFormat_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti=false);
|
|
TESTPROCAPI t_ISpTTSEngine_SetRate_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti=false) ;
|
|
TESTPROCAPI t_ISpTTSEngine_SetVolume_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti=false) ;
|
|
TESTPROCAPI t_RealTimeRateChange_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_RealTimeVolumeChange_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_SpeakStop_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_CheckEventsSAPI_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti) ;
|
|
TESTPROCAPI t_LexiconMulti_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLSAPIMarkup_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLContext_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLBookmark_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLSilence_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLPronounce_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLRate_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLVolume_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLPitch_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
TESTPROCAPI t_XMLSpell_Test(UINT uMsg,
|
|
TPPARAM tpParam,
|
|
LPFUNCTION_TABLE_ENTRY lpFTE,
|
|
ISpVoice *cpVoice,
|
|
bool bCalledByMulti);
|
|
|
|
#endif //__TTSCOMP_H__
|