Asterisk
To use CPA on Asterisk, you must make of use the Asterisk-UniMRCP modules (see our Asterisk documentation article for details). CPA does not work with the older, deprecated "connector bridge" module (res_speech_lumenvox.so).
Once UniMRCP is installed and working within Asterisk, CPA and tone detection works just like any other speech Asterisk application.
As mentioned in our Asterisk documentation, there are two methods for performing ASR within Asterisk: MRCPRecog() and SpeechBackground(). Depending on which method you prefer to use, your code will look a little different.
CPA with SpeechBackground
Here is a very simple dialplan to perform CPA with SpeechBackground. This assumes you have a CPA grammar in /etc/grammars/ called cpa.grxml:
[simple-cpa-example]
exten => s,1,Answer()
exten => s,n,SpeechCreate()
exten => s,n,SpeechLoadGrammar(cpa,/etc/grammars/cpa.
grxml)
exten => s,n,SpeechActivateGrammar(cpa)
exten => s,n,SpeechBackground(silence,10)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "Human Residence"]?Live)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "Human Business"]?Live)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "Unknown Silence"]?Machine)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "Unknown Speech"]?Machine)
exten => s,n(Live),Playback(you-are-a-human)
exten => s,n,Hangup
exten => s,n(Machine),Playback(you-are-a-machine)
exten => s,n,Hangup
As you can see, this application loads the CPA grammar, activates it, calls SpeechBackground, checks the result, and performs a goto based on the return.
Tone detection with SpeechBackground
Here is a very simple dialplan to perform tone detection with SpeechBackground. This assumes you have a tone detection grammar in /etc/grammars/ called tone_detect.grxml:
[simple-amd-example]
exten => s,1,Answer()
exten => s,n,SpeechCreate()
exten => s,n,SpeechLoadGrammar(tone_detect,/etc/grammars/tone_detect.
grxml)
exten => s,n,SpeechActivateGrammar(tone_detect)
exten => s,n,SpeechBackground(greeting,60)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "AMD"]?Machine)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "FAX"]?Fax)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "SIT"]?SIT)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "BUSY"]?BUSY)
exten => s,n(Machine),Playback(you-are-a-machine)
exten => s,n,Hangup
exten => s,n(Fax),Hangup
exten => s,n(SIT),Hangup
exten => s,n(BUSY),Hangup
The application loads the grammar, activates it, and calls SpeechBackground. It then evaluates the return and plays a message out if a tone is detected and hangs up if a fax, SIT or busy tone is detected.
CPA and Tone Detection with MRCPRecog
Using CPA with MRCPRecog is practically a single line:
[MRCPRecog CPA]
exten => s,n,MRCPRecog(/etc/grammars/cpa.
grxml)
The application would simply need to evaluate the contents of the ${RECOG_RESULT} channel variable, as described in our documentation on MRCPRecog.
The tone detection example looks similar:
exten => s,n,MRCPRecog(/etc/grammars/tone_detection.
grxml)
However, as we note in our introduction to Asterisk speech development article, the MRCPRecog() application currently returns an NLSML-formatted XML string that the application must parse. For that reason, it may be easier for CPA developers to favor SpeechBackground until that issue is resolved.
Mixing Tone Detection and CPA
There are several ways to mix tone detection and CPA together. Here is a sample with comments included:
[simple-cpa-plus-tone-detection-example]
exten => s,n,SpeechCreate()
; Load our grammars
exten => s,n,SpeechLoadGrammar(cpa,/etc/grammars/cpa.gram)
exten => s,n,SpeechLoadGrammar(tone_detect,/etc/grammars/tone_detect.
grxml)
; We are going to use both for the initial prompt
exten => s,n,SpeechActivateGrammar(cpa)
exten => s,n,SpeechActivateGrammar(tone_detect)
; We want to play silence at first as we listen to the initial greeting
exten => s,n,SpeechBackground(silence,10)
; SpeechBackground returns and we deactivate both grammars
exten => s,n,SpeechDeactivateGrammar(cpa)
exten => s,n,SpeechDeactivateGrammar(tone_detect)
; We now evaluate the result
; If we got a tone, act accordingly
exten => s,n(toneDetected),GotoIf($[ ${SPEECH_TEXT(0)} = "AMD"]?AMD)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "FAX"]?Fax)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "SIT"]?SIT)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "BUSY"]?BUSY)
; If it's a live human, we act accordingly
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "HUMAN RESIDENCE"]?Live)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "HUMAN BUSINESS"]?Live)
; If it's not a live human, we're going to wait for a tone to be detected
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "UNKOWN SILENCE"]?WaitForMachine)
exten => s,n,GotoIf($[ ${SPEECH_TEXT(0)} = "UNKOWN SPEECH"]?WaitForMachine)
; This is our WaitForMachine area. Here we're just going to wait for a tone while playing silence
exten => s,n,SpeechActivateGrammar(tone_detect)
exten => s,n,SpeechBackground(silence,120)
exten => s,n,SpeechDeactivateGrammar(tone_detect)
exten => s,n,Goto(toneDetected)
; Here's where we would do something for a human
; Start writing your custom application here:
exten => s,n(Live),Playback(you-are-a-human)
exten => s,n,Hangup
; Here we can leave a customized message for an answering machine
exten => s,n(AMD),Playback(you-are-a-machine)
exten => s,n,Hangup
; Just hangup on a Fax, SIT or Busy
exten => s,n(Fax),Hangup
exten => s,n(SIT),Hangup
exten => s,n(BUSY),Hangup
Avaya Aura Experience Portal
LumenVox and Avaya have tested CPA on Avaya Aura Experience Portal versions 6 and later. CPA and tone detection work as described. You can follow the instructions in the VXML Platforms section below, for sample code that has been tested on AAEP.
VXML Platforms
Simple CPA VXML
The following VoiceXML code should work on any VXML platform:
<
vxml version="2.0" xmlns="
http://www.w3.org/2001/vxml"
xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd">
<form id="main">
<property name="completetimeout" value="1200ms"/>
<property name="incompletetimeout" value="10s"/>
<property name="maxspeechtimeout" value="10s"/>
<property name="timeout" value="10s"/>
<field name="CPAValue">
<grammar src="cpa.
grxml" type="application/srgs+xml"/>
</field>
<filled>
<prompt>
We detected a
<value expr="application.lastresult$.interpretation"/>
</prompt>
<exit />
</filled>
</form>
</vxml>
This VXML relies on the following grammar being stored on the same server as the VXML file:
<?
xml version='1.0'?>
<!-- ********************************************************************** -->
<!-- This is a special grammar used to switch decode modes to Call Progress
Analysis mode -->
<grammar
xml:lang="en-US" version="1.0" root="root" mode="voice"
xmlns="
http://www.w3.org/2001/06/grammar"
tag-format="semantics/1.0.2006">
<!-- ************************************************************** -->
<!-- This value changes the detection mode to Call Progress Analysis -->
<!-- ************************************************************** -->
<meta name="STREAM|DETECTION_MODE" content="CPA"/>
<meta name="STREAM|BARGE_IN_TIMEOUT" content="10000"/>
<meta name="STREAM|END_OF_SPEECH_TIMEOUT" content="10000"/>
<meta name="STREAM|VAD_EOS_DELAY" content="1200"/>
<meta name="STREAM|VAD_STREAM_INIT_DELAY" content="0"/>
<!-- ************************************************************** -->
<!-- These values may be changed to customize CPA timeouts for
certain states -->
<!-- ************************************************************** -->
<meta name="STREAM|CPA_HUMAN_RESIDENCE_TIME" content="1800"/>
<meta name="STREAM|CPA_HUMAN_BUSINESS_TIME" content="3000"/>
<meta name="STREAM|CPA_UNKNOWN_SILENCE_TIMEOUT" content="5000"/>
<!-- ************************************************************** -->
<!-- These values may be changed to modify the semantic
interpretations returned -->
<!-- ************************************************************** -->
<meta name="HUMAN_RESIDENCE_CUSTOM_INPUT_TEXT"
content="HUMAN RESIDENCE"/>
<meta name="HUMAN_BUSINESS_CUSTOM_INPUT_TEXT"
content="HUMAN BUSINESS"/>
<meta name="UNKNOWN_SPEECH_CUSTOM_INPUT_TEXT"
content="UNKNOWN SPEECH"/>
<meta name="UNKNOWN_SILENCE_CUSTOM_INPUT_TEXT"
content="UNKNOWN SILENCE"/>
<rule id="root" scope="public">
<one-of>
<item>HUMAN RESIDENCE<tag>out="HUMAN RESIDENCE"</tag>
</item>
<item>HUMAN BUSINESS<tag>out="HUMAN BUSINESS"</tag>
</item>
<item>UNKNOWN SPEECH<tag>out="UNKNOWN SPEECH"</tag>
</item>
<item>UNKNOWN SILENCE<tag>out="UNKNOWN SILENCE"</tag>
</item>
</one-of>
</rule>
</grammar>
Simple Tone Detection VXML
The following is a very simple VXML script to do tone detection:
<vxml version="2.0" xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2001/vxml
http://www.w3.org/TR/voicexml20/vxml.xsd">
<form id="main">
<property name="timeout" value="120s"/>
<property name="completetimeout" value="120s"/>
<property name="incompletetimeout" value="120s"/>
<property name="maxspeechtimeout" value="120s"/>
<field name="toneDetection">
<grammar src="tone_detection.grxml" type="application/srgs+xml"/>
<prompt>
You can customize an outbound message here.
</prompt>
<catch event="noinput">
<exit />
</catch>
</field>
<filled>
<prompt>
A tone was detected. Here is where you could customize
a message to be left on a voicemail.
</prompt>
<exit />
</filled>
</form>
</vxml>
It depends on having the following grammar as tone_detection.grxml in the same location as the VXML document:
<?xml version='1.0'?>
<!-- ****************************************************************** -->
<!-- special grammar used to switch decode modes to tone detection mode -->
<!-- ****************************************************************** -->
<grammar xml:lang="en-US" version="1.0" root="root" mode="voice"
xmlns="http://www.w3.org/2001/06/grammar"
tag-format="semantics/1.0.2006">
<!-- ************************************************************ -->
<!-- This value changes the detection mode to tone detection mode -->
<!-- ************************************************************ -->
<meta name="STREAM|DETECTION_MODE" content="Tone"/>
<meta name="STREAM|BARGE_IN_TIMEOUT" content="120000"/>
<meta name="STREAM|END_OF_SPEECH_TIMEOUT" content="120000"/>
<meta name="STREAM|VAD_EOS_DELAY" content="800"/>
<meta name="STREAM|VAD_STREAM_INIT_DELAY" content="100"/>
<!-- ************************************************************ -->
<!-- Activate/Deactivate AMD, FAX, SIT detection -->
<!-- ************************************************************ -->
<meta name="AMD_CUSTOM_ENABLE" content="true"/>
<meta name="FAX_CUSTOM_ENABLE" content="true"/>
<meta name="SIT_CUSTOM_ENABLE" content="true"/>
<meta name="BUSY_CUSTOM_ENABLE" content="false"/>
<!-- ************************************************************ -->
<!-- These values may be changed to modify the semantic
interpretations returned -->
<!-- ************************************************************ -->
<!--
The below meta tags map each state (AMD/FAX/SIT) to an input
text in the SRGS grammar below. This is the mechanism used to map
each of the states to an input text in the grammar.
**************************************************************** -->
<meta name="AMD_CUSTOM_INPUT_TEXT" content="AMD"/>
<meta name="FAX_CUSTOM_INPUT_TEXT" content="FAX"/>
<meta name="BUSY_CUSTOM_INPUT_TEXT" content="BUSY"/>
<meta name="SIT_REORDER_LOCAL_CUSTOM_INPUT_TEXT"
content="SIT REORDER LOCAL"/>
<meta name="SIT_VACANT_CODE_CUSTOM_INPUT_TEXT"
content="SIT VACANT CODE"/>
<meta name="SIT_NO_CIRCUIT_LOCAL_CUSTOM_INPUT_TEXT"
content="SIT NO CIRCUIT LOCAL"/>
<meta name="SIT_INTERCEPT_CUSTOM_INPUT_TEXT"
content="SIT INTERCEPT"/>
<meta name="SIT_REORDER_DISTANT_CUSTOM_INPUT_TEXT"
content="SIT REORDER DISTANT"/>
<meta name="SIT_NO_CIRCUIT_DISTANT_CUSTOM_INPUT_TEXT"
content="SIT NO CIRCUIT DISTANT"/>
<meta name="SIT_OTHER_CUSTOM_INPUT_TEXT"
content="SIT OTHER"/>
<!--
Below is the SRGS grammar. If the input text is changed in any
of them, the appropriate changes need to be made to the above
states to input text mapping. The semantic interpretation can
be changed as necessary by modifying the out tags below.
*********************************************************** -->
<rule id="root" scope="public">
<one-of>
<item>AMD<tag>out="AMD"</tag></item>
<item>FAX<tag>out="FAX"</tag></item>
<item>BUSY<tag>out="BUSY"</tag></item>
<item>SIT REORDER LOCAL<tag>out="SIT"</tag></item>
<item>SIT VACANT CODE<tag>out="SIT"</tag></item>
<item>SIT NO CIRCUIT LOCAL<tag>out="SIT"</tag></item>
<item>SIT INTERCEPT<tag>out="SIT"</tag></item>
<item>SIT REORDER DISTANT<tag>out="SIT"</tag></item>
<item>SIT NO CIRCUIT DISTANT<tag>out="SIT"</tag></item>
<item>SIT OTHER<tag>out="SIT"</tag></item>
</one-of>
</rule>
</grammar>