From 4544eff62c4eda6fc12827c7936bb5f3e6e05f77 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Thu, 9 Jun 2022 07:32:09 +0200 Subject: [PATCH] Add an option to fully download the file before starting HTML5 stream This can be useful for use cases such as games that use many long background tracks, which are all downloaded before the game starts. Using Web Audio would cause huge memory footprint as all tracks get fully decoded after downloading. Using HTML5 streaming avoids that, but didn't allow to fully download the file beforehand, which now can be done by setting `preload` property to `full`. --- README.md | 2 +- src/howler.core.js | 37 +++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4a4766f0..6ce7fedf 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ Set to `true` to force HTML5 Audio. This should be used for large audio files so #### loop `Boolean` `false` Set to `true` to automatically loop the sound forever. #### preload `Boolean|String` `true` -Automatically begin downloading the audio file when the `Howl` is defined. If using HTML5 Audio, you can set this to `'metadata'` to only preload the file's metadata (to get its duration without download the entire file, for example). +Automatically begin downloading the audio file when the `Howl` is defined. If using HTML5 Audio, you can set this to `'metadata'` to only preload the file's metadata (to get its duration without download the entire file, for example), or to `'full'` to download the whole file first before starting streaming. #### autoplay `Boolean` `false` Set to `true` to automatically start playback when sound is loaded. #### mute `Boolean` `false` diff --git a/src/howler.core.js b/src/howler.core.js index aa60cdbe..32dd8bf2 100644 --- a/src/howler.core.js +++ b/src/howler.core.js @@ -585,7 +585,7 @@ self._muted = o.mute || false; self._loop = o.loop || false; self._pool = o.pool || 5; - self._preload = (typeof o.preload === 'boolean' || o.preload === 'metadata') ? o.preload : true; + self._preload = (typeof o.preload === 'boolean' || o.preload === 'metadata' || o.preload === 'full') ? o.preload : true; self._rate = o.rate || 1; self._sprite = o.sprite || {}; self._src = (typeof o.src !== 'string') ? o.src : [o.src]; @@ -603,6 +603,7 @@ self._endTimers = {}; self._queue = []; self._playLock = false; + self._blobUrl = null; // Setup event listeners. self._onend = o.onend ? [{fn: o.onend}] : []; @@ -721,12 +722,22 @@ self._webAudio = false; } - // Create a new sound object and add it to the pool. - new Sound(self); + if (self._html5 && self._preload === 'full') { + // Preload the whole file before starting streaming + loadBuffer(self, function(arraybuffer, self) { + var blob = new Blob([arraybuffer]); + self._blobUrl = window.URL.createObjectURL(blob); + + new Sound(self); + }); + } else { + // Create a new sound object and add it to the pool. + new Sound(self); + } // Load and decode the audio data for playback. if (self._webAudio) { - loadBuffer(self); + loadBuffer(self, decodeAudioData); } return self; @@ -1798,6 +1809,12 @@ // Clear global errors. Howler.noAudio = false; + // Revoke blob URL + if (self._blobUrl) { + window.URL.revokeObjectURL(self._blobUrl); + self._blobUrl = null; + } + // Clear out `self`. self._state = 'unloaded'; self._sounds = []; @@ -2270,8 +2287,8 @@ self._node.addEventListener('ended', self._endFn, false); // Setup the new audio node. - self._node.src = parent._src; - self._node.preload = parent._preload === true ? 'auto' : parent._preload; + self._node.src = parent._blobUrl ? parent._blobUrl : parent._src; + self._node.preload = (parent._preload === true || parent._preload === 'full') ? 'auto' : parent._preload; self._node.volume = volume * Howler.volume(); // Begin loading the source. @@ -2377,10 +2394,10 @@ var cache = {}; /** - * Buffer a sound from URL, Data URI or cache and decode to audio source (Web Audio API). + * Buffer a sound from URL, Data URI or cache. * @param {Howl} self */ - var loadBuffer = function(self) { + var loadBuffer = function(self, callback) { var url = self._src; // Check if the buffer has already been cached and use it instead. @@ -2402,7 +2419,7 @@ dataView[i] = data.charCodeAt(i); } - decodeAudioData(dataView.buffer, self); + callback(dataView.buffer, self); } else { // Load the buffer from the URL. var xhr = new XMLHttpRequest(); @@ -2425,7 +2442,7 @@ return; } - decodeAudioData(xhr.response, self); + callback(xhr.response, self); }; xhr.onerror = function() { // If there is an error, switch to HTML5 Audio.