Prerequisites


  1. First of all, a development environment is basically needed. Concerning that the code has been written in C#, you need for example a MS Visual Studio (download: http://www.microsoft.com/hu-hu/download/visualstudio.aspx?q=visual+studio) (or any other development environment that supports this programming language). In addition, you need to install .NET Framework (download: http://www.microsoft.com/hu-hu/download/details.aspx?id=30653) your PC.
  2. To be able to develop any VoIP application, you need to add some VoIP components into your references in MS Visual Studio (or something else). For this purpose, in this project the background support of Ozeki VoIP SIP SDK was used. This way, Ozeki VoIP SIP SDK (download: http://voip-sip-sdk.com/p_21-download-ozeki-voip-sip-sdk-voip.html) installed on your PC is also required.
  3. Finally, for using your application, there is a need for a phone system. If you do not have any, download and use the free trial version of Ozeki Phone System XE (http://www.ozekiphone.com). (It was also built by using Ozeki VoIP SIP SDK.)

Before starting the project…


The following guide explains how to improve your existing softphone with autodialer functionality. If you have a softphone that is ready to be more professional, just go ahead follow the instructions below. If you have not any softphone, first you need one. As this article focuses on the implementation of the autodialer, in this tutorial the process of the softphone development is not described. On the following pages you can find detailed information on how to create your own VoIP softphone application in C# that can be used to make and receive phone calls through the Internet:

At Codeplex:
https://csharpsoftphonevoip.codeplex.com/
At the official website of Ozeki VoIP SIP SDK:
http://voip-sip-sdk.com/p_532-how-to-build-a-softphone-with-ozeki-voip-sip-sdk-voip.html

Code analysis


It is very important to design and separate functions and classes carefully, because the autodialer should be able to communicate with users, read and parse (.csv) files, make and manage parallel calls, use TextToSpeech function, etc. In this project five classes have been used:
  1. Softphone.cs that is a simple softphone which is able to register to a PBX and it can create call objects.
  2. Program.cs is a class that is used to communicate with users (it asks them about registration information, amount of simultaneous calls and a .csv file path that contains the phone numbers).
  3. CallInfo.cs whose instances are functioning as types (each CallInfo objects represents a line within the csv file and contains a telephone number and a message separately).
  4. CallHandler.cs that is used to create instances for each of the calls made simultaneously in order to handle those separately in the same time.
  5. Autodialer.cs class creates CallHandler objects to manage the calls defined by the CallInfo objects.

The implementation of softphone.cs and program.cs


1. Softphone.cs

This class is able to register to a PBX, provides information about the state of the phone line and creates call objects as well. (Note: As the softphone does not need to be able to receive incoming calls, it should nott be registered to a PBX. It means that the registrationRequired field of the SIP account can be set to "false". Without registering to a PBX, the autodialer is still able to dial phone numbers through the PBX.)

SIP Registration

As this article focuses on the implementation of the autodialer, the SIP registration – as the initiasl step of softphone development – does not described here. Find out more about SIP account, NAT Traversal methods and SIP registration at:
http://voip-sip-sdk.com/p_535-how-to-register-to-a-pbx-using-sip-account-voip.html

Create call objects

The softphone.cs class has a public function which will be called within the CallHandler class to create call objects:
    public IPhoneCall CreateCall(string member)  
    {  
        return _softphone.CreateCallObject(_phoneLine, member);  
    }  

2. Program.cs

This class uses a softphone instance, handles the state of the phone line, asks the user about SIP registration information, amount of simultaneous calls and a .csv file path, reads, parses the given file and creates CallInfo objects to represent the file's lines. When these are done, it calls the Start() method of the Autodialer class.

Specify the number of simultanous calls

Specifying that how many calls can be made simultaneously is very important, because network providers usually restrict the amount of simultaneous calls. On order to avoid any further complications, set this integer number. If you set it to "20" there will be 20 or less outgoing calls simultaneously.

Store and provide call information in a csv file

A csv file (comma-separated values) stores tabular data (numbers and text) in plain-text form.
An example csv file:
phonenumber1;message1
phonenumber2;message2
This demo project is able to handle both of ’,’ and ’;’ as separation sign.
Since the file path has already been set by default or by the user, it can be opened and read by the help of a StreamReader object, which reads the file line by line until the end of the file:
static void ReadCSV()  
    {  
        try  
        {  
            using (StreamReader sr = new StreamReader(_path))  
            {  
                string line;  
                while ((line = sr.ReadLine()) != null)  
                {  
                    ParseCSVLineToObjectList(line);  
                }  
            }  
            StartAutodialer();  
        }  
        catch (Exception ex)  
        {  
            Console.WriteLine("Error occured: {0}", ex.Message.ToString());  
        }  
    }  


Every line contains a telephone number, a message, and a separator sign between them, so the application should split them by those characters, and create CallInfo object from the data, as you can see below:
static void ParseCSVLineToObjectList(string line)  
    {  
        try  
        {  
            string[] parse = line.Split(',', ';');  
            _callInfo = new CallInfo(parse[0], parse[1]);  
            _callList.Add(_callInfo);  
        }  
        catch (Exception ex)  
        {  
            Console.WriteLine("Error occured: {0}", ex.Message);  
        }  
    }  


After this the list of the CallInfo objects will be completed, and the autodialer is ready to use. For creating the Autodialer instance, youi need to set three parameters:
*a softphone that will communicate with the called parties
*the list of the CallInfo objects (that is: the phone numbers to be called, and the messages to be played)
*and the maximum amount of calls can be made simultaneously:
static void StartAutodialer()  
    {  
        _autodialer = new Autodialer(_mySoftphone, _callList, _maxConcurrentCall);  
        _autodialer.Start();  
    }  


The implementation of the autodialer class


3. CallInfo.cs

Each CallInfo object is a line within the csv file, which is being used as a complex value, as it stores a phone number and a message to be sent to that party. CallHandler objects will be created for all of the CallInfo objects, to manage the calls separately.

4. CallHandler.cs

The softphone can handle multiple calls simultaneously, and each of those is beinghandled by a CallHandler instance, set by a CallInfo object. Since a CallInfo object stores a phone number, the call will be created to that number, and the message is being passed to the TextToSpeech() method which converts the text message to audio data, than being played into the call, when that is being answered.

Manage simultaneous calls

Instances of the class are being created from information stored in CallInfo objects, and within theStart() method of the class, a call object is being created by the Softphone class's CreateCall()method to the CallInfo object's PhoneNumber value, and it also subscribes to the calls' events etc., than makes the call.
public void Start()  
    {  
        var call = _softphone.CreateCall(_callInfo.PhoneNumber);  
        call.CallStateChanged += OutgoingCallStateChanged;  
        call.CallErrorOccured += OutgoingCallErrorOccured;  
        _mediaSender.AttachToCall(call);  
        call.Start();  
    }  


Since the application is being notified when a call's state is being changed, tasks can be done during those changes:
* When the call is being Answered, the CallInfo object's Message value is being played into the call with the help of the TextToSpeech() method.
* The call can be ended by several reasons: the destination is busy or could not be reached, the call is completed etc. In these cases, the Completed event is being invoked:
private void OutgoingCallStateChanged(object sender, VoIPEventArgs<CallState> e)  
    {  
        if (e.Item == CallState.Answered)  
        {  
            TextToSpeech(_callInfo.Message);  
        }  
        else if (e.Item.IsCallEnded())  
        {  
            var handler = Completed;  
            if (handler != null)  
                handler(this, EventArgs.Empty);  
        }  
    }  


Text-to-Speech

Ozeki VoIP SIP SDK provides media handler, called TextToSpeech for the purpose to convert text to audio data. The TextToSpeech() method uses this media handler as an AudioHandler to send the CallInfo object's Message value into the call, through a PhoneCallAudioSender object:
void TextToSpeech(string text)  
    {  
        var textToSpeech = new TextToSpeech();  
        _audioHandler = textToSpeech;  
  
        _connector.Connect(_audioHandler, _mediaSender);  
        textToSpeech.AddAndStartText(text);  
    }

5. Autodialer.cs

The Autodialer class creates the CallHandler instances from CallInfo objects and starts them by calling their Start() method.
This class manages two lists:
  • list of CallInfo objects
  • list of CallHandler objects

With the help of these lists and the previously introduced classes, it creates CallHandler instances for all of the members of the CallInfos' list. (It won't work with more calls in the same time, than it was set before by the user.)

public void Start()  
    {  
        Task.Factory.StartNew(() =>  
        {  
            foreach (var callInfo in _callList)  
            {  
                if (_currentConcurrentCall < _maxConcurrentCall)  
                {  
                    StartCallHandler(callInfo);  
                }  
                else  
                {  
                    _autoResetEvent.WaitOne();  
                    StartCallHandler(callInfo);  
                }  
            }  
        });  
    }  


If there would be too much call, the application waits for a call to be ended.

What happens, when the CallHandler is being started?

When the CallHandler is being started, it increases the amount of currently managed calls, a CallHandler object is being added to the list of the CallHandlers, and it also checks if there is another call to be made by calling the Start() method of the class.

What happens, when the CallHandler's job is done?

The CallHandler object is being removed from the list of the CallHandlers, and the amount of currently managed calls is being decrased. The application is also being notified about the completed call, so it can start to make a new call.

Summary


Summarizing, improving your softphone can be quite easy if you use prewritten VoIP components. I found Ozeki VoIP SIP SDK really effective. If you use it, there is no need to worry about the necessary network protocols and technical details. Just download the source code, follow the instructions and enjoy the performance of your advanced VoIP network!

Get more related information / tutorial


Download required software:



Further VoIP example projects:



Good luck!

Last edited May 9, 2014 at 12:55 PM by simonrobert, version 23