Pages Menu
TwitterRssFacebook

Posted by on May 28, 2015 in Learn Skype Web SDK

Learn Skype Web SDK Day 21 : Up-scaling to an Audio Call

Learn Skype Web SDK Day 21 : Up-scaling to an Audio Call

This is one post in a series of more than 30, devoted to helping you get up to speed quickly with the new Skype Web SDK. Each lesson comes with source code (in GitHub) and a working demo page so you can see what it does. Check out the full list of posts on the Skype Web SDK page.


View Demo

Adding audio to an existing call isn’t as hard as it sounds. With Skype for Web API, each modality (method of communication) is handled separately. Therefore, if you have already set up an Instant Message conversation, you can easily add Audio to the mix by finding the Audio Service (which is part of the conversation) and starting it. Likewise, you can safely stop it later without worrying about impacting any other modalities which may be active, such as Instant Messaging.

It really is an simple as:

conversation.audioService.start();

and:

conversation.audioService.stop();

In the example code for this post, you start by creating an Instant Messaging conversation. Once that is established you can start and stop the audio service repeatedly.

Of course, it might be you (or your user) which initiates the audio. Your application needs to be able to respond to incoming audio requests. Provided you are subscribing to state change events on the audio of the self participant (which you tend to do anyway to know whether the call has connected or is disconnected etc) you can do this.

For incoming call requsts on an existing conversation, the state will change to “Notified”. This indicates an incoming call request. You can then accept this request to start the audio call, or choose to reject it:

 conversation.selfParticipant.audio.state.changed(function (newState) {
 if (newState == 'Notified')
 {
	 //either
	conversation.audioService.accept();
	//or
	conversation.audioService.reject();
 }

In the example code for this post, once the Instant Message conversation has been created, incoming requests to upscale to audio will show an Accept and Reject button.

 <div class="form-horizontal">
    <div class="form-group">
      <label for="username" class="col-sm-2 control-label">Username</label>
      <div class="col-sm-10">
        <input type="email" class="form-control" id="username" placeholder="Email">
      </div>
    </div>
    <div class="form-group">
      <label for="password" class="col-sm-2 control-label">Password</label>
      <div class="col-sm-10">
        <input type="password" class="form-control" id="password" placeholder="Password">
      </div>
    </div>

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <button class="btn btn-default" id="btnLogIn">Log in</button>
      </div>
    </div>

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <button class="btn btn-default" id="btnLogOut">Log out</button>
      </div>
    </div>
  </div>

  <div>
    <span id="loginStatus"></span>
  </div>

  <hr/>

  <div class="form-horizontal">
    <div class="form-group">
      <label for="contact" class="col-sm-2 control-label">Start Conversation with:</label>
      <div class="col-sm-10">
        <input type="email" class="form-control" id="contact" placeholder="Contact SIP Address">
      </div>
    </div>  

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <button class="btn btn-default" id="btnStartConversation" disabled="disabled">Start</button>
      </div>
    </div>
  </div>

  <div>
    Conversation State: <span id="lblConversationState"></span>
    <ul id='conversationText'></ul>

<hr/>

  <div class="form-horizontal">
    <div class="form-group">
      <label for="message" class="col-sm-2 control-label">Send Message:</label>
      <div class="col-sm-10">
        <input type="email" class="form-control" id="message" placeholder="Message to send">
      </div>
    </div>  

    <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <button class="btn btn-default" id="btnSendIM" disabled="disabled">Send IM</button>
      </div>
    </div>

  
   <div class="form-group">
    <label for="message" class="col-sm-2 control-label">Audio:</label>
      <div class="col-sm-10">
        <button class="btn btn-default" id="btnStartAudio" disabled="disabled">Start Audio</button>
		<button class="btn btn-default" id="btnStopAudio" disabled="disabled">Stop Audio</button>		
		<button class="btn btn-success" id="btnAcceptIncomingAudio" style="display: none;">Accept Incoming Audio</button>
		<button class="btn btn-danger" id="btnRejectIncomingAudio" style="display: none;">Reject Incoming Audio</button>
		<br/> Audio Status: <span id="lblAudioStatus"></span>
      </div>
    </div>
  </div>
  
    </div>
  
<div id="footer"></div>
  
  <!-- This is not needed for the samples to run, but adds standard headers and footers to the samples, to display title, instructions, about etc.
       If you're taking this code and using it yourself, you can remove this.-->
   <script type="text/javascript" src="../../assets/layoutcodesample-min.js"></script>
  
  <script type="text/javascript">

  <!-- These variables are only needed for laying out the code sample, they are not part of the sample code. -->
  var pageTitle = 'Upscaling an Instant Message Conversation';
  var blogPostLocation = "http://thoughtstuff.co.uk";
  var githubLocation = "http://github.com";
 


  var client;
  $(function () {
    'use strict';

	Skype.initialize({
            apiKey: 'SWX-BUILD-SDK',
        }, function (api) {
            client = new api.application();           
			
		// whenever client.state changes, display its value
		client.signInManager.state.changed(function (state) {
		$('#loginStatus').text("Login State: " + state);
		});
	
        }, function (err) {
            alert('Error loading Skype Web SDK: ' + err);
        }); 

    $('#btnLogIn').click(function () {

        // start signing in
        client.signInManager.signIn({
          username: $('#username').val(),
          password: $('#password').val()
        }).then(function () {
            //log in worked!
            alert('Logged in!');
            $('#btnStartConversation').prop('disabled', false);
            
          }, function (error) {
            //Something went wrong.
            alert(error);
          });
      });

    $('#btnLogOut').click(function () {
        // start signing out
        client.signInManager.signOut()
        .then(function () {
               //log out worked!
               alert('Logged out!');
               $('#btnStartConversation').prop('disabled', true);
               $('#btnSendIM').prop('disabled', true);
             }, function (error) {
                //Something went wrong.
                alert(error);
              });
      });

    $('#btnStartConversation').click(function () {
      StartConversation($('#contact').val());
    });

    $('#btnSendIM').click(function () {
      conversation.chatService.sendMessage($('#message').val());
    });
	
	$('#btnStartAudio').click(function () {
      conversation.audioService.start();
    });
	
	$('#btnStopAudio').click(function () {
      conversation.audioService.stop();
    });
	
	$('#btnAcceptIncomingAudio').click(function () {
		conversation.audioService.accept();
	});
	
	$('#btnRejectIncomingAudio').click(function () {
		conversation.audioService.reject();
	});
		

  function StartConversation (contactSIP) {
  //first, get the person to start a conversation with. Assume one person.
  var person;
  GetContactFromName(contactSIP).then(function (results) {
    results.forEach(function (result) {
    person = result.result;          
  });

 //create the conversation object
 conversation = client.conversationsManager.createConversation();

 //add the person to the conversation by creating a conversation participant object
 var convParticipant = conversation.createParticipant(person);
 conversation.participants.add(convParticipant);

 //add the newly created conversation to the ConversationManager list
 client.conversationsManager.conversations.add(conversation);

//register for the conversation state changing to connected.
  conversation.chatService.state.changed(function(newState){
  $('#lblConversationState').text(newState);
  if (newState == 'Connected') {
    $('#btnSendIM').prop('disabled', false); //safe to send IMs and upscale to audio now
	$('#btnStartAudio').prop('disabled', false); 
	$('#btnStopAudio').prop('disabled', false); 
  }
  });

//register for new messages added to the historyService object
  conversation.historyService.activityItems.added(function (newMsg){
    if (newMsg.type() == 'TextMessage')
    {
         var direction;
         if (newMsg.direction() == 'Incoming') 
            direction = "<--";          
         else
         {
            direction = "-->";          
          }

         $("#conversationText").append('<li><b>' + direction + '</b>&amp;nbsp; ' + newMsg.sender.displayName() + ' : ' + newMsg.text() + '</li>');  
    }
  });
 
 //register for changes in the audio state
 conversation.selfParticipant.audio.state.changed(function (newState) {
 $('#lblAudioStatus').text(newState);
 
 //if the newState is Notified, it signifies an incoming audio request, so give the user the option to accept
 if (newState == 'Notified')
 {
	   $('#btnAcceptIncomingAudio').show();
	   $('#btnRejectIncomingAudio').show();
 }
 else
 {
 	   $('#btnAcceptIncomingAudio').hide();
	   $('#btnRejectIncomingAudio').hide(); 
 }
 
 
 });
 
 //finally, just start the IM modality on the conversation (leaving audio to be upscaled manually later) 
 conversation.chatService.start();
});

}

function GetContactFromName(contactSIP)
{
 var query = client.personsAndGroupsManager.createPersonSearchQuery();
 query.text(contactSIP);
 query.limit(1);
 return query.getMore();          
}

});

</script>

Demo Online

You can try this code out against your own Skype for Business environment by going to the demo page. From here you can also download the code from GitHub if you want to host it locally, or take it and use it in your next project.

Disclaimer: This is sample code, intended to inform and educate. It is not production-ready and is lacking key components such as error handling. You use it entirely at your own risk. You should fully understand the effects, limitations and risks of the code before executing, and understand the implications of any set-up steps. By using these code examples you are using the Skype Web SDK, so you should read the Skype Software License Terms to which you are agreeing.

Good to Know

Written by Tom Morgan

Tom is a Microsoft Teams Platform developer and Microsoft MVP who has been blogging for over a decade. Find out more.
Buy the book: Building and Developing Apps & Bots for Microsoft Teams. Now available to purchase online with free updates.

1 Comment

  1. Hay Tom,

    I’m trying up-scaling to video conversation from a connected audio conversation.. but when I call videoService.start() right after audioService.state() becomes “Connected” it fails to start the video. however if I waited say for 5 seconds and tried to start the video it succeeds.. what am I doing wrong? here’s the code:

    audioService.state.changed(
    function(state) {
    if (state == "Connected") {
    videoInterval = setInterval(addVideo, 5000);
    //addVideo();
    }
    });

    function addVideo() {
    conversation.videoService.start().then(function () { conversation.selfParticipant.video.channels(0).stream.source.sink.container(document.getElementById("divSelfVideoWindow"));
    });
    }

Post a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.