audio-manager
Version:
Play sounds using Web Audio, fallback to HTML5 Audio
149 lines (128 loc) • 5.57 kB
JavaScript
var inherits = require('util').inherits;
var ISound = require('./ISound.js');
var PLAY_OPTIONS = { playAudioWhenScreenIsLocked: false };
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Audio wrapper using HTML5 <Audio>
* @author Cedric Stoquer
*
*/
function Sound() {
ISound.call(this);
var audio = new Audio();
audio.loop = false;
audio.type = 'audio/mpeg';
this._audio = audio;
this._onEnd = null;
// if available, use webAudio for better performances
if (this.audioContext) {
this.source = this.audioContext.createMediaElementSource(audio);
this.source.connect(this.audioContext.destination);
}
}
inherits(Sound, ISound);
module.exports = Sound;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Sound.prototype.setVolume = function (value) {
this.volume = this._audio.volume = value;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Sound.prototype.setLoop = function (value) {
this.loop = this._audio.loop = value;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Load sound
* @private
*/
Sound.prototype._load = function () {
var self = this;
function loadFail(error) {
// TODO: keep track that loading has failed to not retry to loading it
self._finalizeLoad(error);
}
function onAudioLoaded() {
this.removeEventListener('canplaythrough', onAudioLoaded);
this.removeEventListener('error', onAudioError);
self.usedMemory = this.duration;
self.audioManager.usedMemory += this.duration;
self._finalizeLoad(null);
}
function onAudioError(error) {
this.removeEventListener('canplaythrough', onAudioLoaded);
this.removeEventListener('error', onAudioError);
loadFail(error);
}
function loadAudio(uri) {
self._loading = true;
self._audio.addEventListener('canplaythrough', onAudioLoaded);
self._audio.addEventListener('error', onAudioError);
self._audio.src = uri;
self._audio.load();
}
var getFileUri = this.audioManager.settings.getFileUri;
var audioPath = this.audioManager.settings.audioPath;
if (getFileUri.length > 2) {
// asynchronous
getFileUri(audioPath, this.id, function onUri(error, uri) {
if (error) return loadFail(error);
loadAudio(uri);
});
} else {
// synchronous
try {
var uri = getFileUri(audioPath, this.id);
if (!uri) return loadFail('emptyUri');
loadAudio(uri);
} catch (error) {
loadFail(error);
}
}
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Unload sound from memory */
Sound.prototype.unload = function () {
if (ISound.prototype.unload.call(this)) {
this._audio.volume = 1.0;
this._audio.src = '';
this._audio.load();
}
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Play sound. If sound is not yet loaded, it is loaded in memory and flagged to be played
* once loading has finished. If loading take too much time, playback may be cancelled.
*/
Sound.prototype._play = function (pitch) {
// TODO: sound pan
// TODO: fade-in
this._audio.volume = this.volume;
this._audio.pause();
this._audio.currentTime = 0;
this._audio.play(PLAY_OPTIONS);
this.playing = true;
// add timeout to determine when sound playback has ended
if (this.loop) return;
var duration = this._audio.duration * 1000;
if (isNaN(duration) || duration <= 0) return;
var self = this;
this._onEnd = window.setTimeout(function () {
self._onEnd = null;
self._playing = false;
self.onEnd && self.onEnd();
}, duration);
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Stop sound
*
* @param {Function} [cb] - optional callback function (use it when sound has a fade out)
*/
Sound.prototype.stop = function (cb) {
// clear sound on end timeout if set
if (this._onEnd !== null) {
window.clearTimeout(this._onEnd);
this._onEnd = null;
}
this._audio.pause();
this._audio.currentTime = 0;
this._playTriggered = 0;
this.playing = false;
return cb && cb(); // TODO: fade-out
};