--- /dev/null
+--[[
+FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+Copyright (C) 2005/2006, Anthony Minessale II <anthm@freeswitch.org>
+
+Version: MPL 1.1
+
+The contents of this file are subject to the Mozilla Public License Version
+1.1 (the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS IS" basis,
+WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+for the specific language governing rights and limitations under the
+License.
+
+The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+
+The Initial Developer of the Original Code is
+Anthony Minessale II <anthm@freeswitch.org>
+ Portions created by the Initial Developer are Copyright (C)
+the Initial Developer. All Rights Reserved.
+
+Contributor(s):
+
+Brian West <brian@freeswitch.org>
+
+ Example for Speech Enabled LUA Applications.
+]]
+
+-- Used in parse_xml
+function parseargs_xml(s)
+ local arg = {}
+ string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
+ arg[w] = a
+ end)
+ return arg
+end
+
+-- Turns XML into a lua table.
+function parse_xml(s)
+ local stack = {};
+ local top = {};
+ table.insert(stack, top);
+ local ni,c,label,xarg, empty;
+ local i, j = 1, 1;
+ while true do
+ ni,j,c,label,xarg, empty = string.find(s, "<(%/?)(%w+)(.-)(%/?)>", i);
+ if not ni then
+ break
+ end
+ local text = string.sub(s, i, ni-1);
+ if not string.find(text, "^%s*$") then
+ table.insert(top, text);
+ end
+ if empty == "/" then
+ table.insert(top, {label=label, xarg=parseargs_xml(xarg), empty=1});
+ elseif c == "" then
+ top = {label=label, xarg=parseargs_xml(xarg)};
+ table.insert(stack, top);
+ else
+ local toclose = table.remove(stack);
+ top = stack[#stack];
+ if #stack < 1 then
+ error("nothing to close with "..label);
+ end
+ if toclose.label ~= label then
+ error("trying to close "..toclose.label.." with "..label);
+ end
+ table.insert(top, toclose);
+ end
+ i = j+1;
+ end
+ local text = string.sub(s, i);
+ if not string.find(text, "^%s*$") then
+ table.insert(stack[stack.n], text);
+ end
+ if #stack > 1 then
+ error("unclosed "..stack[stack.n].label);
+ end
+ return stack[1];
+end
+
+function dump(o)
+ if type(o) == 'table' then
+ local s = '{ '
+ for k,v in pairs(o) do
+ if type(k) ~= 'number' then k = '"'..k..'"' end
+ s = s .. '['..k..'] = ' .. dump(v) .. ','
+ end
+ return s .. '} '
+ else
+ return tostring(o)
+ end
+end
+
+-- Used to parse the XML results.
+function getResults(s)
+ local xml = parse_xml(s);
+ local stack = {}
+ local top = {}
+
+ -- freeswitch.consoleLog("crit", "\n" .. dump(xml) .. "\n");
+ table.insert(stack, top)
+ top = {grammar=xml[2].xarg.grammar, score=xml[2].xarg.confidence, text=xml[2][1][1][1]}
+ table.insert(stack, top)
+ return top;
+end
+
+-- This is the input callback used by dtmf or any other events on this session such as ASR.
+function onInput(s, type, obj)
+ freeswitch.consoleLog("info", "Callback with type " .. type .. "\n");
+ if (type == "dtmf") then
+ freeswitch.consoleLog("info", "DTMF Digit: " .. obj.digit .. "\n");
+ else if (type == "event") then
+ local event = obj:getHeader("Speech-Type");
+ if (event == "begin-speaking") then
+ freeswitch.consoleLog("info", "\n" .. obj:serialize() .. "\n");
+ -- Return break on begin-speaking events to stop playback of the fire or tts.
+ return "break";
+ end
+ if (event == "detected-speech") then
+ freeswitch.consoleLog("info", "\n" .. obj:serialize() .. "\n");
+ if (obj:getBody()) then
+ -- Pause speech detection (this is on auto but pausing it just in case)
+ session:execute("detect_speech", "pause");
+ -- Parse the results from the event into the results table for later use.
+ results = getResults(obj:getBody());
+ end
+ return "break";
+ end
+ end
+ end
+end
+
+
+--Used to map returned names to extension numbers
+extensions = {
+ ["anthony"] = 3000,
+ ["michael"] = 3001,
+ ["brian"] = 3002
+}
+
+-- Create the empty results table.
+results = {};
+-- Answer the call.
+session:answer();
+-- Define TTS Engine
+session:set_tts_params("flite", "slt");
+-- Register the input callback
+session:setInputCallback("onInput");
+-- Sleep a little bit to give media time to be fully up.
+session:sleep(200);
+session:speak("Welcome to the directory.");
+-- Start the detect_speech app. This attaches the bug to fire events
+session:execute("detect_speech", "pocketsphinx directory directory");
+
+-- Magic happens here.
+-- It would be ok to loop like 3 times and error to the operator if this doesn't work or revert to reading names off with TTS.
+while (session:ready() == true) do
+ session:sleep(100);
+ -- Who are they looking for?
+ session:speak("Say the name of the person you're trying to reach.");
+ -- This sleep is what blocks till the detected-speech event. This has to give you enough time to speak plus get the results.
+ session:sleep(3000);
+ session:sleep(3000);
+ -- If the results aren't null and we have an extension in the table.
+ if (results.text ~= nil and extensions[results.text] ~= nil) then
+ -- Letting the caller know we are trying.
+ session:speak("Please hold while I transfer your call.");
+ -- It's critical to stop the detect_detect otherwise it will continue to fire speech events and waste resources.
+ session:execute("detect_speech", "stop");
+ -- Transfer the call to the extension out of the lua table.
+ session:execute("transfer", extensions[results.text] .. " XML default");
+ end
+ -- We didn't have them in our directory table.
+ session:speak("Sorry, I don't have that person listed, please try again.");
+ -- Clear any results we have just in case.
+ results = {};
+ -- Resume detect_speech.
+ session:execute("detect_speech", "resume");
+end