Programmable Voice

  1. Home
  2. Docs
  3. Programmable Voice
  4. Tutorials
  5. Getting Started

Getting Started

Download

This tutorial is a walkthrough explanation of the Getting Started sample solution that can be downloaded from your demo dashboard.  If you haven’t already, sign up for a demo account and get 100 minutes of free call time for the next 30 days with the Voice Elements servers.

This tutorial covers how to write simple Programmable Voice and SMS applications using Voice Elements. It is important to note that this project is set up to run either as a windows service or as a Windows form.  While you are debugging, you will want to simply start the application using the default Windows form.  Once you are ready to deploy for production, we recommend that you install as a Windows Service. For more information, see Documentation for .NET Windows Services.

Once you have the project downloaded, unzip it and open GettingStarted.sln with Visual Studio.

This application is already complete and ready to go, so you can test it out to see what it does before you start to read through the code to see how it works.  When you first compile the solution, it will automatically get the Voice Elements Client from NuGet.

Voice Elements MainCode

The core class of this project is IvrApplication. This class contains a lot of logic that sets up the application as a windows service so you can ignore a lot of the code in it for now. The most important method here is MainCode().

When the application is run, it starts a new thread which runs MainCode(). This connects to the Voice Elements servers in the cloud. Then loops indefinitely checking for new tasks to run, and inbound call events.

Note that Log.Write() is used frequently to log call progress and help with debugging. It is recommended that you continue to do this as you program your own Voice Elements applications.

The first thing MainCode() does is connect to the Voice Elements servers. This is done by constructing a new TelephonyServer object passing in server ip, username, and password as parameters. These values have already been generated for your account but you can change them in your Settings.settings file.

MainCode() also sets the CacheMode on the TelephonyServer object.

ClientSession mode means that the server will stream and cache the files to and from your client machine. These files are flushed after you disconnect. Server mode means that the files reside on the server and will use the full path name to find them there. Note that Server mode can only be used on your own dedicated Voice Elements server.

After connecting to the server and setting its cache mode the new call event should be subscribed to. This sets a method to be called when an incoming call is received. In this example TelephonyServer_NewCall() is the method to be called on new incoming call events.

RegisterDNIS() is then called on the TelephonyServer to tell the server which phone numbers the application will handle. This method can be called with no parameters to instruct Voice Elements to handle calls from all phone numbers on your account. Otherwise you can specify numbers to handle as parameters.

MainCode() then sets everything on the TelephonyServer needed for SMS. Private and public keypairs have already been generated for your account, located in Settings. The API Keys can be found and changed in the customer portal.  The TelephonyServer is then subscribed to the TelephonyServer_SmsMessage() method for handling inbound texts and the TelephonyServer_SmsDeliveryReport() method for logging message delivery status.

try
{
    Log.Write("Connecting to: {0}", Properties.Settings.Default.PhoneServer);

    s_telephonyServer = new TelephonyServer("gtcp://"; + Properties.Settings.Default.PhoneServer, Properties.Settings.Default.UserName, Properties.Settings.Default.Password);

    // CHANGE YOUR CACHE MODE HERE
    s_telephonyServer.CacheMode = VoiceElements.Interface.CacheMode.ClientSession;

    // SUBSCRIBE to the new call event.
    s_telephonyServer.NewCall += new VoiceElements.Client.NewCall(TelephonyServer_NewCall);
    s_telephonyServer.RegisterDNIS();

    s_telephonyServer.SmsMyPrivateKeyXml = Properties.Settings.Default.CustomerKeyPairXml;
    s_telephonyServer.SmsBorderElementsPublicKeyXml = Properties.Settings.Default.BorderKeyPairXml;
    s_telephonyServer.SmsMessage += TelephonyServer_SmsMessage;
    s_telephonyServer.SmsDeliveryReport += TelephonyServer_SmsDeliveryReport;

    // Subscribe to the connection events to allow you to reconnect if something happens to the internet connection.
    // If you are running your own VE server, this is less likely to happen except when you restart your VE server.
    s_telephonyServer.ConnectionLost += new ConnectionLost(TelephonyServer_ConnectionLost);
    s_telephonyServer.ConnectionRestored += new ConnectionRestored(TelephonyServer_ConnectionRestored);
}

Inbound Call

Let’s take a look at the logic for taking care of an inbound call. In the MainCode() of IvrApplication we set TelephonyServer_NewCall() to be called when a new phone call is received. This generates the ChannelResource which you can basically think of as the object that is the phone line. The ChannelResource class contains all of the methods and properties that you would expect to be able to perform with a phone line. The InboundCall class is used for handling all of the logic for inbound phone calls.

TelephonyServer_NewCall() constructs a new InboundCall object for which the TelephonyServer object and the ChannelResource object are provided as parameters.

The RunScript() method is then called on the new InboundCall object. This method contains much of the logic for programmable voice on inbound phone calls.

static void TelephonyServer_NewCall(object sender, VoiceElements.Client.NewCallEventArgs e)
{
    try
    {
        Log.Write("NewCall Arrival! DNIS: {0}  ANI: {1}  Caller ID Name: {2}", e.ChannelResource.Dnis, e.ChannelResource.Ani, e.ChannelResource.CallerIdName);

        InboundCall inboundCall = new InboundCall(s_telephonyServer, e.ChannelResource);
        inboundCall.RunScript();
    }
    catch (Exception ex)
    {
        Log.WriteException(ex, "IvrApplication::NewCall");
        e.ChannelResource.Disconnect();
        e.ChannelResource.Dispose();
    }
}

RunScript() contains the basic logic for handling an inbound phone call. The call is answered by calling the Answer() method on the ChannelResource. Then text to speech and recorded messages can be played by calling PlayTTS() and Play() on the VoiceResource. The phone call is then disconnected by calling Disconnect() on the ChannelResource. This can also be used to reject a call instead of answering.

try
{
    // Answer the call
    Log.WriteWithId(m_channelResource.DeviceName, "Answering...");
    m_channelResource.Answer();

    // Play text
    Log.WriteWithId(m_channelResource.DeviceName, "Playing text to speech...");
    m_channelResource.VoiceResource.PlayTTS("Hello, " + Properties.Settings.Default.FirstName + ".");

    // Play a recorded message
    Log.WriteWithId(m_channelResource.DeviceName, "Playing welcome message...");
    m_channelResource.VoiceResource.Play("..\\..\\WelcomeMessage.wav");


    // Uncomment this to test receiving digits from the caller
    // ReceiveDigits();


    // Uncomment this to see how a voicemail message could be recorded
    // RecordVoicemail();
}

Programmable Voice Methods

The InboundCall class also contains some methods for some more advanced voice operations ReceiveDigits() contains the basic structure for getting numeric input from the connected phone. This method is set up to simply accept input, then text to speech that input back to the caller.

This is done by first setting the ClearDigitBuffer property of the VoiceResource to false. This makes it so that if the user begins to press digits during the prompt for input, those digits will not be lost.

The MaximumDigits property is also set, So that the program will only take up to that many digits as input.

The TerminationDigits property can also be set. This sets the next voice function to terminate on the input of any of these digits. The GetDigits() method is the called on the VoiceResource. At this point the input digits are located in the DigitBuffer property of the VoiceResource as a string. Be sure to reset the ClearDigitBuffer property back to true. The TerminationDigits property can also be reset.

private void ReceiveDigits()
{
    // Use the log file often to help with debugging.
    Log.Write("Prompting for extension...");
    
    // Prompt the user for an extension
    m_voiceResource.PlayTTS("Please enter an extension, followed by the Pound sign");
    
    // Instruct the voice resource NOT to clear the digit buffer on the next voice function.
    m_voiceResource.ClearDigitBuffer = false;
    
    // Instruct the voice resource to return a maximum of 10 digits
    m_voiceResource.MaximumDigits = 4;
    
    // Instruct the voice resource to terminate the next voice function on just a "#" DTMF digit.
    m_voiceResource.TerminationDigits = "#";
    
    // Wait until the above condition are met.
    _voiceResource.GetDigits();
    
    // Log the response.
    Log.Write("Digits Returned: " + m_voiceResource.DigitBuffer);
    
    // On the next voice function, clear the digit buffer.
    m_voiceResource.ClearDigitBuffer = true;
    
    // On the next voice function, Do not permit the play to be terminated
    m_voiceResource.TerminationDigits = "";
    
    // Play the prompt: "You have entered..."
    m_voiceResource.PlayTTS("You entered " + m_voiceResource.DigitBuffer);
}

RecordVoiceMail() contains the basic structure for recording a voice mail. The caller is first prompted to record a message with the PlayTTS() method. A filename is also created and stored as a string. The TerminationDigits property can also be set. This sets the next voice function to terminate on the input of any of these digits. The Record() method is then called on the VoiceResource with the desired file name as a parameter. At this point the program is recording and will stop recording when on of the TerminationDigits is pressed by the caller.

private void RecordVoicemail()
{
    // Play prompt text (or play your own previously recorded file too)
    m_voiceResource.PlayTTS("Please record your message");

    string filename = "VM_" + DateTime.Now.ToString("yyMMddhhmmss") + m_channelResource.DeviceName + ".wav";

    // Log Often
    Log.Write("Recording to " + filename);

    // On the next voice function, stop on any DTMF digit.
    m_voiceResource.TerminationDigits = "ANY";

    // Record the caller's message to your local hard disk
    m_voiceResource.Record(filename);

    // Log often
    Log.Write("Playing " + filename);

    // On the next voice function, clear the digit buffer.
    m_voiceResource.ClearDigitBuffer = true;

    // Play back the recorded message to the caller.
    m_voiceResource.Play(filename);
}

Outbound Call

Let’s look at how making an outbound call with Voice Elements works. The IvrApplication class contains the method MakeOutboundCall()  which is called when the button is clicked on the GUI. This project uses the OutboundCall class to handle the logic for outbound calls. This method first constructs a new OutboundCall object passing in the TelephonyServer object and the number that is to be called. The constructor creates a new ChannelResource to the TelephonyServer. A new thread is then started to call the RunScript() method on the new OutboundCall object.

public static void MakeOutboundCall(string number)
{
    OutboundCall outbound = new OutboundCall(s_telephonyServer, number);

    // Always spawn calls on new threads
    ThreadStart ts = new ThreadStart(outbound.RunScript);
    Thread t = new Thread(ts);
    t.Name = "Outbound";

    t.Start();
}

The RunScript() method contains all of the logic for making an outbound call. It first sets the OriginatingPhoneNumber property of the ChannelResource, which sets the outbound Caller ID. It then sets the MaximumTime property on the ChannelResource so that the call will fail after 30 seconds if it does not connect. To actually place the call, the Dial() method is called on the ChannelResource passing in the number to be called as a parameter. This method returns a DialResult property which is used to determine if the call connects or not. RunScript() then has logic to determine what to do if the call is answered or not. If the call is answered here is where more voice operations logic can be located. The call is then disconnected and all of the resources are cleaned up.

try
{
    // Use WriteWithId to differentiate between separate instances of the class
    Log.WriteWithId(m_channelResource.DeviceName, "OutboundCall Script Starting");
    Log.WriteWithId(m_channelResource.DeviceName, "Dialing {0}", m_numberToCall);

    // With this, the server will detect if a human or machine answers the phone
    // m_channelResource.CallProgress = CallProgress.AnalyzeCall;

    // You can display any outbound Caller ID phone number if needed (this is disabled for testing)
    m_channelResource.OriginatingPhoneNumber = Properties.Settings.Default.TestPhoneNumber;

    // Instruct the server to wait no more then 30 seconds for a connection
    m_channelResource.MaximumTime = 30;

    // Place the call
    DialResult dr = m_channelResource.Dial(m_numberToCall);

    Log.WriteWithId(m_channelResource.DeviceName, "The dial result for {0} was: {1}", m_numberToCall, dr);

    if (dr == DialResult.Connected)
    {
        Log.WriteWithId(m_channelResource.DeviceName, "Playing File..");
        m_voiceResource.Play("../../WelcomeMessage.wav");
    }
    else
    {
        Log.WriteWithId(m_channelResource.DeviceName, "Unexpected dial result, cancelling Call");

        if (dr == DialResult.OperatorIntercept && m_channelResource.GeneralCause == 402)
            Log.WriteWithId(m_channelResource.DeviceName, "You have ran out of minutes. Contact customer support to have more added");
    }
}

SMS

Let’s take a look at the logic behind programmable SMS using Voice Elements. In the MainCode() of IvrApplication private and public key pairs are set on the TelephonyServer. And methods are subscribed to for the SmsMessage event and SmsDeliveryReport event. TelephonyServer_SmsMessage() is called when a SmsMessage event is generated, this is when a message is received as an inbound text. This is where you would put the logic for handling inbound SMS messages. And the TelephonyServer_SmsDeliveryReport() method is called when a SmsDeliveryReport event is generated. This tracks the delivery status of the message.

private static void TelephonyServer_SmsMessage(object sender, SmsMessageEventArgs e)
{
    Log.Write("Received SMS (From: {e.SmsFrom}, To: {e.SmsTo}): {e.SmsMessage}");
}

private static void TelephonyServer_SmsDeliveryReport(object sender, SmsDeliveryReportEventArgs e)
{
    if (string.IsNullOrWhiteSpace(e.DeliveryDescription) && e.DeliveryState.ToLower() == "waiting")
    {
        Log.Write("Received SMS Delivery Report (From: {e.SmsFrom}, To: {e.SmsTo}): Message delivery is pending...");
    }
    else
    {
        Log.Write("Received SMS Delivery Report (From: {e.SmsFrom}, To: {e.SmsTo}): {e.DeliveryDescription}");
    }
}

SendSms() is the method that handles logic for outbound sms messages. This method is called by the gui when a button is pressed, passing in the number and message to be texted as parameters. The SmsSendMessage(string message, string toSmsNumber, string fromSmsNumber, string customerTag, out Guid messageGuid, out string[] headers, out string[] sdp) method is called to actually send the sms which returns a sipStatusCode.

public static void SendSms(string number, string smsText)
{
    try
    {
        Guid messageGuid;
        string[] headers;
        string[] sdp;

        var sipStatusCode = s_telephonyServer.SmsSendMessage(smsText, number, Properties.Settings.Default.TestPhoneNumber, null, out messageGuid, out headers, out sdp);
        Log.Write("SMS Result: {0}", sipStatusCode);
    }
    catch (Exception ex)
    {
        Log.WriteException(ex, "SendSms()");
    }
}
Was this article helpful to you? Yes 6 No

How can we help?