Microsoft Speech API — Синтез речи
Наверное, уже все слышали о 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.// Использование модуля
разрешено только в некоммерческих проектах
// при
сохранении данного комментария.
// Если вы что-то
добавите в этот класс, пришлите изменения мне.
//---------------------------------------------------------------------------
#ifndef
VoiceTextH
#define
VoiceTextH
//---------------------------------------------------------------------------
#include
<mmsystem.h>
#include <initguid.h>
#include
<objbase.h>
#include <objerror.h>
#include
<ole2ver.h>
#include <speech.h>//---------------------------------------------------------------------------
#include
<Registry.hpp>
#include
<shellapi.h>
//---------------------------------------------------------------------------
typedef
void __fastcall (__closure *VoiceTextOnBookMark)
(System::TObject* Sender, QWORD qTimeStamp, DWORD dwMarkNum);
typedef void
__fastcall (__closure *VoiceTextOnTextDone)
(System::TObject* Sender, QWORD qTimeStamp, DWORD dwFlags);
typedef void
__fastcall (__closure *VoiceTextOnTextStart)
(System::TObject* Sender, QWORD qTimeStamp);
typedef void __fastcall
(__closure *VoiceTextOnWordPosition)
(System::TObject*
Sender, QWORD qTimeStamp, DWORD
dwByteOffset);
//---------------------------------------------------------------------------
class
CBufNotify;
// Forward
declaration
//---------------------------------------------------------------------------
class
VoiceText
{
private:
bool IsPaused;
TRegistry*
Reg;
// Pointer to system registry
AnsiString
AppKey;
// Name of the key in system registry
protected:
PITTSCENTRAL
TTS_Central; //
ISRCentral interface to TTS engine
PITTSDIALOGS
TTS_Dialog;
TTSMODEINFO
ModeInfo;
// Desired mode
CBufNotify*
MyNotify;
//---------------------------------------------------------------------------
PITTSCENTRAL __fastcall FindAndSelect(PTTSMODEINFO
pTTSInfo);
bool __fastcall
BeginOLE(void);
bool __fastcall
EndOLE(void);
//---------------------------------------------------------------------------
// Notification functions
VoiceTextOnBookMark FOnBookMark;
VoiceTextOnTextDone FOnTextDone;
VoiceTextOnTextStart FOnTextStart;
VoiceTextOnWordPosition
FOnWordPosition;
//---------------------------------------------------------------------------
void __fastcall EngineInit(LANGID Language, WORD Gender);
void __fastcall EngineTerm(void);
public:
__fastcall
VoiceText(void);
__fastcall VoiceText(AnsiString Key,
LANGID Language, WORD Gender);
__fastcall
~VoiceText(void);
void __fastcall Speak(AnsiString
Text);
void __fastcall Pause(void);
void __fastcall Resume(void);
void __fastcall
Reset(void);
void __fastcall
GeneralDlg(void);
void __fastcall
AboutDlg(void);
void __fastcall
LexiconDlg(void);
void __fastcall
TranslateDlg(void);
void __fastcall ChangeEngine(LANGID
Language, WORD Gender);
TTSMODEINFO __fastcall
LoadState(void);
void __fastcall
SaveState(void);
bool __fastcall GetIsPause(void) { return
IsPaused; };
//__published:
__property
VoiceTextOnBookMark OnBookMark =
{
read = FOnBookMark, write = FOnBookMark };
__property
VoiceTextOnTextDone OnTextDone =
{
read = FOnTextDone, write = FOnTextDone };
__property
VoiceTextOnTextStart OnTextStart =
{ read = FOnTextStart, write = FOnTextStart };
__property
VoiceTextOnWordPosition OnWordPosition
=
{ read = FOnWordPosition, write
= FOnWordPosition
};
};
//---------------------------------------------------------------------------
class
CBufNotify : public ITTSBufNotifySink
{
private:
// TSapiTTSOnTextDone
MyOnTextDone;
public:
CBufNotify(void);
~CBufNotify(void);
// IUnknown members
STDMETHODIMP QueryInterface
(REFIID, LPVOID FAR *);
STDMETHODIMP_(ULONG)
AddRef(void);
STDMETHODIMP_(ULONG)
Release(void);
// ITTSNotifySink
STDMETHOD (BookMark) (QWORD,
DWORD);
STDMETHOD (TextDataDone) (QWORD,
DWORD);
STDMETHOD (TextDataStarted)
(QWORD);
STDMETHOD (WordPosition)
(QWORD, DWORD);
// Main class
VoiceText*
VoiceText;
};
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
Исходный код модуля сопряжения синтеза речи с помощью MS Speech API:
//
voicetext.cpp
//---------------------------------------------------------------------------// 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 = "
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;
Удачи!