2009年10月24日 星期六

在 Windows 環境下使用 SAPI 開發語音程式(二): 程式碼概觀


我個人在 coding 的時候有個癖好,就是喜歡極簡風。所以當我要寫
一個軟體模組,我都會盡量堅持一個 .cpp 和一個 .hpp 。

先把 code 秀出來好了。

// SpeechRecognizer.hpp
#ifndef _SPEECHRECOGNIZER_HPP
#define _SPEECHRECOGNIZER_HPP

#include < sphelper.h >
#include < windows.h >

class SpeechRecognizer
{
public:
void activate();
void deactivate();
static void thrRun(void*);

void initSAPI();
void cleanupSAPI();
void executeCommand(ISpPhrase *pPhrase);
void exitError(LPTSTR lpszFunction);
//protected:
// for running
bool _active;

// for SAPI
CComPtr< ISpRecoContext > _cpRecoCtxt;
CComPtr< ISpRecoGrammar > _cpGrammar;
CComPtr< ISpRecognizer > _cpEngine;
};
#endif

##CONTINUE##

// SpeechRecognizer.cpp

#include "SpeechRecognizer.hpp"
#include < process.h >
#include < iostream >

using namespace std;

void SpeechRecognizer::activate()
{
initSAPI();
_active = true;
_beginthread(SpeechRecognizer::thrRun, 0, this);
}

void SpeechRecognizer::deactivate()
{
_active = false;
cleanupSAPI();
}

void SpeechRecognizer::thrRun(void* ptr)
{
SpeechRecognizer* pSR = (SpeechRecognizer*)ptr;

HRESULT hr;
while(pSR->_active){
hr = pSR->_cpRecoCtxt->WaitForNotifyEvent(100);
if(hr == 0){
CSpEvent event;
event.GetFrom(pSR->_cpRecoCtxt);
pSR->executeCommand(event.RecoResult());
}
}
_endthread();
}

void SpeechRecognizer::initSAPI()
{
if(FAILED(::CoInitialize(NULL))){ exitError(TEXT("init COM Failed!")); }

HRESULT hr;

// create a recognition engine
hr = _cpEngine.CoCreateInstance(CLSID_SpSharedRecognizer);
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpEngine.CoCreateInstance")); }

// create the command recognition context
hr = _cpEngine->CreateRecoContext( &_cpRecoCtxt );
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpEngine->CreateRecoContext")); }

// Let SR know that window we want it to send event information to, and using
// what message
hr = _cpRecoCtxt->SetNotifyWin32Event();
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpRecoCtxt->SetNotifyWin32Event")); }

// Tell SR what types of events interest us. Here we only care about command
// recognition.
hr = _cpRecoCtxt->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpRecoCtxt->SetInterest")); }

// Load our grammar, which is the compiled form of simple.xml bound into this executable as a
hr = _cpRecoCtxt->CreateGrammar(0, &_cpGrammar);
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpRecoCtxt->CreateGrammar")); }

hr = _cpGrammar->LoadCmdFromFile(L"test.xml", SPLO_DYNAMIC);
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpCmdGrammar->LoadCmdFromFile")); }

// Set rules to active, we are now listening for commands
hr = _cpGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE);
if(FAILED(hr)){ cleanupSAPI(); exitError(TEXT("_cpGrammar->SetRuleState")); }
}

void SpeechRecognizer::cleanupSAPI()
{
// Release grammar, if loaded
if (_cpGrammar){
_cpGrammar.Release();
}
// Release recognition context, if created
if (_cpRecoCtxt){
_cpRecoCtxt->SetNotifySink(NULL);
_cpRecoCtxt.Release();
}
// Release recognition engine instance, if created
if (_cpEngine){
_cpEngine.Release();
}
CoUninitialize();
}

void SpeechRecognizer::executeCommand(ISpPhrase *pPhrase)
{
SPPHRASE* pElements;
if(SUCCEEDED(pPhrase->GetPhrase(&pElements))){
switch(pElements->Rule.ulId){
case 1:
cout << "robot" << endl;
break;
case 2:
cout << "hello" << endl;
break;
default:
;
}
}
}
void SpeechRecognizer::exitError(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(dw);
}




跟一般程式不同的地方在於, SAPI 需要「文法」,才能真正擁有語音辨識的能力。

通常關於文法需要由另一個 .xml 檔案提供。



robot
hello

沒有留言: