我個人在 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