Sources.RU Magazine Поиск по журналу
 

TopList

Microsoft Speech API — Синтез речи

Автор: RaD

Введение

Наверное, уже все слышали о 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>                 // To work with system registry
#include <shellapi.h>                   // To work with system shell
//---------------------------------------------------------------------------
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 = "
\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;

Удачи!



 Design by Шишкин Алексей (Лёха)  ©2004-2008 by sources.ru