OpenVXI是一个用来解释VoiceXML对话标记语言的开源库,它严格支持VoiceXML2.0草案。OpenVXI提供了一个强大的基本框架让使用者构建自己的语音平台。
1. VoiceXML简介
OpenVXI是一个开源的语音XML(VoiceXML)解释器,用于构建语音应用程序。使用VoiceXML的应用系统中,不要求用户学习复杂的高级语言,就可灵活扩充新业务。而无需再与开发商联系,重新定制开发,只需要编写几个VoiceXML页面就可以实现新的业务流程。而且编制好的VoiceXML脚本可以随时随地加入到系统中,而不会影响系统的正常运行。
2. OpenVXI系统架构
OpenVXI由以下实现:
-
VXI:解析所有的VoiceXML标记,并且担任程序中的主控角色。VXI实现了所有Voice2.0规范中所需要的部分功能和大部分可选功能。
-
VXI Parser Interface: 提供了对XML DOM解析器的访问,OpenVXI中使用的是开源的Apache Xerces SAX and DOM的解析接口。
-
Internet Interface:提供了通过http://和file://的方式访问应用文档,同时也支持POSTING数据返回到应用服务器。
-
ECMAScript Interface: 提供了对ECMAScript(JavaScript)执行服务的访问。OpenVXI的实现整合了Mozilla SpiderMonkey开源引擎。
-
Logging Interface:用来报告系统操作的错误,事件以及诊断信息。OpenVXI的实现包括输出日志到文件和可选的标准输出。
实现平台必须提供以下资源接口:
-
Recognizer Interface: 提供语法管理和必须由VoiceXML指定的识别,包括动态创建和启用语法。它通过电话平台来获得呼叫输入。
-
Prompt Interface:提供完整的放音服务,能够处理静态音频文件并且提供语音合成服务。
-
Telephony Interface:提供呼叫控制服务,包括转接、挂断呼叫并提供电话时间的能力。
-
Object Interface:提供对象访问,平台可以通过对象元素访问已定义的VoiceXML语言扩展。对象能够很容易地被定义以满足指定的电话扩展、CTI系统的弹屏显示以及其他需求。
3. 介于OpenVXI开发自定义对话控制
3.1 Recognizer Interface
VXIrecInterface是OpenVXI中用于处理语音识别功能的接口。该接口提供了语音识别的核心功能,包括初始化、语法加载、识别启动与停止、结果获取等,是OpenVXI平台中实现语音交互的重要组成部分。
Recognizer接口定义如下:
typedef struct VXIrecInterface {
// 获取接口实现的版本号
VXIint32 (*GetVersion)(void);
// 获取接口实现的名称
const VXIchar* (*GetImplementationName)(void);
// 开始会话,为每个新会话调用,允许进行特定于调用的处理或资源绑定
VXIrecResult (*BeginSession)(struct VXIrecInterface *pThis, VXIMap *args);
// 结束会话,在呼叫结束时进行清理,允许进行特定于呼叫的终止或资源释放
VXIrecResult (*EndSession)(struct VXIrecInterface *pThis, VXIMap *args);
// 通过URI加载语法,非阻塞接口
VXIrecResult (*LoadGrammarURI)(struct VXIrecInterface *pThis,
const VXIMap *properties,
const VXIchar *type,
const VXIchar *uri,
const VXIMap *uriArgs,
VXIrecGrammar **gram);
// 加载一个内嵌在VoiceXML页面的语法,非阻塞接口
VXIrecResult (*LoadGrammarString)(struct VXIrecInterface *pThis,
const VXIMap *properties,
const VXIchar *type,
const VXIchar *gramDef,
VXIrecGrammar **gram);
// 激活加载的语法以进行后续识别调用
VXIrecResult (*ActivateGrammar)(struct VXIrecInterface *pThis,
const VXIMap *properties,
VXIrecGrammar *gram);
// 停用加载的语法文件以进行后续识别调用。
VXIrecResult (*DeactivateGrammar)(struct VXIrecInterface *pThis,
VXIrecGrammar *gram);
// 释放已加载的语法文件
VXIrecResult (*FreeGrammar)(struct VXIrecInterface *pThis,
VXIrecGrammar **gram);
// 使用当前激活的语法进行识别,接口会阻塞直到识别完成、按键输入
// 完成或者超时或出现错误
VXIrecResult (*Recognize)(struct VXIrecInterface *pThis,
const VXIMap *properties,
VXIrecRecognitionResult **recogResult);
// 保存录音信息
VXIrecResult (*Record)(struct VXIrecInterface *pThis,
const VXIMap *properties,
VXIrecRecordResult **recordResult);
// 获取识别结果ID匹配的语法
VXIrecResult (*GetMatchedGrammar)(struct VXIrecInterface * pThis,
const VXIchar *grammarID,
const VXIrecGrammar **gram);
// 使用VoiceXMl元素创建语法
VXIrecResult (*LoadGrammarOption)(struct VXIrecInterface *pThis,
const VXIMap *properties,
const VXIVector *gramChoices,
const VXIVector *gramValues,
const VXIVector *gramAcceptance,
const VXIbool isDTMF,
VXIrecGrammar **gram);
// 提供热词识别终止转接
VXIrecResult (*HotwordTransfer)(struct VXIrecInterface * pThis,
struct VXItelInterface * tel,
const VXIMap *properties,
const VXIchar* transferDest,
VXIrecTransferResult ** transferResult);
// 是否支持热词识别终止转接
VXIbool (*SupportsHotwordTransfer)(struct VXIrecInterface *pThis,
const VXIMap * properties,
const VXIchar * transferDest);
} VXIrecInterface;
Recognizer示例:
<form id="ASR-1">
<property name="maxspeechtime" value="10000"/>
<field name="user_input" type="voice dtmf">
<prompt bargein="true">
请说出您要查询的城市名
</prompt>
<grammar src="/grammars/voice.grxml" type="application/srgs+xml"/>
<filled>
<assign name="asr_result" expr="user_input"/>
<goto next="#LogicCompare-2"/>
</filled>
</field>
</form>
field是一个输入域,用户必须给field提供一个值,否则就不可能进行到form中的下一个元素。如上图,type表示接受的是语音输入还是按键输入。OpenVXI在执行时,执行Prompt放音并调用Recognize接口,该接口实现我们将启动识别,并阻塞在等待识别结果返回后指定filled。识别接结果会填写到field,通过user_input获取到识别结果。
3.2 Prompt Interface
VXIpromptInterface接口提供了一套用于处理语音提示的功能。这些功能包括获取版本信息、获取实现名称、开始和结束会话、播放队列中的提示、预取提示、排队提示、等待提示播放完成等。这些功能共同作用,使得系统能够高效地处理语音提示,提供良好的响应时间和低CPU及网络开销。
Prompt接口定义如下:
typedef struct VXIpromptInterface {
// 获取接口实现的版本号
VXIint32 (*GetVersion)(void);
// 获取实现的名称,要确保每个实现的名称都是唯一的
const VXIchar* (*GetImplementationName)(void);
// 为新会话进行初始化,可以设计资源绑定或运行时调用特定操作
VXIpromptResult (*BeginSession)(struct VXIpromptInterface *pThis, VXIMap *args);
// 在通话结束时进行清理,可能释放资源或执行其他终止操作
VXIpromptResult (*EndSession)(struct VXIpromptInterface *pThis, VXIMap *args);
// 开始播放队列中的提示,这是一个非阻塞操作
VXIpromptResult (*Play)(struct VXIpromptInterface *pThis);
// 播放特殊填充提示,通常用于在文档获取期间播放提示,并在获取完成后中断
VXIpromptResult (*PlayFiller)(struct VXIpromptInterface *pThis,
const VXIchar *type,
const VXIchar *src,
const VXIchar *text,
const VXIMap *properties,
VXIlong minPlayMsec);
// 预取提示,以便在需要时快速播放,这是一个非阻塞操作
VXIpromptResult (*Prefetch)(struct VXIpromptInterface *pThis,
const VXIchar *type,
const VXIchar *src, /* deprecated - NOT USED */
const VXIchar *text,
const VXIMap *properties);
// 将提示放到播放队列,直到调用Play方法后开始播放队列
VXIpromptResult (*Queue)(struct VXIpromptInterface *pThis,
const VXIchar *type,
const VXIchar *src, /* deprecated - NOT USED */
const VXIchar *text,
const VXIMap *properties);
// 等待所有已排队的提示播放完毕,这是一个阻塞操作
VXIpromptResult (*Wait)(struct VXIpromptInterface *pThis,
VXIpromptResult *playResult);
} VXIpromptInterface;
Prompt示例:
<prompt bargein="true">
<audio src="http://172.31.185.37:6036/ivr-audio.wav"/>
欢迎拨打这个演示系统
</prompt>
OpenVXI在执行Prompt时,会调用PlayFiller接口,PlayFile传参type表示文本格式,src对应audio原始的src属性,播放静态提示音时使用。text对应prompt的文本内容,将播放tts合成音。properties则携带prompt的属性,比如上例中的bargein。
3.3 Telephony Interface
VXItelInterface用于处理与电信相关的操作。这些操作包括获取版本信息、实现名称、会话管理、状态查询、断开连接、盲转接、桥转接和咨询转接等。
Telephony接口定义如下:
typedef struct VXItelInterface {
// 返回当前实现的版本号
VXIint32 (*GetVersion)(void);
// 返回实现的名称
const VXIchar* (*GetImplementationName)(void);
// 开始会话,看具体实现,可能是无用接口
VXItelResult (*BeginSession)(struct VXItelInterface *pThis, VXIMap *args);
// 结束会话,看具体实现,可能是无用接口
VXItelResult (*EndSession)(struct VXItelInterface *pThis, VXIMap *args);
// 获取当前线路的状态
VXItelResult (*GetStatus)(struct VXItelInterface *pThis, VXItelStatus *status);
// 挂断通话
VXItelResult (*Disconnect)(struct VXItelInterface *pThis, const VXIMap *namelist);
// 盲转到指定目标
VXItelResult (*TransferBlind)(struct VXItelInterface * pThis,
const VXIMap *properties,
const VXIchar *transferDestination,
VXIMap **resp);
// 桥接转,这个接口实现依赖平台的具体实现。这个接口只会阻塞到开始发起转接,不会等待被叫应答
VXItelResult (*TransferBridge)(struct VXItelInterface *pThis,
const VXIMap *properties,
const VXIchar* transferDestination,
VXIMap **resp);
// 咨询转,接口会阻塞,直到连接完成(被叫应答、被叫未应答、被叫忙活其他方式结束)
VXItelResult (*TransferConsultation)(struct VXItelInterface * pThis,
const VXIMap *properties,
const VXIchar* transferDestination,
VXIMap **resp);
} VXItelInterface;
3.4 Object Interface
VXIobjectInterface接口用于实现VoiceXML对象的功能。通过这个接口,开发者可以定义和执行VoiceXML语言扩展,从而为应用程序提供几乎任何所需的扩展功能。
该接口的目的是为VoiceXML解释器提供一个统一的机制,一边在多线程/多线路环境中执行自定义的VoiceXML对象。这些对象可以包含复杂的逻辑和功能,并且可以通过标准的接口进行调用和管理。
接口使用场景:
- 开发VoiceXML解释器的集成工具或插件。
- 需要扩展VoiceXML标准功能的应用程序。
- 需要在VoiceXML中添加自定义对象以处理特定业务逻辑的场景。
Object接口定义如下:
typedef struct VXIobjectInterface {
// 获取接口实现的版本号
VXIint32 (*GetVersion)(void);
// 获取实现的名称
const VXIchar* (*GetImplementationName)(void);
// 执行对象名称
VXIobjResult (*Execute)(struct VXIobjectInterface *pThis,
const VXIMap *properties,
const VXIMap *parameters,
VXIValue **result);
// 验证对象的正确性
VXIobjResult (*Validate)(struct VXIobjectInterface *pThis,
const VXIMap *properties,
const VXIMap *parameters);
} VXIobjectInterface;
下面是一个object示例:
<form id="query_weather">
<object name="weather_result" classid="weather" data="http://172.20.106.102:6003/weather">
<param name="location" expr="asr_result"/>
</object>
<block>
<if cond="weather_result.data==''">
<prompt bargein="false">对不起,无法查询您要的信息</prompt>
<goto next="#server_finished"/>
<else/>
<prompt bargein="false"><value expr="weather_result.data"/></prompt>
</if>
</block>
</form>
OpenVXI在执行VoiceXML的object时,会调用到上述Execute接口,可在properties中获取到object的属性(上例中的name、classid、data),parameters中获取到object的参数(如上例中的location)。执行后返回的结果通过result返回。
以上实现以上四个接口,通过接口控制freeswitch放音、启动识别就可以驱动对话流程执行。