GeSHi Source Viewer: howler.diff.txtView Raw


  1. --- howler.orig.js	Fri May 23 11:52:20 2025
  2. +++ howler.js	Sun May 25 12:46:31 2025
  3. @@ -1,11 +1,12 @@
  4. -/*!
  5. - *  howler.js v2.2.3
  6. - *  howlerjs.com
  7. +/*! 
  8. + *  @file howler.js v2.2.3 | howlerjs.com
  9. + *  @version 2.2.3-planeptune
  10. + *  @author James Simpson
  11. + *  @copyright 2013-2020, James Simpson of GoldFire Studios | goldfirestudios.com
  12. + *  @license MIT
  13.   *
  14. - *  (c) 2013-2020, James Simpson of GoldFire Studios
  15. - *  goldfirestudios.com
  16. - *
  17. - *  MIT License
  18. + *  This version of howler has been modified by zefie, for the Planeptune Loop Player ("Planeptune Edition"), and minified using uglify-js v3.x
  19. + *  See https://loops.planeptune.org/geshi/source/js/howler.diff.txt for modifications to "Planeptune Edition"
  20.   */
  21.  
  22.  (function() {
  23. @@ -29,6 +30,7 @@
  24.       */
  25.      init: function() {
  26.        var self = this || Howler;
  27. +      console.log('howler v2.2.3-planeptune initialized');
  28.  
  29.        // Create a global ID counter.
  30.        self._counter = 1000;
  31. @@ -61,6 +63,37 @@
  32.        return self;
  33.      },
  34.  
  35. +   /**
  36. +    * Check if this browser is Internet Explorer.
  37. +    * Returns the version if so, otherwise returns false
  38. +    */
  39. +
  40. +  detectIE: function () {
  41. +    var ua = window.navigator.userAgent;
  42. +
  43. +    var msie = ua.indexOf('MSIE ');
  44. +    if (msie > 0) {
  45. +      // IE 10 or older => return version number
  46. +      return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
  47. +    }
  48. +
  49. +    var trident = ua.indexOf('Trident/');
  50. +    if (trident > 0) {
  51. +      // IE 11 => return version number
  52. +      var rv = ua.indexOf('rv:');
  53. +      return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
  54. +    }
  55. +
  56. +    var edge = ua.indexOf('Edge/');
  57. +    if (edge > 0) {
  58. +       // Edge (IE 12+) => return version number
  59. +       return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
  60. +    }
  61. +
  62. +    // other browser
  63. +    return false;
  64. +  },
  65. +
  66.      /**
  67.       * Get/set the global volume for all sounds.
  68.       * @param  {Float} vol Volume from 0.0 to 1.0.
  69. @@ -84,7 +117,7 @@
  70.          }
  71.  
  72.          // When using Web Audio, we just need to adjust the master gain.
  73. -        if (self.usingWebAudio) {
  74. +        if (self._webAudio && !self._html5) {
  75.            self.masterGain.gain.setValueAtTime(vol, Howler.ctx.currentTime);
  76.          }
  77.  
  78. @@ -126,7 +159,7 @@
  79.        self._muted = muted;
  80.  
  81.        // With Web Audio, we just need to mute the master gain.
  82. -      if (self.usingWebAudio) {
  83. +      if (self._webAudio) {
  84.          self.masterGain.gain.setValueAtTime(muted ? 0 : self._volume, Howler.ctx.currentTime);
  85.        }
  86.  
  87. @@ -176,7 +209,7 @@
  88.        }
  89.  
  90.        // Create a new AudioContext to make sure it is fully reset.
  91. -      if (self.usingWebAudio && self.ctx && typeof self.ctx.close !== 'undefined') {
  92. +      if (self._webAudio && self.ctx && typeof self.ctx.close !== 'undefined') {
  93.          self.ctx.close();
  94.          self.ctx = null;
  95.          setupAudioContext();
  96. @@ -208,7 +241,7 @@
  97.        self._autoSuspend();
  98.  
  99.        // Check if audio is available.
  100. -      if (!self.usingWebAudio) {
  101. +      if (!self._webAudio) {
  102.          // No audio is available on this system if noAudio is set to true.
  103.          if (typeof Audio !== 'undefined') {
  104.            try {
  105. @@ -557,8 +590,8 @@
  106.      var self = this;
  107.  
  108.      // Throw an error if no source is provided.
  109. -    if (!o.src || o.src.length === 0) {
  110. -      console.error('An array of source files must be passed with any new Howl.');
  111. +    if (((!o.src || o.src.length === 0) && (!o.arraybuffer || !(o.arraybuffer instanceof ArrayBuffer) || !o.contenttype)) || (o.html5 && !o.src)) {
  112. +      console.error('An array of source files, or an ArrayBuffer+ContentType, must be passed with any new Howl. Also, ArrayBuffer+ContentType are incompatible with html5 mode.');
  113.        return;
  114.      }
  115.  
  116. @@ -588,7 +621,11 @@
  117.        self._preload = (typeof o.preload === 'boolean' || o.preload === 'metadata') ? o.preload : true;
  118.        self._rate = o.rate || 1;
  119.        self._sprite = o.sprite || {};
  120. +      self._buffer = o.arraybuffer || null;
  121. +      self._contenttype = o.contenttype || null;	  
  122.        self._src = (typeof o.src !== 'string') ? o.src : [o.src];
  123. +      self._nextsprite = o.nextsprite || null;
  124. +      self._timeroffset = o.timeroffset || 30;	  
  125.        self._volume = o.volume !== undefined ? o.volume : 1;
  126.        self._xhr = {
  127.          method: o.xhr && o.xhr.method ? o.xhr.method : 'GET',
  128. @@ -598,6 +635,8 @@
  129.  
  130.        // Setup all other default properties.
  131.        self._duration = 0;
  132. +      self._decoded = null;
  133. +      self._hires = null;		  
  134.        self._state = 'unloaded';
  135.        self._sounds = [];
  136.        self._endTimers = {};
  137. @@ -610,6 +649,7 @@
  138.        self._onload = o.onload ? [{fn: o.onload}] : [];
  139.        self._onloaderror = o.onloaderror ? [{fn: o.onloaderror}] : [];
  140.        self._onplayerror = o.onplayerror ? [{fn: o.onplayerror}] : [];
  141. +      self._onspritechange = o.onspritechange ? [{fn: o.onspritechange}] : [];	    
  142.        self._onpause = o.onpause ? [{fn: o.onpause}] : [];
  143.        self._onplay = o.onplay ? [{fn: o.onplay}] : [];
  144.        self._onstop = o.onstop ? [{fn: o.onstop}] : [];
  145. @@ -662,7 +702,14 @@
  146.          self._emit('loaderror', null, 'No audio support.');
  147.          return;
  148.        }
  149. -
  150. +    
  151. +      if (self._buffer instanceof ArrayBuffer && !self._html5) {
  152. +        if (!self._src) {
  153. +          url = ['internal_buffer'];
  154. +          self._src = url;
  155. +        }
  156. +      }
  157. +    
  158.        // Make sure our source is in an array.
  159.        if (typeof self._src === 'string') {
  160.          self._src = [self._src];
  161. @@ -684,7 +731,11 @@
  162.            }
  163.  
  164.            // Extract the file extension from the URL or base64 data URI.
  165. -          ext = /^data:audio\/([^;,]+);/i.exec(str);
  166. +          if (self._contenttype) {
  167. +            ext = self._contenttype.split("/")[1];        
  168. +          } else {
  169. +            ext = /^data:audio\/([^;,]+);/i.exec(str);
  170. +          }
  171.            if (!ext) {
  172.              ext = /\.([^.]+)$/.exec(str.split('?', 1)[0]);
  173.            }
  174. @@ -869,10 +920,15 @@
  175.            } else {
  176.              sound._loop ? node.bufferSource.start(0, seek, 86400) : node.bufferSource.start(0, seek, duration);
  177.            }
  178. +		  
  179. +		  if (Howler.detectIE() == 11) {
  180. +			// IE11 Workaround where rate gets reset
  181. +			node.playbackRate = sound._rate;
  182. +		  }		  
  183.  
  184.            // Start a new timer if none is present.
  185.            if (timeout !== Infinity) {
  186. -            self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);
  187. +            self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), (timeout - self._timeroffset));
  188.            }
  189.  
  190.            if (!internal) {
  191. @@ -1563,6 +1619,10 @@
  192.              var seek = self.seek(id[i]);
  193.              var duration = ((self._sprite[sound._sprite][0] + self._sprite[sound._sprite][1]) / 1000) - seek;
  194.              var timeout = (duration * 1000) / Math.abs(sound._rate);
  195. +      
  196. +            if (self._webAudio) {
  197. +               timeout = timeout - self._timeroffset;
  198. +            }
  199.  
  200.              // Start a new end timer if sound is already playing.
  201.              if (self._endTimers[id[i]] || !sound._paused) {
  202. @@ -1639,14 +1699,44 @@
  203.          if (typeof seek === 'number' && seek >= 0) {
  204.            // Pause the sound and update position for restarting playback.
  205.            var playing = self.playing(id);
  206. -          if (playing) {
  207. -            self.pause(id, true);
  208. +          var sprite = sound._sprite;
  209. +          sound._rateSeek = 0;
  210. +
  211. +          var soundDuration = ((self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000);
  212. +          if (seek > soundDuration) {
  213. +            seek = 0;
  214. +          }
  215. +
  216. +          if (playing && !self._webAudio) {
  217. +            self.pause(id, true);    
  218.            }
  219.  
  220.            // Move the position of the track and cancel timer.
  221.            sound._seek = seek;
  222.            sound._ended = false;
  223.            self._clearTimer(id);
  224. +		  
  225. +   
  226. +          // Restart the playback if the sound was playing.
  227. +          if (playing && self._webAudio) {
  228. +            var oldbuffer = sound._node.bufferSource;
  229. +            self._refreshBuffer(sound);
  230. +            sound._playStart = Howler.ctx.currentTime;
  231. +            if (typeof sound._node.bufferSource.start === 'undefined') {
  232. +              sound._node.bufferSource.noteGrainOn(0, seek);
  233. +            } else {
  234. +              sound._node.bufferSource.start(0, seek);
  235. +            }
  236. +            if (oldbuffer) {
  237. +              oldbuffer.stop();
  238. +              oldbuffer = null;
  239. +            }
  240. +            var duration = soundDuration - seek;
  241. +            var timeout = (duration * 1000) / Math.abs(sound._rate);
  242. +            if (timeout !== Infinity) {
  243. +              self._endTimers[id] = setTimeout(self._ended.bind(self, sound), (timeout - self._timeroffset));
  244. +            }      
  245. +          }
  246.  
  247.            // Update the seek position for HTML5 Audio.
  248.            if (!self._webAudio && sound._node && !isNaN(sound._node.duration)) {
  249. @@ -1683,6 +1773,10 @@
  250.              return sound._seek + (rateSeek + realTime * Math.abs(sound._rate));
  251.            } else {
  252.              return sound._node.currentTime;
  253. +            // Workaround for IE11 reseting the rate
  254. +            if (Howler.detectIE() == 11) {
  255. +              self.rate(sound._rate,id);
  256. +            }			
  257.            }
  258.          }
  259.        }
  260. @@ -1944,6 +2038,17 @@
  261.      },
  262.  
  263.      /**
  264. +     * Fired when playback is switched to another sprite with nextsprite
  265. +     * @param  {Sound} sound The sound object to work with.
  266. +     * @return {Howl}
  267. +     */  
  268. +   
  269. +    _onspritechange: function (sound) {
  270. +      var self = this;
  271. +      return self;
  272. +    },
  273. +  
  274. +    /**
  275.       * Fired when playback ends at the end of the duration.
  276.       * @param  {Sound} sound The sound object to work with.
  277.       * @return {Howl}
  278. @@ -1968,18 +2073,57 @@
  279.  
  280.        // Restart the playback for HTML5 Audio loop.
  281.        if (!self._webAudio && loop) {
  282. -        self.stop(sound._id, true).play(sound._id);
  283. +        if (self._nextsprite && sound._sprite != self._nextsprite) {
  284. +          sprite = self._nextsprite;
  285. +          self._nextsprite = null;
  286. +          sound._sprite = sprite; // this line may be redundant
  287. +          self.stop(sound._id, true).play(sprite);
  288. +          self._emit('spritechange',sound);
  289. +        } else {
  290. +          self.stop(sound._id, true).play(sound._id);
  291. +        }
  292.        }
  293.  
  294.        // Restart this timer if on a Web Audio loop.
  295.        if (self._webAudio && loop) {
  296. -        self._emit('play', sound._id);
  297. -        sound._seek = sound._start || 0;
  298. -        sound._rateSeek = 0;
  299. -        sound._playStart = Howler.ctx.currentTime;
  300. -
  301. -        var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate);
  302. -        self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), timeout);
  303. +        var currpos = Math.round(self.seek() * 1000);
  304. +        var wanted = self._sprite[sprite][0] + self._sprite[sprite][1];
  305. +        var offset = (currpos - wanted);
  306. +        self._clearTimer(sound._id);
  307. +        if (offset < -6) {
  308. +          // "hi-res" mode
  309. +          if (!self._hires) {
  310. +            console.log("entering hi-res emulation mode");
  311. +            self._hires = setInterval(self._ended.bind(self, sound), 1);
  312. +          }
  313. +          return false;
  314. +        }
  315. +
  316. +        if (self._hires) {
  317. +          console.log("exiting hi-res emulation mode");
  318. +          clearInterval(self._hires)
  319. +          self._hires = null;
  320. +        }
  321. +        console.log("howler: end timer fired. playtime was "+currpos+"ms, expected "+wanted+"ms ("+offset+"ms off)");    
  322. +        sound._rateSeek = 0;    
  323. +        sound._playStart = Howler.ctx.currentTime;  
  324. +        if (self._nextsprite && sound._sprite != self._nextsprite) {
  325. +          // manually change sprite
  326. +          sprite = self._nextsprite;
  327. +          self._nextsprite = null;
  328. +          sound._sprite = sprite;      
  329. +          self._emit('spritechange',sound);      
  330. +          sound._start = self._sprite[sprite][0] / 1000;
  331. +          sound._stop = (self._sprite[sprite][0] + self._sprite[sprite][1]) / 1000;
  332. +          sound._loop = loop;
  333. +          sound._seek = sound._start || 0;
  334. +          self.seek(sound._seek,sound._id);
  335. +        } else {
  336. +          self._emit('play', sound._id);
  337. +          sound._seek = sound._start || 0;
  338. +          var timeout = ((sound._stop - sound._start) * 1000) / Math.abs(sound._rate);
  339. +          self._endTimers[sound._id] = setTimeout(self._ended.bind(self, sound), (timeout - self._timeroffset));      
  340. +        }
  341.        }
  342.  
  343.        // Mark the node as paused.
  344. @@ -2378,6 +2522,11 @@
  345.     */
  346.    var loadBuffer = function(self) {
  347.      var url = self._src;
  348. +  
  349. +    if (self._buffer instanceof ArrayBuffer && !self._html5) {
  350. +      decodeAudioData(self._buffer, self);
  351. +      return;
  352. +    }
  353.  
  354.      // Check if the buffer has already been cached and use it instead.
  355.      if (cache[url]) {
  356. @@ -2472,9 +2621,9 @@
  357.  
  358.      // Decode the buffer into an audio source.
  359.      if (typeof Promise !== 'undefined' && Howler.ctx.decodeAudioData.length === 1) {
  360. -      Howler.ctx.decodeAudioData(arraybuffer).then(success).catch(error);
  361. +      Howler.ctx.decodeAudioData(arraybuffer.slice(0)).then(success).catch(error);
  362.      } else {
  363. -      Howler.ctx.decodeAudioData(arraybuffer, success, error);
  364. +      Howler.ctx.decodeAudioData(arraybuffer.slice(0), success, error);
  365.      }
  366.    }
  367.  
  368. @@ -2496,6 +2645,7 @@
  369.  
  370.      // Fire the loaded event.
  371.      if (self._state !== 'loaded') {
  372. +      self._decoded = buffer;
  373.        self._state = 'loaded';
  374.        self._emit('load');
  375.        self._loadQueue();
  376.