Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add eager playback global option #1591

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,9 +378,11 @@ Automatically attempts to enable audio on mobile (iOS, Android, etc) devices and
Each HTML5 Audio object must be unlocked individually, so we keep a global pool of unlocked nodes to share between all `Howl` instances. This pool gets created on the first user interaction and is set to the size of this property.
#### autoSuspend `Boolean` `true`
Automatically suspends the Web Audio AudioContext after 30 seconds of inactivity to decrease processing and energy usage. Automatically resumes upon new playback. Set this property to `false` to disable this behavior.
#### ctx `Boolean` *`Web Audio Only`*
#### eagerPlayback `Boolean` `false`
When enabled, allows playback to begin before the browser has estimated that enough data has loaded in order to play the sound to its end, without having to stop and buffer for more content.
#### ctx `AudioContext` `null` *`Web Audio Only`*
Exposes the `AudioContext` with Web Audio API.
#### masterGain `Boolean` *`Web Audio Only`*
#### masterGain `GainNode` `null` *`Web Audio Only`*
Exposes the master `GainNode` with Web Audio API. This can be useful for writing plugins or advanced usage.


Expand Down
37 changes: 31 additions & 6 deletions src/howler.core.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
self.usingWebAudio = true;
self.autoSuspend = true;
self.ctx = null;
self.eagerPlayback = false;

// Set to false to disable the auto audio unlocker.
self.autoUnlock = true;
Expand All @@ -63,8 +64,8 @@

/**
* Get/set the global volume for all sounds.
* @param {Float} vol Volume from 0.0 to 1.0.
* @return {Howler/Float} Returns self or current volume.
* @param {float} vol Volume from 0.0 to 1.0.
* @return {Howler/float} Returns self or current volume.
*/
volume: function(vol) {
var self = this || Howler;
Expand Down Expand Up @@ -539,6 +540,22 @@
self._resumeAfterSuspend = true;
}

return self;
},

/**
* Use `canplay` event for determining when playback can begin. In contrast with the `canplaythrough` event,
* `canplay` is fired when enough data has been loaded to begin playing the media, but not necessarily enough to
* play without stopping and buffering additional data.
* @return {Howler}
*/
_enableEagerPlayback: function() {
var self = this;

if (self._canPlayEvent === 'canplaythrough') {
self._canPlayEvent = 'canplay';
}

return self;
}
};
Expand Down Expand Up @@ -623,6 +640,10 @@
// Web Audio or HTML5 Audio?
self._webAudio = Howler.usingWebAudio && !self._html5;

if (Howler.eagerPlayback) {
Howler._enableEagerPlayback();
}

// Automatically try to enable audio.
if (typeof Howler.ctx !== 'undefined' && Howler.ctx && Howler.autoUnlock) {
Howler._unlockAudio();
Expand Down Expand Up @@ -897,7 +918,11 @@
} else {
// Fire this when the sound is ready to play to begin HTML5 Audio playback.
var playHtml5 = function() {
node.currentTime = seek;
// When `eagerPlayback` is enabled, setting `currentTime` to the same value prevents the
// play promise from ever resolving in Chromium-based browsers.
if (node.currentTime !== seek) {
node.currentTime = seek;
}
node.muted = sound._muted || self._muted || Howler._muted || node.muted;
node.volume = sound._volume * Howler.volume();
node.playbackRate = sound._rate;
Expand Down Expand Up @@ -974,7 +999,7 @@
node.load();
}

// Play immediately if ready, or wait for the 'canplaythrough'e vent.
// Play immediately if ready, or wait for the '_canPlayEvent' event.
var loadedNoReadyState = (window && window.ejecta) || (!node.readyState && Howler._navigator.isCocoonJS);
if (node.readyState >= 3 || loadedNoReadyState) {
playHtml5();
Expand All @@ -984,7 +1009,7 @@

var listener = function() {
self._state = 'loaded';

// Begin playback.
playHtml5();

Expand Down Expand Up @@ -2260,7 +2285,7 @@
self._errorFn = self._errorListener.bind(self);
self._node.addEventListener('error', self._errorFn, false);

// Listen for 'canplaythrough' event to let us know the sound is ready.
// Listen for '_canPlayEvent' event to let us know the sound is ready.
self._loadFn = self._loadListener.bind(self);
self._node.addEventListener(Howler._canPlayEvent, self._loadFn, false);

Expand Down
3 changes: 2 additions & 1 deletion tests/core.html5audio.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<div id="logo"></div>
</div>
<script src="../src/howler.core.js"></script>
<script src="./js/parseGlobalOptions.js"></script>
<script src="./js/core.html5audio.js"></script>
</body>
</html>
</html>
3 changes: 2 additions & 1 deletion tests/core.webaudio.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<button class="button" id="start" disabled>LOADING...</button>
</div>
<script src="../src/howler.core.js"></script>
<script src="./js/parseGlobalOptions.js"></script>
<script src="./js/core.webaudio.js"></script>
</body>
</html>
</html>
42 changes: 40 additions & 2 deletions tests/css/styles.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
html {
width: 100%;
height: 100%;
height: 100%;
overflow: hidden;
padding: 0;
margin: 0;
Expand Down Expand Up @@ -89,8 +89,46 @@ body {
align-items: center;
}

#globalOptionsHeader {
font-size: 3vw;
margin: 0 0 1rem;
}

#globalOptions {
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 92%;
margin: 0 2rem 3rem;
}

.option {
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-justify-content: space-between;
-webkit-box-pack: justify;
-ms-flex-pack: distribute;
justify-content: space-between;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
margin: 0 2rem 1rem 0;
font-size: 2vw;
}

.option > * {
margin-bottom: 0;
}

.option > label {
margin-right: 0.5rem;
}

@media screen and (max-height: 400px) {
.button {
padding: 5px;
}
}
}
80 changes: 63 additions & 17 deletions tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,70 @@
<link rel="stylesheet" href="./css/styles.css">
</head>
<body>
<div id="container">
<button class="button" id="webaudio">RUN WEB AUDIO TESTS</button>
<button class="button" id="html5">RUN HTML5 TESTS</button>
<button class="button" id="spatial">RUN SPATIAL TESTS</button>
</div>
<div id="container">
<h6 id="globalOptionsHeader">Global Options:</h6>
<form id="globalOptions">
<div class="option">
<label for="usingWebAudio">usingWebAudio:</label>
<input type="checkbox" id="usingWebAudio" name="usingWebAudio" checked>
</div>
<div class="option">
<label for="noAudio">noAudio:</label>
<input type="checkbox" id="noAudio" name="noAudio">
</div>
<div class="option">
<label for="autoUnlock">autoUnlock:</label>
<input type="checkbox" id="autoUnlock" name="autoUnlock" checked>
</div>
<div class="option">
<label for="html5PoolSize">html5PoolSize:</label>
<input type="number" id="html5PoolSize" name="html5PoolSize" min="1" max="10" value="10" style="width:2rem">
</div>
<div class="option">
<label for="autoSuspend">autoSuspend:</label>
<input type="checkbox" id="autoSuspend" name="autoSuspend" checked>
</div>
<div class="option">
<label for="eagerPlayback">eagerPlayback:</label>
<input type="checkbox" id="eagerPlayback" name="eagerPlayback">
</div>
</form>
<button class="button" id="webaudio">RUN WEB AUDIO TESTS</button>
<button class="button" id="html5">RUN HTML5 TESTS</button>
<button class="button" id="spatial">RUN SPATIAL TESTS</button>
</div>

<script>
document.getElementById('webaudio').onclick = function() {
window.location = 'core.webaudio.html';
};
<script>
function navigateToTest(testLocation) {
var formElements = document.getElementById('globalOptions').elements;
var globalOptionsElements = Array.prototype.slice.call(formElements, 0);
var globalOptions = globalOptionsElements.map(function(el) {
if (el.type === 'checkbox') {
return el.name + '=' + el.checked;
}

document.getElementById('html5').onclick = function() {
window.location = 'core.html5audio.html';
};
if (el.type === 'number' && el.value) {
var value = Math.max(el.min, Math.min(el.max, el.value))
return el.name + '=' + value;
}
});
var globalOptionsQueryString = globalOptions.filter(Boolean).join('&');
window.location = globalOptionsQueryString.length
? testLocation + '?' + globalOptionsQueryString
: testLocation;
}

document.getElementById('spatial').onclick = function() {
window.location = 'spatial.html';
};
</script>
document.getElementById('webaudio').onclick = function() {
navigateToTest('core.webaudio.html');
};

document.getElementById('html5').onclick = function() {
navigateToTest('core.html5audio.html');
};

document.getElementById('spatial').onclick = function() {
navigateToTest('spatial.html');
};
</script>
</body>
</html>
</html>
12 changes: 7 additions & 5 deletions tests/js/core.html5audio.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ sound1.once('load', function() {
// Define the tests to run.
var id;
var tests = [
function(fn) {
id = sound1.play();
function(fn) {
sound1.once('play', function() {
label.innerHTML = 'PLAYING';
setTimeout(fn, 2000);
});

label.innerHTML = 'PLAYING';
setTimeout(fn, 2000);
id = sound1.play();
},

function(fn) {
Expand Down Expand Up @@ -265,4 +267,4 @@ var chain = function(i) {
start.addEventListener('click', function() {
tests[0](chain(1));
start.style.display = 'none';
}, false);
}, false);
4 changes: 2 additions & 2 deletions tests/js/core.webaudio.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var tests = [
label.innerHTML = 'PLAYING';
setTimeout(fn, 2000);
});

id = sound1.play();
},

Expand Down Expand Up @@ -269,4 +269,4 @@ if (Howler.usingWebAudio) {
}, false);
} else {
window.location = 'core.html5audio.html';
}
}
33 changes: 33 additions & 0 deletions tests/js/parseGlobalOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Set global options from query params
function parseValueFromEntry(entry) {
var key = entry[0];
var value = entry[1];
var parsedValue = value;

if (value.toLowerCase() === 'true') {
parsedValue = true;
} else if (value.toLowerCase() === 'false') {
parsedValue = false;
} else if (!isNaN(value)) {
parsedValue = parseFloat(value);
}

return [key, parsedValue];
}

function setGlobalOptions(entry) {
var key = entry[0];
var value = entry[1];

if (Howler.hasOwnProperty(key)) {
Howler[key] = value;
}
}

window.location.search
.slice(1)
.split('&')
.filter(function(entry) { return entry[0]; })
.map(function(pair) { return pair.split('='); })
.map(parseValueFromEntry)
.forEach(setGlobalOptions);
4 changes: 2 additions & 2 deletions tests/js/spatial.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ var tests = [
label.innerHTML = 'PLAYING';
setTimeout(fn, 2000);
});

id = sound1.play();
},

Expand Down Expand Up @@ -116,4 +116,4 @@ if (Howler.usingWebAudio) {
}, false);
} else {
window.location = 'core.html5audio.html';
}
}
3 changes: 2 additions & 1 deletion tests/spatial.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</div>
<script src="../src/howler.core.js"></script>
<script src="../src/plugins/howler.spatial.js"></script>
<script src="./js/parseGlobalOptions.js"></script>
<script src="./js/spatial.js"></script>
</body>
</html>
</html>