Course 3 / Lecture 1

How to build an IVR with Ozeki VoIP SIP SDK

How to develop a basic IVR in C#

download icon Download: basic-ivr.zip

Here you can learn how to reach media handlers, handle devices and attach them to the call object and how to create a console application for Interactive Voice Response (IVR).

In this tutorial example we will learn the followings:

  • How to reach media handlers, handle devices and attach them to the call object
  • How to create a console application for Interactive Voice Response

What knowledges would you need?

To fully understand this guide, you might need to study the following chapters first:

  • SIP registration: you can learn how to begin softphone developing and how to be able to register to a pbx with a sip account.
    Learn more...
  • Managing media handlers: you can find here examples and a simple guide about how to be able to use, connect and manage different media handlers, and how can you attach them to calls.
    Learn more...
  • Making and accepting calls: from this guide you can learn how can your softphone make and accept calls, and handle the calls' states.
    Learn more...

Build up our solution

You want to create your own application to reproduce this solution, You have to create a new console application in Visual Studio.

We are using three classes in this example:

  • Softphone.cs
    The softphone's implementation goes here, all of it's events, methods, functions, variables.
  • CallHandler.cs
    This class is for handling the incoming calls of the IVR.
  • Program.cs
    Our class with the Main() method, this class controls the console window, interacts with the user, by using softphone and callhandler objects.

Softphone.cs

This class is used to introduce how to declare, define and initialize a softphone, how to handle some of the Ozeki VoIP SIP SDK's events and how to use some of that's functions. In other words, we would like to create a "telephone software", which has the same functions (or much more), as an ordinary mobile (or any other) phone. In the Program.cs class we will use this class to create a new softphone, so we can use the functions, we can listen to the events placed here.

First in the "using section" we need to add some extra lines

using Ozeki.VoIP;
using Ozeki.VoIP.SDK;

then make softphone and phone line object by using IsoftPhone and IPhoneLine interfaces.

The most important method in the Softphone class is the Register method which allows you to register your SIP account to the server.

public void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost, int domainPort)
{
	try
    {
       var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
       Console.WriteLine("\n Creating SIP account {0}", account);

       phoneLine = softphone.CreatePhoneLine(account);
       Console.WriteLine("Phoneline created.");

       phoneLine.RegistrationStateChanged += phoneLine_RegistrationStateChanged;

       softphone.RegisterPhoneLine(phoneLine);
    }
    catch(Exception ex)
    {
       Console.WriteLine("Error during SIP registration" + ex.ToString());
    }
}

The method gets the registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost and domainPort parameters and use them to create an SIP account with the constructor of the SIPAccount class. Then the system can create the phone line (CreatePhoneLine method) with the help of the account and it can register the phone line.

If you would like to know more about the specification of the softphone (and the creation of it), then visit our How to build a softphone page, where you can find discriptions about the most important functions, methods and events of this topic.

CallHandler.cs

This class is used to managing the incoming calls for the IVR system. If there is an inbound call, then the caller will hear a greeting message through the speaker and he/she can choose between different menu items by using DTMF buttons.

Objects

We need to create some objects to manage the incoming calls of the IVR. These are the call, mediaConnector, audioHandler, phoneCallAudioSender and greetingMessageTimer objects from the ICall interface and the MediaConnector, AudioHandler, PhoneCallAudioSender and Timer classes. You can find more details about these features of the Ozeki VoIP SDK here.

ICall call;
MediaConnector mediaConnector;
PhoneCallAudioSender phoneCallAudioSender;
Timer greetingMessageTimer;
TextToSpeech tts;
MP3StreamPlayback mp3Player;

The constructor of the class gets an ICall type parameter and it makes the basic setups for the class. It sets the interval of the greetingMessageTimer (30 seconds), what is for repeating the greeting message on the main menu level and it attaches the phoneCallAudioSender to the call.

public CallHandler(ICall call)
{
      greetingMessageTimer = new Timer();
      greetingMessageTimer.Interval = 30000;
      greetingMessageTimer.Elapsed += greetingMessageTimer_Elapsed;
      this.call = call;
      phoneCallAudioSender = new PhoneCallAudioSender();
      phoneCallAudioSender.AttachToCall(call);
      mediaConnector = new MediaConnector();
}

Methods

A method is a code block that contains a series of statements. One of the most important methods of this class is the Start() method because that will be called by the main method of the program. In this method we subscribe on the CallStateChanged and DtmfReceived events and accept the call. The call state change event is one of the most important events to notice. This informs both the server and the client about a change in the call state. The DTMF received event informs the server about the client pressed a button on his/her phone because he/she want to step into another menu level. Dual-tone multi-frequency signaling (DTMF) is used for telecommunication signaling over analog telephone lines in the voice-frequency band between telephone handsets and other communications devices and the switching center.

public void Start()
{
    call.CallStateChanged += call_CallStateChanged;
    call.DtmfReceived += call_DtmfReceived;
    call.Answer();
}

In the Start method we subscribed on the DtmfReceived event with the help of the call_DtmfReceived() method and we can set what happens when a client press a DTMF button. In this example if you press the button one, than you can hear more information about the Ozeki Ltd. and the Ozeki VoIP SDK with the help of the constructor of TextToSpeech class. By pressing button two, you can hear a sample mp3 song by calling Mp3ToSpeaker method.

void call_DtmfReceived(object sender, VoIPEventArgs<DtmfInfo> e)
{
    DisposeCurrentHandler();
    switch (e.Item.Signal.Signal)
    {
         case 0: break;
         case 1: TextToSpeech("Ozeki Informatics Ltd. is a leading mobile messaging software vendor and Ozeki VoIP SIP SDK is an excellent software development kit that allows you to establish VoIP calls from your application easily and quickly. You do not need to have expert programming skills, with the most basic programming knowledge you will be able to create extraordinary VoIP solutions with this tool."); break;
         case 2: MP3ToSpeaker(); break;
    }
}

With the TextToSpeech method you can add a your text message what you want to hear, read by the TextToSpeech engine. Just make a TextToSpeech object, connect it to the phoneCallAudioSender with the help of the Media Connector and call the AddAndStartText method. For the description of the TextToSpeech function of the Ozeki VoIP SDK, please visit this page.

private void TextToSpeech(string text)
{
    greetingMessageTimer.Stop();
	tts = new TextToSpeech();
    tts.Stopped += tts_Stopped;

    mediaConnector.Connect(tts, phoneCallAudioSender);
    tts.AddAndStartText(text);
}

By using the functions of the MP3ToSpeaker method the IVR can easily play the contents of a sample mp3 file to the caller (for example a welcome message in mp3 format). Only need to create an MP3StreamPlayback object with the file path parameter, connect it to the PhoneCallAudioSender via the mediaConnector and start the streaming (Start() method).

private void MP3ToSpeaker()
{
	greetingMessageTimer.Stop();
	mp3Player = new MP3StreamPlayback("../../test.mp3");

    mp3Player.Stopped += mp3Player_Stopped;
    mediaConnector.Connect(mp3Player, phoneCallAudioSender);
    mp3Player.Start();
}

Program.cs

This class will introduce the usage of softphone and callHandler objects and handles the console and DTMF events came from the user.

The Main() method

In the Main section you need to create the softphone object to reach the created methods in the Softphone class. Then there will be some instructions to the code and the call of the sipAccountInitialization method. We can add our SIP account values in this section. Then we need to subscribe on the IncomingCall event for handle the incoming calls.

static void Main(string[] args)
{
      callHandlers = new List<CallHandler>();
      var softphone = new Softphone();

      Console.WriteLine("/* Program usage description */");

      sipAccountInitialization(softphone);

      softphone.IncomigCall += softphone_IncomigCall;
      Console.ReadLine();
}

The sipAccountInitialization() method

This method is responsible for getting the SIP account components from the user. Like you read in the Softphone class part the SIPAccount constructor needs the next values:

  • Authentication ID
  • User name (default is the authentication ID)
  • Display name (default is the authentication ID)
  • Password
  • Domain Host (default is the local host)
  • Domain Port (default is 5060)

So you need to add your SIP account values for the program and it will call the Register method of the Softphone class with these values.

private static void sipAccountInitialization(Softphone softphone)
{
     Console.WriteLine("Please setup your SIP account!\n");
     Console.WriteLine("Please set your authentication ID: ");
     var authenticationId = Read("authenticationId", true);

     Console.WriteLine("Please set your user name (default:" +authenticationId +"): ");
     var userName = Read("userName", false);
     if (string.IsNullOrEmpty(userName))
             userName = authenticationId;

     Console.WriteLine("Please set your name to be displayed (default: " +authenticationId +"): ");
     var displayName = Read("displayName", false);
     if (string.IsNullOrEmpty(displayName))
             displayName = authenticationId;

     Console.WriteLine("Please set your registration password: ");
     var registrationPassword = Read("registrationPassword", true);

     Console.WriteLine("Please set the domain name (default: your local host): ");
     var domainHost = Read("domainHost", false);
     if (string.IsNullOrEmpty(domainHost))
             domainHost = NetworkAddressHelper.GetLocalIP().ToString();
     Console.WriteLine(domainHost);

     Console.WriteLine("Please set the port number (default: 5060): ");
     int domainPort;
     string port = Read("domainPort", false);
     if (string.IsNullOrEmpty(port))
     {
           domainPort = 5060;
     }
     else
     {
           domainPort = Int32.Parse(port);
     }
     Console.WriteLine("\nCreating SIP account and trying to register...\n");
     softphone.Register(true, displayName, userName, authenticationId, registrationPassword, domainHost, domainPort);
}

The Read() function

This function has two parameters:

  • inputName,
  • readWhileEmpty.

If the readWhileEmpty is false, that means there is a default value of this component (for example 5060 for the Domain Port) so the input can be empty or null. Else you need to add an (correct) input for the system and the function will return with the input to the Main method.

private static string Read(string inputName, bool readWhileEmpty)
{
     while (true)
     {
          string input = Console.ReadLine();

          if (!readWhileEmpty)
          {
               return input;
          }

          if (!string.IsNullOrEmpty(input))
          {
               return input;
          }

          Console.WriteLine(inputName +" cannot be empty!");
          Console.WriteLine(inputName +": ");
    }
}

The softphone_IncomingCall method

If we would like to be notified when there is an incoming call, we need to set an event for this purpose. This is the IncomingCall event. When the event notifies that there is a call waiting to be accepted, in this case the call has to be accepted by the IVR system. In this method there is a callHandler object for managing the metehods of the CallHandler class. If there is an incoming call, then call Start() method of the CallHandler.

static void softphone_IncomigCall(object sender, Ozeki.VoIP.VoIPEventArgs<Ozeki.VoIP.IPhoneCall> e)
{
     Console.WriteLine("Incoming call!");
     var callHandler = new CallHandler(e.Item);
     callHandler.Completed += callHandler_Completed;

     lock (callHandlers)
           callHandlers.Add(callHandler);

     callHandler.Start();
}

Conclusion

After reading this documentation and studying the source code, we must be familiar about how to add reference to the Ozeki VoIP SIP SDK and how to create a basic IVR system with the help of the elements of this SDK.

The next example will introduce how to:

  • Use the BlindTransfer() method
  • Make a voice controlled IVR

More information