Conferencing

Conferencing allows you to let 3 or more parties speak to each other. It can also be used for advanced features like coaching.

What is conferencing?

When you want two parties to speak to each other, you can use Full Route both Channel Resources together so they can hear one another. If you want to add a third person, you would need to create a conference and then add all the associated Channel Resources to the conference so that all parties can speak to each other.

Using Coach / Pupil modes, you can determine which parties can listen and hear each other. For example, let’s say you run a call center and you want supervisors to listen in on new employees. You could add them as a coach so that what the coach said is only heard by the pupil and not by the other person that the pupil is speaking to.

How do I add conferencing to my app?

The first step is to get a Conference resource. You can do this by calling the GetConference() function on the TelephonyServer class, like below:

Conference m_Conference = TelephonyServer.GetConference();

After creating the conference, you can add the associated parties:

m_Conference.Add(ChannelResource);
m_Conference.Add(ChannelResource2);
m_Conference.Add(ChannelResource3);

When users hangup, you will then want to remove their associated Channel Resources:

m_Conference.Remove(ChannelResource);

Lastly, once you are finished with a conference resource you will need to dispose it:

m_Conference.Dispose();

Which participants voices get mixed?

Our conferencing algorithm allows 3 mixed talkers to speak at a time (anything more and the audio quality would suffer). This means that only the three loudest talkers at any given time will be able to speak and the rest will only have access to listen.

How do I add a muted participant?

Often, when you have large conferences, you may not want everyone to have the ability to talk. It’s a good idea to add users that don’t need to speak as monitors.

m_Conference.Monitor(ChannelResource);

Do you have any sample code?

The code below shows a very basic conferencing app. That includes the following features:
1) Get a conference PIN code from a user
2) Create a conference
3) Add a user to a conference
4) Add a monitor to a conference (this is a participant that is muted)

using System;
using System.Collections.Generic;
using System.Text;
using VoiceElements.Client;
using VoiceElements.Common;
using VoiceElements.Interface;
using System.Threading;
 
namespace VoiceApp
{
    public class InboundConference
    {
        public ChannelResource ChannelResource
        {
            get;
            set;
        }
 
        public VoiceResource VoiceResource
        {
            get;
            set;
        }
 
        public TelephonyServer TelephonyServer
        {
            get;
            set;
        }
 
        public Log Log
        {
            get;
            set;
        }
 
        public string DeviceName
        {
            get;
            set;
        }
 
        public static Conference Conference
        {
            get;
            set;
        }
 
        public static object SyncVar = new object();
 
        public bool ConferenceStarted = false;
 
        private ManualResetEvent m_TerminateCall = new ManualResetEvent(false);
 
        public InboundConference(TelephonyServer telephonyServer, ChannelResource channelResource, Log log)
        {
            TelephonyServer = telephonyServer;
            ChannelResource = channelResource;
            VoiceResource = channelResource.VoiceResource;
            Log = log;
 
            // Subscribe to the Disconnected event so that we know when the user hangs up and can dispose of the necessary resources
            ChannelResource.Disconnected += new VoiceElements.Client.Disconnected(ChannelResource_Disconnected);
        }
 
        void ChannelResource_Disconnected(object sender, VoiceElements.Client.DisconnectedEventArgs e)
        {
            // This will signal to the other thread that the call has disconnected so it may now clean up the associated telephony resources.
            m_TerminateCall.Set();
        }
 
 
 
        public void RunScript()
        {
            try
            {
                Log.Write("Starting Script");
 
                ChannelResource.Answer();
                VoiceResource.TerminationDigits = "ANY";
 
                int i = 0;
 
                for (i = 0; i < 3; i++)
                {
                    if (i == 0)
                    {
                        VoiceResource.PlayTTS("Please enter your passcode followed by the pound sign");
                    }
                    else
                    {
                        VoiceResource.PlayTTS("Invalid Conference ID. Please try again. Enter the passcode followed by the pound sign");
                    }
 
                    VoiceResource.GetDigits(5, 20, "#", 4, false);
 
                    string passcode = VoiceResource.DigitBuffer;
                    VoiceResource.WipeDigitBuffer();
 
                    if (passcode == "123")
                    {
                        Log.Write("User entered correct passcode");
                        // You can add logic to do a database lookup instead!
                        break;
                    }
                }
 
                if (i >= 3)
                {
                    // Disconnect the user because they exceeded the max attempts
                    VoiceResource.PlayTTS("Goodbye!");
                    ChannelResource.Disconnect();
                    return;
                }
 
                lock (InboundConference.SyncVar)
                {
                    // We'll create the conference if it doesn't exist. Otherwise we will add the user to the conference
                    // If you would like to manage multiple conferences .NET Dictionaries come in handy, that way you can do a lookup to see if the conference they are trying to enter already exists.
                    if (InboundConference.Conference == null)
                    {
                        // Get the Conference resource
                        InboundConference.Conference = TelephonyServer.GetConference();
 
                        // Subscribe to the conference changed event
                        InboundConference.Conference.ConferenceChanged += new ConferenceChanged(Conference_ConferenceChanged);
 
                       // Set the ConferenceNotifyMode to On so that we are notified when users enter or leave the conference
                        InboundConference.Conference.ConferenceNotifyMode = ConferenceNotifyMode.On;
                    }
                }
 
 
                // We will clamp DTMF tones so that way when we add a menu other users won't hear when they press digits
                ChannelResource.ConferenceAttributes.MemberToneClamp = true;
 
                // We will add echo cancellation to improve voice quality
                ChannelResource.ConferenceAttributes.EchoCancellation = true;
 
                // Now we'll add the user to the conference
                InboundConference.Conference.Add(ChannelResource);
 
                // If you would like to add listen only (muted) participants you can user Monitors
                // Because the audio from monitors is not used for mixing in the conference you can create very large conferences if most of the participants are monitors
                //InboundConference.Conference.Monitor(ChannelResource);
 
                ConferenceStarted = true;
 
                // We will wait until the user disconnects before terminating the call
                m_TerminateCall.WaitOne();
            }
            catch (HangupException hex)
            {
                Log.Write("Hangup Detected!");
            }
            catch (Exception ex)
            {
                Log.WriteException(ex, "Unexpected Exception");
            }
            finally
            {
                Log.Write("Ending call");
                try
                {
                    ChannelResource.Disconnect();
                }
                catch { }
 
                try
                {
                    ChannelResource.Dispose();
                }
                catch { }
            }
        }
 
        void Conference_ConferenceChanged(ConferenceChangedEventArgs ccea)
        {
            Log.WriteWithId(InboundConference.Conference.ConferenceName, "The conference changed. Participants: {0} Monitors: {1}", InboundConference.Conference.Participants.Count, InboundConference.Conference.Monitors.Count);
 
            // We want to check to see if there are participants. If there are not we will dispose of the conference.
            if (!ConferenceStarted)
            {
                return;
            }
            if (InboundConference.Conference.Participants.Count > 0)
            {
                return;
            }
 
            //Unsubscribe from the conference changed event
            InboundConference.Conference.ConferenceChanged -= new ConferenceChanged(Conference_ConferenceChanged);
 
            // Dispose the conference and set to null
            InboundConference.Conference.Dispose();
            InboundConference.Conference = null;
        }
    }
}