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) {
281 if(localStorage && localStorage.volume)
283 volStart = localStorage.volume;
285 $(AudioPlayer.volumeControlSelector).noUiSlider({
291 orientation: "horizontal",
292 slide: function (aaa, ccc) {
293 var volumeSlider = parseInt($(AudioPlayer.volumeControlSelector).val(), 10);
294 console.log("new volume: " + volumeSlider);
295 AudioPlayer.volume = volumeSlider;
296 AudioPlayer.setAudioVolume(volumeSlider);
299 AudioPlayer.setAudioVolume(volStart);
302 if (AudioPlayer.timeProgressBarSelelector !== null) {
303 $(AudioPlayer.timeProgressBarSelelector).timeProgressBar("init");
304 $(AudioPlayer.timeProgressBarSelelector).bind('positionChanged', function (e, data) {
305 console.log("positionChanged " + data.position);
306 AudioPlayer.setAudioPosition(data.position);
310 AudioPlayer.registerPlayerListeners(AudioPlayer.audio, "AUDIO");
311 AudioPlayer.registerPlayerListeners(AudioPlayer.video, "VIDEO");
313 AudioPlayer.updateUIControls();
315 registerPlayerListeners: function(player, type) {
317 player.addEventListener('canplay', function () {
318 console.log("canplay " + type);
319 AudioPlayer.playPause(AudioPlayer.playOn);
322 player.addEventListener('canplaythrough', function () {
323 console.log("canplaythrough " + type);
324 AudioPlayer.playPause(AudioPlayer.playOn);
327 player.addEventListener('play', function () {
328 console.log("play " + type);
331 player.addEventListener('playing', function () {
332 console.log("playing " + type);
335 player.addEventListener('error', function () {
336 console.log("error " + type);
339 player.addEventListener('waiting', function () {
340 console.log("waiting " + type);
343 player.addEventListener('timeupdate', function () {
344 //console.log("timeupdate " + type + " " + player.currentTime + " " + player.duration);
345 if (!!player.currentTime && !isNaN(player.currentTime) && player.currentTime !== "Infinity" && player.currentTime > 0 &&
346 !!player.duration && !isNaN(player.duration) && player.duration !== "Infinity" && player.duration > 0) {
347 AudioPlayer.currentTime = player.currentTime;
348 AudioPlayer.duration = player.duration;
351 AudioPlayer.updateProgressInfoPanel();
352 AudioPlayer.spectrumAnalyzer(AudioPlayer.playOn);
355 player.addEventListener('ended', function () {
360 playAudioContent: function(audioContent, indexToPlay, play, type) {
361 if (!!AudioPlayer.player) {
362 AudioPlayer.player.pause();
363 AudioPlayer.player.src = "";
365 AudioPlayer.model = audioContent;
366 AudioPlayer.playOn = play;
367 AudioPlayer.playerType = type;
368 if (type === "AUDIO") {
369 AudioPlayer.player = AudioPlayer.audio;
370 } else if (type === "VIDEO") {
371 AudioPlayer.player = AudioPlayer.video;
373 AudioPlayer.loadAudio(indexToPlay);
376 * Updates the audio UI controls and panels according to loaded song and audio status.
378 * @method updateUIControls
380 updateUIControls: function () {
381 AudioPlayer.playPauseButtons(AudioPlayer.playOn);
382 AudioPlayer.shuffleButton(AudioPlayer.shuffleOn);
383 AudioPlayer.repeatButton(AudioPlayer.repeatOn);
384 AudioPlayer.nextButton(AudioPlayer.index, AudioPlayer.repeatOn, AudioPlayer.shuffleOn);
385 AudioPlayer.previousButton(AudioPlayer.index, AudioPlayer.repeatOn, AudioPlayer.shuffleOn);
386 AudioPlayer.spectrumAnalyzer(AudioPlayer.playOn);
387 AudioPlayer.volumeControl(AudioPlayer.volume);
388 AudioPlayer.updateAudioInfoPanel(AudioPlayer.index);
389 AudioPlayer.updateProgressInfoPanel();
392 * Sets the audio play/pause button to active/inactive state.
394 * @method playPauseButtons
395 * @param isPaused {Boolean} State of play/pause button.
397 playPauseButtons: function (isPaused) {
398 if (AudioPlayer.ctrlButtonsSelector !== null) {
400 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPauseActive');
402 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPlayActive');
407 * Sets the audio shuffle button to active/inactive state.
409 * @method shuffleButton
410 * @param isActive {Boolean} State of shuffle button.
412 shuffleButton: function (isActive) {
413 if (AudioPlayer.ctrlButtonsSelector !== null) {
415 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonShuffleActive');
417 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonShuffleInactive');
422 * Sets the audio repeat button to active/inactive state.
424 * @method repeatButton
425 * @param isActive {Boolean} State of repeat button.
427 repeatButton: function (isActive) {
428 if (AudioPlayer.ctrlButtonsSelector !== null) {
430 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonRepeatActive');
432 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonRepeatInactive');
437 * Sets the audio next button to active/inactive state.
440 * @param index {Number} Index of the song.
441 * @param repeat {Boolean} State of the repeat option.
442 * @param shuffle {Boolean} State of the shuffle option.
444 nextButton: function (index, repeat, shuffle) {
445 if (AudioPlayer.ctrlButtonsSelector !== null) {
446 if (repeat || shuffle || index < AudioPlayer.model.length - 1) {
447 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonNextActive');
449 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonNextInactive');
454 * Sets the audio previous button to active/inactive state.
456 * @method previousButton
457 * @param index {Number} Index of the song.
458 * @param repeat {Boolean} State of the repeat option.
459 * @param shuffle {Boolean} State of the shuffle option.
461 previousButton: function (index, repeat, shuffle) {
462 if (AudioPlayer.ctrlButtonsSelector !== null) {
463 if (repeat || shuffle || index > 0) {
464 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPreviousActive');
466 $(AudioPlayer.ctrlButtonsSelector).buttonControls('buttonPreviousInactive');
471 * Sets the audio spectrum analyzer to active/inactive state.
473 * @method spectrumAnalyzer
474 * @param playOn {Boolean} State of the audio playback.
476 spectrumAnalyzer: function (playOn) {
477 if (AudioPlayer.spectrumAnalyzerSelelector !== null) {
479 $(AudioPlayer.spectrumAnalyzerSelelector).spectrumAnalyzer('spectrumAnalyzerRandomize');
481 $(AudioPlayer.spectrumAnalyzerSelelector).spectrumAnalyzer('clearSpectrumAnalyzer');
486 * Sets the audio volume control.
488 * @method volumeControl
489 * @param volume {Number} Volume of the audio in percentage (0% - 100%).
491 volumeControl: function (volume) {
494 } else if (volume < 0) {
497 AudioPlayer.volume = volume;
498 if (AudioPlayer.volumeControlSelector !== null) {
499 $(AudioPlayer.volumeControlSelector).val(volume);
501 AudioPlayer.setAudioVolume(volume);
504 * Sets the audio volume.
506 * @method setAudioVolume
507 * @param volume {Number} Volume of the audio in percentage (0% - 100%).
509 setAudioVolume: function (volume) {
512 } else if (volume < 0) {
515 AudioPlayer.volume = volume;
516 AudioPlayer.player.volume = volume / 100;
519 localStorage.volume = volume;
523 * Updates the audio info panel (artist, album, name).
525 * @method updateAudioInfoPanel
526 * @param index {Number} Index of the song.
528 updateAudioInfoPanel: function (index) {
529 if (AudioPlayer.model.length && index >= 0 && index < AudioPlayer.model.length ) {
530 var audioContent = AudioPlayer.model[index],
533 if (!!audioContent) {
534 if (AudioPlayer.infoPanelSelector !== null) {
535 objInfo.title = 'NOW PLAYING';
536 objInfo.artist = Utils.getArtistName(audioContent);
537 objInfo.album = Utils.getAlbumName(audioContent);
538 objInfo.name = Utils.getMediaItemTitle(audioContent);
539 $(AudioPlayer.infoPanelSelector).infoPanel('show', objInfo);
541 if (AudioPlayer.thumbnailSelector !== null) {
542 $(AudioPlayer.thumbnailSelector).get(0).src = Utils.getThumbnailPath(audioContent, AudioPlayer.playerType);
548 * Updates the audio time progress bar.
550 * @method updateProgressInfoPanel
552 updateProgressInfoPanel: function () {
553 if (AudioPlayer.duration < 0) {
554 AudioPlayer.duration = 0;
557 if (AudioPlayer.currentTime < 0) {
558 AudioPlayer.currentTime = 0;
561 if (AudioPlayer.currentTime > AudioPlayer.duration) {
562 AudioPlayer.currentTime = AudioPlayer.duration;
565 AudioPlayer.position = (AudioPlayer.currentTime / AudioPlayer.duration) * 100;
567 if (AudioPlayer.position < 0) {
568 AudioPlayer.position = 0;
571 if (AudioPlayer.position > 100) {
572 AudioPlayer.position = 100;
575 var remainingTime = AudioPlayer.duration - AudioPlayer.currentTime,
576 durationMins = Math.floor(remainingTime / 60, 10),
577 durationSecs = Math.round(remainingTime) - durationMins * 60,
578 estimationTime = '-' + durationMins + ':' + (durationSecs > 9 ? durationSecs : '0' + durationSecs),
579 initProgressBarObj = {
580 count: AudioPlayer.model.length,
581 index: AudioPlayer.model.length <= 0 ? 0 : AudioPlayer.index + 1,
582 estimation: estimationTime,
583 position: AudioPlayer.position
586 if (AudioPlayer.timeProgressBarSelelector !== null) {
587 $(AudioPlayer.timeProgressBarSelelector).timeProgressBar("show", initProgressBarObj);
591 * Sets and loads the audio source.
594 * @param index {Number} Index of the song.
596 loadAudio: function (index, delay) {
597 if (!AudioPlayer.model.length || index < 0 || index > AudioPlayer.model.length - 1 || !AudioPlayer.player) {
601 AudioPlayer.player.pause();
602 AudioPlayer.player.src = "";
603 AudioPlayer.index = index;
604 AudioPlayer.position = 0;
605 AudioPlayer.currentTime = 0;
606 AudioPlayer.duration = 0;
608 var audioContent = AudioPlayer.model[index];
610 if (!!audioContent) {
611 if (!!audioContent.contentURI && audioContent.contentURI !== "") {
612 if (typeof(delay) === 'undefined') {
613 AudioPlayer.player.src = audioContent.contentURI;
614 AudioPlayer.player.load();
616 setTimeout(function() {
617 AudioPlayer.player.src = audioContent.contentURI;
618 AudioPlayer.player.load();
622 if (!!audioContent.duration && audioContent.duration >= 0) {
623 AudioPlayer.duration = audioContent.duration / 1000;
626 AudioPlayer.updateUIControls();
628 play: function (index) {
629 if (index !== AudioPlayer.index) {
630 AudioPlayer.loadAudio(index);
634 * Sets the position/current time of loaded song.
636 * @method setAudioPosition
637 * @param position {Number} Position in percentage of song.
639 setAudioPosition: function (position) {
640 if (position > 100) {
642 } else if (position < 0) {
645 AudioPlayer.currentTime = position / 100 * AudioPlayer.duration;
646 if (!!AudioPlayer.player) {
647 AudioPlayer.player.currentTime = AudioPlayer.currentTime;
649 AudioPlayer.updateProgressInfoPanel();
652 * Starts/pauses playback of the song.
655 * @param playing {Boolean} State of song playback.
657 playPause: function (playing) {
658 AudioPlayer.playOn = playing;
659 if (!!AudioPlayer.player && AudioPlayer.player.currentSrc !== "") {
660 if (AudioPlayer.playOn) {
661 AudioPlayer.player.play();
663 AudioPlayer.player.pause();
666 AudioPlayer.playOn = false;
668 AudioPlayer.updateUIControls();
671 * Sets next, random (if shuffle is on) index of song or first index (if repeat is on) and loads it from model.
676 if (AudioPlayer.model.length) {
677 var newIndex = AudioPlayer.index, previousIndex = AudioPlayer.index;
679 if (AudioPlayer.shuffleOn) {
680 while (newIndex === AudioPlayer.index) {
681 newIndex = Math.floor((Math.random() * AudioPlayer.model.length));
683 } else if (AudioPlayer.repeatOn && AudioPlayer.index >= AudioPlayer.model.length - 1) {
685 } else if (AudioPlayer.index < AudioPlayer.model.length - 1) {
686 newIndex = AudioPlayer.index + 1;
688 AudioPlayer.playOn = false;
691 if (newIndex !== previousIndex && !!AudioPlayer.indexChangeCallback) {
692 AudioPlayer.indexChangeCallback(newIndex);
695 AudioPlayer.loadAudio(newIndex, 150);
699 * Sets previous, random (if shuffle is on) index of song or last index (if repeat is on) and loads it from model.
703 previous: function () {
704 if (AudioPlayer.model.length) {
705 var newIndex = AudioPlayer.index, previousIndex = AudioPlayer.index;
707 if (AudioPlayer.shuffleOn) {
708 while (newIndex === AudioPlayer.index) {
709 newIndex = Math.floor((Math.random() * AudioPlayer.model.length));
711 } else if (AudioPlayer.repeatOn && AudioPlayer.index <= 0) {
712 newIndex = AudioPlayer.model.length - 1;
713 } else if (AudioPlayer.index > 0) {
714 newIndex = AudioPlayer.index - 1;
717 if (newIndex !== previousIndex && !!AudioPlayer.indexChangeCallback) {
718 AudioPlayer.indexChangeCallback(newIndex);
721 AudioPlayer.loadAudio(newIndex, 150);
724 getCurrentPlayerType: function () {
725 return AudioPlayer.playerType;
730 * jQuery extension method for {{#crossLink "AudioPlayer"}}{{/crossLink}} plugin.
731 * @param method {Object|jQuery selector} Identificator (name) of method or jQuery selector.
734 * @return Result of called method.
736 $.fn.audioAPI = function (method) {
737 // Method calling logic
738 if (AudioPlayer[method]) {
739 return AudioPlayer[method].apply(this, Array.prototype.slice.call(arguments, 1));
742 if (typeof method === 'object' || !method) {
743 return AudioPlayer.init.apply(this, arguments);
746 $.error('Method ' + method + ' does not exist on jQuery.audioAPI');