Creating a jQuery Music Player using the Ignite UI Linear Gauge.

Marina Stoyanova / Friday, November 8, 2013

The Linear gauge widget can be very handy if you want to create a custom music player. In this blog you can see how to use this control to build the volume slider and the player slider while making a Music player like the one described in O'Reilly’s jQuery Cook Book. The example picture demonstrates the final result.

Step one

First of all, we need some HTML tags to host our player elements. We are going to use the HTML5 audio tag to hold our song.

  1. <audio src="/Music/song.mp3">audio>

Then we are going to set the basics for the player. We will need a play and pause button, a volume button and of course the controls for progress and volume. You can use some CSS to style those elements

  1. <div class="musicPlayer">
  2.     <div class="buttons">
  3.         <a class="playpause" href="#">
  4.             <span class="ui-icon ui-icon-play">span>
  5.             <span class="ui-icon ui-icon-pause">span>
  6.             a>
  7.     div>
  8.     <span class="currenttime">span>
  9.     <div id="linearGauge">div>
  10.     <span class="duration">span>
  11.     <div class="volume-container">
  12.         <div class="volume">
  13.             <a href="#" >
  14.                 <span class="vol ui-icon ui-icon-volume-on">span>
  15.             a>
  16.         div>
  17.     div>
  18.     <div id="linearGauge1">div>
  19. div>

Step two

Now that we have the construction of our music player let’s make it functional. First you will need the following code to enable the play and the pause buttons.

  1. $('.buttons .playpause').click(function () {
  2.     var player = $(this).parents('.musicPlayer');
  3.     if (player.is('.paused')) {
  4.         $('.musicPlayer').removeClass('paused');
  5.         audioEl.play();
  6.     } else {
  7.         $('.musicPlayer').addClass('paused');
  8.         audioEl.pause();
  9.     }
  10.     return false;
  11. }).hover(function () { $(this).addClass('ui-state-hover'); },
  12. function () { $(this).removeClass('ui-state-hover'); })
  13. .focus(function () { $(this).addClass('ui-state-focus'); })
  14. .blur(function () { $(this).removeClass('ui-state-focus'); });
  15.  
  16. $('.musicPlayer').addClass('paused');

For the slider we are using the linear gauge. As you will see from the following lines, the maximum value for the control depends on the song’s duration. That way the chart will always respond to the length of the currently playing song. We will make two ranges: one static and one dynamic, which will change with the progress of the song.

 

  1. var newduration = audioEl.duration / 60;
  2.  
  3. //linear gauge
  4.  
  5. $(".musicPlayer #linearGauge").igLinearGauge({
  6.     width: "65%",
  7.     height: "45px",
  8.     minimumValue: "0",
  9.     maximumValue: newduration,
  10.     minorTickCount: 1,
  11.     needleBreadth: 4,
  12.     interval: 1,
  13.     value: 1.2,
  14.     labelInterval: 1,
  15.     needleShape: "custom",
  16.     needleInnerBaseWidth: 0.1,
  17.     needleOuterBaseWidth: 0.1,
  18.     needleInnerExtent: .6,
  19.     needleOuterExtent: 0.1,
  20.     needleInnerPointExtent: .3,
  21.     needleInnerPointWidth: 0.3,
  22.     needleOuterPointExtent: 0.4,
  23.     needleOuterPointWidth: .3,
  24.     needleBrush: "black",
  25.     backingBrush: "#e6e6e6",
  26.     backingOutline: "#e6e6e6",
  27.     fontBrush: "black",
  28.     ranges: [
  29.     {
  30.         name: "666",
  31.         startValue: 0,
  32.         endValue: newduration,
  33.         brush: "#666",
  34.         innerStartExtent: .2,
  35.         innerEndExtent: .2,
  36.         outerStartExtent: 0.5,
  37.         outerEndExtent: 0.5
  38.     }, {
  39.         name: "track",
  40.         startValue: 0,
  41.         endValue: 0,
  42.         brush: "#19A3A3",
  43.         innerStartExtent: .2,
  44.         innerEndExtent: .2,
  45.         outerStartExtent: 0.5,
  46.         outerEndExtent: 0.5
  47.     }]
  48. });

To take the duration of the tune you need to make sure that its metadata is loaded. To avoid divergence of time we are going to wrap the control in a function which will execute only after the loadmetadata event occurs.

  1. audioEl.addEventListener("loadedmetadata", function () {
  2.     . . .
  3. }

In this function we will initialize the handler function for the timeupdate event where we are going to change the value of the control because we want the needle to show the current time. The other change that we are making is update  end value of the dynamic range. Again, the value that we are assigning is the current time. That way we can color only the part of the scale that corresponds to the  elapsed time of the song.

  1. $('audio').bind('timeupdate', function (event) {
  2.     $('.musicPlayer .currenttime').text(minAndSec(audioEl.currentTime));
  3.     $(".musicPlayer #linearGauge").igLinearGauge("option", "value", audioEl.currentTime / 60);
  4.     $(".musicPlayer #linearGauge").igLinearGauge("option", "ranges", [{
  5.         name: "track",
  6.         endValue: audioEl.currentTime / 60
  7.     }]);
  8. });

In the same way you can add one more range to show the buffer progress.

We want to be able to change the current time of the tune, whenever we want. For that reason we will enable the needle to drag and we will  make audio element’s current time equal to the value at which we drag the needle to.

  1. var lastPointX = 0, lastPointY = 0, isDragging = false, lastPointXVol = 0, lastPointYVol = 0;
  2.  
  3. // Start the needle drag only on a mousedown on the needle
  4. document.getElementById("linearGauge").addEventListener("mousedown", function (e) {
  5.     dragNeedle(e, true);
  6.     document.addEventListener("mousemove", mousemove);
  7.     document.addEventListener("mouseup", mouseup);
  8.  
  9. });
  10.  
  11. // Function that performs the needle drag/tap to the new point
  12. dragNeedle = function (e, isMouseDown) {
  13.     if (!isMouseDown && !isDragging) {
  14.         return;
  15.     }
  16.  
  17.     e.preventDefault();
  18.     var pointX = e.pageX - $("#linearGauge").offset().left;
  19.     var pointY = e.pageY - $("#linearGauge").offset().top;
  20.     if (isMouseDown) {
  21.         var isClickPointValid = $("#linearGauge").igLinearGauge("needleContainsPoint", pointX, pointY);
  22.         if (isClickPointValid) {
  23.             lastPointX = pointX;
  24.             lastPointY = pointY;
  25.         } else {
  26.             isClickPointValid = $("#linearGauge").igLinearGauge("needleContainsPoint", (pointX + 4 * lastPointX) / 5, (pointY + 4 * lastPointY) / 5);
  27.         }
  28.         isDragging = true;
  29.         if (!isClickPointValid) {
  30.             isDragging = false;
  31.             return;
  32.         }
  33.  
  34.     }
  35.  
  36.     var value = $("#linearGauge").igLinearGauge("getValueForPoint", pointX, pointY);
  37.     if (isNaN(value))
  38.         return;
  39.  
  40.     // Prevent needle from dragging beyond the scale bounds
  41.     var minimumValue = $("#linearGauge").igLinearGauge("option", "minimumValue");
  42.     var maximumValue = $("#linearGauge").igLinearGauge("option", "maximumValue");
  43.  
  44.     var startValue = minimumValue <= maximumvalue="" minimumvalue="" :="" span="">
  45.     var endValue = minimumValue > maximumValue ? minimumValue : maximumValue;
  46.  
  47.     if (value > startValue && value < endValue) {
  48.         $("#linearGauge").igLinearGauge("option", "value", value);
  49.         $(".musicPlayer #linearGauge").igLinearGauge("option", "ranges", [{ name: "track", endValue: value }]);
  50.         audioEl['currentTime'] = value * 60;
  51.  
  52.     } else {
  53.         value = value >= endValue ? endValue : startValue;
  54.         $("#linearGauge").igLinearGauge("option", "value", value);
  55.         $(".musicPlayer #linearGauge").igLinearGauge("option", "ranges", [{ name: "track", endValue: value }]);
  56.         audioEl['currentTime'] = value * 60;
  57.  
  58.     }
  59. }

 

Music Player

Step three

As our music player is already functional and playing the last thing to do is control the volume of the song. As we mentioned we are using another linear gauge to take care of the volume. As you can guess it is vertical and the range is set from 0 to 1. We use the hide and toggle jQuery functions to  make it appear only when the user click  on the volume icon.  In the range we set the end value to audio element’s volume.

  1. $("#linearGauge1").hide();
  2. $(".vol").click(function () { $("#linearGauge1").toggle(); });
  3. $("#linearGauge1").igLinearGauge({
  4.     width: "40px",
  5.     height: "110px",
  6.     minimumValue: "0",
  7.     maximumValue: "1",
  8.     orientation: "vertical",
  9.     minorTickCount: 1,
  10.     interval: 1,
  11.     tickEndExtent: 0.5,
  12.     scaleStartExtent: .1,
  13.     scaleEndExtent: 0.9,
  14.     value: 1,
  15.     fontBrush: "black",
  16.     ickStrokeThickness: 1,
  17.     labelInterval: 1,
  18.     backingBrush: "#B2CCCC",
  19.     ranges: [{
  20.         name: "volume",
  21.         startValue: 0,
  22.         endValue: audioEl.volume,
  23.         brush: "#D1E0E0",
  24.         outerStartExtent: 0,
  25.         outerEndExtent: 0.9
  26.     }]
  27. });

We need to be able to drag this needle if we want to change the volume. We use the same function as the above mentioned. The only difference is that we don’t set the current time but we set the volume making it equal to the value of the needle.

  1. audioEl['volume'] = value;

 

Volume

 

Conclusion

The linear gauge is not only a static control for Data Visualization, it can be dynamic and functional. You can use it to create music player, video player , thermometers and whatever else you can think of.  You can find more information about this control and how to add it to your application in my blog: “How to express your data using Ignite UI’s jQuery Linear Gauge control.”

 

Sample.

 

You can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!