www.pudn.com > xvoice-0.8.1.rar > Voice.cc
/**
* Voice.cc
*
* Description: Interface to IBM's ViaVoice SMAPI lib
*
* Copyright (c) 1999, David Z. Creemer, Tom Doris.
* See the LICENSE file. All rights not granted therein are reserved.
*
* @author David Z. Creemer
* @author Tom Doris
* @version 1.0
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
// NOTE: Most of this file is taken from IBM sample code,
// which has the following copyright notice, and which supercedes
// the above copyright:
/*================================================================*/
/* COPYRIGHT: */
/* ---------- */
/* Copyright (C) International Business Machines Corp., 1999. */
/* */
/* DISCLAIMER OF WARRANTIES: */
/* ------------------------- */
/* The following [enclosed] code is sample code created by IBM */
/* Corporation. This sample code is not part of any standard IBM */
/* product and is provided to you solely for the purpose of */
/* assisting you in the development of your applications. The */
/* code is provided "AS IS", without warranty of any kind. IBM */
/* shall not be liable for any damages arising out of your use of */
/* the sample code, even if they have been advised of the */
/* possibility of such damages. */
/*================================================================*/
#include <smapi.h>
#include <stack>
#include <map>
#include <string>
#include <stdio.h>
#include <ctype.h>
#include "Error.h"
#include "Target.h"
#include "MainWin.h"
#include "xvoice.h"
#include "config.h"
#include "EventStream.h"
#include "ParseEventStream.h"
#include <regex.h>
#include <Voice.h>
#include <vtbnfc.h>
/* defines */
#define CheckSmRC() do { \
int rc; \
SmGetRc(reply, &amt;rc); \
if (rc != SM_RC_OK) { \
fprintf(stderr, ">s: rc = >d\n", __FUNCTION__, rc); \
gMainWin->errorMsg(E_FATAL, __FUNCTION__, "Voice engine error, rc = >d\n", rc ); \
return (SM_RC_OK); \
} \
} while (0)
/*
* For SMAPI return codes indicating bad params, memory errors, etc.
* These are usually fatal.
*/
#define SMAPI_ERROR "Error calling SMAPI function"
/* local variables */
static applicationList *gAppList = NULL;
static char vTextVocab[] = "text"; // Dictation vocabulary
static char *vIdleVocabName = "IdleVocab";
static defVocab vIdleVocab[] = {
{ CCOMMAND, "command mode" },
{ CDICTATE, "dictate mode" },
{ CMICOFF, "microphone off" },
{ CREBUILD, "build grammar files" },
{ -1, NULL }
};
static char *vCommandVocabName = "CommandVocab";
static defVocab vCommandVocab[] = {
{ CSTOPCOMMAND, "stop command" },
{ CIDLE, "idle mode" },
{ -1, NULL }
};
static char *vDictateVocabName = "DictateVocab";
static defVocab vDictateVocab[] = {
{ CSTOPDICTATE, "stop dictation" },
{ CCORRECTION, "correction" },
{ CIDLE, "idle mode" },
{ -1, NULL }
};
struct lstrcmp // map of current grammars
{
bool operator()(const string s1, const string s2) const
{
return s1.compare(s2) < 0;
}
};
class grammar
{
public:
void *trans;
gramHdlr handler;
void *user;
grammar(gramHdlr h, void *t, void *u) {
handler = h;
trans = t;
user = u;
}
grammar() { };
};
typedef map<string, grammar, lstrcmp> gramMap;
static gramMap actvGram;
class vocab
{
public:
string name;
defVocab *trans;
vocHdlr handler;
void *user;
vocab(char *n, vocHdlr h, defVocab *t, void *u) {
name = n;
handler = h;
trans = t;
user = u;
}
vocab() { };
};
typedef list<vocab> vocList;
static vocList actvVoc;
typedef list<string> grList;
static grList appGram;
// ViaVoice information
static char userid [ 80 ] = SM_USE_CURRENT;
static char enrollid [ 80 ] = SM_USE_CURRENT;
static char taskid [ 80 ] = SM_USE_CURRENT;
static bool paragraphStart = true; // dictate support
static bool sentenceStart = true;
static stack<int> wordLengths;
static stack<SM_WORD> words;
/* functions */
/*
* moved notifier to MainWin, on the grounds that the event loop is
* a ui thing, not a voice engine thing. No gdk references in Voice.cc.
* We still need this wrapper because of the c++ idiocy which mucks up
* the idea of callbacks.
*/
static int myNotifier(int socket_handle, int (*recv_fn)(),
void * recv_data, void * client_data )
{
gMainWin->notify(socket_handle, recv_fn, recv_data, client_data);
return SM_RC_OK;
}
int loadGrammars()
{
if (gAppList != NULL) delete gAppList;
gAppList=parseFile(xvoicexml, grammarDir);
/* look for a global config */
if (gAppList == NULL)
gAppList=parseFile(DATADIR "/" PACKAGE "/xvoice.xml", grammarDir);
/*
* Some desperate measures in case the program hasn't been installed yet
*/
if (gAppList == NULL) gAppList=parseFile("xvoice.xml", grammarDir);
if (gAppList == NULL) gAppList=parseFile("../xvoice.xml", grammarDir);
if (gAppList == NULL) LogMessage(E_FATAL, "Could not find a config file.\nNo grammars will be available.");
return false;
}
/**
* clear all corrections pending
*/
static void ClearCorrections()
{
while (!wordLengths.empty())
wordLengths.pop();
while (!words.empty())
words.pop();
}
void TurnMicOn()
{
int rc;
rc = SmMicOn(SmAsynchronous);
if (rc != SM_RC_OK) {
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
}
}
void TurnMicOff()
{
int rc;
rc = SmMicOff(SmAsynchronous);
if (rc != SM_RC_OK) {
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
}
}
bool disableVocab(char *name)
{
SmDisableVocab(name, SmAsynchronous);
}
bool enableVocab(char *name)
{
SmEnableVocab(name, SmAsynchronous);
}
static SmHandler DefineVocabCB(SM_MSG reply, caddr_t client, caddr_t call_data);
bool installVocab(char *name, vocHdlr callback, defVocab *cmds,
void *user)
{
SM_MSG reply;
SM_VOCWORD *voc_words;
SM_VOCWORD **voc_ptrs;
int i, count;
int rc;
for (count = 0; cmds[count].phrase != NULL; ++count);
voc_words = (SM_VOCWORD *)malloc(count*sizeof(SM_VOCWORD));
voc_ptrs = (SM_VOCWORD **)malloc(count*sizeof(SM_VOCWORD));
/*-------------------------------------------------------------------*/
/* The SmDefineVocab call expects an array of pointers to SM_VOCWORD */
/* structures rather than the SM_VOCWORDs themself */
/*-------------------------------------------------------------------*/
for (i = 0; i < count; i++) {
dbgprintf(("vocab >s, cmd >s\n", name, cmds[i].phrase));
voc_ptrs[i] = &amt;(voc_words[i]);
voc_words[i].spelling = cmds[i].phrase;
voc_words[i].spelling_size = strlen(voc_words[i].spelling);
voc_words[i].flags = 0;
}
dbgprintf((">d commands\n",i));
/* using synchronous mode here to avoid a race condition. */
/* i'm not sure this is necessary */
rc = SmDefineVocab(name, i, voc_ptrs, &amt;reply);
if (rc != SM_RC_OK) {
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
free(voc_words);
free(voc_ptrs);
return false;
}
actvVoc.push_back(vocab(name, callback, cmds, user));
dbgprintf(("vocab count >d\n", actvVoc.size()));
DefineVocabCB (reply, (caddr_t)NULL, (caddr_t)NULL );
free(voc_words);
free(voc_ptrs);
return true;
}
static SmHandler SetCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
return (SM_RC_OK);
}
static SmHandler MicOnCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
int rc;
SmGetRc(reply, &amt;rc);
switch (rc) {
/* MicStateCB should handle these */
case SM_RC_MIC_OFF_PENDING:
case SM_RC_MIC_ON_PENDING:
case SM_RC_MIC_ALREADY_ON:
case SM_RC_OK:
break;
default:
gMainWin->micOff();
LogMessage(E_CONFIG, "Couldn't turn on microphone.\nIs it locked by another application?\n\nSMAPI error >d\n", rc);
return SM_RC_OK;
}
/*-------------------------------------------------------------------*/
/* VERY IMPORTANT - this tells the recognizer to 'go' (ie. start */
/* capturing the audio and processing it) */
/*-------------------------------------------------------------------*/
SmRecognizeNextWord(SmAsynchronous);
return (SM_RC_OK);
}
static SmHandler MicOffCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
int rc;
SmGetRc(reply, &amt;rc);
switch (rc) {
/* MicStateCB should handle these */
case SM_RC_MIC_OFF_PENDING:
case SM_RC_MIC_ON_PENDING:
case SM_RC_MIC_ALREADY_OFF:
case SM_RC_OK:
break;
default:
gMainWin->micOn();
LogMessage(E_CONFIG, "Couldn't turn off microphone.\nIs it locked by another application?\n\nSMAPI error >d\n", rc);
return SM_RC_OK;
}
}
static SmHandler MicStateCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
int rc;
unsigned long state;
SmGetMicState(reply, &amt;state);
if (state == SM_NOTIFY_MIC_ON) gMainWin->micOn();
else gMainWin->micOff();
return SM_RC_OK;
}
static SmHandler EnableVocabCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
char *vocab;
SmGetVocabName(reply, &amt;vocab);
dbgprintf((">s\n", vocab));
CheckSmRC();
gMainWin->vocab(true, vocab);
return (SM_RC_OK);
}
static SmHandler DisableVocabCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
char *vocab;
SmGetVocabName(reply, &amt;vocab);
dbgprintf((">s\n", vocab));
CheckSmRC();
gMainWin->vocab(false, vocab);
return (SM_RC_OK);
}
static SmHandler DefineGrammarCB(SM_MSG reply, caddr_t client, caddr_t caller)
{
int rc;
char *vocab;
fstring msg, filename;
SM_VOCWORD *missing;
unsigned long i, num_missing;
SmGetRc(reply, &amt;rc);
SmGetVocabName(reply, &amt;vocab);
switch (rc) {
case SM_RC_OK:
break;
/*
* XXX -- supposedly we can get both of these delivered for a single
* grammar request. This is inconvenient since we need to erase the
* actvGram element once if either or both are returned. When we start
* using externs, this may cause us to barf on erase(), or pop up
* multiple errors.
*/
case SM_RC_NOT_INVOCAB:
case SM_RC_MISSING_EXTERN:
SmGetVocWords(reply, &amt;num_missing, &amt;missing);
msg.appendf("Missing >d word(s) from '>s': >s\n",
num_missing, vocab, msg.c_str());
for (i = 0; i < num_missing; i++)
msg.appendf(">s ", missing[i].spelling);
actvGram[vocab].handler(G_FAIL, vocab, msg.c_str(), NULL);
actvGram.erase(vocab);
/* CB return codes are not documented. wtf is this -1 for? */
return (-1);
break;
case SM_RC_SERVER_FILE_OPEN_ERROR:
case SM_RC_SERVER_FILE_READ_ERROR:
msg.appendf("File error while reading grammar '>s'\n", vocab);
actvGram[vocab].handler(G_FAIL, actvGram[vocab].user,
msg.c_str(), NULL);
actvGram.erase(vocab);
return (SM_RC_OK);
break;
default:
msg.appendf(">s: >d\n", SMAPI_ERROR, rc );
actvGram[vocab].handler(G_FAIL, actvGram[vocab].user,
msg.c_str(), NULL);
actvGram.erase(vocab);
return (SM_RC_OK);
}
dbgprintf(( "Defined '>s'\n", vocab));
/* translations */
void *tr;
filename.appendf(">s/>s.fst", grammarDir, vocab);
dbgprintf(( "translation is >s\n", filename.c_str()));
if (VtLoadFSG((char*)filename.c_str(), &amt;tr)) {
msg.appendf("No translation >s\n",filename.c_str());
actvGram[vocab].handler(G_FAIL, actvGram[vocab].user, msg.c_str(),
NULL);
actvGram.erase(vocab);
SmUndefineVocab((char*)vocab, SmAsynchronous);
return SM_RC_OK;
} else {
actvGram[vocab].trans = tr;
}
/*
* XXX - We don't bother trying to clean up actvGram if
* EnableVocabCB reports an error. In that case, either
* the voice engine or XVoice is toast, anyway.
*/
actvGram[vocab].handler(G_SUCCESS, actvGram[vocab].user, NULL, NULL);
rc = SmEnableVocab(vocab, SmAsynchronous);
return (0);
}
static SmHandler FocusCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
CheckSmRC();
return (SM_RC_OK);
}
static char* getApp()
{
regex_t preg;
const char *target = gMainWin->getTarget()->name();
applicationList::iterator app;
for(app=gAppList->begin(); app!=gAppList->end(); app++) {
dbgprintf(("(>s) looking at >s\n",target, app->name));
if (regcomp(&amt;preg, app->expr, REG_EXTENDED|REG_NOSUB) == 0) {
if (regexec(&amt;preg, target, 0, NULL, 0) == 0) {
dbgprintf(("matched\n"));
return app->name;
}
}
}
return NULL;
}
static void appHandler(int ev, const char* n, const char *ph, const char *tr);
static void grammarEvent(Event&amt; ev)
{
if (ev.val.grammar.enable) {
appGram.push_back(ev.val.grammar.name);
enableCommandGrammar(ev.val.grammar.name, appHandler,
(void *)appGram.back().c_str());
} else {
disableCommandGrammar(ev.val.grammar.name);
grList::iterator gr;
for (gr=appGram.begin(); gr!=appGram.end(); gr++) {
if (!strcmp(gr->c_str(), ev.val.grammar.name)) {
appGram.erase(gr);
break;
}
}
}
}
static bool dispatch(const char *translation, Target *targ)
{
EventStream* es = parseBuff(translation);
dbgprintf(("The translation is : \">s\"\n",translation));
dbgprintf(("Length of event stream >d\n", es->size()));
if (!es) {
LogMessage(E_CONFIG, "XML error parsing \">s\"\n",translation);
delete es;
return false;
}
while (!es->empty()) {
Event ev = es->front();
if (ev.type == EVGRAM) {
dbgprintf(("grammar event >d >s\n",
ev.val.grammar.enable,
ev.val.grammar.name));
grammarEvent(ev);
es->pop_front();
} else if (targ->sendEventStream(es)) { /* lost target */
gMainWin->setTarget();
vCommand(CIDLE);
break;
}
}
delete es;
return false;
}
static void navHandler(int ev, const char *name, const char *ph,
const char *tr)
{
switch (ev) {
case G_FAIL:
LogMessage(E_CONFIG, "Couldn't activate Window Manager commands\n>s", ph);
break;
case G_SUCCESS:
break;
case G_RECO:
dispatch(tr, gMainWin->getNavTarget());
break;
default:
break;
}
}
static void appHandler(int ev, const char *name, const char *ph,
const char *tr)
{
grList::iterator gr;
switch (ev) {
case G_FAIL:
LogMessage(E_CONFIG, "Couldn't activate grammar >s\n>s\n", name, ph);
for (gr=appGram.begin(); gr!=appGram.end(); gr++) {
if (!strcmp(gr->c_str(), name)) {
appGram.erase(gr);
break;
}
}
break;
case G_SUCCESS:
break;
case G_RECO:
dispatch(tr, gMainWin->getTarget());
break;
default:
break;
}
}
/*
* This method is a callback which is invoked when speech has been recognised
* when speaking while a grammar (not dynamic vocab nor dictation) is active
*/
static SmHandler RecoPhraseCB(SM_MSG reply, caddr_t client, caddr_t caller)
{
int rc;
SM_WORD * firm;
unsigned long num_firm;
int i;
char *vocab ;
unsigned long flags;
SmGetVocabName(reply, &amt;vocab);
SmGetRc(reply, &amt;rc);
if (rc != SM_RC_OK)
{
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
/*------------------------------------------------------------------*/
/* Get the 'phrase state' - this tells (among other things) whether */
/* the phrase was accepted or rejected by the engine */
/*------------------------------------------------------------------*/
rc = SmGetPhraseState(reply, &amt;flags);
if (rc != SM_RC_OK)
{
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
/*------------------------------------------------------------------*/
/* As with a recognized command, extract the recognized words */
/* (the 'phrase') from the reply structure */
/*------------------------------------------------------------------*/
rc = SmGetFirmWords(reply, &amt;num_firm, &amt;firm);
if (rc != SM_RC_OK)
{
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
/*------------------------------------------------------------------*/
/* And go through them, making a string that has the complete */
/* phrase in it so that it can be displayed */
/*------------------------------------------------------------------*/
fstring phrase = firm [0].spelling;
for (i = 1 ; i < num_firm ; i++)
phrase.appendf( " >s", firm [i].spelling );
dbgprintf((">s\n",phrase.c_str()));
if (!(flags &amt; SM_PHRASE_ACCEPTED)) {
gMainWin->reco(phrase.c_str(), false);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
gMainWin->reco(phrase.c_str(), true);
/* dunno how to get new() to do this. it barfs on pointer types. */
char **words = (char**)malloc(sizeof(char*)*(num_firm+1));
for (i = 0 ; i < num_firm ; i++) words[i]=firm[i].spelling;
words[i]=0;
char *translation;
if (VtGetTranslation(actvGram[vocab].trans, words, &amt;translation)) {
LogMessage(E_SEVERE, "No translation found for >s\n", phrase.c_str());
free(words);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
actvGram[vocab].handler(G_RECO, actvGram[vocab].user, phrase.c_str(),
translation);
free(words);
rc = SmRecognizeNextWord(SmAsynchronous);
return (rc);
}
static SmHandler DefineVocabCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
char *vocab;
int rc;
SM_VOCWORD *missing;
unsigned long i, num_missing;
fstring msg;
SmGetRc(reply, &amt;rc);
SmGetVocabName(reply, &amt;vocab);
if (rc != SM_RC_OK) {
msg.appendf(">s: >d\n", SMAPI_ERROR, rc);
vocList::iterator voc;
for (voc=actvVoc.begin(); voc!=actvVoc.end(); voc++) {
if (!strcmp(voc->name.c_str(), vocab)) {
voc->handler(G_FAIL, voc->user, msg.c_str(), 0);
actvVoc.erase(voc);
break;
}
}
return SM_RC_OK;
}
/*-------------------------------------------------------------------*/
/* Check to see if any of the words from the vocabulary are missing */
/* from the recognizers pool(s) */
/*-------------------------------------------------------------------*/
rc = SmGetVocWords(reply, &amt; num_missing, &amt; missing );
if (num_missing) {
for (i = 0; i < num_missing; i++)
msg.appendf(">s ", missing[i].spelling);
LogMessage(E_CONFIG, "Missing >d word(s) from '>s': >s\n",
num_missing, vocab, msg.c_str());
}
return (SM_RC_OK);
}
/*
* This loads the .fsg grammar file
*/
bool enableCommandGrammar(const char *name, gramHdlr callback, void *user)
{
int rc;
fstring filename;
SM_MSG reply;
if (name == NULL || actvGram.find(name) != actvGram.end()) return false;
filename.appendf( ">s/>s.fsg", grammarDir, name );
dbgprintf (( "grammar is >s\n", filename.c_str() ));
/*
* XXX - There is a potential problem here if someone clears the grammars
* before this is enabled.
*/
actvGram[name]=grammar(callback, (void *)NULL, user);
actvGram[name].user = user;
//IMPORTANT: the define grammar callback enables the new grammar
//for us to ensure everything's ok
rc = SmDefineGrammar ((char*)name, (char*)filename.c_str(), 0, SmAsynchronous);
return true;
}
bool disableCommandGrammar(const char *name)
{
if (actvGram.find(name) == actvGram.end()) return false;
dbgprintf(("removing >s\n", name));
VtUnloadFSG(actvGram[name].trans);
actvGram.erase(name);
SmDisableVocab((char*)name, SmAsynchronous );
SmUndefineVocab((char*)name, SmAsynchronous);
return true;
}
#if 0
static SmHandler UndefineVocabCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
char *vocab;
CheckSmRC();
SmGetVocabName(reply, &amt; vocab);
actvGram.erase(vocab);
}
#endif
static SmHandler GetNextWordCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
CheckSmRC();
return (SM_RC_OK);
}
/*
* If this is too slow we should use a hash to look up
* commands.
*/
static bool matchCommand(char *word)
{
int j;
defVocab *t;
vocList::iterator voc;
for (voc=actvVoc.begin(); voc!=actvVoc.end(); voc++) {
for (t = voc->trans; t->phrase != NULL; ++t) {
if (!strcmp(t->phrase, word)) {
voc->handler(G_RECO, voc->user, t->phrase, t->val);
return true;
}
}
}
return false;
}
static bool commanding = false;
static bool dictating = false;
static void CommandState(bool enter)
{
char *name;
if (enter) {
dbgprintf(("enter\n"));
while (appGram.size() > 0) {
disableCommandGrammar(appGram.front().c_str());
appGram.pop_front();
}
name = getApp();
if (name != NULL) {
appGram.push_back(name);
enableCommandGrammar(name, appHandler, (void*)appGram.back().c_str());
if (!commanding)
enableVocab(vCommandVocabName);
commanding = true;
} else {
enter = false; /* fall through to disable */
}
}
if (!enter &amt;&amt; commanding) {
disableVocab(vCommandVocabName);
while (appGram.size() > 0) {
disableCommandGrammar(appGram.front().c_str());
appGram.pop_front();
}
commanding = false;
}
gMainWin->targState(commanding, dictating);
return;
}
static void DictateState(bool enter)
{
if (enter &amt;&amt; !dictating) {
dbgprintf(("enter\n"));
enableVocab(vTextVocab);
enableVocab(vDictateVocabName);
dictating = true;
} else if (!enter &amt;&amt; dictating) {
disableVocab(vDictateVocabName);
disableVocab(vTextVocab);
dictating = false;
}
gMainWin->targState(commanding, dictating);
}
void vCommand(vCommandType t)
{
if (t == CCOMMAND || t == CDICTATE) {
if (gMainWin->setTarget()) {
dbgprintf(("new window\n"))
DictateState(false);
CommandState(false);
}
if (gMainWin->getTarget()->name() == NULL) {
t = CIDLE;
}
}
switch (t) {
case CMICON:
TurnMicOn();
return;
case CMICOFF:
TurnMicOff();
return;
case CREBUILD:
loadGrammars();
return;
case CIDLE:
DictateState(false);
CommandState(false);
break;
case CCOMMAND:
CommandState(true);
break;
case CSTOPCOMMAND:
CommandState(false);
break;
case CDICTATE:
DictateState(true);
break;
case CSTOPDICTATE:
DictateState(false);
break;
case CCORRECTION:
{
Target *targ = gMainWin->getTarget();
dbgprintf(("correction\n"));
if (targ != NULL &amt;&amt; !wordLengths.empty())
{
int lastWordLen = wordLengths.top();
wordLengths.pop();
fstring buffer(lastWordLen, '\b');
if (gMainWin->getTarget()->sendText( buffer.c_str() )) { /* lost target */
gMainWin->setTarget();
vCommand(CIDLE);
}
}
}
break;
default:
break;
}
}
static void vocHandler(int ev, void *user, const char *ph, int val)
{
switch (ev) {
case G_FAIL:
break;
case G_SUCCESS:
break;
case G_RECO:
vCommand((enum vCommandType)val);
break;
}
}
/*
* This method is called when a word from a dynamic vocab has been recognised
*/
static SmHandler RecoWordCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
char *vocab;
int rc;
unsigned long i, num_firm;
SM_WORD *firm;
CheckSmRC();
SmGetVocabName(reply, &amt;vocab);
rc = SmGetFirmWords(reply, &amt;num_firm, &amt;firm);
for (i = 0 ; i < num_firm; i++) {
if (firm[i].spelling[0] == '\0') {
gMainWin->reco("(unknown)", true);
} else {
gMainWin->reco(firm[i].spelling, true);
if (!matchCommand(firm[i].spelling)) {
LogMessage(E_FATAL, "Recognized word we don't have a record of.\nThis should never happen\n");
}
}
}
/*-------------------------------------------------------------------*/
/* Tell the recognizer to 'go' again. It stops so that if we wanted */
/* to, we could change vocabs... */
/*-------------------------------------------------------------------*/
rc = SmRecognizeNextWord(SmAsynchronous);
return (SM_RC_OK);
}
/*
* This method is called when a chunk of text has been recognised in dictation
* mode. The method sends this text to the target application
*/
static SmHandler RecoTextCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
int rc;
int i;
unsigned long num_firm;
SM_WORD *firm;
fstring buffer;
CheckSmRC();
Target *t = gMainWin->getTarget();
if(t==NULL){
LogMessage(E_SEVERE, "Attempt to dictate with no active target!\n");
return SM_RC_OK;
}
rc = SmGetFirmWords(reply, &amt;num_firm, &amt;firm);
for (i = 0 ; i < ( int ) num_firm; i++)
{
buffer.resize(0); // no clear() function?
char* word = firm[i].spelling;
gMainWin->reco(firm[i].spelling, true);
//printf("Word: >s Tag:>d\n",firm[i].spelling, firm[i].tag);
//fist deal with special case stuff sent by the engine:
if (strcmp(word,"NEW-PARAGRAPH")==0) {
if (gMainWin->getTarget()->sendText("\r\r")) { /* lost target */
gMainWin->setTarget();
vCommand(CIDLE);
return SM_RC_OK;
}
paragraphStart=true;
continue;
} else if (strcmp(word,"NEW-LINE")==0) {
if (gMainWin->getTarget()->sendText("\r")) { /* lost target */
gMainWin->setTarget();
vCommand(CIDLE);
return SM_RC_OK;
}
sentenceStart=true;
continue;
} else if (( strlen( word ) == 1) &amt;&amt;
ispunct( word[0] ) ) {
buffer = word;
char p = word[0];
if (p == '.' || p == '?' || p == '!') {
sentenceStart = true;
}
} else {//this is an ordinary word
//if this is the start of a new paragraph, indent &amt; capitalise
if(paragraphStart) {
word[0]=toupper(word[0]);
buffer.appendf(" >s", word );
paragraphStart = false;
} else if(sentenceStart) {
// capitalize first word
word[0]=toupper(word[0]);
buffer.appendf(" >s", word);
sentenceStart = false;
} else {
//word in the middle of a sentence
buffer.appendf(" >s", word);
}
}
wordLengths.push( buffer.size());
words.push(firm[i]);
if (gMainWin->getTarget()->sendText( buffer.c_str() )) { /* lost target */
gMainWin->setTarget();
vCommand(CIDLE);
}
}
return (SM_RC_OK);
}
static SmHandler UtteranceCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
dbgprintf(("\n"));
/*-------------------------------------------------------------------*/
/* The engine has turned the mic off and processed all of the audio */
/*-------------------------------------------------------------------*/
//printf("Utterance callback \n");
//SmSet(SM_SAVE_AUDIO,true,&amt;reply);
// TurnMicOn();
//int rc=SmQuery(SM_SAVE_AUDIO,&amt;reply);
// printf("SM_SAVE_AUDIO=>d\n",rc);
return (SM_RC_OK);
}
static int initialState()
{
installVocab(vCommandVocabName, vocHandler, vCommandVocab, NULL);
installVocab(vDictateVocabName, vocHandler, vDictateVocab, NULL);
installVocab(vIdleVocabName, vocHandler, vIdleVocab, NULL);
SmEnableVocab(vIdleVocabName, SmAsynchronous);
gMainWin->initVocabs();
gMainWin->setTarget();
enableCommandGrammar("windowmanagershortcuts", navHandler, (void*)NULL);
return true;
}
/*---------------------------------------------------------------------*/
/* called after connection to the speech recognition engine */
/*---------------------------------------------------------------------*/
static SmHandler ConnectCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
CheckSmRC();
initialState();
return (SM_RC_OK);
}
static SmHandler DisconnectCB(SM_MSG reply, caddr_t client, caddr_t call_data)
{
return (SM_RC_OK);
}
bool exitVoice()
{
SM_MSG reply;
/*
* This better be synchronous, because we're about to lose
* our event loop &amt; our connect to the voice engine goes with it.
*/
SmDisconnect(0, NULL, &amt;reply);
return true;
}
//This is called almost immediately after the app launches
//it initialises the speech engine
bool initVoice(bool save)
{
static int first = true;
int rc;
int smc;
SmArg smargs [30];
static int input_id;
if (first)
{
smc = 0;
SmSetArg(smargs [smc], SmNapplicationName, PACKAGE); smc++;
SmSetArg(smargs [smc], SmNexternalNotifier, myNotifier); smc++;
SmSetArg(smargs [smc], SmNexternalNotifierData, &amt; input_id); smc++;
/*-----------------------------------------------------------------*/
/* The call to SmOpen initializes any data that's inside of libSm */
/*-----------------------------------------------------------------*/
rc = SmOpen(smc, smargs);
if (rc != SM_RC_OK) {
LogMessage(E_FATAL, ">s: >d\n", SMAPI_ERROR, rc);
}
/*-----------------------------------------------------------------*/
/* Add the callbacks to catch the messages coming back from the */
/* reco engine */
/*-----------------------------------------------------------------*/
SmAddCallback(SmNconnectCallback, ConnectCB, NULL);
SmAddCallback(SmNdisconnectCallback, DisconnectCB, NULL);
SmAddCallback(SmNsetCallback, SetCB, NULL);
SmAddCallback(SmNmicOnCallback, MicOnCB, NULL);
SmAddCallback(SmNmicOffCallback, MicOffCB, NULL);
SmAddCallback(SmNmicStateCallback, MicStateCB, NULL);
SmAddCallback(SmNenableVocabCallback, EnableVocabCB, NULL);
SmAddCallback(SmNdisableVocabCallback, DisableVocabCB, NULL);
SmAddCallback(SmNdefineVocabCallback, DefineVocabCB, NULL);
SmAddCallback(SmNdefineGrammarCallback, DefineGrammarCB, NULL);
SmAddCallback(SmNrecognizeNextWordCallback, GetNextWordCB, NULL);
SmAddCallback(SmNrecognizedWordCallback, RecoWordCB, NULL);
SmAddCallback(SmNrecognizedPhraseCallback, RecoPhraseCB, NULL);
SmAddCallback(SmNrecognizedTextCallback, RecoTextCB, NULL);
SmAddCallback(SmNutteranceCompletedCallback, UtteranceCB, NULL);
SmAddCallback(SmNfocusGrantedCallback, FocusCB, NULL);
first = false;
}
/*-----------------------------------------------------------------*/
/* Now connect to the engine (asynchronously, which means that */
/* the ConnectCB will get the results) */
/*-----------------------------------------------------------------*/
smc = 0;
SmSetArg(smargs [smc], SmNuserId, userid ); smc++;
SmSetArg(smargs [smc], SmNenrollId, enrollid ); smc++;
SmSetArg(smargs [smc], SmNtask, taskid ); smc++;
SmSetArg(smargs [smc], SmNrecognize, true ); smc++;
SmSetArg(smargs [smc], SmNoverrideLock, true ); smc++;
rc = SmConnect(smc, smargs, SmAsynchronous);
if (rc != SM_RC_OK) {
LogMessage(E_FATAL, ">s: >d\nThis probably means you did not run '. vvsetenv' prior to running >s.\nDo that and try again.\n", SMAPI_ERROR, PACKAGE, rc);
return false;
}
SM_MSG reply;
if (save) SmSet(SM_SAVE_AUDIO, true, &amt;reply);
SmSet(SM_NOTIFY_MIC_STATE, true, &amt;reply);
return true;
}
int compileGrammar(char *name)
{
int vtc;
VtArg vtargs[5];
fstring ifilename;
fstring ofilename;
#ifndef VTCOMPILE
ifilename.appendf("/usr/lib/ViaVoice/bin/vtbnfc -en -o >s/>s.fsg >s/>s.bnf",
grammarDir, name, grammarDir, name);
system(ifilename.c_str());
ofilename.appendf("/usr/lib/ViaVoice/bin/vtbnfc -tr -o >s/>s.fst >s/>s.bnf",
grammarDir, name, grammarDir, name);
system(ofilename.c_str());
#else
/*
* this code consistantly segfaults in weird ways.
* a VtCompileGrammar() bug? or something here?
*/
ifilename.appendf(">s/>s.bnf", grammarDir, name);
ofilename.appendf(">s/>s.fst", grammarDir, name);
vtc = 0;
VtSetArg(vtargs[vtc], VtNbnfFile, ifilename.c_str()); vtc++;
VtSetArg(vtargs[vtc], VtNfsgFile, ofilename.c_str()); vtc++;
VtSetArg(vtargs[vtc], VtNtrMode, 1); vtc++;
VtSetArg(vtargs[vtc], VtNfsgFlags, SM_PHRASE_ALLOW_SILENCES); vtc++;
VtCompileGrammar(vtc, vtargs);
ofilename.resize(0);
ofilename.appendf(">s/>s.fsg", grammarDir, name);
vtc = 0;
VtSetArg(vtargs[vtc], VtNbnfFile, ifilename.c_str()); vtc++;
VtSetArg(vtargs[vtc], VtNfsgFile, ofilename.c_str()); vtc++;
VtSetArg(vtargs[vtc], VtNenMode, 1); vtc++;
VtSetArg(vtargs[vtc], VtNfsgFlags, SM_PHRASE_ALLOW_SILENCES); vtc++;
VtCompileGrammar(vtc, vtargs);
#endif
return 1;
}