Наверное, уже все слышали о Microsoft Speech API, многие пользовались программами написанными для данной библиотеки и совсем небольшое количество смогли написать свои программы, которые хоть как-то использовали возможности синтеза речи. Почти два года назад я начал изучение возможности библиотеки в области синтеза речи, скачал в сети синтезаторы русской речи компании Lernout&Hauspie и Digalo (для последней мой друзья подогнали лекарство от жадности :). Синтезатор английской речи поставляются в MS Speech API SDK.
MS Speech API предствляет собой набор COM-объектов, взаимодействуя с которыми можно научить свою программу как распознавать, так и синтезировать речь. В данном документе мы будем касаться только синтеза речи.
Хоть во многих умных книжках пишут, что использование технологии COM значительно сокращает время разработки программ, я с этим не согласен. Работать просто, но вот пока разберёшься уйдёт достаточно много времени. Поэтому я решил написать класс, который всю работу с COM объектами будет делать за меня. Класс неоднократно использовался в моих программах с синтезатором русской речи компании Lernout&Hauspie.
Заголовочный файл
Заголовочный файл модуля синтеза
речи с помощью MS Speech API:
//
voicetext.h //--------------------------------------------------------------------------- //
2001 Ruslan Popov aka RaD / rad@kmc.ru //
Модуль синтеза речи с помощью MS Speech API.
// Использование модуля
разрешено только в некоммерческих проектах // при
сохранении данного комментария. // Если вы что-то
добавите в этот класс, пришлите изменения мне.
// 2001 Ruslan Popov aka RaD /
rad@kmc.ru //
Модуль синтеза речи с помощью MS Speech API. //
Использование модуля разрешено только в некоммерческих
проектах // при сохранении данного
комментария. // Если вы что-то добавите в этот класс,
пришлите изменения мне.
//--------------------------------------------------------------------------- #include
#pragma hdrstop #include
"VoiceText.h" //--------------------------------------------------------------------------- #pragma
package(smart_init) //--------------------------------------------------------------------------- __fastcall
VoiceText::VoiceText(void) { // Work with system
registry AppKey = "\SOFTWARE\RaD\VoiceText"; EngineInit(LANG_RUSSIAN,
GENDER_MALE); } //--------------------------------------------------------------------------- __fastcall
VoiceText::VoiceText(AnsiString Key, LANGID Language, WORD
Gender) { // Work with system
registry AppKey = "\SOFTWARE\RaD\" +
Key; EngineInit(Language,
Gender); } //--------------------------------------------------------------------------- __fastcall
VoiceText::~VoiceText(void) {
SaveState();
EngineTerm(); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::EngineInit(LANGID Language, WORD
Gender) { IsPaused = false; Reg =
new TRegistry(); Reg->RootKey =
HKEY_CURRENT_USER; Reg->LazyWrite =
false; if (!BeginOLE()) {
MessageBox(NULL, "Can not open OLE.", NULL, MB_OK); return;
} // Initialize TTS engine
memset(&ModeInfo, 0, sizeof(ModeInfo));
ModeInfo.wGender = Gender; ModeInfo.language.LanguageID =
Language; TTS_Central =
FindAndSelect(&ModeInfo); char* AuthorWebSite =
"http://ts.kmc.ru"; if (!TTS_Central)
{ if (MessageBox(NULL, "During
program's initialization no useful speech
"
"synthesis engine has
found!\n"
"Do you want to visit the program's site to download
"
"needed
components?",
"Synthesizer error", MB_YESNO |
MB_ICONWARNING)
== IDYES)
ShellExecute(Application->Handle, NULL,
AuthorWebSite,
NULL, NULL, SW_SHOWNORMAL);
Application->Terminate(); } //
Create notification object and fill necessary items. // MyNotify =
new CBufNotify(); // MyNotify->VoiceText =
this; // Create dialog object
TTS_Central->QueryInterface(IID_ITTSDialogsW,
(void**)&TTS_Dialog); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::EngineTerm(void) { if (Reg)
delete Reg; if (TTS_Dialog)
TTS_Dialog->Release(); if (MyNotify) delete
MyNotify; if
(!EndOLE()) {
Application->MessageBox("Can not close OLE.", NULL, MB_OK); return;
} } //--------------------------------------------------------------------------- void
__fastcall VoiceText::Speak(AnsiString MyText) { SDATA
data; char* Text = MyText.c_str();
data.dwSize = lstrlen(Text); data.pData =
Text; // Put the text buffer into the engine's speaking
queue, and give // the engine the address of the
ITTSBufNotifySink interface for // buffer-related
notifications. TTS_Central->TextData(CHARSET_TEXT, 0,
data, NULL, IID_ITTSBufNotifySink); //
TTS_Central->TextData(CHARSET_TEXT, 0, data, MyNotify,
IID_ITTSBufNotifySink); } //--------------------------------------------------------------------------- TTSMODEINFO
__fastcall VoiceText::LoadState(void) { TTSMODEINFO
ttsInfo; if (Reg->KeyExists(AppKey) == false) return
ttsInfo; Reg->OpenKey(AppKey,
true); if (Reg->ValueExists("TTSModeInfo") == false)
return ttsInfo; Reg->ReadBinaryData("TTSModeInfo" ,
&ttsInfo, sizeof(ttsInfo));
Reg->CloseKey(); return
ttsInfo; } //--------------------------------------------------------------------------- void
__fastcall VoiceText::SaveState(void) { TTSMODEINFO
ttsInfo;
TTS_Central->ModeGet(&ttsInfo);
Reg->OpenKey(AppKey, true);
Reg->WriteBinaryData("TTSModeInfo" , &ttsInfo,
sizeof(ttsInfo));
Reg->CloseKey(); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::ChangeEngine(LANGID Language, WORD
Gender) { EngineTerm();
EngineInit(Language,
Gender); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::Pause(void) { IsPaused =
true;
TTS_Central->AudioPause(); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::Resume(void) { IsPaused =
false;
TTS_Central->AudioResume(); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::Reset(void) { IsPaused =
false;
TTS_Central->AudioReset(); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::GeneralDlg(void) {
TTS_Dialog->GeneralDlg(Application->Handle,
NULL); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::AboutDlg(void) {
TTS_Dialog->AboutDlg(Application->Handle,
NULL); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::LexiconDlg(void) {
TTS_Dialog->LexiconDlg(Application->Handle,
NULL); } //--------------------------------------------------------------------------- void
__fastcall VoiceText::TranslateDlg(void) {
TTS_Dialog->TranslateDlg(Application->Handle,
NULL); } //--------------------------------------------------------------------------- PITTSCENTRAL
__fastcall VoiceText::FindAndSelect(PTTSMODEINFO
pTTSInfo) {
HRESULT
hRes;
TTSMODEINFO
ttsResult; // final
result
PITTSFIND
pITTSFind; // find
interface PIAUDIOMULTIMEDIADEVICE pIAMM; // multimedia
device interface for audio-dest
PITTSCENTRAL
pITTSCentral; // central interface //
PITTSBUFNOTIFYSINK
pBufNotify; hRes = CoCreateInstance(CLSID_TTSEnumerator,
NULL, CLSCTX_ALL,
IID_ITTSFind, (void**)&pITTSFind); if
(FAILED(hRes)) return NULL; hRes =
pITTSFind->Find(pTTSInfo, NULL, &ttsResult); if
(hRes) { pITTSFind->Release(); return NULL; } //
error // Get the audio dest hRes =
CoCreateInstance(CLSID_MMAudioDest, NULL, CLSCTX_ALL,
IID_IAudioMultiMediaDevice, (void**)&pIAMM); if (hRes)
{ pITTSFind->Release(); return NULL; } //
error
pIAMM->DeviceNumSet(WAVE_MAPPER); // Pass off the
multi-media-device interface as an IUnknown (since it is
one) // Should do select now hRes =
pITTSFind->Select(ttsResult.gModeID, &pITTSCentral, (LPUNKNOWN)
pIAMM); if (hRes) { pITTSFind->Release(); return NULL;
} // free random stuff up
pITTSFind->Release(); return
pITTSCentral; } //--------------------------------------------------------------------------- bool
__fastcall VoiceText::BeginOLE(void) { DWORD
dwVer; // Initialize OLE
SetMessageQueue(96); dwVer =
CoBuildVersion(); if (rmm != HIWORD(dwVer)) return false;
// error if (FAILED(CoInitialize(NULL))) return
false; return
true; } //--------------------------------------------------------------------------- bool
__fastcall VoiceText::EndOLE(void) { // Free up all of
OLE CoUninitialize(); return
true; } //--------------------------------------------------------------------------- //
Notification class
constructor. //--------------------------------------------------------------------------- CBufNotify::CBufNotify(void) { } //--------------------------------------------------------------------------- //
Notification class
destructor. //--------------------------------------------------------------------------- CBufNotify::~CBufNotify(void) { } //--------------------------------------------------------------------------- STDMETHODIMP
CBufNotify::QueryInterface (REFIID riid, LPVOID
*ppObj) { *ppObj = NULL; //
always return our IUnkown for IID_IUnknown if (IsEqualIID
(riid, IID_IUnknown) ||
IsEqualIID(riid,IID_ITTSBufNotifySinkW))
{ *ppObj = (LPVOID) this; return NOERROR; } // otherwise,
cant find return
ResultFromScode(E_NOINTERFACE); } //--------------------------------------------------------------------------- STDMETHODIMP_
(ULONG) CBufNotify::AddRef(void) { return 1;
} //--------------------------------------------------------------------------- STDMETHODIMP_
(ULONG) CBufNotify::Release(void) { return 1;
} //--------------------------------------------------------------------------- STDMETHODIMP
CBufNotify::BookMark (QWORD qwTime, DWORD dwMark) { if
(VoiceText->OnBookMark) VoiceText->OnBookMark(NULL, qwTime,
dwMark); return
NOERROR; } //--------------------------------------------------------------------------- STDMETHODIMP
CBufNotify::TextDataDone (QWORD qwTime, DWORD
dwFlags) { if (VoiceText->OnTextDone)
VoiceText->OnTextDone(NULL, qwTime, dwFlags); return
NOERROR; } //--------------------------------------------------------------------------- STDMETHODIMP
CBufNotify::TextDataStarted (QWORD qwTime) { if
(VoiceText->OnTextStart) VoiceText->OnTextStart(NULL,
qwTime); return
NOERROR; } //--------------------------------------------------------------------------- STDMETHODIMP
CBufNotify::WordPosition (QWORD qwTime, DWORD dwVal) {
if (VoiceText->OnWordPosition) VoiceText->OnWordPosition(NULL, qwTime,
dwVal); return
NOERROR; } //---------------------------------------------------------------------------
Пример использования
Пример использования модуля в
своём приложении:
VoiceText* TTS;
TTS = new VoiceText(Version->ProductName, LANG_RUSSIAN, GENDER_MALE);
TTS->Speak("Привет, вот у вас и заработал мой класс.");
TTS->Speak("Внимательно изучите класс, он много чего ещё может!");
if (TTS) delete TTS;