musquito
Advanced tools
Comparing version 2.0.4 to 2.1.0
@@ -1,1 +0,1 @@ | ||
module.exports = require('./dist/musquito-2.0.4'); | ||
module.exports = require('./dist/musquito-2.1.0'); |
{ | ||
"name": "musquito", | ||
"version": "2.0.4", | ||
"version": "2.1.0", | ||
"description": "An audio engine for HTML5 games and interactive websites", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
244
src/Buzz.js
import engine, { EngineEvents, EngineState, ErrorType } from './Engine'; | ||
import Queue from './Queue'; | ||
import utility from './Utility'; | ||
import emitter from './Emitter'; | ||
import DownloadStatus from './DownloadStatus'; | ||
import Queue from './Queue'; | ||
import utility from './Utility'; | ||
import emitter from './Emitter'; | ||
import Sound from './Sound'; | ||
import DownloadStatus from './DownloadStatus'; | ||
/** | ||
* Enum that represents the different states of a sound group (buzz). | ||
* Enum that represents the different states of a buzz (sound group). | ||
* @enum {string} | ||
@@ -174,2 +175,16 @@ */ | ||
/** | ||
* Web API's audio context. | ||
* @type {AudioContext} | ||
* @private | ||
*/ | ||
_context = null; | ||
/** | ||
* The group's gain node. | ||
* @type {GainNode} | ||
* @private | ||
*/ | ||
_gainNode = null; | ||
/** | ||
* True if the group is currently fading. | ||
@@ -188,5 +203,17 @@ * @type {boolean} | ||
/** | ||
* Number of audio resource loading calls in progress. | ||
* @type {number} | ||
* @private | ||
*/ | ||
_noOfLoadCalls = 0; | ||
/** | ||
* Array of sounds belongs to this group. | ||
* @type {Array} | ||
* @private | ||
*/ | ||
_soundsArray = []; | ||
/** | ||
* Initializes the internal properties. | ||
@@ -226,3 +253,2 @@ * @param {string|Array<string>|object} args The input parameters of this sound group. | ||
this._engine.setup(); | ||
this._engine.on(EngineEvents.Resume, this._onEngineResume = this._onEngineResume.bind(this)); | ||
@@ -235,2 +261,14 @@ // If no audio is available throw error. | ||
// Store the audio context. | ||
this._context = this._engine.context(); | ||
// Add the created buzz to the engine and connect the gain nodes. | ||
this._engine.add(this); | ||
this._gainNode = this._engine.context().createGain(); | ||
this._gainNode.gain.setValueAtTime(this._muted ? 0 : this._volume, this._context.currentTime); | ||
this._gainNode.connect(this._engine.masterGain()); | ||
// Subscribe to engine's resume event. | ||
this._engine.on(EngineEvents.Resume, this._onEngineResume = this._onEngineResume.bind(this)); | ||
if (typeof args === 'string') { | ||
@@ -418,3 +456,3 @@ this._src = [args]; | ||
if (isIdPassed) { | ||
const sound = this._engine.sound(soundOrId); | ||
const sound = this.sound(soundOrId); | ||
sound && this._play(sound); | ||
@@ -437,3 +475,3 @@ return this; | ||
destroyCallback: () => { | ||
this._engine.removeSound(this._compatibleSrc, this._id, newSoundId); | ||
this._removeSound(newSoundId); | ||
this._fire(BuzzEvents.Destroy, newSoundId); | ||
@@ -458,3 +496,5 @@ emitter.clear(newSoundId); | ||
const newSound = this._engine.sound(this._compatibleSrc, this._id, soundArgs); | ||
const newSound = new Sound(soundArgs); | ||
newSound._gain().connect(this._gainNode); | ||
this._soundsArray.push(newSound); | ||
this._play(newSound); | ||
@@ -480,4 +520,5 @@ }; | ||
pause(id) { | ||
const isGroup = typeof id === 'undefined'; | ||
this._removePlayActions(id); | ||
typeof id !== 'number' && this.fadeStop(); | ||
isGroup && this.fadeStop(); | ||
this._sounds(id).forEach(sound => sound.pause()); | ||
@@ -495,4 +536,5 @@ this._fire(BuzzEvents.Pause, id); | ||
stop(id) { | ||
const isGroup = typeof id === 'undefined'; | ||
this._removePlayActions(id); | ||
typeof id !== 'number' && this.fadeStop(); | ||
isGroup && this.fadeStop(); | ||
this._sounds(id).forEach(sound => sound.stop()); | ||
@@ -510,7 +552,13 @@ this._fire(BuzzEvents.Stop, id); | ||
mute(id) { | ||
const isGroup = typeof id !== 'number'; | ||
isGroup && this.fadeStop(); | ||
this._sounds(id).forEach(sound => sound.mute()); | ||
isGroup && (this._muted = true); | ||
const isGroup = typeof id === 'undefined'; | ||
if (isGroup) { | ||
this.fadeStop(); | ||
this._gainNode.gain.setValueAtTime(0, this._context.currentTime); | ||
this._muted = true; | ||
} else { | ||
const sound = this.sound(id); | ||
sound && sound.mute(); | ||
} | ||
this._fire(BuzzEvents.Mute, id, this._muted); | ||
@@ -527,7 +575,13 @@ | ||
unmute(id) { | ||
const isGroup = typeof id !== 'number'; | ||
isGroup && this.fadeStop(); | ||
this._sounds(id).forEach(sound => sound.unmute()); | ||
isGroup && (this._muted = false); | ||
const isGroup = typeof id === 'undefined'; | ||
if (isGroup) { | ||
this.fadeStop(); | ||
this._gainNode.gain.setValueAtTime(this._volume, this._context.currentTime); | ||
this._muted = false; | ||
} else { | ||
const sound = this.sound(id); | ||
sound && sound.unmute(); | ||
} | ||
this._fire(BuzzEvents.Mute, id, this._muted); | ||
@@ -545,8 +599,14 @@ | ||
volume(volume, id) { | ||
const isGroup = typeof id !== 'number'; | ||
const isGroup = typeof id === 'undefined'; | ||
if (typeof volume === 'number' && volume >= 0 && volume <= 1.0) { | ||
isGroup && this.fadeStop(); | ||
this._sounds(id).forEach(sound => sound.volume(volume)); | ||
typeof id !== 'number' && (this._volume = volume); | ||
if (isGroup) { | ||
this.fadeStop(); | ||
this._gainNode.gain.setValueAtTime(this._muted ? 0 : volume, this._context.currentTime); | ||
this._volume = volume; | ||
} else { | ||
const sound = this.sound(id); | ||
sound && sound.volume(volume); | ||
} | ||
this._fire(BuzzEvents.Volume, id, this._volume); | ||
@@ -557,3 +617,3 @@ return this; | ||
if (!isGroup) { | ||
const sound = this._engine.sound(id); | ||
const sound = this.sound(id); | ||
return sound ? sound.volume() : null; | ||
@@ -574,3 +634,3 @@ } | ||
fade(to, duration, type = 'linear', id) { | ||
const isGroup = typeof id !== 'number'; | ||
const isGroup = typeof id === 'undefined'; | ||
@@ -583,7 +643,11 @@ if (isGroup && this._fading) { | ||
this._sounds(id).forEach(sound => sound.fade(to, duration, type)); | ||
if (isGroup) { | ||
this._fading = true; | ||
if (type === 'linear') { | ||
this._gainNode.gain.linearRampToValueAtTime(to, this._context.currentTime + duration); | ||
} else { | ||
this._gainNode.gain.exponentialRampToValueAtTime(to, this._context.currentTime + duration); | ||
} | ||
this._fadeTimer = setTimeout(() => { | ||
@@ -598,2 +662,5 @@ this.volume(to); | ||
}, duration * 1000); | ||
} else { | ||
const sound = this.sound(id); | ||
sound && sound.fade(to, duration, type); | ||
} | ||
@@ -610,11 +677,11 @@ | ||
fadeStop(id) { | ||
const isGroup = typeof id !== 'number'; | ||
const isGroup = typeof id === 'undefined'; | ||
if (isGroup && !this._fading) { | ||
return this; | ||
} | ||
if (isGroup) { | ||
if (!this._fading) { | ||
return this; | ||
} | ||
this._sounds(id).forEach(sound => sound.fadeStop()); | ||
this._gainNode.gain.cancelScheduledValues(this._context.currentTime); | ||
if (isGroup) { | ||
if (this._fadeTimer) { | ||
@@ -626,2 +693,5 @@ clearTimeout(this._fadeTimer); | ||
this._fading = false; | ||
} else { | ||
const sound = this.sound(id); | ||
sound && sound.fadeStop(); | ||
} | ||
@@ -641,5 +711,7 @@ | ||
rate(rate, id) { | ||
const isGroup = typeof id === 'undefined'; | ||
if (typeof rate === 'number' && rate >= 0.5 && rate <= 5) { | ||
this._sounds(id).forEach(sound => sound.rate(rate)); | ||
typeof id !== 'number' && (this._rate = rate); | ||
isGroup && (this._rate = rate); | ||
this._fire(BuzzEvents.Rate, id, this._rate); | ||
@@ -649,4 +721,4 @@ return this; | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
if (!isGroup) { | ||
const sound = this.sound(id); | ||
return sound ? sound.rate() : null; | ||
@@ -699,10 +771,12 @@ } | ||
loop(loop, id) { | ||
const isGroup = typeof id === 'undefined'; | ||
if (typeof loop === 'boolean') { | ||
this._sounds(id).forEach(sound => sound.loop(loop)); | ||
typeof id !== 'number' && (this._loop = loop); | ||
isGroup && (this._loop = loop); | ||
return this; | ||
} | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
if (!isGroup) { | ||
const sound = this.sound(id); | ||
return sound ? sound.loop() : null; | ||
@@ -720,3 +794,3 @@ } | ||
playing(id) { | ||
const sound = this._engine.sound(id); | ||
const sound = this.sound(id); | ||
return sound ? sound.isPlaying() : null; | ||
@@ -731,8 +805,8 @@ } | ||
muted(id) { | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
return sound ? sound.muted() : null; | ||
if (typeof id === 'undefined') { | ||
return this._muted; | ||
} | ||
return this._muted; | ||
const sound = this.sound(id); | ||
return sound ? sound.muted() : null; | ||
} | ||
@@ -746,8 +820,8 @@ | ||
state(id) { | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
return sound ? sound.state() : null; | ||
if (typeof id === 'undefined') { | ||
return this._state; | ||
} | ||
return this._state; | ||
const sound = this.sound(id); | ||
return sound ? sound.state() : null; | ||
} | ||
@@ -761,8 +835,8 @@ | ||
duration(id) { | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
return sound ? sound.duration() : null; | ||
if (typeof id === 'undefined') { | ||
return this._duration; | ||
} | ||
return this._duration; | ||
const sound = this.sound(id); | ||
return sound ? sound.duration() : null; | ||
} | ||
@@ -801,10 +875,15 @@ | ||
this.stop(); | ||
this._soundsArray.forEach(sound => sound.destroy()); | ||
this._queue.clear(); | ||
this._engine.off(EngineEvents.Resume, this._onEngineResume); | ||
this._engine.free(false, this._compatibleSrc, this._id); | ||
this._engine.releaseForGroup(this._compatibleSrc, this._id); | ||
this._stream && this._engine.releaseForGroup(this._compatibleSrc, this._id); | ||
this._gainNode.disconnect(); | ||
this._engine.remove(this); | ||
this._soundsArray = []; | ||
this._buffer = null; | ||
this._queue = null; | ||
this._context = null; | ||
this._engine = null; | ||
this._gainNode = null; | ||
this._state = BuzzState.Destroyed; | ||
@@ -836,2 +915,23 @@ | ||
/** | ||
* Removes the inactive sounds. | ||
*/ | ||
free() { | ||
const now = new Date(); | ||
this._soundsArray = this._soundsArray.filter(sound => { | ||
const inactiveDurationInSeconds = (now - sound.lastPlayed()) / 1000; | ||
if (sound.isPersistent() || | ||
sound.isPlaying() || | ||
sound.isPaused() || | ||
inactiveDurationInSeconds < this._engine.inactiveTime() * 60) { | ||
return true; | ||
} | ||
sound.destroy(); | ||
return false; | ||
}); | ||
} | ||
/** | ||
* Subscribes to an event for the sound or the group. | ||
@@ -870,2 +970,10 @@ * @param {string} eventName The event name. | ||
/** | ||
* Returns the gain node. | ||
* @return {GainNode} | ||
*/ | ||
gain() { | ||
return this._gainNode; | ||
} | ||
/** | ||
* Returns the audio resource loading status. | ||
@@ -892,6 +1000,14 @@ * @return {LoadState} | ||
sound(id) { | ||
return this._engine.sound(id); | ||
return this._soundsArray.find(x => x.id() === id); | ||
} | ||
/** | ||
* Returns all the sounds. | ||
* @return {Array<Sound>} | ||
*/ | ||
sounds() { | ||
return this._soundsArray; | ||
} | ||
/** | ||
* Returns true if the passed sound exists. | ||
@@ -986,10 +1102,24 @@ * @param {number} id The sound id. | ||
if (typeof id === 'number') { | ||
const sound = this._engine.sound(id); | ||
const sound = this._soundsArray.find(x => x.id() === id); | ||
return sound ? [sound] : []; | ||
} | ||
return this._engine.sounds(this._id); | ||
return this._soundsArray; | ||
} | ||
/** | ||
* Removes the passed sound from the array. | ||
* @param {number|Sound} sound The sound. | ||
* @private | ||
*/ | ||
_removeSound(sound) { | ||
if (typeof sound === 'number') { | ||
this._soundsArray = this._soundsArray.filter(x => x.id() !== sound); | ||
return; | ||
} | ||
this._soundsArray.splice(this._soundsArray.indexOf(sound), 1); | ||
} | ||
/** | ||
* Fires an event of group or sound. | ||
@@ -996,0 +1126,0 @@ * @param {string} eventName The event name. |
import BufferLoader from './BufferLoader'; | ||
import MediaLoader from './MediaLoader'; | ||
import emitter from './Emitter'; | ||
import Heap from './Heap'; | ||
import Queue from './Queue'; | ||
import utility from './Utility'; | ||
import Sound from './Sound'; | ||
@@ -101,3 +99,3 @@ /** | ||
/** | ||
* The heap clean-up period. | ||
* The inactive sounds clean-up period. | ||
* @type {number} | ||
@@ -165,7 +163,7 @@ * @private | ||
/** | ||
* The sound heap. | ||
* @type {Heap} | ||
* Array of buzzes. | ||
* @type {Array<Buzz>} | ||
* @private | ||
*/ | ||
_heap = null; | ||
_buzzesArray = []; | ||
@@ -187,9 +185,9 @@ /** | ||
/** | ||
* Instantiates the heap and action queue. | ||
* Instantiates the action queue. | ||
* @constructor | ||
*/ | ||
constructor() { | ||
this._heap = new Heap(this._inactiveTime); | ||
this._queue = new Queue(); | ||
this._resumeAndRemoveListeners = this._resumeAndRemoveListeners.bind(this); | ||
this.free = this.free.bind(this); | ||
} | ||
@@ -203,3 +201,3 @@ | ||
* @param {number} [args.maxNodesPerSource = 10] Maximum number of HTML5 audio objects allowed for a url. | ||
* @param {number} [args.cleanUpInterval = 5] The heap clean-up interval period in minutes. | ||
* @param {number} [args.cleanUpInterval = 5] The sounds garbage collection interval period in minutes. | ||
* @param {boolean} [args.autoEnable = true] Auto-enables audio in first user interaction. | ||
@@ -267,3 +265,5 @@ * @param {function} [args.onstop] Event-handler for the "stop" event. | ||
// Create the media loader. | ||
this._mediaLoader = new MediaLoader(this._maxNodesPerSource, this._heap); | ||
this._mediaLoader = new MediaLoader(this._maxNodesPerSource, (src) => { | ||
this._buzzesArray.forEach(buzz => buzz.getCompatibleSource() === src && buzz.free()); | ||
}); | ||
@@ -281,3 +281,3 @@ // Auto-enable audio in first user interaction. | ||
this._intervalId = window.setInterval(this._heap.free, this._cleanUpInterval * 60 * 1000); | ||
this._intervalId = window.setInterval(this.free, this._cleanUpInterval * 60 * 1000); | ||
@@ -309,2 +309,27 @@ this._state = this._context.state !== 'suspended' ? EngineState.Ready : EngineState.Suspended; | ||
/** | ||
* Stores the buzz in the internal collection. | ||
* @param {Buzz} buzz The newly created buzz. | ||
* @return {Engine} | ||
*/ | ||
add(buzz) { | ||
if (this._buzzesArray.indexOf(buzz) > -1) { | ||
return this; | ||
} | ||
this._buzzesArray.push(buzz); | ||
return this; | ||
} | ||
/** | ||
* Removes the stored buzz from the internal collection. | ||
* @param {Buzz} buzz The buzz. | ||
* @return {Engine} | ||
*/ | ||
remove(buzz) { | ||
this._buzzesArray.splice(this._buzzesArray.indexOf(buzz), 1); | ||
return this; | ||
} | ||
/** | ||
* Loads audio node for group. | ||
@@ -461,3 +486,3 @@ * @param {string} url The audio file url. | ||
// Stop all the sounds. | ||
this._heap.sounds().forEach(sound => sound.stop()); | ||
this.buzzes().forEach(buzz => buzz.stop()); | ||
@@ -545,5 +570,4 @@ // Fire the "stop" event. | ||
// Destroy the heap. | ||
this._heap.destroy(); | ||
this._heap = null; | ||
// Destroy all the buzzes. | ||
this._buzzesArray.forEach(buzz => buzz.destroy()); | ||
@@ -562,2 +586,3 @@ // Clear the cache and remove the loader. | ||
this._buzzesArray = []; | ||
this._context = null; | ||
@@ -620,48 +645,7 @@ this._queue.clear(); | ||
/** | ||
* Returns the existing sound in heap or create a new one and return. | ||
* @param {number|string} idOrUrl The sound id or audio url/base64 string. | ||
* @param {number} [groupId] The group id. | ||
* @param {object} [args] The sound creation arguments. | ||
* @return {Sound} | ||
*/ | ||
sound(idOrUrl, groupId, args) { | ||
if (typeof idOrUrl === 'number') { | ||
return this._heap.sound(idOrUrl); | ||
} | ||
const sound = new Sound(args); | ||
this._heap.add(idOrUrl, groupId, sound); | ||
sound._gain().connect(this._gainNode); | ||
return sound; | ||
} | ||
/** | ||
* Removes the destroyed sound. | ||
* @param {string} src The audio url. | ||
* @param {number} groupId The group id. | ||
* @param {number} soundId The sound id. | ||
*/ | ||
removeSound(src, groupId, soundId) { | ||
this._heap.removeSound(src, groupId, soundId); | ||
} | ||
/** | ||
* Returns the sounds belongs to a group or all the sounds from the heap. | ||
* @param {number} [groupId] The group id. | ||
* @return {Array<Sound>} | ||
*/ | ||
sounds(groupId) { | ||
return this._heap.sounds(groupId); | ||
} | ||
/** | ||
* Destroys the sounds belong to the passed group. | ||
* @param {boolean} idle True to destroy only the idle sounds. | ||
* @param {string} [src] The audio resource url. | ||
* @param {number} [groupId] The group id. | ||
* Removes the inactive sounds. | ||
* @return {Engine} | ||
*/ | ||
free(idle, src, groupId) { | ||
this._heap.free(idle, src, groupId); | ||
free() { | ||
this._buzzesArray.forEach(buzz => buzz.free()); | ||
this._mediaLoader.cleanUp(); | ||
@@ -728,2 +712,27 @@ return this; | ||
/** | ||
* Returns the buzz for the passed id. | ||
* @param {number} [id] The buzz id. | ||
* @return {Buzz} | ||
*/ | ||
buzz(id) { | ||
return this._buzzesArray.find(x => x.id() === id); | ||
} | ||
/** | ||
* Returns all the buzzes. | ||
* @return {Array<Buzz>} | ||
*/ | ||
buzzes() { | ||
return this._buzzesArray; | ||
} | ||
/** | ||
* Returns in active time. | ||
* @return {number} | ||
*/ | ||
inactiveTime() { | ||
return this._inactiveTime; | ||
} | ||
/** | ||
* Fires an event of engine. | ||
@@ -730,0 +739,0 @@ * @param {string} eventName The event name. |
@@ -18,6 +18,6 @@ import utility from './Utility'; | ||
* The sounds store. | ||
* @type {Heap} | ||
* @type {function} | ||
* @private | ||
*/ | ||
_heap = null; | ||
_soundCleanUpCallback = null; | ||
@@ -32,7 +32,7 @@ /** | ||
/** | ||
* True if the heap is cleaned manually. | ||
* True if the `soundCleanUpCallback` called. | ||
* @type {boolean} | ||
* @private | ||
*/ | ||
_heapCleaned = false; | ||
_cleanUpCalled = false; | ||
@@ -42,7 +42,7 @@ /** | ||
* @param {number} maxNodesPerSource Maximum number of audio nodes allowed for a resource. | ||
* @param {Heap} heap The sounds store. | ||
* @param {function} soundCleanUpCallback The inactive sounds cleanup callback. | ||
*/ | ||
constructor(maxNodesPerSource, heap) { | ||
constructor(maxNodesPerSource, soundCleanUpCallback) { | ||
this._maxNodesPerSource = maxNodesPerSource; | ||
this._heap = heap; | ||
this._soundCleanUpCallback = soundCleanUpCallback; | ||
} | ||
@@ -189,3 +189,2 @@ | ||
Object.keys(this._resourceNodesMap).forEach(src => this.releaseForSource(src)); | ||
this._heap = null; | ||
} | ||
@@ -268,9 +267,9 @@ | ||
if (!this._heapCleaned) { | ||
this._heap.free(src); | ||
this._heapCleaned = true; | ||
if (!this._cleanUpCalled) { | ||
this._soundCleanUpCallback(src); | ||
this._cleanUpCalled = true; | ||
this._checkMaxNodesForSrc(src); | ||
} | ||
this._heapCleaned = false; | ||
this._cleanUpCalled = false; | ||
@@ -277,0 +276,0 @@ throw new Error(`Maximum nodes reached for resource ${src}`); |
import Html5AudioPool from './Html5AudioPool'; | ||
import Heap from './Heap'; | ||
@@ -8,4 +7,3 @@ describe('Html5AudioPool', () => { | ||
const heap = new Heap(1), | ||
url = '/base/sounds/bg.mp3', | ||
const url = '/base/sounds/bg.mp3', | ||
groupId = 1, | ||
@@ -15,3 +13,3 @@ soundId = 100; | ||
beforeEach(() => { | ||
html5AudioPool = new Html5AudioPool(2, heap); | ||
html5AudioPool = new Html5AudioPool(2, () => {}); | ||
}); | ||
@@ -18,0 +16,0 @@ |
@@ -34,6 +34,6 @@ import Html5AudioPool from './Html5AudioPool'; | ||
* @param {number} maxNodesPerSource Maximum number of audio nodes allowed for a url. | ||
* @param {Heap} heap The sounds store. | ||
* @param {function} soundCleanUpCallback The inactive sounds cleanup callback. | ||
*/ | ||
constructor(maxNodesPerSource, heap) { | ||
this._audioPool = new Html5AudioPool(maxNodesPerSource, heap); | ||
constructor(maxNodesPerSource, soundCleanUpCallback) { | ||
this._audioPool = new Html5AudioPool(maxNodesPerSource, soundCleanUpCallback); | ||
} | ||
@@ -40,0 +40,0 @@ |
@@ -236,3 +236,3 @@ import engine from './Engine'; | ||
* @param {object} args The input parameters of the sound. | ||
* @param {string} args.id The unique id of the sound. | ||
* @param {number} args.id The unique id of the sound. | ||
* @param {boolean} [args.stream = false] True to use HTML5 audio node for playing sound. | ||
@@ -239,0 +239,0 @@ * @param {Audio} [args.audio] The pre-loaded HTML5 audio object. |
@@ -29,3 +29,3 @@ import webpack from 'webpack'; | ||
`/*! | ||
* musquito v2.0.4 | ||
* musquito v2.1.0 | ||
* http://musquitojs.com | ||
@@ -32,0 +32,0 @@ * |
Sorry, the diff of this file is not supported yet
4329819
43
4322