Examples directory lua asr tts

From FreeSWITCH Wiki
Jump to: navigation, search


Warning

Superseded by https://confluence.freeswitch.org/display/FREESWITCH/Lua+ASR+TTS+Directory+Example

 


Warning

NOTE It seems this page has not been updated for a while (e.g. references to SVN that should be GIT.... therefore read and understand the content, don't just copy paste the commands and configs....)

 


Example ASR TTS Company Directory

This guide should allow you to install FreeSWITCH and configure an ASR company directory. There are a few things we are going to use for constants in this guide.


===================
By Matt Williams (Hmmhesays in #freeswitch)
===================


Please run through this and let me know if there is anything missing.


  • Network Addresses
    • FreeSWITCH box 192.168.1.100
  • Phones
    • Phone 1 extension 1000
    • Phone 2 extension 1001
  • Directories
    • FreeSWITCH source directory /usr/src/freeswitch
    • FreeSWITCH installation diretory /usr/local/freeswitch

Obtain and Build the Source Code

  • First you must obtain the code. In this example we're using Linux with a bash s shell
#!/bin/bash
#change directory to where we want to download FreeSWITCH. In this guide /usr/src/freeswitch is going to be the home of our FreeSWITCH source. 
cd /usr/src/
svn checkout http://svn.freeswitch.org/svn/freeswitch/trunk freeswitch
#Change your directory to the freeswitch directory
cd freeswitch
#run bootstrap.sh
./bootstrap.sh
#run configure script
./configure
  • Copy modules.conf into the proper location
#!/bin/bash
#change directory to your freeswitch source directory
cd /usr/src/freeswitch
#copy modules.conf into the root source directory
cp build/modules.conf.in modules.conf
  • Now we must tell FreeSWITCH which modules to build. Open up modules.conf with your favorite text editor and uncomment the following lines if they are commented. They should look like the following.
...
#asr_tts/mod_flite
asr_tts/mod_pocketsphinx
#asr_tts/mod_cepstral
...
#languages/mod_spidermonkey_odbc
languages/mod_lua
#languages/mod_perl
...
  • Now we make and install FreeSWITCH, sounds and MOH (music on hold) files
#!/bin/bash
make install
make sounds-install
make moh-install
  • Make sure these build and install without errors before each command.
  • Our next step is to set up directory entries for our users on the local network. We need to create a new directory and copy a couple files into it.
#!/bin/bash
#Change your directory to the freeswitch directory
cd /usr/local/freeswitch/conf/directory
#Make a new file were we are going to keep our user settings for our local network
mkdir 192.168.1.100
#Copy default.xml to 192.168.1.100.xml for for our domain settings
cp default.xml 192.168.1.100.xml
#copy over a directory files. These hold the phone registration info, voicemail, nat. These are similar to a peer/user entry in asterisks sip.conf or users.conf
cp default/brian.xml 192.168.1.100/1000.xml
cp default/brian.xml 192.168.1.100/1001.xml
  • Now we need to edit our domain info. Open up 192.168.1.100.xml and edit it so it looks like the following
<include>
  <!--the domain or ip (the right hand side of the @ in the addr-->
  <domain name="192.168.1.100">
    <params>
      <param name="dial-string" value="{presence_id=${dialed_user}@${dialed_domain},transfer_fallback_extension=${dialed_user}}${sofia_contact(${dialed_domain}/${dialed_user}@${dialed_domain})}"/>
    </params>

    <variables>
      <variable name="record_stereo" value="true"/>
    </variables>

    <X-PRE-PROCESS cmd="include" data="192.168.1.100/*.xml"/>
  </domain>
</include>                            
  • Next we need to edit the directory entries for each phone. Change directories into 192.168.1.100 and open up 1000.xml in your favorite text editor. The following are the lines we should be concerned with. These are pretty self explanatory. Username, password, context. Do this for 1001.xml entry also.
<user id="1000" mailbox="1000">
<param name="password" value="1234"/>
<variable name="user_context" value="internal"/>
  • Now we set up our internal sip profile. Change directories to our sip_profiles directory and open up internal.xml in your favorite editor modify the following lines.
#!/bin/bash
#Change directories to our sip_profiles directory and open up internal.xml in your favorite editor modify the following lines.
cd /usr/local/freeswitch/conf/sip_profiles
...
<profile name="internal">
...
<aliases>
   <alias name="192.168.1.100"/>
</aliases>
...
  • Next we need to set up a our dial plan to call between our phones.
#!/bin/bash
cd /usr/local/freeswitch/conf/dialplan
touch internal.xml
  • Now open up internal.xml in your favorite text editor and make it look like the following
<?xml version="1.0" encoding="utf-8"?>
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
<include>
  <context name="internal">
<extension name="users">
        <condition field="destination_number" expression="^{1\d\d\d}$">
                <action application="bridge" data="sofia/$1%192.168.1.100"/>
        </condition>
</extension>

<extension name="directory">
	<condition field="destination_number" expression="^411$">
		<action application="lua" data="directory.lua"/>
	</condition>
</extension>
  </context>
</include>

  • Next we need to tell FreeSWITCH to load mod_pocketsphinx and mod_lua on start up. Open up /usr/local/freeswitch/conf/autoload_configs/modules.conf.xml in your favorite editor and change the following lines.
<!--modules.conf.xml-->
...
<!-- ASR /TTS -->
<!-- <load module="mod_flite"/> -->
<load module="mod_pocketsphinx"/>
<!-- <load module="mod_cepstral"/> -->
...
<!-- <load module="mod_java"/> -->
<load module="mod_lua"/>

...
  • At this point you should be able to start FreeSWITCH, register your phones and call between them.
  • Now on to the fun ASR stuff change your directory to /usr/local/freeswitch/scripts and create directory.lua
  • Please note the extensions array. This is where you map the names to extension numbers.
--[[
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

-- Used to parse the XML results.
function getResults(s) 
   local xml = parse_xml(s);
   local stack = {}
   local top = {}
   table.insert(stack, top)
   top = {grammar=xml[1].xarg.grammar, score=xml[1].xarg.score, text=xml[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 = {
   ["MATTHEW_WILLIAMS"] = 1000,
   ["MATT_WILLIAMS"] = 1000,
   ["JOSEPH_SMITH"] = 1001,
   ["JOE_SMITH"] = 1000
   }

-- Create the empty results table.
results = {};

-- Answer the call.
session:answer();
-- Register the input callback 
session:setInputCallback("onInput");
-- Sleep a little bit to give media time to be fully up.
session:sleep(200);
session:streamFile("/usr/local/freeswitch/sounds/en/us/directory/directory-welcome.wav");
-- 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:streamFile("/usr/local/freeswitch/sounds/en/us/directory/directory-speak_name.wav");
   -- 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) then
      -- Letting the caller know we are trying.
      session:streamFile("/usr/local/freeswitch/sounds/en/us/directory/directory-please_hold.wav");
      -- 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 home");
   end
   -- We didn't have them in our directory table.
   session:streamFile("/usr/local/freeswitch/sounds/en/us/directory/directory-not_found.wav");
   -- Clear any results we have just in case.
   results = {};
   -- Resume detect_speech.
   session:execute("detect_speech", "resume");
end
  • Next we create our dictionary and language model file. Change your directory to /usr/local/freeswitch/grammar and create a file called directory.sent We are using Matthew Williams and Joe Smith as our examples.
<s> MATTHEW_WILLIAMS </s>
<s> MATT_WILLIAMS </s>
<s> JOSEPH_SMITH </s>
<s> JOE_SMITH </s>
  • Type make in the /usr/local/freeswitch/grammar directory. This should give you the following files.
ls -la directory
-rw-r--r--  1 root root   82 2008-08-13 10:59 directory.dic
-rw-r--r--  1 root root  727 2008-08-13 10:59 directory.lm


  • directory.dic currently looks like this.
JOE     JH OW
JOSEPH  JH OW S AH F
JOSEPH(2)       JH OW Z AH F
MATT    M AE T
MATTHEW M AE TH Y UW
SMITH   S M IH TH
WILLIAMS        W IH L Y AH M Z
  • You will need to make it look like the following. Note this file is tab delimited.
JOE_SMITH       JH OW S M IH TH
JOSEPH_SMITH    JH OW Z AH F S M IH TH
JOSEPH_SMITH(2) JH OW S AH F S M IH TH
MATT_WILLIAMS   M AE T W IH L Y AH M Z
MATTHEW_WILLIAMS        M AE TH Y UW W IH L Y AH M Z
  • Now we have to create the directory for the sound files. In this example we have US EN prompts
#!/bin/bash
cd /usr/local/freeswitch/sounds/en/us
mkdir directory
cd directory
wget www.freeswitch.org/eg/directory.tar.gz
tar -xvzf directory.tar.gz
  • You should now be able to dial 411 and access the directory.