今回はWindows 10の音声合成の環境のアップデート。
Win7, 8, 8.1にはSAPIが搭載されており、sapi.hにOS標準の音声合成APIが含まれていました。Windows10も同様、標準のボイスの情報はSPCAT_VOICESに定義されているレジストリキー(HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices)にあり、バージョンは11.0となっています。

さらに、Speech Platform Language Packでインストールされるボイスは”Speech Server”下に定義されます。このレジストリキーはSpEnumTokens関数でTokenを取得するときに第一引数として使用できます。(ただしSpeech Serverは Speech Platfrom Runtimeをインストールしないと、登録されていないボイスというエラーが返ってきます)
さらに、Windows10で話題のCortana(コルタナ)ですが、同じフォーマットでSpeech_OneCoreに定義されています。このボイスはSpEnumTokens関数で指定可能です。
SpEnumTokensでCortanaのボイスを指定する例)
hr = SpEnumTokens(L”HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech_OneCore\\Voices”, strReqAttribute, NULL, &pEnum);
CortanaにはパラメータがSAPIよりも用意されていますが、公開されているかは不明です。今のところ、SAPIから使用可能ということだけでしょうか。あと、日本語Cortanaのボイス名は”Ichiro”と”Ayumi”となっています。

誰を使って、AI作ろうか。
今回作った。SAPIの関数(COMのHRESULTのエラー処理は省略)
HRESULT speak(LPWSTR strSpeakString)
{
HRESULT hr = S_OK;
//ISpVoice * pVoice = NULL;
ISpVoice *pVoice = 0;
ISpObjectToken *pToken = 0;
IEnumSpObjectTokens *pEnum = 0;
if (FAILED(CoInitialize(NULL)))
{
::MessageBoxW(NULL, L"Error to intiliaze COM", L"DBG", MB_OK);
return S_OK;
}
hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if (SUCCEEDED(hr))
{
hr = SpEnumTokens(SPCAT_VOICES, L"Language = 411", NULL, &pEnum); //SAPI default voice (OS language pack) 411:Japanese
//hr = SpEnumTokens(L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech Server\\v11.0\\Voices", L"Language = 411", NULL, &pEnum); //Speech Platform v11.0 (Required runtime)
//hr = SpEnumTokens(L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech_OneCore\\Voices", L"Language = 411", NULL, &pEnum); //Cortana voice
ULONG ulCount = 0;
USHORT usVolume = 0;
long lRate = 0;
hr = pEnum->GetCount(&ulCount);
pEnum->Item(0, &pToken);
hr = pVoice->SetVoice(pToken);
hr = pVoice->GetVolume(&usVolume);
hr = pVoice->SetVolume(100); //0 - 100
hr = pVoice->GetRate(&lRate);
hr = pVoice->SetRate(0); // -10 to 10
hr = pVoice->Speak(NULL, SPF_PURGEBEFORESPEAK, 0);
hr = pVoice->Speak(strSpeakString, SPF_IS_XML | SPF_DEFAULT, NULL);
hr = pVoice->WaitUntilDone(INFINITE);
if(pToken)
{
hr = pToken->Release();
pToken = 0;
}
if (pVoice)
{
hr = pVoice->Release();
pVoice = 0;
}
if (pEnum)
{
hr = pEnum->Release();
pEnum = 0;
}
}
if (pToken)
{
hr = pToken->Release();
pToken = 0;
}
CoUninitialize();
return S_OK;
}