mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 21:53:12 +02:00
Add first pass at Windows speech recognizer implementation
This commit is contained in:
parent
66a2639fae
commit
a45d4b2d7d
2 changed files with 151 additions and 5 deletions
|
@ -10,20 +10,39 @@
|
|||
//
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QDebug>
|
||||
|
||||
#include "SpeechRecognizer.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
SpeechRecognizer::SpeechRecognizer() :
|
||||
QObject(),
|
||||
_enabled(false),
|
||||
_commands() {
|
||||
_commands(),
|
||||
_comInitialized(false),
|
||||
_speechRecognizer(NULL),
|
||||
_speechRecognizerContext(NULL),
|
||||
_speechRecognizerGrammar(NULL),
|
||||
_commandRecognizedEvent(NULL),
|
||||
_commandRecognizedNotifier(NULL) {
|
||||
|
||||
HRESULT hr = ::CoInitialize(NULL);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
_comInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
SpeechRecognizer::~SpeechRecognizer() {
|
||||
|
||||
if (_comInitialized) {
|
||||
::CoUninitialize();
|
||||
}
|
||||
|
||||
if (_speechRecognizerGrammar) {
|
||||
_speechRecognizerGrammar.Release();
|
||||
}
|
||||
}
|
||||
|
||||
void SpeechRecognizer::handleCommandRecognized(const char* command) {
|
||||
|
@ -31,15 +50,53 @@ void SpeechRecognizer::handleCommandRecognized(const char* command) {
|
|||
}
|
||||
|
||||
void SpeechRecognizer::setEnabled(bool enabled) {
|
||||
if (enabled == _enabled) {
|
||||
if (enabled == _enabled || !_comInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
_enabled = enabled;
|
||||
|
||||
if (_enabled) {
|
||||
|
||||
} else {
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
// Create a shared recognizer.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext.CoCreateInstance(CLSID_SpSharedRecoContext);
|
||||
}
|
||||
|
||||
// Set up event notification mechanism.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext->SetNotifyWin32Event();
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
_commandRecognizedEvent = _speechRecognizerContext->GetNotifyEventHandle();
|
||||
if (_commandRecognizedEvent == NULL) {
|
||||
hr = S_FALSE;
|
||||
}
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
_commandRecognizedNotifier = new QWinEventNotifier(_commandRecognizedEvent);
|
||||
connect(_commandRecognizedNotifier, SIGNAL(activated(HANDLE)), SLOT(notifyCommandRecognized(HANDLE)));
|
||||
}
|
||||
|
||||
// Set which events to be notified of.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext->SetInterest(SPFEI(SPEI_RECOGNITION), SPFEI(SPEI_RECOGNITION));
|
||||
}
|
||||
|
||||
// Create grammar and load commands.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext->CreateGrammar(NULL, &_speechRecognizerGrammar);
|
||||
}
|
||||
if (SUCCEEDED(hr)) {
|
||||
reloadCommands();
|
||||
}
|
||||
|
||||
_enabled = SUCCEEDED(hr);
|
||||
|
||||
} else {
|
||||
_speechRecognizerContext.Release();
|
||||
}
|
||||
|
||||
emit enabledUpdated(_enabled);
|
||||
|
@ -56,7 +113,75 @@ void SpeechRecognizer::removeCommand(const QString& command) {
|
|||
}
|
||||
|
||||
void SpeechRecognizer::reloadCommands() {
|
||||
if (!_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext->Pause(NULL);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
WORD langId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
|
||||
hr = _speechRecognizerGrammar->ResetGrammar(langId);
|
||||
}
|
||||
|
||||
DWORD ruleID = 0;
|
||||
SPSTATEHANDLE initialState;
|
||||
for (QSet<QString>::const_iterator iter = _commands.constBegin(); iter != _commands.constEnd(); iter++) {
|
||||
ruleID += 1;
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerGrammar->GetRule(NULL, ruleID, SPRAF_TopLevel | SPRAF_Active | SPRAF_Dynamic, TRUE, &initialState);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
const std::wstring command = (*iter).toStdWString(); // DJRTODO: Better, shorter name
|
||||
hr = _speechRecognizerGrammar->AddWordTransition(initialState, NULL, command.c_str(), L" ", SPWT_LEXICAL, 1.0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerGrammar->Commit(0);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerContext->Resume(NULL);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = _speechRecognizerGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE);
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
qDebug() << "ERROR: Didn't successfully reload commands";
|
||||
}
|
||||
}
|
||||
|
||||
void SpeechRecognizer::notifyCommandRecognized(HANDLE handle) {
|
||||
|
||||
SPEVENT eventItem;
|
||||
memset(&eventItem, 0, sizeof(SPEVENT));
|
||||
HRESULT hr = _speechRecognizerContext->GetEvents(1, &eventItem, NULL);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (eventItem.eEventId == SPEI_RECOGNITION && eventItem.elParamType == SPET_LPARAM_IS_OBJECT) {
|
||||
ISpRecoResult* recognitionResult = reinterpret_cast<ISpRecoResult*>(eventItem.lParam);
|
||||
wchar_t* pText;
|
||||
|
||||
hr = recognitionResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, FALSE, &pText, NULL);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
QString text = QString::fromWCharArray(pText);
|
||||
handleCommandRecognized(text.toStdString().c_str());
|
||||
::CoTaskMemFree(pText);
|
||||
}
|
||||
|
||||
recognitionResult->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // Q_OS_WIN
|
|
@ -16,6 +16,13 @@
|
|||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <QWinEventNotifier>
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <sapi.h>
|
||||
#endif
|
||||
|
||||
class SpeechRecognizer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -40,8 +47,22 @@ protected:
|
|||
private:
|
||||
bool _enabled;
|
||||
QSet<QString> _commands;
|
||||
#if defined(Q_OS_MAC)
|
||||
void* _speechRecognizerDelegate;
|
||||
void* _speechRecognizer;
|
||||
#elif defined(Q_OS_WIN)
|
||||
bool _comInitialized;
|
||||
CComPtr<ISpRecognizer> _speechRecognizer;
|
||||
CComPtr<ISpRecoContext> _speechRecognizerContext;
|
||||
CComPtr<ISpRecoGrammar> _speechRecognizerGrammar;
|
||||
HANDLE _commandRecognizedEvent;
|
||||
QWinEventNotifier* _commandRecognizedNotifier;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
private slots:
|
||||
void notifyCommandRecognized(HANDLE handle);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // hifi_SpeechRecognizer_h
|
||||
|
|
Loading…
Reference in a new issue