wavesurfer.directive.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /**
  2. * Created by intelWorx on 19/11/2015.
  3. */
  4. (function () {
  5. 'use strict';
  6. /**
  7. * Main module, your application should depend on this
  8. * @module {mdWavesurfer}
  9. */
  10. var app = angular.module('mdWavesurfer', ['ngMaterial']);
  11. /**
  12. * @ngdoc service
  13. * @name $mdWavesurferUtils
  14. *
  15. * @description
  16. *
  17. * Utility service for this directive, exposes method:
  18. * - getLength(url), which returns a promise for the length of the audio specified by URL
  19. *
  20. * ```js
  21. * app.directive('myFancyDirective', function(mdWavesurferUtils) {
  22. * return {
  23. * restrict: 'e',
  24. * link: function(scope, el, attrs) {
  25. * mdWavesurferUtils(attrs.url)
  26. * .then(function(l){
  27. * scope.length = l;
  28. * }, function(){
  29. * someErrorhandler()
  30. * })
  31. * ;
  32. * }
  33. * };
  34. * });
  35. * ```
  36. */
  37. app.factory('mdWavesurferUtils', ['$q', '$document', '$timeout',
  38. function ($q, $document, $timeout) {
  39. return {
  40. getLength: function (object) {
  41. var deferred = $q.defer();
  42. var estimateLength = function (url) {
  43. var audio = $document[0].createElement('audio');
  44. audio.src = url;
  45. audio.addEventListener('loadeddata', function listener() {
  46. deferred.resolve(this.duration);
  47. audio.removeEventListener('loadeddata', listener);
  48. audio.src = 'data:audio/mpeg,0';//destroy loading.
  49. });
  50. audio.addEventListener('error', function (e) {
  51. deferred.resolve(e.target.error);
  52. });
  53. };
  54. if (typeof object === 'string') {
  55. //this is a URL
  56. estimateLength(object);
  57. } else {
  58. $timeout(function () {
  59. deferred.reject(new DOMError("NotSupportedError", "Specified argument is not supported"));
  60. });
  61. }
  62. return deferred.promise;
  63. }
  64. };
  65. }
  66. ]);
  67. /**
  68. * @ngdoc filter
  69. * @name mdWavesurferTimeFormat
  70. *
  71. * Simple filter to convert value in seconds to MM:SS format
  72. *
  73. * @param Number duration in seconds
  74. */
  75. app.filter('mdWavesurferTimeFormat', function () {
  76. return function (input) {
  77. if (!input) {
  78. return "00:00";
  79. }
  80. var minutes = Math.floor(input / 60);
  81. var seconds = Math.ceil(input) % 60;
  82. return (minutes < 10 ? '0' : '')
  83. + minutes
  84. + ":"
  85. + (seconds < 10 ? '0' : '') + seconds;
  86. };
  87. });
  88. app.controller('mdWavesurferAudioController', ['$attrs', '$element',
  89. function (attributes, $element) {
  90. var audio = this;
  91. audio.tracks = [];
  92. audio.selectedIndex = audio.selectedIndex || 0;
  93. audio.currentTrack = null;
  94. //adds to an audio track
  95. audio.addTrack = function (trackScope) {
  96. if (audio.tracks.indexOf(trackScope) < 0) {
  97. audio.tracks.push(trackScope);
  98. }
  99. if (!audio.currentTrack) {
  100. audio.currentTrack = audio.tracks[audio.selectedIndex];
  101. }
  102. };
  103. //remove audio track
  104. audio.removeTrack = function (trackScope) {
  105. var idx = audio.tracks.indexOf(trackScope);
  106. if (idx >= 0) {
  107. audio.tracks.splice(idx, 1);
  108. }
  109. };
  110. audio.playerProperties = {}
  111. var nKey;
  112. for (var attr in attributes) {
  113. if (attr.match(/^player/)) {
  114. nKey = attr.replace(/^player([A-Z])/, function (m, $1) {
  115. return $1.toLowerCase();
  116. });
  117. audio.playerProperties[nKey] = attributes[attr];
  118. }
  119. }
  120. var getPlayer = function(){
  121. return $element.find('md-wavesurfer-player').controller('mdWavesurferPlayer');
  122. };
  123. var setAutoPlay = function (forcePlay) {
  124. var controller = getPlayer();
  125. if (controller && (forcePlay || controller.surfer.isPlaying())) {
  126. controller.autoPlay = true;
  127. }
  128. };
  129. audio.setTrack = function (idx, forcePlay) {
  130. if (audio.tracks.length > idx) {
  131. if (audio.selectedIndex === idx) {
  132. var ctrl = getPlayer();
  133. ctrl.surfer.playPause();
  134. } else {
  135. setAutoPlay(forcePlay);
  136. audio.currentTrack = audio.tracks[idx];
  137. audio.selectedIndex = idx;
  138. }
  139. }
  140. };
  141. audio.extraButtons = [{
  142. icon: 'zmdi zmdi-skip-previous',
  143. title: 'Previous',
  144. action: function ($event) {
  145. if (audio.selectedIndex > 0) {
  146. audio.setTrack(audio.selectedIndex - 1);
  147. }
  148. },
  149. class: ''
  150. }, {
  151. icon: 'zmdi zmdi-skip-next',
  152. title: 'Next',
  153. action: function ($event) {
  154. if (audio.selectedIndex < audio.tracks.length - 1) {
  155. audio.setTrack(audio.selectedIndex + 1);
  156. }
  157. },
  158. class: ''
  159. }];
  160. }
  161. ]);
  162. /**
  163. * @ngdoc directive
  164. * @name md-wavesurfer-audio
  165. *
  166. * Directive for playing a set of audio files. This directive is analogous to `<audio>` HTML tag.
  167. * The audio files, should be specified using the `md-wavesurfer-source`
  168. *
  169. * WaveSurfer properties can be passed in using the prefix : player-* for attributes, e.g. `player-wave-color` is
  170. * equivalent to WaveSurfer's waveColor option.
  171. *
  172. * Must be used as an element.
  173. *
  174. * @usage
  175. * ```html
  176. * <md-wavesurfer-audio player-wave-color="gray" player-progress-color="black" player-backend="MediaElement">
  177. * <md-wavesurfer-source src="source1" title="Title-1"></md-wavesurfer-source>
  178. * <md-wavesurfer-source src="source2" title="Title-2"></md-wavesurfer-source>
  179. * <md-wavesurfer-source src="source3" title="Title-3"></md-wavesurfer-source>
  180. * ...
  181. * <md-wavesurfer-source src="sourceN" title="Рассказы о сновидениях"></md-wavesurfer-source>
  182. * </md-wavesurfer-audio>
  183. * ```
  184. *
  185. * @param string player-* specifies WaveSurfer properties.
  186. *
  187. */
  188. app.directive('mdWavesurferAudio', [
  189. function () {
  190. return {
  191. restrict: 'E',
  192. templateUrl: 'md-player-audio.partial.html',
  193. transclude: true,
  194. controller: 'mdWavesurferAudioController',
  195. controllerAs: 'audio'
  196. };
  197. }
  198. ]);
  199. /**
  200. * @ngdoc directive
  201. *
  202. * @name md-wavesurfer-source
  203. *
  204. * This directive is used within the `md-wavesurfer-audio` directive to specify an audio file source, it is
  205. * synonymous to `<source>` tag in HTML
  206. *
  207. * The directive cannot be used as standalone.
  208. *
  209. * @usage
  210. *
  211. * ```html
  212. * <md-wavesurfer-source src="source3" title="Title-3" album-art="Album-Art-Url" duration=""></md-wavesurfer-source>
  213. * ```
  214. * @param String src the URL to the audio file, this is required.
  215. * @param String title track title
  216. * @param String album-art the album art URL
  217. * @param Number duration the length of the audio file in seconds, will be auto-detected if not specified.
  218. *
  219. */
  220. app.directive('mdWavesurferSource', ['mdWavesurferUtils',
  221. function (mdWavesurferUtils) {
  222. return {
  223. restrict: 'E',
  224. require: '^mdWavesurferAudio',
  225. scope: {
  226. src: '@',
  227. albumArt: '@',
  228. title: '@',
  229. duration: '='
  230. },
  231. link: function (scope, element, attrs, audio) {
  232. audio.addTrack(scope);
  233. if (!scope.duration) {
  234. mdWavesurferUtils.getLength(scope.src).then(function (dur) {
  235. scope.duration = dur;
  236. }, function (e) {
  237. scope.duration = 0;
  238. console.log('Failed to get audio length, reason: ', e.message);
  239. });
  240. }
  241. element.on('$destroy', function () {
  242. audio.removeTrack(audio);
  243. });
  244. }
  245. };
  246. }
  247. ]);
  248. app.controller('mdWavesurferPlayerController', ['$element', '$scope', '$attrs', '$interval', '$mdTheming',
  249. function ($element, $scope, attributes, $interval, $mdTheme) {
  250. var control = this, timeInterval;
  251. control.themeClass = "md-" + $mdTheme.defaultTheme() + "-theme";
  252. control.isReady = false;
  253. control.surfer = null;
  254. control.toggleMute = function () {
  255. if (control.surfer) {
  256. control.surfer.toggleMute();
  257. control.isMute = !control.isMute;
  258. }
  259. };
  260. var initWaveSurfer = function () {
  261. control.isReady = false;
  262. control.currentTime = 0;
  263. if (!control.surfer) {
  264. var options = {
  265. container: $element[0].querySelector('.waveSurferWave')
  266. }, defaults = {
  267. scrollParent: true,
  268. waveColor: 'violet',
  269. progressColor: 'purple'
  270. };
  271. options = angular.extend(defaults, attributes, (control.properties || {}), options);
  272. control.surfer = WaveSurfer.create(options);
  273. control.surfer.on('ready', function () {
  274. control.isReady = true;
  275. if (control.autoPlay) {
  276. control.surfer.play();
  277. }
  278. $scope.$apply();
  279. });
  280. control.surfer.on('pause', function () {
  281. stopInterval();
  282. });
  283. control.surfer.on('finish', function () {
  284. stopInterval();
  285. });
  286. control.surfer.on('play', function () {
  287. startInterval();
  288. });
  289. }
  290. control.title = control.title || control.src.split('/').pop();
  291. control.surfer.load(control.src);
  292. };
  293. var startInterval = function () {
  294. timeInterval = $interval(function () {
  295. control.currentTime = control.isReady ? control.surfer.getCurrentTime() : 0;
  296. }, 1000);
  297. }, stopInterval = function () {
  298. $interval.cancel(timeInterval);
  299. };
  300. initWaveSurfer();
  301. $scope.$watch('control.src', function (src1, src2) {
  302. if (src1 != src2) {
  303. initWaveSurfer();
  304. }
  305. });
  306. $element.on('$destroy', function () {
  307. if (control.surfer) {
  308. control.surfer.destroy();
  309. }
  310. stopInterval();
  311. });
  312. $scope.$watch(function () {
  313. var div = $element[0].querySelector('.audioPlayerWrapper');
  314. return div ? div.offsetWidth : 0;
  315. }, function (width) {
  316. if (width < 1) {
  317. //hidden
  318. control.surfer.pause();
  319. }
  320. });
  321. }
  322. ]);
  323. /**
  324. * @ngdoc directive
  325. *
  326. * @name md-wavesurfer-player
  327. *
  328. * @usage
  329. * This directive can be used as a stand-alone directive to display Audio WaveSurfer with a few controls, by default
  330. * this will only display play/pause, fast-forward, rewind and mute toggle buttons, however, you can add extra
  331. * buttons using the `extra-buttons` parameters.
  332. *
  333. * ```html
  334. * <md-wavesurfer-player url="trackUrl" title="Track Title"
  335. * extra-buttons="extraButtons" properties="properties">
  336. * </md-wavesurfer-player>
  337. * ```
  338. *
  339. * @param {string} url the URL of the audio file
  340. * @param {string} title title of the audio track
  341. * @param {object} properties an object specifying init options for WaveSurfer
  342. * @param {boolean} auto-play specifies if the player should start as soon as it's loaded.
  343. * @param {object[]} extra-buttons a list of extra buttons to add to the control panel
  344. * each button should be an object with the following properties:
  345. * {
  346. * title: "button title"
  347. * action: "call back to call when button is clicked, executed in parent scope",
  348. * icon: "md-font-icon parameter for the button"
  349. * class: "extra classes to add to the button."
  350. * }
  351. *
  352. * Every other attribute passed to this directive is assumed to a WaveSurver init parameter.
  353. */
  354. app.directive('mdWavesurferPlayer', function () {
  355. return {
  356. restrict: 'E',
  357. templateUrl: 'md-player.partial.html',
  358. scope: {
  359. src: '@url',
  360. title: '@',
  361. extraButtons: '=',
  362. toolbarClass: '@',
  363. autoPlay: '=',
  364. properties: '='
  365. },
  366. controller: 'mdWavesurferPlayerController',
  367. controllerAs: 'control',
  368. bindToController: true
  369. };
  370. });
  371. ;
  372. })();