392 lines
11 KiB
C++
392 lines
11 KiB
C++
|
#include <windows.h>
|
||
|
#include <atlbase.h>
|
||
|
extern CComModule _Module;
|
||
|
#include <atlcom.h>
|
||
|
#include "sapi.h"
|
||
|
#include "sphelper.h"
|
||
|
#include "spddkhlp.h"
|
||
|
#include "spttseng.h"
|
||
|
#include "spttseng_i.c"
|
||
|
#include "ms1033ltsmap.h" // BUGBUG: Move to somewhere... else.
|
||
|
|
||
|
|
||
|
#ifdef _WIN32_WCE
|
||
|
#define GOTO_LRETURN(hres) {RETAILMSG(1, (_T("File: %s Line :%d, hr=%08x\n"),_T(__FILE__),__LINE__, hres)); goto LReturn;}
|
||
|
#define MARK(text) {RETAILMSG(1, (_T("%s Line :%d\n"), text,__LINE__));}
|
||
|
#else
|
||
|
#define GOTO_LRETURN(hres) {wprintf(L"Line :%d, hr=%08x\n",__LINE__, hres); goto LReturn;}
|
||
|
#define MARK(text) {wprintf(L"%s Line :%d\n", text,__LINE__);}
|
||
|
#endif
|
||
|
|
||
|
// This code does not ship
|
||
|
|
||
|
// This code creates the registry entries for the TTS voices. The
|
||
|
// datafiles registered here are the ones checked in the slm source tree. This is not
|
||
|
// done using a reg file because we need to compute the absolute path of the datafiles
|
||
|
// which can be different on different machines because of different root slm directories.
|
||
|
|
||
|
#ifndef _WIN32_WCE
|
||
|
#define DIRS_TO_GO_BACK_VOICE 3 // Back 3 levels to 'Voices" directory
|
||
|
#define DIRS_TO_GO_BACK_LEX 6 // Back 3 levels to Lex Data directory
|
||
|
#else
|
||
|
#define DIRS_TO_GO_BACK_VOICE 1 // Back 3 levels to 'Voices" directory
|
||
|
#define DIRS_TO_GO_BACK_LEX 1 // Back 3 levels to Lex Data directory
|
||
|
#endif
|
||
|
|
||
|
CSpUnicodeSupport g_Unicode;
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CreateLexSubKey *
|
||
|
*------------------*
|
||
|
* Description:
|
||
|
* Each TTS voice gets installed under one registry sub-key.
|
||
|
* This function installs the single voice from the passed params.
|
||
|
*
|
||
|
********************************************************************** MC ***/
|
||
|
HRESULT CreateLexSubKey(
|
||
|
ISpObjectToken * pToken,
|
||
|
const WCHAR * pszSubKeyName,
|
||
|
const CLSID * pclsid,
|
||
|
const WCHAR * pszFilePath,
|
||
|
const WCHAR * pszLexName,
|
||
|
const WCHAR * pszPhoneMap)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//---------------------------------------
|
||
|
// Create the lex sub-key (Lex or LTS)
|
||
|
//---------------------------------------
|
||
|
CComPtr<ISpObjectToken> cpSubToken;
|
||
|
hr = SpGetSubTokenFromToken(pToken, pszSubKeyName, &cpSubToken, TRUE);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpSetCommonTokenData(
|
||
|
cpSubToken,
|
||
|
pclsid,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
WCHAR szLexDataPath[MAX_PATH];
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//--------------------------------
|
||
|
// Lex DATA file location
|
||
|
//--------------------------------
|
||
|
wcscpy(szLexDataPath, pszFilePath);
|
||
|
wcscat(szLexDataPath, pszLexName);
|
||
|
|
||
|
hr = cpSubToken->SetStringValue(L"DataFile", szLexDataPath);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr) && pszPhoneMap)
|
||
|
{
|
||
|
CComPtr<ISpObjectToken> cpPhoneToken;
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = SpGetSubTokenFromToken(cpSubToken, L"PhoneConverter", &cpPhoneToken, TRUE);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = SpSetCommonTokenData(cpPhoneToken, &CLSID_SpPhoneConverter, NULL, 0, NULL, NULL);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = cpPhoneToken->SetStringValue(L"PhoneMap", pszPhoneMap);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CreateUISubKey *
|
||
|
*-----------------*
|
||
|
* Description:
|
||
|
* Creates the UI SubKey under a voice.
|
||
|
*
|
||
|
********************************************************************** AH ***/
|
||
|
HRESULT CreateUISubKey(
|
||
|
ISpObjectToken * pToken,
|
||
|
const CLSID * pclsid)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
//-----------------------
|
||
|
// Create the UI sub-key
|
||
|
//-----------------------
|
||
|
CComPtr<ISpObjectToken> cpSubToken;
|
||
|
hr = SpGetSubTokenFromToken(pToken, L"UI", &cpSubToken, TRUE);
|
||
|
|
||
|
//--------------------------------------
|
||
|
// Create the EngineProperties sub-key
|
||
|
//--------------------------------------
|
||
|
CComPtr<ISpObjectToken> cpSubSubToken;
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
hr = SpGetSubTokenFromToken( cpSubToken, L"EngineProperties", &cpSubSubToken, TRUE );
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SpSetCommonTokenData(
|
||
|
cpSubSubToken,
|
||
|
pclsid,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CreateVoiceSubKey *
|
||
|
*--------------------*
|
||
|
* Description:
|
||
|
* Each TTS voice gets installed under one registry sub-key.
|
||
|
* This function installs the single voice from the passed params.
|
||
|
*
|
||
|
********************************************************************** MC ***/
|
||
|
HRESULT CreateVoiceSubKey(
|
||
|
const WCHAR * pszSubKeyName,
|
||
|
const WCHAR * pszDescription,
|
||
|
BOOL fVendorDefault,
|
||
|
const WCHAR * pszGender,
|
||
|
const WCHAR * pszAge,
|
||
|
const WCHAR * pszVoicePath,
|
||
|
const WCHAR * pszVoiceName,
|
||
|
const WCHAR * pszLexPath)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
CComPtr<ISpObjectToken> cpToken;
|
||
|
CComPtr<ISpDataKey> cpDataKeyAttribs;
|
||
|
hr = SpCreateNewTokenEx(
|
||
|
SPCAT_VOICES,
|
||
|
pszSubKeyName,
|
||
|
&CLSID_MSVoiceData,
|
||
|
pszDescription,
|
||
|
0x409,
|
||
|
pszDescription,
|
||
|
&cpToken,
|
||
|
&cpDataKeyAttribs);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"Name", pszSubKeyName);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"Gender", pszGender);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"Age", pszAge);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"Vendor", L"Microsoft");
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"Language", L"409");
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr) && fVendorDefault)
|
||
|
{
|
||
|
hr = cpDataKeyAttribs->SetStringValue(L"VendorPreferred", L"");
|
||
|
}
|
||
|
|
||
|
WCHAR szVoiceDataPath[MAX_PATH];
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//--------------------------------
|
||
|
// Voice DATA file location
|
||
|
//--------------------------------
|
||
|
wcscpy(szVoiceDataPath, pszVoicePath);
|
||
|
wcscat(szVoiceDataPath, pszVoiceName);
|
||
|
wcscat(szVoiceDataPath, L".vdt");
|
||
|
|
||
|
hr = cpToken->SetStringValue(L"VoiceData", szVoiceDataPath);
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
//--------------------------------
|
||
|
// Voice DEF file location
|
||
|
//--------------------------------
|
||
|
wcscpy(szVoiceDataPath, pszVoicePath);
|
||
|
wcscat(szVoiceDataPath, pszVoiceName);
|
||
|
wcscat(szVoiceDataPath, L".vdf");
|
||
|
|
||
|
hr = cpToken->SetStringValue(L"VoiceDef", szVoiceDataPath);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------
|
||
|
// Register TTS lexicons
|
||
|
//------------------------------------------------
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CreateLexSubKey(cpToken, L"Lex", &CLSID_SpCompressedLexicon, pszLexPath, L"LTTS1033.LXA", NULL);
|
||
|
}
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CreateLexSubKey(cpToken, L"LTS", &CLSID_SpLTSLexicon, pszLexPath, L"r1033tts.lxa", pszms1033ltsmap);
|
||
|
}
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
hr = CreateUISubKey( cpToken, &CLSID_SpTtsEngUI );
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* main *
|
||
|
*-------*
|
||
|
* Description:
|
||
|
* Locate the abs path to the Mary, Mike and Sam voices
|
||
|
* and register them in the system registry.
|
||
|
*
|
||
|
********************************************************************** MC ***/
|
||
|
int wmain()
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
CoInitialize(NULL);
|
||
|
|
||
|
//----------------------------------------
|
||
|
// Get the exe's location...
|
||
|
//----------------------------------------
|
||
|
WCHAR szVoiceDataPath[MAX_PATH];
|
||
|
if (!g_Unicode.GetModuleFileName(NULL, szVoiceDataPath, MAX_PATH))
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(::GetLastError());
|
||
|
}
|
||
|
|
||
|
WCHAR szLexDataPath[MAX_PATH];
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
wcscpy(szLexDataPath, szVoiceDataPath);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------
|
||
|
// ...and derive abs path to VOICE data
|
||
|
//----------------------------------------
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// modulename is "<sapi5>\Src\TTS\msttsdrv\voices\RegVoices\debug_x86\RegVoices.exe"
|
||
|
// Data is at "<sapi5>\Src\TTS\msttsdrv\voices\"
|
||
|
WCHAR * psz;
|
||
|
psz = szVoiceDataPath;
|
||
|
|
||
|
for (int i = 0; i < DIRS_TO_GO_BACK_VOICE; i++)
|
||
|
{
|
||
|
psz = wcsrchr(psz, '\\');
|
||
|
if (!psz)
|
||
|
{
|
||
|
hr = E_FAIL;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*psz = 0;
|
||
|
psz = szVoiceDataPath;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
wcscat(szVoiceDataPath, L"\\");
|
||
|
}
|
||
|
#ifdef _WIN32_WCE
|
||
|
MARK(szVoiceDataPath);
|
||
|
// if (SUCCEEDED(hr))
|
||
|
// {
|
||
|
// wcscat(szVoiceDataPath, L"voices\\");
|
||
|
// }
|
||
|
// MARK(szVoiceDataPath);
|
||
|
#endif // _WIN32_WCE
|
||
|
|
||
|
//----------------------------------------
|
||
|
// Derive abs path to LEX data
|
||
|
//----------------------------------------
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// modulename is "<sapi5>\Src\TTS\msttsdrv\voices\RegVoices\debug_x86\RegVoices.exe"
|
||
|
// Data is at "<sapi5>\Src\lexicon\data\"
|
||
|
WCHAR * psz = szLexDataPath;
|
||
|
for (int i = 0; i < DIRS_TO_GO_BACK_LEX; i++)
|
||
|
{
|
||
|
psz = wcsrchr(psz, '\\');
|
||
|
if (!psz)
|
||
|
{
|
||
|
hr = E_FAIL;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*psz = 0;
|
||
|
psz = szLexDataPath;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
MARK(szLexDataPath);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
wcscat(szLexDataPath, L"\\");
|
||
|
}
|
||
|
MARK(szLexDataPath);
|
||
|
|
||
|
//------------------------------------------------
|
||
|
// ...then register the three Microsoft voices..
|
||
|
//------------------------------------------------
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CreateVoiceSubKey(L"MSMary",
|
||
|
L"MS Mary",
|
||
|
TRUE,
|
||
|
L"Female",
|
||
|
L"Adult",
|
||
|
szVoiceDataPath,
|
||
|
L"Mary",
|
||
|
szLexDataPath);
|
||
|
}
|
||
|
/* if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CreateVoiceSubKey(L"MSMike",
|
||
|
L"MS Mike",
|
||
|
FALSE,
|
||
|
L"Male",
|
||
|
L"Adult",
|
||
|
szVoiceDataPath,
|
||
|
L"Mike",
|
||
|
szLexDataPath);
|
||
|
}
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = CreateVoiceSubKey(L"MSSam",
|
||
|
L"MS Sam",
|
||
|
FALSE,
|
||
|
L"Male",
|
||
|
L"Adult",
|
||
|
szVoiceDataPath,
|
||
|
L"Sam",
|
||
|
szLexDataPath);
|
||
|
}
|
||
|
*/
|
||
|
CoUninitialize();
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|