📄 promptmanager.cpp
字号:
/****************License************************************************
*
* Copyright 2000-2003. ScanSoft, Inc.
*
* Use of this software is subject to notices and obligations set forth
* in the SpeechWorks Public License - Software Version 1.2 which is
* included with this software.
*
* ScanSoft is a registered trademark of ScanSoft, Inc., and OpenSpeech,
* SpeechWorks and the SpeechWorks logo are registered trademarks or
* trademarks of SpeechWorks International, Inc. in the United States
* and other countries.
*
***********************************************************************/
#include <vxibuildopts.h>
#if P_VXI
#include "PromptManager.hpp"
#include "SimpleLogger.hpp" // for SimpleLogger
#include "vxi/VXIprompt.h" // for VXIpromptInterface
#include "VXML.h" // for node names
#include "DocumentModel.hpp" // for VXMLNode, VXMLElement
#include "CommonExceptions.hpp" // for InterpreterEvent, OutOfMemory
#include "PropertyList.hpp" // for PropertyList
#include <sstream>
#include <iostream>
#ifdef _MSC_VER
#pragma warning(disable:4061)
#endif
static VXIchar SPACE = ' ';
//#############################################################################
bool static ConvertValueToString(const VXIValue * value, vxistring & source)
{
if (value == NULL) return false;
switch (VXIValueGetType(value)) {
case VALUE_BOOLEAN:
{
std::basic_stringstream<VXIchar> attrStream;
if(VXIBooleanValue(reinterpret_cast<const VXIBoolean *>(value)) == TRUE)
attrStream << L"true";
else
attrStream << L"false";
source = attrStream.str();
return true;
}
case VALUE_INTEGER:
{
std::basic_stringstream<VXIchar> attrStream;
attrStream << VXIIntegerValue(reinterpret_cast<const VXIInteger *>(value));
source = attrStream.str();
return true;
}
case VALUE_FLOAT:
{
std::basic_stringstream<VXIchar> attrStream;
attrStream << VXIFloatValue(reinterpret_cast<const VXIFloat *>(value));
source = attrStream.str();
return true;
}
case VALUE_STRING:
source = VXIStringCStr(reinterpret_cast<const VXIString *>(value));
return true;
default:
return false;
}
}
inline void AddSSMLAttribute(const VXMLElement & elem, VXMLAttributeType attr,
const VXIchar * const defaultVal,
const VXIchar * const name, vxistring & sofar)
{
// Get attribute or use the default value.
vxistring attrString;
if (!elem.GetAttribute(attr, attrString) || attrString.empty()) {
if (defaultVal == NULL) return;
attrString = defaultVal;
}
sofar += SPACE;
sofar += name;
sofar += L"=\"";
// We need to escape three characters: (",<,&) -> (", <, &)
vxistring::size_type pos = 0;
while ((pos = attrString.find_first_of(L"\"<&", pos)) != vxistring::npos) {
switch (attrString[pos]) {
case '\"': attrString.replace(pos, 1, L"""); ++pos; break;
case '<': attrString.replace(pos, 1, L"<"); ++pos; break;
case '&': attrString.replace(pos, 1, L"&"); ++pos; break;
}
};
sofar += attrString;
sofar += L"\"";
}
inline void AppendTextSegment(vxistring & sofar, const vxistring & addition)
{
if (addition.empty()) return;
vxistring::size_type pos = sofar.length();
sofar += addition;
// We need to escape two characters: (<,&) -> (<, &)
// NOTE: It starts from the old length and operates only on the addition.
while ((pos = sofar.find_first_of(L"<&", pos)) != vxistring::npos) {
switch (sofar[pos]) {
case '<': sofar.replace(pos, 1, L"<"); ++pos; break;
case '&': sofar.replace(pos, 1, L"&"); ++pos; break;
}
}
}
//#############################################################################
////////
// About prompts:
//
// Prompts may be defined in two places within VXML documents.
// 1) form items - menu, field, initial, record, transfer, subdialog, object
// 2) executable content - block, catch, filled
//
// The <enumerate> element only is valid with parent menu or fields having one
// or more option elements. Otherwise an error.semantic results.
//
// From the VXML working drafts, barge-in may be controlled on a prompt by
// prompt basis. There are several unresolved issues with this request.
// Instead this implements a different tactic.
void PromptManager::ThrowEventIfError(VXIpromptResult rc)
{
switch( rc )
{
case VXIprompt_RESULT_SUCCESS:
break;
case VXIprompt_RESULT_TTS_BAD_LANGUAGE:
throw VXIException::InterpreterEvent(EV_UNSUPPORT_LANGUAGE);
case VXIprompt_RESULT_FETCH_TIMEOUT:
case VXIprompt_RESULT_FETCH_ERROR:
case VXIprompt_RESULT_NON_FATAL_ERROR:
throw VXIException::InterpreterEvent(EV_ERROR_BADFETCH);
case VXIprompt_RESULT_HW_BAD_TYPE:
throw VXIException::InterpreterEvent(EV_UNSUPPORT_FORMAT);
case VXIprompt_RESULT_TTS_ACCESS_ERROR:
case VXIprompt_RESULT_NO_RESOURCE:
throw VXIException::InterpreterEvent(EV_ERROR_NORESOURCE);
case VXIprompt_RESULT_NO_AUTHORIZATION:
throw VXIException::InterpreterEvent(EV_ERROR_NOAUTHORIZ);
case VXIprompt_RESULT_BAD_SAYAS_CLASS:
case VXIprompt_RESULT_TTS_BAD_DOCUMENT:
case VXIprompt_RESULT_TTS_SYNTAX_ERROR:
case VXIprompt_RESULT_TTS_ERROR:
default:
throw VXIException::InterpreterEvent(EV_ERROR_SEMANTIC);
}
}
void PromptManager::WaitAndCheckError()
{
VXIpromptResult rc;
prompt->Wait(prompt, &rc);
ThrowEventIfError(rc);
}
bool PromptManager::ConnectResources(SimpleLogger * l, VXIpromptInterface * p)
{
if (l == NULL || p == NULL) return false;
log = l;
prompt = p;
enabledSegmentInQueue = false;
playedBargeinDisabledPrompt = false;
timeout = -1;
return true;
}
void PromptManager::PlayFiller(const vxistring & src,
const VXIMapHolder & properties)
{
log->LogDiagnostic(2, L"PromptManager::PlayFiller()");
if (src.empty()) return;
const VXIMap * propMap = properties.GetValue();
// Get fetchaudiominimum value
VXIint minPlayMsec = 0;
const VXIValue * val = (VXIMapGetProperty(propMap, L"fetchaudiominimum"));
if( val && VXIValueGetType(val) == VALUE_STRING ) {
const VXIchar* fmin =
VXIStringCStr(reinterpret_cast<const VXIString*>(val));
if( fmin )
PropertyList::ConvertTimeToMilliseconds(*log, fmin, minPlayMsec);
}
prompt->PlayFiller(prompt, NULL, src.c_str(), NULL, propMap, minPlayMsec);
}
void PromptManager::Play(bool *playedBDPrompt)
{
log->LogDiagnostic(2, L"PromptManager::Play()");
*playedBDPrompt = playedBargeinDisabledPrompt;
//VXIpromptResult temp;
//prompt->Wait(prompt, &temp);
WaitAndCheckError();
prompt->Play(prompt);
enabledSegmentInQueue = false;
playedBargeinDisabledPrompt = false;
timeout = -1;
}
void PromptManager::PlayAll()
{
log->LogDiagnostic(2, L"PromptManager::PlayAll()");
prompt->Play(prompt);
//VXIpromptResult temp;
//prompt->Wait(prompt, &temp);
WaitAndCheckError();
enabledSegmentInQueue = false;
playedBargeinDisabledPrompt = false;
timeout = -1;
}
int PromptManager::GetMillisecondTimeout() const
{
return timeout;
}
void PromptManager::Queue(const VXMLElement & elem, const VXMLElement & ref,
const PropertyList & propertyList,
PromptTranslator & translator)
{
log->LogDiagnostic(2, L"PromptManager::Queue()");
// (1) Find <field> or <menu> associated with this prompt, if any. This is
// used by the <enumerate> element.
VXMLElement item = ref;
while (item != 0) {
VXMLElementType type = item.GetName();
if (type == NODE_FIELD || type == NODE_MENU) break;
item = item.GetParent();
}
// (2) Get properties.
VXIMapHolder properties;
if (properties.GetValue() == NULL) throw VXIException::OutOfMemory();
// (2.1) Flatten the property list.
propertyList.GetProperties(properties);
// (2.2) Add the URI information.
propertyList.GetFetchobjBase(properties);
// (3) Handle <prompt> elements
// (3.1) Update timeout if specified.
vxistring timeoutString;
if (elem.GetAttribute(ATTRIBUTE_TIMEOUT, timeoutString) == true)
PropertyList::ConvertTimeToMilliseconds(*log, timeoutString, timeout);
// (3.2) Get bargein setting.
PromptManager::BargeIn bargein = PromptManager::ENABLED;
const VXIchar * j = propertyList.GetProperty(PROP_BARGEIN);
if (j != NULL && vxistring(L"false") == j)
bargein = PromptManager::DISABLED;
vxistring bargeinAttr;
if (elem.GetAttribute(ATTRIBUTE_BARGEIN, bargeinAttr) == true) {
bargein = PromptManager::ENABLED;
if (bargeinAttr == L"false") bargein = PromptManager::DISABLED;
// override default bargein attribute
VXIMapSetProperty(properties.GetValue(), PROP_BARGEIN,
(VXIValue*)VXIStringCreate(bargeinAttr.c_str()));
}
vxistring bargeinType;
if (elem.GetAttribute(ATTRIBUTE_BARGEINTYPE, bargeinType) == true) {
// override default bargeintype attribute
VXIMapSetProperty(properties.GetValue(), PROP_BARGEINTYPE,
(VXIValue*)VXIStringCreate(bargeinType.c_str()));
}
// (4) Build prompt, recursively handling the contents.
vxistring content;
for (VXMLNodeIterator it(elem); it; ++it)
ProcessSegments(*it, item, propertyList, translator,
bargein, properties, content);
if (content.empty()) return;
// (5) Add a new segment.
AddSegment(SEGMENT_SSML, content, properties, bargein);
}
void PromptManager::Queue(const vxistring & uri)
{
if (uri.empty()) return;
AddSegment(PromptManager::SEGMENT_AUDIO, uri, VXIMapHolder(),
PromptManager::UNSPECIFIED);
}
bool PromptManager::AddSegment(PromptManager::SegmentType type,
const vxistring & data,
const VXIMapHolder & properties,
PromptManager::BargeIn bargein,
bool throwExceptions)
{
const VXIMap * propMap = properties.GetValue();
VXIpromptResult result;
// Handle the easy case.
switch (type) {
case PromptManager::SEGMENT_AUDIO:
result = prompt->Queue(prompt, NULL, data.c_str(), NULL, propMap);
break;
case PromptManager::SEGMENT_SSML:
result = prompt->Queue(prompt, VXI_MIME_SSML, NULL, data.c_str(), propMap);
break;
}
if (throwExceptions) {
ThrowEventIfError(result);
}
switch (bargein) {
case PromptManager::DISABLED:
if (!enabledSegmentInQueue) {
prompt->Play(prompt);
playedBargeinDisabledPrompt = true;
}
break;
case PromptManager::ENABLED:
enabledSegmentInQueue = true;
break;
case PromptManager::UNSPECIFIED:
break;
}
return result == VXIprompt_RESULT_SUCCESS;
}
void PromptManager::ProcessSegments(const VXMLNode & node,
const VXMLElement & item,
const PropertyList & propertyList,
PromptTranslator & translator,
PromptManager::BargeIn bargein,
VXIMapHolder & props,
vxistring & sofar)
{
// (1) Handle bare content.
switch (node.GetType()) {
case VXMLNode::Type_VXMLContent:
{
const VXMLContent & content = reinterpret_cast<const VXMLContent &>(node);
sofar += content.GetValue();
return;
}
case VXMLNode::Type_VXMLElement:
break;
default: // This should never happen.
log->LogError(999, SimpleLogger::MESSAGE,
L"unexpected type in PromptManager::ProcessSegments");
throw VXIException::Fatal();
}
// (2) Retrieve fetch properties (if we haven't already)
const VXMLElement & elem = reinterpret_cast<const VXMLElement &>(node);
vxistring attr;
switch (elem.GetName()) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -