High performance VoIP SDK for .Net developers

VoIP SIP SDK

Visual Basic .NET DTMF IVR

Download: 34_DTMFIVR_VB.NET.zip

This article introduces a DTMF navigated IVR system written in Visual Basic .NET programming language. The IVR tree is built up from an .xml file and the SIP communication is supported by Ozeki VoIP SIP SDK.

Introduction

Interactive Voice Response (IVR) systems use a tree structured menu that can be navigated by using the DTMF (Dual-tone multi-frequency) signals that are sent to a telephoning client when the remote user presses a button on the telephone or softphone keypad.

The DTMF signals are special sound frequencies that can be recognized in the VoIP client and if there is an implemented function for the use of these sounds, the VoIP client can use them for any purpose.

In the IVR systems the caller clients can navigate in the menu tree by using DTMF signals. This operation usually made by the IVR server playing an audio that instructs the user to press certain keypad buttons in order to get to the answer for their problem. When the user pressed the right button, the IVR system enters the appropriate branch of the menu tree and the instruction process continues. This process is done until the user reaches the bottom of the menu tree or presses a button for getting into connection with a human operator if possible.

Ozeki VoIP SIP SDK provides all the support for implementing an IVR system that can be navigated by DTMF signals. You only need to use the provided classes and methods and you will soon have your own IVR server.

The following program code uses the background support of Ozeki VoIP SIP SDK, therefore you will need to download and install the SDK on your computer before starting to use the program code. You will also need to have Visual Studio 2010 or compatible IDE and .NET Framework installed on your system, as the program code below is written in Visual Basic .NET language.

Source Code Explanation

This IVR system registers a softphone to a PBX with a SIP account that is defined in an application configuration file. This file is a standard tool that can be used for configuration purposes.
Note that you need to set your own SIP data in the app.config file if you want your IVR work properly.

Code 1 shows the part of the app.config file that defines the SIP information for the IVR system. You can set your own SIP account and PBX information in this file.

<appSettings>
        <add key="IsRegRequired" value="true" />
        <add key="displayname" value="oz879" />
        <add key="username" value="oz879" />
        <add key="registername" value="oz879" />
        <add key="regpass" value="oz879" />
        <add key="domainhost" value="192.168.112.100" />
        <add key="port" value="5060" />
        <add key="nat" value="0" />
        <add key="ClientSettingsProvider.ServiceUri" value="" />
</appSettings>

Code 1 - SIP account data in the application configuration file

The app.config file is parsed in the Softphone class. There is a register() method (Code 2) taht does the parsing. The application configuration file can be handled by using a standard tool called ConfigurationManager that is a class in the System.Configuration namespace.

If you add the SIP information in the app.config file the same way as in this example, you can gain the information by using the ConfigurationManager.AppSettings tool. In this case you can use the key element in the app.setting to index the AppSettings array and you can get the values for the keys. In some cases, you need to convert the gained data as they are all stored as strings in the application configuration file.

Private Sub register()
        Try
                Dim cm = ConfigurationManager.AppSettings
                IsRegRequired = Boolean.Parse(cm("IsRegRequired"))
                displayname = cm("displayname")
                username = cm("username")
                registername = cm("registername")
                regpass = cm("regpass")
                domainhost = cm("domainhost")
                port = Int32.Parse(cm("port"))

                Select Case Int32.Parse(cm("nat"))
                        Case 0
                                natTraversal = NatTraversalMethod.None
                        Case 1
                                natTraversal = NatTraversalMethod.STUN
                        Case 2
                                natTraversal = NatTraversalMethod.TURN
                End Select

                Dim sa As New SIPAccount(IsRegRequired, displayname, username, registername, regpass, domainhost, port)
                Dim nc As New NatConfiguration(natTraversal)

                phoneLine = softPhone_Renamed.CreatePhoneLine(sa, nc)
                AddHandler phoneLine.PhoneLineStateChanged, AddressOf phoneLine_PhoneLineInformation

                softPhone_Renamed.RegisterPhoneLine(phoneLine)
        Catch ex As Exception
                MessageBox.Show(String.Format("You didn't set the right IP address. Please set your IP address and try again" & vbLf & " {0}", ex.Message), String.Empty, MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
End Sub

Code 2 - Parsing the app.config file

The IVR tree information in this example program is stored in an XML file those structures can be seen in Code 3. The tree is built up from this file, that can be modified in a simple way being a text file.

<?xml version="1.0" encoding="utf-8" ?>
<node>
         <name>Name</name>
         <message>Message</message>
         <signal>signal</signal>
         <children>
                      <node>...
         </children>
</node>

Code 3 - The XML structure for the IVR tree information

The actual IVR tree is built up in the Softphone class within the buildTree method (Code 4). This method calls a recursive method in the TreeXMLParser class, specifies the .xml file of the IVR information and sets up the TreeView that will be the graphical representation of the IVR tree on thye GUI.

Private Sub buildTree()
        root = TreeXMLParser.parse(TreeXMLParser.treedoc, Nothing)
        treeView1.Nodes.Add(root.treeNode)
        selectedNode = root
        selectedTreeNode = root.treeNode
        Try
                treeView1.SelectedNode = selectedTreeNode
                treeView1.SelectedNode.ForeColor = Color.Red
        Catch e1 As Exception

                MessageBox.Show("Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
End Sub

Code 4 - Building up the IVR tree using the TreeXMLParser class

The TreeXMLParser (Code 5) is a service class that only contains a recursive method for the XML parsing and the actual .xml file path that it parses. If you want to use a different IVR xml file for the tree, you can specify the file path in this class.

The parse function recursively parses the specified .xml file and builds up a tree structure of the IVRNode objects. The IVRNode class is also implemented in the example project.

Friend Class TreeXMLParser
        Public Shared treedoc As XElement = XElement.Load("../../TreeData.xml")

        Public Shared Function parse(ByVal element As XElement, ByVal parentNode As IVRNode) As IVRNode
                Dim node As New IVRNode(element.Element("name").Value.ToString(), element.Element("message").Value.ToString(), parentNode)
                For Each childnode As XElement In element.Element("children").Elements("node")
                        Try
                                node.AddChild(Int32.Parse(childnode.Element("signal").Value.ToString()), parse(childnode, node))
                        Catch ex As Exception
                                MessageBox.Show(String.Format("error {0}", ex), "error", MessageBoxButtons.OK, MessageBoxIcon.Error)
                        End Try
                Next childnode
                Return node
        End Function

End Class

Code 5 - The TreeXMLParser class

The IVRNode class (Code 6) represents a node in the IVR tree. A node stores the IVR message information, the name element, tha parent node (except for the root element) and a Dictionary object for the child nodes.

The key element in the children Dictionary stores the DTMF signal code that needs to be pressed for reaching that certain child node from the actual IVR node. The class also provides some functions that can be used for adding and getting child nodes to and from the node.

The addChild function is used when building up the IVR tree, while the getChild is typically the one used for the IVR tree navigation.

Friend Class IVRNode
        Public treeNode As TreeNode
        Private parent As IVRNode
        Private children As New Dictionary(Of Integer, IVRNode)()
        Private name As String
        Public message As String

        Public Sub New(ByVal name As String, ByVal message As String)
                Me.name = name
                Me.message = message
                Me.parent = Nothing
                treeNode = New TreeNode(name)
        End Sub

        Public Sub New(ByVal name As String, ByVal message As String, ByVal parent As IVRNode)
                Me.name = name
                Me.message = message
                Me.parent = parent
                treeNode = New TreeNode(name)
        End Sub

        Public Sub AddChild(ByVal signal As Integer, ByVal child As IVRNode)
                children.Add(signal, child)
                treeNode.Nodes.Add(child.treeNode)
        End Sub

        Public Function getChild(ByVal signal As Integer) As IVRNode
                If children.ContainsKey(signal) Then
                        Return children(signal)
                End If
                Return Nothing
        End Function
End Class

Code 6 - The class that stores the IVR tree nodes

The IVR tree navigation is made by receiving DTMF signals. This means that the call need to be subscribed for the DtmfReceived event and a specified event handler needs to be written like the one in Code 7.

The class always stores the actually selected IVRNode that stores in a Dictionary object all its children. When a DTMF signal is received, the method checks if it is a star of a hash mark as they hold special meaning. Pressing the star will always restart the actual tree node message, pressing the hash will navigate back to the root if you are not there already. When the caller pressed a number key on the keypad, the method tries to get the actual node's child that is attached to that DTMF signal. If there is no such child, the message will go on without any jumping in the tree structure. If the incoming DTMF signal refers to a valid child, the program will jump in the tree structure to the specified child and its message will be started. The GUI will also refreshed for showing the latest selected node.

Private Sub call_DtmfReceived(ByVal sender As Object, ByVal e As VoIPEventArgs(Of OzTuple(Of VoIPMediaType, DtmfSignal)))
        InvokeGUIThread(Sub() label1.Text = String.Format("DTMF signal received: {0} ", e.Item.Item2.Signal))
        'you can restart the actual message by pressing star
        If e.Item.Item2.Signal = 10 Then
                ivrReader.StopStreaming()
                ivrReader.AddAndStartText(selectedNode.message)
        End If
        'pressing hash gets you back to the main menu
        If e.Item.Item2.Signal = 11 AndAlso (Not atRoot) Then
                atRoot = True
                selectedNode = root
                selectedTreeNode = root.treeNode
                InvokeGUIThread(Sub()
                        treeView1.SelectedNode.ForeColor = Color.Black
                        treeView1.SelectedNode = selectedTreeNode
                        treeView1.SelectedNode.ForeColor = Color.Red
                End Sub)
                ivrReader.StopStreaming()
                ivrReader.AddAndStartText(selectedNode.message)
        End If
        'stepping into a submenu if exists
        If selectedNode.getChild(e.Item.Item2.Signal) IsNot Nothing Then
                atRoot = False
                ivrReader.StopStreaming()
                selectedNode = selectedNode.getChild(e.Item.Item2.Signal)
                selectedTreeNode = selectedNode.treeNode
                InvokeGUIThread(Sub()
                        treeView1.SelectedNode.ForeColor = Color.Black
                        treeView1.SelectedNode = selectedTreeNode
                        treeView1.SelectedNode.ForeColor = Color.Red
                End Sub)

                ivrReader.AddAndStartText(selectedNode.message)
        End If
End Sub

Code 7 - Navigating according to the received DTMF signal

The incoming call is automatically accepted (Code 8).

Private Sub softPhone_IncomingCall(ByVal sender As Object, ByVal e As VoIPEventArgs(Of IPhoneCall))
        InvokeGUIThread(Sub()
                'inComingCall = true;
                [call] = e.Item
                WireUpCallEvents()
                [call].Accept()
        End Sub)
End Sub

Code 8 - The IVR softphone automatically answers the incoming calls

The IVR tree information is read up using text to speech conversion (Code 9).

Case CallState.InCall

        connector.Connect(ivrReader, mediaSender)

        mediaSender.AttachToCall([call])
        mediaReceiver.AttachToCall([call])

        ivrReader.AddAndStartText(selectedNode.message)

Code 9 - The IVR node data is read up using text to sppech conversion

This article introduced you the basic knowledge about how you can build a simple IVR server in Visual Basic .NET and showed how Ozeki VoIP SIP SDK can help you to fulfill your wishes about this topic. If you have read through this page carefully, you already have all the knowledge you need to start on your own solution.

As you are now familiar with all the terms concerning this topic, now it is time to take a step further and explore what other extraordinary solution Ozeki VoIP SIP SDK can provide to you.

If you have any questions or need assistance, please contact us at info@voip-sip-sdk.com

You can select a suitable Ozeki VoIP SIP SDK license for your project on licensing page

Related Pages