Windows 语音 SAPI:如何列出语音的属性?
Windows Speech SAPI: How to list attributes for voices?
我已经使用此 Stack Overflow Answer 遍历所有已安装的 Windows 文本到语音的声音,但我很难 提取每个声音的属性。例如性别、语言、姓名等
我假设有一种方法可以提取可用于查找声音的属性,例如gender=female;language=409
if ( FAILED( ::CoInitialize( NULL ) ) )
return 1;
HRESULT hr = S_OK;
CComPtr<ISpVoice> cpVoice; //Will send data to ISpStream
CComPtr<ISpStream> cpStream; //Will contain IStream
CComPtr<IStream> cpBaseStream; //raw data
ISpObjectToken* cpToken( NULL ); //Will set voice characteristics
GUID guidFormat;
WAVEFORMATEX* pWavFormatEx = nullptr;
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
CComPtr<ISpObjectTokenCategory> cpSpCategory = NULL;
if ( SUCCEEDED( hr = SpGetCategoryFromId( SPCAT_VOICES, &cpSpCategory ) ) )
{
CComPtr<IEnumSpObjectTokens> cpSpEnumTokens;
if ( SUCCEEDED( hr = cpSpCategory->EnumTokens( NULL, NULL, &cpSpEnumTokens ) ) )
{
CComPtr<ISpObjectToken> pSpTok;
while ( SUCCEEDED( hr = cpSpEnumTokens->Next( 1, &pSpTok, NULL ) ) )
{
// do something with the token here; for example, set the voice
//pVoice->SetVoice( pSpTok, FALSE );
WCHAR *pID = NULL;
hr = pSpTok->GetId( &pID );
// This succeeds, pID is "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_DAVID_11.0"
WCHAR *pName = NULL;
pSpTok->GetStringValue( L"name", &pName );
// pName, pGender and pLanguage are all null
WCHAR *pGender = NULL;
pSpTok->GetStringValue( L"gender", &pGender );
WCHAR *pLanguage = NULL;
pSpTok->GetStringValue( L"language", &pLanguage );
LONG index = 0;
WCHAR *key = NULL;
while ( SUCCEEDED( hr = pSpTok->EnumKeys( index, &key ) ) )
{
// Gets some elements
WCHAR* pValue = NULL;
pSpTok->GetStringValue( key, &pValue );
// Loops once, key value is "Attributes"
index++;
}
index = 0;
while ( SUCCEEDED( hr = pSpTok->EnumValues( index, &key ) ) )
{
// Loops many times, but none of these have what I need
WCHAR* pValue = NULL;
pSpTok->GetStringValue( key, &pValue );
index++;
}
// NOTE: IEnumSpObjectTokens::Next will *overwrite* the pointer; must manually release
pSpTok.Release();
}
}
}
我是 Windows C++ 开发的新手,抱歉。
获取代表语音的ISpObjectToken后,检索其“Attributes”子项,然后查询子项的值:
CComPtr<ISpObjectToken> pSpTok;
while (cpSpEnumTokens->Next(1, &pSpTok, NULL) == S_OK)
{
CComPtr<ISpDataKey> cpSpAttributesKey;
if (SUCCEEDED(hr = pSpTok->OpenKey(L"Attributes", &cpSpAttributesKey)))
{
CSpDynamicString dstrName;
cpSpAttributesKey->GetStringValue(L"Name", &dstrName);
CSpDynamicString dstrGender;
cpSpAttributesKey->GetStringValue(L"Gender", &dstrGender);
// dstrName: Microsoft David Desktop
// dstrGender: Male
}
pSpTok.Release();
}
我正在使用 CSpDynamicString,以便自动释放为字符串分配的内存。您可以选择使用 WCHAR*
,但您需要自己负责调用 CoTaskMemFree。
我还修复了您原始代码中的另一个错误:cpSpEnumTokens->Next
的结果应该与 S_OK 进行比较,而不是传递给 SUCCEEDED,因为 Next returns S_FALSE表示枚举完成。 S_FALSE 是成功的结果,所以使用 SUCCEEDED 会导致无限循环。
参考: Object Tokens and Registry Settings White Paper - §5.3 检查令牌的底层密钥
我已经使用此 Stack Overflow Answer 遍历所有已安装的 Windows 文本到语音的声音,但我很难 提取每个声音的属性。例如性别、语言、姓名等
我假设有一种方法可以提取可用于查找声音的属性,例如gender=female;language=409
if ( FAILED( ::CoInitialize( NULL ) ) )
return 1;
HRESULT hr = S_OK;
CComPtr<ISpVoice> cpVoice; //Will send data to ISpStream
CComPtr<ISpStream> cpStream; //Will contain IStream
CComPtr<IStream> cpBaseStream; //raw data
ISpObjectToken* cpToken( NULL ); //Will set voice characteristics
GUID guidFormat;
WAVEFORMATEX* pWavFormatEx = nullptr;
hr = cpVoice.CoCreateInstance( CLSID_SpVoice );
CComPtr<ISpObjectTokenCategory> cpSpCategory = NULL;
if ( SUCCEEDED( hr = SpGetCategoryFromId( SPCAT_VOICES, &cpSpCategory ) ) )
{
CComPtr<IEnumSpObjectTokens> cpSpEnumTokens;
if ( SUCCEEDED( hr = cpSpCategory->EnumTokens( NULL, NULL, &cpSpEnumTokens ) ) )
{
CComPtr<ISpObjectToken> pSpTok;
while ( SUCCEEDED( hr = cpSpEnumTokens->Next( 1, &pSpTok, NULL ) ) )
{
// do something with the token here; for example, set the voice
//pVoice->SetVoice( pSpTok, FALSE );
WCHAR *pID = NULL;
hr = pSpTok->GetId( &pID );
// This succeeds, pID is "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_DAVID_11.0"
WCHAR *pName = NULL;
pSpTok->GetStringValue( L"name", &pName );
// pName, pGender and pLanguage are all null
WCHAR *pGender = NULL;
pSpTok->GetStringValue( L"gender", &pGender );
WCHAR *pLanguage = NULL;
pSpTok->GetStringValue( L"language", &pLanguage );
LONG index = 0;
WCHAR *key = NULL;
while ( SUCCEEDED( hr = pSpTok->EnumKeys( index, &key ) ) )
{
// Gets some elements
WCHAR* pValue = NULL;
pSpTok->GetStringValue( key, &pValue );
// Loops once, key value is "Attributes"
index++;
}
index = 0;
while ( SUCCEEDED( hr = pSpTok->EnumValues( index, &key ) ) )
{
// Loops many times, but none of these have what I need
WCHAR* pValue = NULL;
pSpTok->GetStringValue( key, &pValue );
index++;
}
// NOTE: IEnumSpObjectTokens::Next will *overwrite* the pointer; must manually release
pSpTok.Release();
}
}
}
我是 Windows C++ 开发的新手,抱歉。
获取代表语音的ISpObjectToken后,检索其“Attributes”子项,然后查询子项的值:
CComPtr<ISpObjectToken> pSpTok;
while (cpSpEnumTokens->Next(1, &pSpTok, NULL) == S_OK)
{
CComPtr<ISpDataKey> cpSpAttributesKey;
if (SUCCEEDED(hr = pSpTok->OpenKey(L"Attributes", &cpSpAttributesKey)))
{
CSpDynamicString dstrName;
cpSpAttributesKey->GetStringValue(L"Name", &dstrName);
CSpDynamicString dstrGender;
cpSpAttributesKey->GetStringValue(L"Gender", &dstrGender);
// dstrName: Microsoft David Desktop
// dstrGender: Male
}
pSpTok.Release();
}
我正在使用 CSpDynamicString,以便自动释放为字符串分配的内存。您可以选择使用 WCHAR*
,但您需要自己负责调用 CoTaskMemFree。
我还修复了您原始代码中的另一个错误:cpSpEnumTokens->Next
的结果应该与 S_OK 进行比较,而不是传递给 SUCCEEDED,因为 Next returns S_FALSE表示枚举完成。 S_FALSE 是成功的结果,所以使用 SUCCEEDED 会导致无限循环。
参考: Object Tokens and Registry Settings White Paper - §5.3 检查令牌的底层密钥