📄 vxml.cxx
字号:
PTRACE(3, "PVXML\tDocument: " << str << " = \"" << val << "\""); // assume any other scope is actually document or application documentVars.SetAt(str, val);}BOOL PVXMLSession::PlayFile(const PString & fn, PINDEX repeat, PINDEX delay, BOOL autoDelete){ if (vxmlChannel == NULL || !vxmlChannel->QueueFile(fn, repeat, delay, autoDelete)) return FALSE; AllowClearCall(); return TRUE;}BOOL PVXMLSession::PlayCommand(const PString & cmd, PINDEX repeat, PINDEX delay){ if (vxmlChannel == NULL || !vxmlChannel->QueueCommand(cmd, repeat, delay)) return FALSE; AllowClearCall(); return TRUE;}BOOL PVXMLSession::PlayData(const PBYTEArray & data, PINDEX repeat, PINDEX delay){ if (vxmlChannel == NULL || !vxmlChannel->QueueData(data, repeat, delay)) return FALSE; AllowClearCall(); return TRUE;}void PVXMLSession::GetBeepData(PBYTEArray & data, unsigned ms){ if (vxmlChannel != NULL) vxmlChannel->GetBeepData(data, ms);}BOOL PVXMLSession::PlaySilence(const PTimeInterval & timeout){ return PlaySilence((PINDEX)timeout.GetMilliSeconds());}BOOL PVXMLSession::PlaySilence(PINDEX msecs){ PBYTEArray nothing; if (vxmlChannel == NULL || !vxmlChannel->QueueData(nothing, 1, msecs)) return FALSE; AllowClearCall(); return TRUE;}BOOL PVXMLSession::PlayResource(const PURL & url, PINDEX repeat, PINDEX delay){ if (vxmlChannel == NULL || !vxmlChannel->QueueResource(url, repeat, delay)) return FALSE; AllowClearCall(); return TRUE;}BOOL PVXMLSession::LoadGrammar(PVXMLGrammar * grammar){ if (activeGrammar != NULL) { delete activeGrammar; activeGrammar = FALSE; } activeGrammar = grammar; return TRUE;}BOOL PVXMLSession::PlayText(const PString & _text, PTextToSpeech::TextType type, PINDEX repeat, PINDEX delay){ PStringArray list; BOOL useCache = !(GetVar("caching") *= "safe"); if (!ConvertTextToFilenameList(_text, type, list, useCache) || (list.GetSize() == 0)) { PTRACE(1, "PVXML\tCannot convert text to speech"); return FALSE; } PVXMLPlayableFilenameList * playable = new PVXMLPlayableFilenameList; if (!playable->Open(*vxmlChannel, list, delay, repeat, !useCache)) { delete playable; PTRACE(1, "PVXML\tCannot create playable for filename list"); return FALSE; } return vxmlChannel->QueuePlayable(playable);}BOOL PVXMLSession::ConvertTextToFilenameList(const PString & _text, PTextToSpeech::TextType type, PStringArray & filenameList, BOOL useCache){ PString prefix = psprintf("tts%i", type); PStringArray lines = _text.Trim().Lines(); for (PINDEX i = 0; i < lines.GetSize(); i++) { PString text = lines[i].Trim(); if (text.IsEmpty()) continue; BOOL spoken = FALSE; PFilePath dataFn; // see if we have converted this text before PString contentType; if (useCache) spoken = PVXMLCache::GetResourceCache().Get(prefix, text, "wav", contentType, dataFn); // if not cached, then use the text to speech converter if (!spoken) { PFilePath tmpfname; if (textToSpeech != NULL) { tmpfname = PVXMLCache::GetResourceCache().GetRandomFilename("tts", "wav"); if (!textToSpeech->OpenFile(tmpfname)) { PTRACE(2, "PVXML\tcannot open file " << tmpfname); } else { spoken = textToSpeech->Speak(text, type); if (!textToSpeech->Close()) { PTRACE(2, "PVXML\tcannot close TTS engine"); } } textToSpeech->Close(); if (useCache) PVXMLCache::GetResourceCache().Put(prefix, text, "wav", contentType, tmpfname, dataFn); else dataFn = tmpfname; } } if (!spoken) { PTRACE(2, "PVXML\tcannot speak text using TTS engine"); } else filenameList.AppendString(dataFn); } return filenameList.GetSize() > 0;}void PVXMLSession::SetPause(BOOL _pause){ if (vxmlChannel != NULL) vxmlChannel->SetPause(_pause);}BOOL PVXMLSession::IsPlaying() const{ return (vxmlChannel != NULL) && vxmlChannel->IsPlaying();}BOOL PVXMLSession::StartRecording(const PFilePath & /*_recordFn*/, BOOL /*_recordDTMFTerm*/, const PTimeInterval & /*_recordMaxTime*/, const PTimeInterval & /*_recordFinalSilence*/){ /* recording = TRUE; recordFn = _recordFn; recordDTMFTerm = _recordDTMFTerm; recordMaxTime = _recordMaxTime; recordFinalSilence = _recordFinalSilence; if (incomingChannel != NULL) { PXMLElement* element = (PXMLElement*) currentNode; if ( element->HasAttribute("name")) { PString chanName = element->GetAttribute("name"); incomingChannel->SetName(chanName); } return incomingChannel->StartRecording(recordFn, (unsigned )recordFinalSilence.GetMilliSeconds()); } */ return FALSE;}void PVXMLSession::RecordEnd(){ if (recording) recordSync.Signal();}BOOL PVXMLSession::EndRecording(){ if (recording) { recording = FALSE; if (vxmlChannel != NULL) return vxmlChannel->EndRecording(); } return FALSE;}BOOL PVXMLSession::IsRecording() const{ return (vxmlChannel != NULL) && vxmlChannel->IsRecording();}PWAVFile * PVXMLSession::CreateWAVFile(const PFilePath & fn, PFile::OpenMode mode, int opts, unsigned fmt){ if (!fn.IsEmpty()) return new PWAVFile(fn, mode, opts, fmt); return new PWAVFile(mode, opts, fmt); }void PVXMLSession::AllowClearCall(){ allowFinish = TRUE;}BOOL PVXMLSession::TraverseAudio(){ if (!currentNode->IsElement()) { PlayText(((PXMLData *)currentNode)->GetString()); } else { PXMLElement * element = (PXMLElement *)currentNode; if (element->GetName() *= "value") { PString className = element->GetAttribute("class"); PString value = EvaluateExpr(element->GetAttribute("expr")); SayAs(className, value); } else if (element->GetName() *= "sayas") { PString className = element->GetAttribute("class"); PXMLObject * object = element->GetElement(); if (!object->IsElement()) { PString text = ((PXMLData *)object)->GetString(); SayAs(className, text); } } else if (element->GetName() *= "break") { // msecs is VXML 1.0 if (element->HasAttribute("msecs")) PlaySilence(element->GetAttribute("msecs").AsInteger()); // time is VXML 2.0 else if (element->HasAttribute("time")) { PTimeInterval time = StringToTime(element->GetAttribute("time")); PlaySilence(time); } else if (element->HasAttribute("size")) { PString size = element->GetAttribute("size"); if (size *= "none") ; else if (size *= "small") PlaySilence(SMALL_BREAK_MSECS); else if (size *= "large") PlaySilence(LARGE_BREAK_MSECS); else PlaySilence(MEDIUM_BREAK_MSECS); } // default to medium pause else { PlaySilence(MEDIUM_BREAK_MSECS); } } else if (element->GetName() *= "audio") { BOOL loaded = FALSE; if (element->HasAttribute("src")) { PString str = element->GetAttribute("src").Trim(); if (!str.IsEmpty() && (str[0] == '|')) { loaded = TRUE; PlayCommand(str.Mid(1)); } else { // get a normalised name for the resource PFilePath fn; PURL url = NormaliseResourceName(str); // load the resource from the cache PString contentType; BOOL useCache = !(GetVar("caching") *= "safe") && !(element->GetAttribute("caching") *= "safe"); if (RetreiveResource(url, contentType, fn, useCache)) { PWAVFile * wavFile = vxmlChannel->CreateWAVFile(fn); if (wavFile == NULL) PTRACE(3, "PVXML\tCannot create audio file " + fn); else if (!wavFile->IsOpen()) delete wavFile; else { loaded = TRUE; PlayFile(fn, 0, 0, !useCache); // make sure we delete the file if not cacheing } } } if (loaded) { // skip to the next node if (element->HasSubObjects()) currentNode = element->GetElement(element->GetSize() - 1); } } } else PTRACE(3, "PVXML\tUnknown audio tag " << element->GetName() << " encountered"); } return TRUE;}BOOL PVXMLSession::TraverseGoto() // <goto>{ PAssert(currentNode != NULL, "ProcessGotoElement(): Expected valid node"); if (currentNode == NULL) return FALSE; // LATER: handle expr, expritem, fetchaudio, fetchhint, fetchtimeout, maxage, maxstale PAssert(currentNode->IsElement(), "ProcessGotoElement(): Expected element"); // nextitem PString nextitem = ((PXMLElement*)currentNode)->GetAttribute("nextitem"); if (!nextitem.IsEmpty()) { // LATER: Take out the optional # currentForm = FindForm(nextitem); currentNode = currentForm; if (currentForm == NULL) { // LATER: throw "error.semantic" or "error.badfetch" -- lookup which return FALSE; } return TRUE; } // next PString next = ((PXMLElement*)currentNode)->GetAttribute("next"); // LATER: fixup filename to prepend path if (!next.IsEmpty()) { if (next[0] == '#') { next = next.Right( next.GetLength() -1 ); currentForm = FindForm(next); currentNode = currentForm; // LATER: throw "error.semantic" or "error.badfetch" -- lookup which return currentForm != NULL; } else { PURL url = NormaliseResourceName(next); return LoadURL(url) && (currentForm != NULL); } } return FALSE;}BOOL PVXMLSession::TraverseGrammar() // <grammar>{ // LATER: A bunch of work to do here! // For now we only support the builtin digits type and do not parse any grammars. // NOTE: For now we will process both <grammar> and <field> here. // NOTE: Later there needs to be a check for <grammar> which will pull // out the text and process a grammar like '1 | 2' // Right now we only support one active grammar. if (activeGrammar != NULL) { PTRACE(2, "PVXML\tWarning: can only process one grammar at a time, ignoring previous grammar"); delete activeGrammar; activeGrammar = NULL; } PVXMLGrammar * newGrammar = NULL; // Is this a built-in type? PString type = ((PXMLElement*)currentNode)->GetAttribute("type"); if (!type.IsEmpty()) { PStringArray tokens = type.Tokenise("?;", TRUE); PString builtintype; if (tokens.GetSize() > 0) builtintype = tokens[0]; if (builtintype *= "digits") { PINDEX minDigits(1); PINDEX maxDigits(100); // look at each parameter for (PINDEX i(1); i < tokens.GetSize(); i++) { PStringArray params = tokens[i].Tokenise("=", TRUE); if (params.GetSize() == 2) { if (params[0] *= "minlength") { minDigits = params[1].AsInteger(); } else if (params[0] *= "maxlength") { maxDigits = params[1].AsInteger(); } else if (params[0] *= "length") { minDigits = maxDigits = params[1].AsInteger(); } } else { // Invalid parameter skipped // LATER: throw 'error.semantic' } } newGrammar = new PVXMLDigitsGrammar((PXMLElement*)currentNode, minDigits, maxDigits, ""); } else { // LATER: throw 'error.unsupported' return FALSE; } } if (newGrammar != NULL) return LoadGrammar(newGrammar); return TRUE;}// Finds the proper event hander for 'noinput', 'filled', 'nomatch' and 'error'// by searching the scope hiearchy from the current fromPXMLElement * PVXMLSession::FindHandler(const PString & event){ PAssert(currentNode->IsElement(), "Expected 'PXMLElement' in PVXMLSession::FindHandler"); PXMLElement * tmp = (PXMLElement *)currentNode; PXMLElement * handler = NULL; // Look in all the way up the tree for a handler either explicitly or in a catch while (tmp != NULL) { // Check for an explicit hander - i.e. <error>, <filled>, <noinput>, <nomatch>, <help> if ((handler = tmp->GetElement(event)) != NULL) return handler; // Check for a <catch> if ((handler = tmp->GetElement("catch")) != NULL) { PString strCond = handler->GetAttribute("cond"); if (strCond.Find(event)) return handler; } tmp = tmp->GetParent(); } return NULL;}void PVXMLSession::SayAs(const PString & className, const PString & _text){ PString text = _text.Trim(); if (!text.IsEmpty()) { PTextToSpeech::TextType type = PTextToSpeech::Literal; if (className *= "digits") type = PTextToSpeech::Digits; else if (className *= "literal") type = PTextToSpeech::Literal; else if (className *= "number") type = PTextToSpeech::Number; else if (className *= "currency") type = PTextToSpeech::Currency; else if (className *= "time") type = PTextToSpeech::Time; else if (className *= "date") type = PTextToSpeech::Date;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -