Main Page   Namespace List   Class Hierarchy   Data Structures   File List   Namespace Members   Data Fields   Globals   Related Pages  

VoiceDev.cpp

Go to the documentation of this file.
00001 /*
00002  * VoiceDev.cpp
00003  *
00004  * Copyright 2003, MobileSpear Inc. (www.mobilespear.com). All rights reserved.
00005  * Copyright 2003, David Resnick. All rights reserved.
00006  *
00007  * See the file doc\license.txt for the terms of usage and distribution.
00008  */
00009 
00010 #include <bogotel/Portability.h>
00011 #include <bogotel/VoiceDev.h>
00012 #include <bogotel/BgtErrors.h>
00013 #include <bogotel/SignalDev.h>
00014 #include <bogotel/util.h>
00015 #include <bogotel/Timer.h>
00016 #include <bogotel/playop.h>
00017 #include <bogotel/dialop.h>
00018 #include <bogotel/getdigop.h>
00019 
00020 namespace bogotel {
00021 
00022     MAP_INT2STR CVoiceDev::s_mapHandle2Filename;
00023     std::string CVoiceDev::s_strWavFilenameBase;
00024     CProperties CVoiceDev::s_propWav2Dtmfs;
00025     const int CVoiceDev::invalidHandle = -1;
00026 
00027     CVoiceDev::CVoiceDev(CBgtRt *pBgtRt) :
00028     CDevBase(pBgtRt),
00029     m_lSCbusTimeslot(-1),
00030     m_lHandle(invalidHandle),
00031     m_pSignalDev(NULL),
00032     m_lTermMsk(0),
00033     m_pIoOp(NULL),
00034     m_pInternalIoOp(NULL),
00035     m_speakerState(speakerState::SILENT),
00036     m_uiStateId(0)
00037     {
00038     }
00039 
00040     int CVoiceDev::init(std::string strName, long lHandle, long lTimeslot)
00041     {
00042         int rc;
00043         if ((rc = CDevBase::init()) != resultSUCCESS) {
00044             return rc;
00045         }
00046 
00047         m_strName = strName;
00048         m_lHandle = lHandle;
00049         m_lSCbusTimeslot = lTimeslot;
00050         return resultSUCCESS;
00051     }
00052 
00053     int CVoiceDev::listen(CSignalDev *pSD)
00054     {
00055         if (m_pSignalDev != NULL) {
00056             return resultPOINTER;
00057         }
00058         m_pSignalDev = pSD;
00059         
00060         return resultSUCCESS;
00061     }
00062 
00063     // Starts playback of WAVs whose filehandles are stored in IOTT structure.
00064     int CVoiceDev::play(DX_IOTT *pIott, const DV_TPT *pTpt, const DX_XPB *pXpb, unsigned short usMode)
00065     {
00066         char method[] = "CVoiceDev::play";
00067         // we only work in asyncronous mode
00068         if (usMode != EV_ASYNC) {
00069             return resultFUNC_BAD_PARAMETER;
00070         }
00071 
00072         try {
00073             registerIoOp(new CPlayOp(this, pIott, pTpt));
00074         } catch (...) {
00075             log(3, "%s(): CVoiceDev::registerIoOp threw an exception", method);
00076             return resultFAIL;
00077         }
00078 
00079         return resultSUCCESS;
00080     }
00081 
00082     // Starts dialing of DTMF digits
00083     int CVoiceDev::dial(const char *szDialStr, const DX_CAP *pCAP, unsigned short usMode)
00084     {
00085         char method[] = "CVoiceDev::dial";
00086         // we only work in asyncronous mode
00087         if (usMode != EV_ASYNC) {
00088             return resultFUNC_BAD_PARAMETER;
00089         }
00090         try {
00091             registerIoOp(new CDialOp(this, szDialStr, targetType::remote));
00092         } catch (std::invalid_argument& x) {
00093             log(3, "%s(): new CDialOp threw an invalid_argument exception. %s", method,
00094                 x.what());
00095             return resultFAIL;
00096         } catch (...) {
00097             log(3, "%s(): CVoiceDev::registerIoOp threw an exception", method);
00098             return resultFAIL;
00099         }
00100 
00101         return resultSUCCESS;
00102     }
00103 
00104     // Sets up state where dtmf tones are listened for.
00105     // The number of digits waited for is specified in the TPT (termination parameter table).
00106     int CVoiceDev::getDigit(const DV_TPT *pTpt, DV_DIGIT *pDigit, unsigned short usMode)
00107     {
00108         char method[] = "CVoiceDev::getDigit";
00109 
00110         // we only work in asyncronous mode
00111         if (usMode != EV_ASYNC) {
00112             return resultFUNC_BAD_PARAMETER;
00113         }
00114 
00115         CGetDigOp *pOp;
00116         try {
00117             pOp = new CGetDigOp(this, pTpt, pDigit);
00118         } catch (std::invalid_argument& x) {
00119             log(3, "%s(): new CGetDigOp threw an invalid_argument exception. %s", method,
00120                 x.what());
00121             return resultFAIL;
00122         } catch (...) {
00123             log(3, "%s(): new CGetDigOp threw an exception", method);
00124             return resultFAIL;
00125         }
00126 
00127         try {
00128             registerIoOp(pOp);
00129         } catch (std::invalid_argument& x) {
00130             log(3, "%s(): CVoiceDev::registerIoOp threw an invalid_argument exception. %s", method,
00131                 x.what());
00132             return resultFAIL;
00133         } catch (...) {
00134             log(3, "%s(): CVoiceDev::registerIoOp threw an exception", method);
00135             return resultFAIL;
00136         }
00137 
00138         return resultSUCCESS;
00139     }
00140 
00141     // This removes an active IO operation (if any).
00142     void CVoiceDev::terminateIoOp(long lTermMsk, bool bInternalIoOp)
00143     {
00144         char method[] = "CVoiceDev::terminateIoOp";
00145 
00146         boost::recursive_mutex::scoped_lock lock(m_mtxIoOps);
00147 
00148         log(9, "%s: Entered.", method);
00149         if (bInternalIoOp) {
00150             if (m_pInternalIoOp) {
00151                 std::string strOpName = m_pInternalIoOp->toString();
00152                 m_pTimer->requestTargetDeletion(m_pInternalIoOp);
00153                 m_pInternalIoOp = NULL;
00154                 log(7, "%s: internal IO op terminated. Op: %s", method,
00155                     strOpName.c_str());
00156             }
00157         } else {
00158             if (m_pIoOp) {
00159                 std::string strOpName = m_pIoOp->toString();
00160                 sendEvent(m_pIoOp->terminationEvent(), lTermMsk);
00161                 // schedule IO operation for deletion
00162                 m_pTimer->requestTargetDeletion(m_pIoOp);
00163                 m_pIoOp = NULL;
00164                 log(7, "%s: IO op terminated. Op: %s", method,
00165                     strOpName.c_str());
00166             }
00167         }
00168         log(9, "%s: Completed.", method);
00169     }
00170 
00171     // Sets parm IO operation as current IO operation.
00172     // Terminates current IO operation (if any) first
00173     void CVoiceDev::registerIoOp(CIOOp *pIoOp, bool bInternalIoOp)
00174     {
00175         char method[] = "CVoiceDev::registerIoOp";
00176         log(9, "%s: Entered. Is internal OP? %s", method, bInternalIoOp? "TRUE" : "FALSE");
00177 
00178         boost::recursive_mutex::scoped_lock lock(m_mtxIoOps);
00179 
00180         terminateIoOp(TM_USRSTOP, bInternalIoOp);
00181         m_pTimer->registerTarget(pIoOp);
00182 
00183         if (bInternalIoOp) {
00184             m_pInternalIoOp = pIoOp;
00185         } else {
00186             m_pIoOp = pIoOp;
00187         }
00188 
00189         pIoOp->start();
00190     }
00191 
00192     int CVoiceDev::stop(unsigned short usMode)
00193     {
00194         int rc = resultSUCCESS;
00195 
00196         terminateIoOp(TM_USRSTOP);
00197 
00198         log(9, "CVoiceDev::stop() completed.");
00199 
00200         return rc;
00201     }
00202 
00203     int CVoiceDev::clearDigitBuffer()
00204     {
00205         while (! m_queueDigitValue.empty()) {
00206             m_queueDigitValue.pop();
00207             m_queueDigitType.pop();
00208         }
00209         eraseDigitHistory();
00210 
00211         log(9, "CVoiceDev::clearDigitBuffer() completed.");
00212 
00213         return resultSUCCESS;
00214     }
00215 
00216     int CVoiceDev::receiveMsg(CMsg *pMsg)
00217     {
00218         long hSignal = m_pSignalDev->getHandle();
00219         char method[] = "CVoiceDev::receiveMsg";
00220 
00221         char szType[30];
00222         pMsg->getMsgTypeString(szType, sizeof(szType));
00223         log(7, "%s(): %s type", method, szType);
00224 
00225         switch(pMsg->m_type) {
00226         case MT_PLAY_WAV: 
00227             updateSpeakerState(speakerState::WAV, pMsg->getParam(PT_WAV_FILENAME).c_str());
00228             break;
00229 
00230         case MT_PLAY_WAV_FINISHED:
00231             // terminate internal OP operation (if any)
00232             terminateIoOp(TM_USRSTOP, true);
00233             updateSpeakerState(speakerState::SILENT);
00234             break;
00235 
00236         case MT_PLAY_DTMF: 
00237             updateSpeakerState(speakerState::DTMF, pMsg->getParam(PT_DTMF).c_str()[0]);
00238             break;
00239 
00240         case MT_PLAY_DTMF_FINISHED:
00241             updateSpeakerState(speakerState::SILENT);
00242             break;
00243 
00244         default:
00245             log(3, "%s: Unknown message type: %i",method , pMsg->m_type);
00246             break;
00247         }
00248 
00249         log(9, "%s completed.", method);
00250         return resultSUCCESS;
00251     }
00252 
00253     void CVoiceDev::updateSpeakerState(speakerState state, std::string strFilename)
00254     {
00255         m_strCurrPlaybackFilename = strFilename;
00256         // see if we know that this wav file has actually a string of DTMFs
00257         if (! attemptRecognition(strFilename)) {
00258             // no DTMFs recognized -- so we just hear the wav file
00259             updateSpeakerState(state);
00260         }
00261     }
00262 
00263     void CVoiceDev::updateSpeakerState(speakerState state, char chDtmf)
00264     {
00265         if (chDtmf == '\0') {
00266             log(3, "Null as new dtmf char");
00267             return;
00268         }
00269         // append newest DTMF to internal queue, if there is room
00270         if (m_queueDigitValue.size() < DG_MAXDIGS) {
00271             m_queueDigitValue.push(chDtmf);
00272             m_queueDigitType.push(D_DTMF);
00273         }
00274         updateSpeakerState(state);
00275     }
00276 
00277     void CVoiceDev::updateSpeakerState(speakerState state)
00278     {
00279         char method[] = "CVoiceDev::updateSpeakerState";
00280         log(9, "%s entered.", method);
00281 
00282         m_speakerState = state;
00283         m_uiStateId++;
00284         switch (state) {
00285         case speakerState::WAV:
00286             log(5, "Now hearing WAV file \"%s\"", m_strCurrPlaybackFilename);
00287             break;
00288         case speakerState::DTMF:
00289             log(5, "Now hearing DTMF \"%c\"", m_queueDigitValue.back());
00290             break;
00291         case speakerState::SILENT:
00292             m_strCurrPlaybackFilename = "";
00293             log(5, "Now hearing nothing.");
00294             break;
00295         }
00296         // call base class to notify all observers of the device
00297         CSubject::notify();
00298 
00299         log(9, "%s completed.", method);
00300     }
00301 
00302     // Check if this wav filename is a recording of DTMFs
00303     // by looking in the map of known wav filenames.
00304     // If perform dial() with DTMFs recognized if any.
00305     bool CVoiceDev::attemptRecognition(std::string& strFilename)
00306     {
00307         char method[] = "CVoiceDev::attemptRecognition";
00308 
00309         std::string strDtmfs = s_propWav2Dtmfs.getProperty(strFilename);
00310         if (strDtmfs != "") {
00311 
00312             strDtmfs.append(g_util->m_propIni.getProperty("DtmfDelimiterChar", "d"));
00313 
00314             log(5, "Wav \"%s\" recognized as DTMFs \"%s\"", strFilename.c_str(), 
00315                 strDtmfs.c_str());
00316 
00317             try {
00318                 // start dial operation that will be played on the local
00319                 // voice device, as if DTMFs were heard in place of the wav
00320                 // file that was found.
00321                 if (m_pInternalIoOp != NULL) {
00322                     log(3, "%s(): m_pInternalIoOp not NULL.", method);
00323                 }
00324                 // register the IoOp as an internal operation
00325                 CIOOp *pIoOp = new CDialOp(this, strDtmfs.c_str(), targetType::local);
00326                 registerIoOp(pIoOp, true);
00327                 return true;
00328             } catch (...) {
00329                 log(3, "%s(): CVoiceDev::registerIoOp threw an exception", method);
00330                 return false;
00331             }
00332         }
00333         return false;
00334     }
00335 
00336     int CVoiceDev::sendMsg(CMsg *pMsg, int iTargetType)
00337     {
00338         char szType[30];
00339         pMsg->getMsgTypeString(szType, sizeof(szType));
00340         log(7, "CVoiceDev::sendMsg(): %s type", szType);
00341         switch (iTargetType) {
00342         case targetType::remote:
00343             return m_pTransport->sendMsg(pMsg);
00344         case targetType::local:
00345             return receiveMsg(pMsg);
00346         default:
00347             log(3, "CVoiceDev::sendMsg(): Invalid target type");
00348             return resultFUNC_BAD_PARAMETER;
00349         }
00350     }
00351 
00352     void CVoiceDev::sendPlayMsg(std::string strFilename)
00353     {
00354         stripBaseFromWavFilename(strFilename);
00355 
00356         m_pMsg->init(MT_PLAY_WAV, MC_VOICE, m_lHandle);
00357         m_pMsg->addParam(PT_WAV_FILENAME, strFilename);
00358         if (sendMsg(m_pMsg) != resultSUCCESS) {
00359             log(3, "CVoiceDev::sendPlayMsg(): sendMsg() failed");
00360         }
00361     }
00362 
00363     void CVoiceDev::sendPlayFinishedMsg()
00364     {
00365         m_pMsg->init(MT_PLAY_WAV_FINISHED, MC_VOICE, m_lHandle);
00366         if (sendMsg(m_pMsg) != resultSUCCESS) {
00367             log(3, "CVoiceDev::sendPlayFinishedMsg(): sendMsg() failed");
00368         }
00369     }
00370 
00371     void CVoiceDev::sendDtmfMsg(char chDtmf, int iTargetType)
00372     {
00373         char method[] = "CVoiceDev::sendDtmfMsg";
00374 
00375         log(9, "%s: dtmf=%c", method, chDtmf);
00376         m_pMsg->init(MT_PLAY_DTMF, MC_VOICE, m_lHandle);
00377         std::string strDtmf;
00378         strDtmf.push_back(chDtmf);
00379         m_pMsg->addParam(PT_DTMF, strDtmf);
00380         if (sendMsg(m_pMsg, iTargetType) != resultSUCCESS) {
00381             log(3, "CVoiceDev::sendDTMFMsg(): sendMsg() failed");
00382         }
00383     }
00384 
00385     void CVoiceDev::sendDtmfFinishedMsg(int iTargetType)
00386     {
00387         m_pMsg->init(MT_PLAY_DTMF_FINISHED, MC_VOICE, m_lHandle);
00388         if (sendMsg(m_pMsg, iTargetType) != resultSUCCESS) {
00389             log(3, "CVoiceDev::sendDTMFFinishedMsg(): sendMsg() failed");
00390         }
00391     }
00392 
00393     void CVoiceDev::stripBaseFromWavFilename(std::string& strFilename)
00394     {
00395         if (strFilename.find(s_strWavFilenameBase) == 0) {
00396             strFilename.erase(0, s_strWavFilenameBase.length());
00397         }
00398     }
00399 
00400     int CVoiceDev::sendEvent(long lType, long lTermMask)
00401     {
00402         // all events are termination events
00403         // so we should always set the termmask to
00404         // a new value when sending an event
00405         m_lTermMsk = lTermMask;
00406 
00407         CEvt *pEvt = new CEvt();
00408 
00409         pEvt->m_pUsrAttr = NULL;
00410         pEvt->m_lLen = 0;
00411         pEvt->m_lType = lType;
00412         pEvt->m_lVoiceDevHandle = m_lHandle;
00413         pEvt->m_lSignalDevHandle = m_pSignalDev->getHandle();
00414         pEvt->m_ulFlags = 0;
00415         pEvt->m_lCrn = INVALID_CRN;
00416         pEvt->m_iSysType = CEvt::SysType::DEVICE;
00417 
00418         int rc;
00419         if ((rc = m_pBgtRt->addEvent(pEvt)) != resultSUCCESS) {
00420             log(3, "CVoiceDev::sendEvent(): CBgtRt::addEvent failed. rc is %d", rc);
00421             return rc;
00422         }
00423         log(7, "CVoiceDev::sendEvent(): %s type", pEvt->toString().c_str());
00424         return rc;
00425     }
00426 
00427     void CVoiceDev::log(int iLevel, char *szFmt, ...)
00428     {
00429         va_list vl;
00430         char szTemp[2000];
00431 
00432         va_start(vl, szFmt);
00433         _vsnprintf(szTemp, sizeof(szTemp), szFmt, vl);
00434         va_end(vl);
00435 
00436         g_util->log(iLevel, m_pSignalDev->getHandle(), szTemp);
00437     }
00438 
00439     long CVoiceDev::getSignalHandle() 
00440     { 
00441         return m_pSignalDev->getHandle(); 
00442     }
00443 
00444     void CVoiceDev::eraseFromDigitHistory(unsigned short iToChop)
00445     {
00446         char method[] = "CVoiceDev::eraseFromDigitHistory";
00447 
00448         DEQUE_CHAR::iterator iterTillToChop = m_dequeDigitHistory.begin();
00449         for (int i = 0; i < iToChop; i++) {
00450             if (iterTillToChop == m_dequeDigitHistory.end()) {
00451                 log(3, "%s: request to remove more digits than in history. Length of history: %d."
00452                     " Number to remove: %d", method, m_dequeDigitHistory.size(),
00453                     iToChop);
00454                 break;
00455             }
00456             iterTillToChop++;
00457         }
00458         m_dequeDigitHistory.erase(m_dequeDigitHistory.begin(), iterTillToChop);
00459     }
00460 
00461     void CVoiceDev::eraseFromDigitHistory(DEQUE_CHAR::iterator iterTillToChop)
00462     {
00463         m_dequeDigitHistory.erase(m_dequeDigitHistory.begin(), iterTillToChop);
00464     }
00465 
00466     void CVoiceDev::eraseDigitHistory()
00467     {
00468         m_dequeDigitHistory.erase(m_dequeDigitHistory.begin(), m_dequeDigitHistory.end());
00469     }
00470 
00471     void CVoiceDev::getDigits(DV_DIGIT *pDigit, int iNumToGet)
00472     {
00473         char method[] = "CVoiceDev::getDigits";
00474 
00475         log(9, "%s: Entered.", method);
00476 
00477         // do some sanity checks
00478         if (iNumToGet > DG_MAXDIGS) {
00479             log(5, "%s: request to get more digits than DG_MAXDIGS. Number requested: %d.",
00480                 method, iNumToGet);
00481             iNumToGet = DG_MAXDIGS;
00482         }
00483         if (iNumToGet > static_cast<int>(m_queueDigitValue.size())) {
00484             log(5, "%s: request to get more digits than in buffer. Number requested: %d."
00485                 " Number in buffer: %d", method, iNumToGet, m_dequeDigitHistory.size());
00486             iNumToGet = m_queueDigitValue.size();
00487         }
00488 
00489         for (int i = 0; i < iNumToGet; i++) {
00490             pDigit->dg_value[i] = m_queueDigitValue.front();
00491             m_queueDigitValue.pop();
00492             pDigit->dg_type[i] = m_queueDigitType.front();
00493             m_queueDigitType.pop();
00494         }
00495         pDigit->dg_value[iNumToGet] = '\0';
00496         pDigit->dg_type[iNumToGet] = (char)DG_END;
00497     }
00498 
00499     const std::string& CVoiceDev::getEventName(long lType)
00500     {
00501         switch(lType) {
00502         case TDX_PLAY:   return eventNames[0];
00503         case TDX_GETDIG: return eventNames[1];
00504         case TDX_ERROR:  return eventNames[2];
00505         case TDX_DIAL:   return eventNames[3];
00506         default:         return eventNames[4];
00507         }
00508     }
00509 
00510     const std::string CVoiceDev::eventNames[5] = {
00511         "TDX_PLAY",
00512         "TDX_GETDIG",
00513         "TDX_ERROR",
00514         "TDX_DIAL",
00515         "Unknown"
00516     };
00517 
00518 }
00519 

Generated on Tue Aug 12 12:41:31 2003 for bogotel by doxygen 1.3. Hosted by SourceForge.net Logo