9 * Represents HTML5 audio element which is utilizing Audio Service API to get/set status of currently playing song (tested with wav audio files).
10 * Audio resources are passed to the player as an array of songs and stored in {{#crossLink "AudioPlayer/model:property"}}{{/crossLink}} property.
12 * This class requires following components:
14 * * {{#crossLink "ButtonControlsObj"}}{{/crossLink}} component
16 * Audio player is extended by option to set buttons to control audio playback (play/pause, next, previous, shuffle, repeat), time progress bar, spectrum analyzer, info panel, volume control.
18 * Use following snippet to include component in your `index.html` file:
20 * <script type="text/javascript" src="./css/car/components/audioPlayer/audioPlayer.js"></script>
22 * and following code to initialize:
24 * $('#multimediaPlayer').audioAPI('init', [], "#audioPlayer", "#videoPlayer");
32 * Array of song objects. Every song object is expected to have album, artist, name, path and image properties.
39 * HTML audio or video DOM element.
47 * HTML audio DOM element.
54 * HTML video DOM element.
61 * Indicates if audio timeupdate event listener was registered.
62 * @property timeUpdateListenerLoaded
66 timeUpdateListenerLoaded: false,
68 * Instance of audio service API.
69 * @property audioPlayerService
70 * @type {AudioService}
73 audioPlayerService: null,
76 * Selector of audio control buttons (play/pause, next, previous, shuffle, repeat).
77 * @property ctrlButtonsSelector
80 ctrlButtonsSelector: null,
82 * Selector of audio time progress bar.
83 * @property timeProgressBarSelelector
86 timeProgressBarSelelector: null,
88 * Selector of audio spectrum analyzer.
89 * @property spectrumAnalyzerSelelector
92 spectrumAnalyzerSelelector: null,
94 * Selector of audio info panel.
95 * @property infoPanelSelector
98 infoPanelSelector: null,
100 * Selector of audio thumbnail image.
101 * @property thumbnailSelector
104 thumbnailSelector: null,
106 * Selector of audio volume control.
107 * @property volumeControlSelector
110 volumeControlSelector: null,
112 * Indicates if repeat option of audio/video player is turned on.
119 * Indicates if shuffle option of audio player is turned on.
120 * @property shuffleOn
126 * Indicates if audio/video is playing (is not paused).
133 * Indicates the index of the loaded audio/video.
140 * Indicates the current playback position in percentage (0% - 100%) in the loaded audio/video.
147 * Indicates the current playback position in the loaded audio/video.
148 * @property currentTime
154 * Indicates the length of the loaded audio/video.
161 * Indicates the volume in percentage (0% - 100%) of the loaded audio/video.
167 indexChangedCallback: null,
169 * Sets the audio spectrum analyzer selector.
171 * @method setSpectrumAnalyzerSelector
172 * @param selector {String} Audio spectrum analyzer selector.
174 setSpectrumAnalyzerSelector: function (selector) {
175 AudioPlayer.spectrumAnalyzerSelelector = selector;
178 * Sets the audio control buttons selector.
180 * @method setControlButtonsSelector
181 * @param selector {String} Audio control buttons selector.
183 setControlButtonsSelector: function (selector) {
184 AudioPlayer.ctrlButtonsSelector = selector;
187 * Sets the audio time progress bar selector.
189 * @method setTimeProgressBarSelector
190 * @param selector {String} Audio time progress bar selector.
192 setTimeProgressBarSelector: function (selector) {
193 AudioPlayer.timeProgressBarSelelector = selector;
196 * Sets the audio info panel selector.
198 * @method setInfoPanelSelector
199 * @param selector {String} Audio info panel selector.
201 setInfoPanelSelector: function (selector) {
202 AudioPlayer.infoPanelSelector = selector;
205 * Sets the audio info panel selector.
207 * @method setThumbnailSelector
208 * @param selector {String} Audio info panel selector.
210 setThumbnailSelector: function (selector) {
211 AudioPlayer.thumbnailSelector = selector;
214 * Sets the audio volume control selector.
216 * @method setVolumeControlSelector
217 * @param selector {String} Audio volume control selector.
219 setVolumeControlSelector: function (selector) {
220 AudioPlayer.volumeControlSelector = selector;
222 addIndexChangeListener: function(indexChangeCallback) {
223 AudioPlayer.indexChangeCallback = indexChangeCallback;
226 * Initializes the audio player user interface, binds the audio controls (control buttons, volume control, time progress bar, ...), adds audio events listeners,
227 * gets the status from audio service API, loads appropriate song from passed model/playlist and updates the UI.
230 * @param modelLib {Array} Array of audio songs.
232 init: function (modelLib, audioPlayerSelector, videoPlayerSelector) {
233 AudioPlayer.thisObj = this;
234 if (!!modelLib && modelLib.length) {
235 AudioPlayer.model = modelLib;
237 if (!!audioPlayerSelector && audioPlayerSelector !== "") {
238 AudioPlayer.audio = $(audioPlayerSelector).get(0);
240 if (!!videoPlayerSelector && videoPlayerSelector !== "") {
241 AudioPlayer.video = $(videoPlayerSelector).get(0);
243 if (!!AudioPlayer.audio) {
244 AudioPlayer.player = AudioPlayer.audio;
245 AudioPlayer.playerType = "AUDIO";
246 } else if (!!AudioPlayer.video) {
247 AudioPlayer.player = AudioPlayer.video;
248 AudioPlayer.playerType = "VIDEO";
251 if (AudioPlayer.ctrlButtonsSelector !== null) {
252 $(AudioPlayer.ctrlButtonsSelector).buttonControls('initAudioPlayerButtons');
253 $(AudioPlayer.ctrlButtonsSelector).unbind();
254 $(AudioPlayer.ctrlButtonsSelector).bind('previousSong', function () {
255 console.log("previousSong clicked");
256 AudioPlayer.previous();
258 $(AudioPlayer.ctrlButtonsSelector).bind('nextSong', function () {
259 console.log("nextSong clicked");
262 $(AudioPlayer.ctrlButtonsSelector).bind('playSong', function () {
263 console.log("playSong clicked");
264 AudioPlayer.playOn = !AudioPlayer.playOn;
265 AudioPlayer.playPause(AudioPlayer.playOn);
267 $(AudioPlayer.ctrlButtonsSelector).bind('shuffleSong', function () {
268 console.log("shuffleSong clicked");
269 AudioPlayer.shuffleOn = !AudioPlayer.shuffleOn;
270 AudioPlayer.updateUIControls();
272 $(AudioPlayer.ctrlButtonsSelector).bind('repeatSong', function () {
273 console.log("repeatSong clicked");
274 AudioPlayer.repeatOn = !AudioPlayer.repeatOn;
275 AudioPlayer.updateUIControls();
279 if (AudioPlayer.volumeControlSelector !== null) {
280 $(AudioPlayer.volumeControlSelector).noUiSlider({
286 orientation: "horizontal",
287 slide: function (aaa, ccc) {
288 var volumeSlider = parseInt($(AudioPlayer.volumeControlSelector).val(), 10);
289 console.log("new volume: " + volumeSlider);
290 AudioPlayer.volume = volumeSlider;
291 AudioPlayer.setAudioVolume(volumeSlider);
294 AudioPlayer.setAudioVolume(50);
297 if (AudioPlayer.timeProgressBarSelelector !== null) {
298 $(AudioPlayer.timeProgressBarSelelector).timeProgressBar("init");
299 $(AudioPlayer.timeProgressBarSelelector).bind('positionChanged', function (e, data) {
300 console.log("positionChanged " + data.position);
301 AudioPlayer.setAudioPosition(data.position);
305 AudioPlayer.registerPlayerListeners(AudioPlayer.audio, "AUDIO");
306 AudioPlayer.registerPlayerListeners(AudioPlayer.video, "VIDEO");
308 AudioPlayer.updateUIControls();
310 registerPlayerListeners: function(player, type) {
312 player.addEventListener('canplay', function () {
313 console.log("canplay " + type);
314 AudioPlayer.playPause(AudioPlayer.playOn);
317 player.addEventListener('canplaythrough', function () {
318 console.log("canplaythrough " + type);
319 AudioPlayer.playPause(AudioPlayer.playOn);
322 player.addEventListener('play', function () {
323 console.log("play " + type);
326 player.addEventListener('playing', function () {
327 console.log("playing " + type);
330 player.addEventListener('error', function () {
331 console.log("error " + type);
334 player.addEventListener('waiting', function () {
335 console.log("waiting " + type);
338 player.addEventListener('timeupdate', function () {
339 //console.log("timeupdate " + type + " " + player.currentTime + " " + player.duration);
340 if (!!player.currentTime && !isNaN(player.currentTime) && player.currentTime !== "Infinity" && player.currentTime > 0 &&
341 !!player.duration && !isNaN(player.duration) && player.duration !== "Infinity" && player.duration > 0) {
342 AudioPlayer.currentTime = player.currentTime;
343 AudioPlayer.duration = player.duration;
346 AudioPlayer.updateProgressInfoPanel();
347 AudioPlayer.spectrumAnalyzer(AudioPlayer.playOn);
350 player.addEventListener('ended', function () {
355 playAudioContent: function(audioContent, indexToPlay, play, type) {
356 if (!!AudioPlayer.player) {
357 AudioPlayer.player.pause();
358 AudioPlayer.player.src = "";
360 AudioPlayer.model = audioContent;
361 AudioPlayer.playOn = play;
362 AudioPlayer.playerType = type;
363 if (type === "AUDIO") {
364 AudioPlayer.player = AudioPlayer.audio;
365 } else if (type === "VIDEO") {
366 AudioPlayer.player = AudioPlayer.video;
368 AudioPlayer.loadAudio(indexToPlay);
371 * Updates the audio UI controls and panels according to loaded song and audio status.
373 * @method updateUIControls
375 updateUIControls: function () {
376 AudioPlayer.playPauseButtons(AudioPlayer.playOn);
377 AudioPlayer.shuffleButton(AudioPlayer.shuffleOn);
378 AudioPlayer.repeatButton(AudioPlayer.repeatOn);
379 AudioPlayer.nextButton(AudioPlayer.index, AudioPlayer.repeatOn, AudioPlayer.shuffleOn);
380 AudioPlayer.previousButton(AudioPlayer.index, AudioPlayer.repeatOn, AudioPlayer.shuffleOn);
381 AudioPlayer.spectrumAnalyzer(AudioPlayer.playOn);
382 AudioPlayer.volumeControl(AudioPlayer.volume);
383 AudioPlayer.updateAudioInfoPanel(AudioPlayer.index);
384 AudioPlayer.updateProgressInfoPanel();
387 * Sets the audio play/pause button to active/inactive state.
389 * @method playPauseButtons
390 * @param isPaused {Boolean} State of play/pause button.
392 playPauseButtons: function (isPaused) {
393 if (AudioPlayer.ctrlButtonsSelector !== null) {
395 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPauseActive');
397 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPlayActive');
402 * Sets the audio shuffle button to active/inactive state.
404 * @method shuffleButton
405 * @param isActive {Boolean} State of shuffle button.
407 shuffleButton: function (isActive) {
408 if (AudioPlayer.ctrlButtonsSelector !== null) {
410 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonShuffleActive');
412 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonShuffleInactive');
417 * Sets the audio repeat button to active/inactive state.
419 * @method repeatButton
420 * @param isActive {Boolean} State of repeat button.
422 repeatButton: function (isActive) {
423 if (AudioPlayer.ctrlButtonsSelector !== null) {
425 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonRepeatActive');
427 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonRepeatInactive');
432 * Sets the audio next button to active/inactive state.
435 * @param index {Number} Index of the song.
436 * @param repeat {Boolean} State of the repeat option.
437 * @param shuffle {Boolean} State of the shuffle option.
439 nextButton: function (index, repeat, shuffle) {
440 if (AudioPlayer.ctrlButtonsSelector !== null) {
441 if (repeat || shuffle || index < AudioPlayer.model.length - 1) {
442 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonNextActive');
444 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonNextInactive');
449 * Sets the audio previous button to active/inactive state.
451 * @method previousButton
452 * @param index {Number} Index of the song.
453 * @param repeat {Boolean} State of the repeat option.
454 * @param shuffle {Boolean} State of the shuffle option.
456 previousButton: function (index, repeat, shuffle) {
457 if (AudioPlayer.ctrlButtonsSelector !== null) {
458 if (repeat || shuffle || index > 0) {
459 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPreviousActive');
461 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPreviousInactive');
466 * Sets the audio spectrum analyzer to active/inactive state.
468 * @method spectrumAnalyzer
469 * @param playOn {Boolean} State of the audio playback.
471 spectrumAnalyzer: function (playOn) {
472 if (AudioPlayer.spectrumAnalyzerSelelector !== null) {
474 $(AudioPlayer.spectrumAnalyzerSelelector).spectrumAnalyzer('spectrumAnalyzerRandomize');
476 $(AudioPlayer.spectrumAnalyzerSelelector).spectrumAnalyzer('clearSpectrumAnalyzer');
481 * Sets the audio volume control.
483 * @method volumeControl
484 * @param volume {Number} Volume of the audio in percentage (0% - 100%).
486 volumeControl: function (volume) {
489 } else if (volume < 0) {
492 AudioPlayer.volume = volume;
493 if (AudioPlayer.volumeControlSelector !== null) {
494 $(AudioPlayer.volumeControlSelector).val(volume);
496 AudioPlayer.setAudioVolume(volume);
499 * Sets the audio volume.
501 * @method setAudioVolume
502 * @param volume {Number} Volume of the audio in percentage (0% - 100%).
504 setAudioVolume: function (volume) {
507 } else if (volume < 0) {
510 AudioPlayer.volume = volume;
511 AudioPlayer.player.volume = volume / 100;
514 * Updates the audio info panel (artist, album, name).
516 * @method updateAudioInfoPanel
517 * @param index {Number} Index of the song.
519 updateAudioInfoPanel: function (index) {
520 if (AudioPlayer.model.length && index >= 0 && index < AudioPlayer.model.length ) {
521 var audioContent = AudioPlayer.model[index],
524 if (!!audioContent) {
525 if (AudioPlayer.infoPanelSelector !== null) {
526 objInfo.title = 'NOW PLAYING';
527 objInfo.artist = Utils.getArtistName(audioContent);
528 objInfo.album = Utils.getAlbumName(audioContent);
529 objInfo.name = Utils.getMediaItemTitle(audioContent);
530 $(AudioPlayer.infoPanelSelector).infoPanel('show', objInfo);
532 if (AudioPlayer.thumbnailSelector !== null) {
533 $(AudioPlayer.thumbnailSelector).get(0).src = Utils.getThumbnailPath(audioContent, AudioPlayer.playerType);
539 * Updates the audio time progress bar.
541 * @method updateProgressInfoPanel
543 updateProgressInfoPanel: function () {
544 if (AudioPlayer.duration < 0) {
545 AudioPlayer.duration = 0;
548 if (AudioPlayer.currentTime < 0) {
549 AudioPlayer.currentTime = 0;
552 if (AudioPlayer.currentTime > AudioPlayer.duration) {
553 AudioPlayer.currentTime = AudioPlayer.duration;
556 AudioPlayer.position = (AudioPlayer.currentTime / AudioPlayer.duration) * 100;
558 if (AudioPlayer.position < 0) {
559 AudioPlayer.position = 0;
562 if (AudioPlayer.position > 100) {
563 AudioPlayer.position = 100;
566 var remainingTime = AudioPlayer.duration - AudioPlayer.currentTime,
567 durationMins = Math.floor(remainingTime / 60, 10),
568 durationSecs = Math.round(remainingTime) - durationMins * 60,
569 estimationTime = '-' + durationMins + ':' + (durationSecs > 9 ? durationSecs : '0' + durationSecs),
570 initProgressBarObj = {
571 count: AudioPlayer.model.length,
572 index: AudioPlayer.model.length <= 0 ? 0 : AudioPlayer.index + 1,
573 estimation: estimationTime,
574 position: AudioPlayer.position
577 if (AudioPlayer.timeProgressBarSelelector !== null) {
578 $(AudioPlayer.timeProgressBarSelelector).timeProgressBar("show", initProgressBarObj);
582 * Sets and loads the audio source.
585 * @param index {Number} Index of the song.
587 loadAudio: function (index, delay) {
588 if (!AudioPlayer.model.length || index < 0 || index > AudioPlayer.model.length - 1 || !AudioPlayer.player) {
592 AudioPlayer.player.pause();
593 AudioPlayer.player.src = "";
594 AudioPlayer.index = index;
595 AudioPlayer.position = 0;
596 AudioPlayer.currentTime = 0;
597 AudioPlayer.duration = 0;
599 var audioContent = AudioPlayer.model[index];
601 if (!!audioContent) {
602 if (!!audioContent.contentURI && audioContent.contentURI !== "") {
603 if (typeof(delay) === 'undefined') {
604 AudioPlayer.player.src = audioContent.contentURI;
605 AudioPlayer.player.load();
607 setTimeout(function() {
608 AudioPlayer.player.src = audioContent.contentURI;
609 AudioPlayer.player.load();
613 if (!!audioContent.duration && audioContent.duration >= 0) {
614 AudioPlayer.duration = audioContent.duration / 1000;
617 AudioPlayer.updateUIControls();
619 play: function (index) {
620 if (index !== AudioPlayer.index) {
621 AudioPlayer.loadAudio(index);
625 * Sets the position/current time of loaded song.
627 * @method setAudioPosition
628 * @param position {Number} Position in percentage of song.
630 setAudioPosition: function (position) {
631 if (position > 100) {
633 } else if (position < 0) {
636 AudioPlayer.currentTime = position / 100 * AudioPlayer.duration;
637 if (!!AudioPlayer.player) {
638 AudioPlayer.player.currentTime = AudioPlayer.currentTime;
640 AudioPlayer.updateProgressInfoPanel();
643 * Starts/pauses playback of the song.
646 * @param playing {Boolean} State of song playback.
648 playPause: function (playing) {
649 AudioPlayer.playOn = playing;
650 if (!!AudioPlayer.player && AudioPlayer.player.currentSrc !== "") {
651 if (AudioPlayer.playOn) {
652 AudioPlayer.player.play();
654 AudioPlayer.player.pause();
657 AudioPlayer.playOn = false;
659 AudioPlayer.updateUIControls();
662 * Sets next, random (if shuffle is on) index of song or first index (if repeat is on) and loads it from model.
667 if (AudioPlayer.model.length) {
668 var newIndex = AudioPlayer.index, previousIndex = AudioPlayer.index;
670 if (AudioPlayer.shuffleOn) {
671 while (newIndex === AudioPlayer.index) {
672 newIndex = Math.floor((Math.random() * AudioPlayer.model.length));
674 } else if (AudioPlayer.repeatOn && AudioPlayer.index >= AudioPlayer.model.length - 1) {
676 } else if (AudioPlayer.index < AudioPlayer.model.length - 1) {
677 newIndex = AudioPlayer.index + 1;
679 AudioPlayer.playOn = false;
682 if (newIndex !== previousIndex && !!AudioPlayer.indexChangeCallback) {
683 AudioPlayer.indexChangeCallback(newIndex);
686 AudioPlayer.loadAudio(newIndex, 150);
690 * Sets previous, random (if shuffle is on) index of song or last index (if repeat is on) and loads it from model.
694 previous: function () {
695 if (AudioPlayer.model.length) {
696 var newIndex = AudioPlayer.index, previousIndex = AudioPlayer.index;
698 if (AudioPlayer.shuffleOn) {
699 while (newIndex === AudioPlayer.index) {
700 newIndex = Math.floor((Math.random() * AudioPlayer.model.length));
702 } else if (AudioPlayer.repeatOn && AudioPlayer.index <= 0) {
703 newIndex = AudioPlayer.model.length - 1;
704 } else if (AudioPlayer.index > 0) {
705 newIndex = AudioPlayer.index - 1;
708 if (newIndex !== previousIndex && !!AudioPlayer.indexChangeCallback) {
709 AudioPlayer.indexChangeCallback(newIndex);
712 AudioPlayer.loadAudio(newIndex, 150);
715 getCurrentPlayerType: function () {
716 return AudioPlayer.playerType;
721 * jQuery extension method for {{#crossLink "AudioPlayer"}}{{/crossLink}} plugin.
722 * @param method {Object|jQuery selector} Identificator (name) of method or jQuery selector.
725 * @return Result of called method.
727 $.fn.audioAPI = function (method) {
728 // Method calling logic
729 if (AudioPlayer[method]) {
730 return AudioPlayer[method].apply(this, Array.prototype.slice.call(arguments, 1));
733 if (typeof method === 'object' || !method) {
734 return AudioPlayer.init.apply(this, arguments);
737 $.error('Method ' + method + ' does not exist on jQuery.audioAPI');