2013-04-24 14:52:04 +00:00
|
|
|
#define WINVER 0x0500 // At least WinXP required
|
|
|
|
#include <windows.h>
|
2013-04-24 16:01:44 +00:00
|
|
|
#include <limits.h>
|
2013-04-24 14:52:04 +00:00
|
|
|
|
|
|
|
#include "speechhlp.hh"
|
|
|
|
#include <string>
|
|
|
|
#include "sapi.hh"
|
|
|
|
#include "sphelper.hh"
|
|
|
|
|
|
|
|
using std::wstring;
|
|
|
|
|
|
|
|
struct _SpeechHelper
|
|
|
|
{
|
|
|
|
ISpVoice * voice;
|
|
|
|
wstring engineId;
|
|
|
|
wstring engineName;
|
|
|
|
bool willInvokeCoUninitialize;
|
|
|
|
|
2013-04-24 16:01:44 +00:00
|
|
|
_SpeechHelper() :
|
|
|
|
willInvokeCoUninitialize(false)
|
2013-04-24 14:52:04 +00:00
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
willInvokeCoUninitialize = (hr != RPC_E_CHANGED_MODE);
|
2013-04-24 16:01:44 +00:00
|
|
|
CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, IID_ISpVoice, (void**)&voice);
|
2013-04-24 14:52:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~_SpeechHelper()
|
|
|
|
{
|
|
|
|
if (voice)
|
|
|
|
voice->Release();
|
|
|
|
|
|
|
|
if (willInvokeCoUninitialize)
|
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-04-24 16:01:44 +00:00
|
|
|
SpeechHelper speechCreate(const wchar_t *engineId)
|
2013-04-24 14:52:04 +00:00
|
|
|
{
|
2013-04-24 16:01:44 +00:00
|
|
|
SpeechHelper sp = new _SpeechHelper();
|
|
|
|
HRESULT hr;
|
|
|
|
ISpObjectToken * spToken;
|
|
|
|
|
|
|
|
hr = SpGetTokenFromId(engineId, &spToken);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
2013-04-24 14:52:04 +00:00
|
|
|
{
|
2013-04-24 16:01:44 +00:00
|
|
|
if (SUCCEEDED(hr) && sp->voice)
|
|
|
|
{
|
|
|
|
sp->voice->SetVoice(spToken);
|
2013-04-24 14:52:04 +00:00
|
|
|
|
2013-04-24 16:01:44 +00:00
|
|
|
WCHAR * engineName = NULL;
|
|
|
|
SpGetDescription( spToken, &engineName );
|
|
|
|
sp->engineId = engineId;
|
|
|
|
if (engineName)
|
|
|
|
{
|
|
|
|
sp->engineName = engineName;
|
|
|
|
CoTaskMemFree(engineName);
|
|
|
|
}
|
|
|
|
}
|
2013-04-24 14:52:04 +00:00
|
|
|
|
2013-04-24 16:01:44 +00:00
|
|
|
spToken->Release();
|
|
|
|
}
|
2013-04-24 14:52:04 +00:00
|
|
|
|
|
|
|
return sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void speechDestroy(SpeechHelper sp)
|
|
|
|
{
|
|
|
|
delete sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool speechAvailable(SpeechHelper sp)
|
|
|
|
{
|
|
|
|
if (!sp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return !!(sp->voice);
|
|
|
|
}
|
|
|
|
|
|
|
|
void speechEnumerateAvailableEngines(EnumerateCallback callback, void *userData)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
2014-01-18 15:11:17 +00:00
|
|
|
IEnumSpObjectTokens * enumSpTokens = NULL;
|
2013-04-24 14:52:04 +00:00
|
|
|
ULONG count = 0;
|
|
|
|
bool next = true;
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
bool willInvokeCoUninitialize = (hr != RPC_E_CHANGED_MODE);
|
|
|
|
hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &enumSpTokens);
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
hr = enumSpTokens->GetCount(&count);
|
|
|
|
|
|
|
|
for (ULONG i = 0; i < count && next; i++)
|
|
|
|
{
|
|
|
|
ISpObjectToken * spToken = NULL;
|
|
|
|
WCHAR * engineName = NULL;
|
|
|
|
WCHAR * engineId = NULL;
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
hr = enumSpTokens->Next(1, &spToken, NULL);
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
hr = SpGetDescription(spToken, &engineName);
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
hr = spToken->GetId(&engineId);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
next = callback(spToken, engineId, engineName, userData);
|
|
|
|
|
|
|
|
if( spToken )
|
|
|
|
spToken->Release();
|
|
|
|
|
|
|
|
if (engineName)
|
|
|
|
CoTaskMemFree(engineName);
|
|
|
|
if (engineId)
|
|
|
|
CoTaskMemFree(engineId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( enumSpTokens )
|
|
|
|
enumSpTokens->Release();
|
|
|
|
|
|
|
|
if (willInvokeCoUninitialize)
|
|
|
|
CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t * speechEngineId(SpeechHelper sp)
|
|
|
|
{
|
|
|
|
if (!sp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return sp->engineId.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t * speechEngineName(SpeechHelper sp)
|
|
|
|
{
|
|
|
|
if (!sp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return sp->engineName.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool speechTell(SpeechHelper sp, const wchar_t *text)
|
|
|
|
{
|
|
|
|
if (!sp || !sp->voice || !text)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
HRESULT hr = sp->voice->Speak(text, SPF_ASYNC | SPF_IS_NOT_XML, 0);
|
|
|
|
return !!SUCCEEDED(hr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool speechTellFinished(SpeechHelper sp)
|
|
|
|
{
|
|
|
|
if (!sp || !sp->voice)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
SPVOICESTATUS es;
|
|
|
|
sp->voice->GetStatus(&es, NULL);
|
|
|
|
return es.dwRunningState == SPRS_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool speechSay(SpeechHelper sp, const wchar_t *text)
|
|
|
|
{
|
|
|
|
if (!sp || !sp->voice || !text)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
HRESULT hr = sp->voice->Speak(text, SPF_IS_NOT_XML, 0);
|
|
|
|
return !!SUCCEEDED(hr);
|
|
|
|
}
|
2013-04-26 13:41:39 +00:00
|
|
|
|
|
|
|
int setSpeechVolume( SpeechHelper sp, int newVolume )
|
|
|
|
{
|
|
|
|
if( !sp || !sp->voice || newVolume < 0 || newVolume > 100 )
|
|
|
|
return -1;
|
|
|
|
unsigned short oldVolume;
|
|
|
|
HRESULT hr = sp->voice->GetVolume( &oldVolume );
|
|
|
|
if( !SUCCEEDED( hr ) )
|
|
|
|
return -1;
|
|
|
|
sp->voice->SetVolume( (unsigned short) newVolume );
|
|
|
|
return oldVolume;
|
|
|
|
}
|
|
|
|
|
|
|
|
int setSpeechRate( SpeechHelper sp, int newRate )
|
|
|
|
{
|
|
|
|
if( !sp || !sp->voice || newRate < 0 || newRate > 100 )
|
|
|
|
return -1;
|
|
|
|
long oldRate;
|
|
|
|
HRESULT hr = sp->voice->GetRate( &oldRate );
|
|
|
|
if( !SUCCEEDED( hr ) )
|
|
|
|
return -1;
|
|
|
|
sp->voice->SetRate( ( newRate - 50 ) / 5 );
|
|
|
|
return oldRate * 5 + 50;
|
|
|
|
}
|