;LumenVox -- Speech Understood
;
;Copyright (C) 2006, LumenVox, LLC
;
;Support <support@lumenvox.com>
;
;See http://www.LumenVox.com for more information about
;our speech techology products.  This is provided as only
;a working example of a speech-enabled IVR application
;using the integration of the LumenVox Speech
;Engine with Asterisk.  For more information on Asterisk 
;visit http://asterisk.org.
;
;This Dial Plan is provided as a free example. No warranties
;are expressed or implied.
;
;This macro will perform confirmation on recognitions
;if the confidence score falls below the THRESHOLD value.
[macro-speech-confirm]
exten => s,1,SpeechActivateGrammar(yes_no)
exten => s,2,Set(TEMPSPEECH_TEXT=${TEXT})
exten => s,3,Set(CONFARG=$[Confirm${ARG1}])
exten => s,4,SpeechBackground(${CONFARG})
exten => s,5,SpeechStart
exten => s,6,Noop
exten => s,7,Set(CONFIRM=${SPEECH_TEXT(0)})
exten => s,8,GotoIf($["${SPEECH_TEXT(0)}" = "yes"]?9:10)
exten => s,9,Set(CONFIRM=yes)
exten => s,10,Set(TEXT=${TEMPSPEECH_TEXT})
exten => s,11,SpeechDeactivateGrammar(yes_no)

;This is the entry point for the pizza demo IVR
[order-pizza]
exten => s,1,Playback(GP-Greeting)
exten => s,2,Goto(pizza-delivery,s,1)
;The THRESHOLD is a value compared to the 
;SPEECH_SCORE(x), which is the confidence score
;returned by the Speech Engine.
exten => s,3,Set(THRESHOLD=500)

;This context will get whether the caller
;requests delivery or takeout
[pizza-delivery]
exten => s,1,SpeechActivateGrammar(order)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-DeliveryorTakeout)
;If there are no results (no input) then we re-prompt them.
exten => s,4,GotoIf($["${SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NoDeliveryorTake-out)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
;If the confidence score is below the threshold then we confirm the intent
;of the spoken phrase.
exten => s,8,Gotoif($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?12:9)
exten => s,9,Macro(speech-confirm,${SPEECH_TEXT(0)})
exten => s,10,GotoIf($["${CONFIRM}" = "no"]?3:11)
exten => s,11,Noop(speech_score=${SPEECH_SCORE(0)})
;If the intent has been confirmed then the type of order is fixed
exten => s,12,Set(TYPE_OF_ORDER=${TEXT})
exten => s,13,SpeechDeactivateGrammar(order)
exten => s,14,Noop
exten => s,15,GotoIf($["${ARSO}" = "1"]?16:17)
exten => s,16,Goto(confirm-order,s,1)
exten => s,17,Goto(pizza-size,s,1)

;Now that we have the delivery option we want to know
;what size pizza they want.  Each context will continue to 
;check for caller input and confidence scores.
[pizza-size]
exten => s,1,SpeechActivateGrammar(size)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-Size)
exten => s,4,GotoIf($["{SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NI)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
exten => s,8,GotoIf($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,9,Macro(speech-confirm,${SPEECH_TEXT(0)})
exten => s,10,GotoIf($["${CONFIRM}" = "no"]?3:11)
exten => s,11,Set(SIZE=${TEXT})
exten => s,12,SpeechDeactivateGrammar(size)
exten => s,13,GotoIf($["${ARSO}" = "1"]?14:15)
exten => s,14,Goto(confirm-order,s,1)
exten => s,15,Goto(pizza-crust,s,1)

;After we know what size pizza callers want, it is time 
;for them to decide which crust style they want.
[pizza-crust]
exten => s,1,SpeechActivateGrammar(crust)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-Crust)
exten => s,4,GotoIf($["${SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NI)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
exten => s,8,GotoIf($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,9,Macro(speech-confirm,${SPEECH_TEXT(0)})
exten => s,10,GotoIf($["${CONFIRM}" = "no"]?3:11)
exten => s,11,Set(CRUST=${TEXT})
exten => s,12,SpeechDeactivateGrammar(crust)
exten => s,13,GotoIf($["${ARSO}" = "1"]?14:15)
exten => s,14,Goto(confirm-order,s,1)
exten => s,15,Goto(speciality-or-custom,s,1)

;Here we present callers with the option to select
;from one of the speciality pizzas or pick their own
;toppings. See what happens when you try to change
;the toppings once a specialty pizza has been chosen.
[speciality-or-custom]
exten => s,1,SpeechActivateGrammar(specialityorcustom)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-SpecialtyorCustom)
exten => s,4,GotoIf($["${SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NI)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
exten => s,8,GotoIf($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,9,Macro(speech-confirm,${SPEECH_TEXT(0)})
exten => s,10,GotoIf($["${CONFIRM}" = "no"]?3:11)
exten => s,11,SpeechDeactivateGrammar(specialityorcustom)
exten => s,12,Set(SORC=${TEXT})
exten => s,13,GotoIf($["${SPEECH_TEXT(0)}" = "Speciality"]?14:15)
exten => s,14,Goto(select-speciality,s,1)
exten => s,15,Goto(select-toppings,s,1)
exten => s,16,Noop

;If callers want one of the speciality pizzas,
;here is their oportunity to choose one.
[select-speciality]
exten => s,1,SpeechActivateGrammar(speciality)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-SpecialtyList)
exten => s,4,GotoIf($["${SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NI)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
exten => s,8,GotoIf($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,9,Macro(speech-confirm,${SPEECH_TEXT(0)})
exten => s,10,GotoIf($["${CONFIRM}" = "no"]?3:11)
exten => s,11,Set(TOPPING=${TEXT})
exten => s,12,Noop
exten => s,13,SpeechDeactivateGrammar(speciality)
exten => s,14,Goto(confirm-order,s,1)

;If callers want to customise their own pizza
;here is their chance.
[select-toppings]
exten => s,1,SpeechActivateGrammar(pizza)
exten => s,2,SpeechStart
exten => s,3,SpeechBackground(GP-Toppings)
exten => s,4,GotoIf($["${SPEECH(results)}" = "0"]?5:7)
exten => s,5,Playback(GP-NI)
exten => s,6,Goto(2)
exten => s,7,Set(TEXT=${SPEECH_TEXT(0)})
exten => s,8,Gotoif($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?9:6)
exten => s,9,Set(TOPPING=${TEXT})
exten => s,10,SpeechDeactivateGrammar(pizza)
;Since the Dial Plan cannot handle the complex object in the XML 
;format returned by the pizza grammar it needs to be converted into
;a string that the Dial Plan can easily pass around.
exten => s,11,AGI(xml-to-string.agi, ${TOPPING})
exten => s,12,Goto(confirm-order,s,1)

;When the choice of toppings has been made we need
;to confirm the order. You can see that a series 
;of prompts are played back based on what the caller
;has selected.
[confirm-order]
exten => s,1,SpeechActivateGrammar(yes_no)
exten => s,2,SpeechStart
exten => s,3,Playback(GP-You_ordered_a)
exten => s,4,Playback(${SIZE})
exten => s,5,Playback(${CRUST})
;If the caller selected a specialty pizza then the 
;name of that pizza is played back.
exten => s,6,GotoIf($["${SORC}"="Speciality"]?7:10)
exten => s,7,Playback(${TOPPING})
exten => s,8,Playback(pizza)
exten => s,9,Goto(13)
exten => s,10,Playback(pizza)
exten => s,11,Playback(GP-With)
;If callers select their own toppings then
;the say_ingredients AGI Perl script will play those 
;ingredients back.
exten => s,12,AGI(say_ingredients.agi, ${TOPPING})
exten => s,13,Noop(${TOPPING})
;Now we ask if this is what callers wanted, giving 
;them an opportunity to make changes to their order.
exten => s,14,SpeechBackground(GP-WasThisRight)
exten => s,15,GotoIf($["${SPEECH(results)}" = "0"]?16:18)
exten => s,16,Playback(GP-NI)
exten => s,17,Goto(2)
exten => s,18,GotoIf($["${SPEECH_TEXT(0)}" = "no"]?19:22)
exten => s,19,SpeechDeactivateGrammar(yes_no)
exten => s,20,Set(SPEECH(results)=0)
exten => s,21,Goto(arso,s,1)
exten => s,22,Goto(complete-order,s,1)

;ARSO (Add Remove or Start Over): we want to see where in
;the order process callers want to make changes.  Add a topping,
remove a topping, change the size, crust, or just start over.
[arso]
exten => s,1,SpeechActivateGrammar(arso)
exten => s,2,SpeechStart
exten => s,3,Playback(GP-Wanted=No)
exten => s,4,SpeechBackground(GP-ARSO)
exten => s,5,GotoIf($["${SPEECH(results)}" = "0"]?6:8)
exten => s,6,Playback(GP-NI)
exten => s,7,Goto(4)
exten => s,8,GotoIf($["${SPEECH_TEXT(0)}" = "delivery"]?9:11)
exten => s,9,Set(ARSO=1)
exten => s,10,Goto(pizza-delivery,s,1)
exten => s,11,GotoIf($["${SPEECH_TEXT(0)}" = "size"]?12:14)
exten => s,12,Set(ARSO=1)
exten => s,13,Goto(pizza-size,s,1)
exten => s,14,GotoIf($["${SPEECH_TEXT(0)}" = "crust"]?15:17)
exten => s,15,Set(ARSO=1)
exten => s,16,Goto(pizza-crust,s,1)
exten => s,17,GotoIf($["${SPEECH_TEXT(0)}" = "startover"]?18:20)
exten => s,18,Set(ARSO=0)
exten => s,19,Goto(order-pizza,s,1)
exten => s,20,GotoIf($["${SPEECH_TEXT(0)}" = "add_topping"]?21:23)
exten => s,21,Set(ARSO=1)
exten => s,22,Goto(add_topping,s,1)
exten => s,23,GotoIf($["${SPEECH_TEXT(0)}" = "rem_topping"]?24:4)
exten => s,24,Set(ARSO=1)
exten => s,25,Goto(remove_topping,s,1)

;This context will get any new topping(s) from the caller
[add_topping]
exten => s,1,SpeechActivateGrammar(pizza)
exten => s,2,SpeechStart
exten => s,3,GotoIf($["${SORC}"="Speciality"]?4:6)
exten => s,4,Playback(GP-ChangeSpec)
exten => s,5,Goto(speciality-or-custom,s,1)
exten => s,6,SpeechBackground(GP-Adding)
exten => s,7,GotoIf($["${SPEECH(results)}" = "0"]?9:11)
exten => s,8,Playback(GP-NI)
exten => s,9,Goto(2)
exten => s,10,Gotoif($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,11,Set(NEWTOPPING=${SPEECH_TEXT(0)})
exten => s,12,SpeechDeactivateGrammar(pizza)
;After getting the topping(s) it is converted to a string, then
;appended to the existing string of topping(s), and then the order
;is reconfirmed.
exten => s,13,AGI(xml-to-string-newtopping.agi, ${NEWTOPPING})
exten => s,14,AGI(add_topping.agi, ${TOPPING},${NEWTOPPING})
exten => s,15,Goto(confirm-order,s,1)

;This will capture the topping(s) to be removed from the pizza.
[remove_topping]
exten => s,1,SpeechActivateGrammar(pizza)
exten => s,2,SpeechStart
exten => s,3,GotoIf($["${SORC}"="Speciality"]?4:6)
exten => s,4,Playback(GP-ChangeSpec)
exten => s,5,Goto(speciality-or-custom,s,1)
exten => s,6,SpeechBackground(GP-Remove)
exten => s,7,GotoIf($["${SPEECH(results)}" = "0"]?9:11)
exten => s,8,Playback(GP-NI)
exten => s,9,Goto(2)
exten => s,10,Gotoif($[ "${SPEECH_SCORE(0)}" > "${THRESHOLD}" ]?11:9)
exten => s,11,Set(NEWTOPPING=${SPEECH_TEXT(0)})
exten => s,12,SpeechDeactivateGrammar(pizza)
;After getting the topping(s) it is converted to a string, then
;appended to the existing string of topping(s), and then the order
;is reconfirmed.
exten => s,13,AGI(xml-to-string-newtopping.agi, ${NEWTOPPING})
exten => s,14,AGI(remove_topping.agi, ${TOPPING},${NEWTOPPING})
exten => s,15,Goto(confirm-order,s,1)

;Finally, when the order is confirmed we tell the caller how to 
;expect receipt of the pizza. We perform some clean-up on the 
;speech object and the call by the SpeechDestroy and Hangup
;command lines.
[complete-order]
exten => s,1,Playback(${TYPE_OF_ORDER})
exten => s,2,SpeechDestroy/
exten => s,3,Wait(1)
exten => s,4,Hangup

[pizza]
exten => s,1,Answer
exten => s,2,Wait(1)
exten => s,3,SpeechCreate
exten => s,4,Goto(order-pizza,s,1)

[speech_demo]
exten => s,1,Goto(pizza,s,1)
exten => s,1,Musiconhold
exten => s,1,Playback(pbx-invalid)
exten => s,2,Congestion

[default]
include => speech_demo
