phaser/src/sound/BaseSoundManager.js

593 lines
16 KiB
JavaScript
Raw Normal View History

2018-02-12 16:01:20 +00:00
/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2018 Photon Storm Ltd.
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
2017-11-09 13:37:41 +00:00
var Class = require('../utils/Class');
var EventEmitter = require('eventemitter3');
2018-02-12 12:25:30 +00:00
var NOOP = require('../utils/NOOP');
2018-01-26 14:35:09 +00:00
2018-02-12 12:25:30 +00:00
/**
* @classdesc
* The sound manager is responsible for playing back audio via Web Audio API or HTML Audio tag as fallback.
* The audio file type and the encoding of those files are extremely important.
* Not all browsers can play all audio formats.
* There is a good guide to what's supported [here](https://developer.mozilla.org/en-US/Apps/Fundamentals/Audio_and_video_delivery/Cross-browser_audio_basics#Audio_Codec_Support).
*
2018-02-12 15:18:31 +00:00
* @class BaseSoundManager
2018-02-13 01:39:22 +00:00
* @extends EventEmitter
2018-02-12 12:25:30 +00:00
* @memberOf Phaser.Sound
* @constructor
2018-01-06 16:38:17 +00:00
* @author Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*
* @param {Phaser.Game} game - Reference to the current game instance.
2018-01-06 16:38:17 +00:00
*/
var BaseSoundManager = new Class({
2018-02-12 12:25:30 +00:00
Extends: EventEmitter,
2018-01-26 14:35:09 +00:00
2018-02-12 12:25:30 +00:00
initialize:
function BaseSoundManager (game)
2018-01-26 14:35:09 +00:00
{
EventEmitter.call(this);
2018-01-26 14:35:09 +00:00
/**
* Local reference to game.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#game
* @type {Phaser.Game}
* @readOnly
* @since 3.0.0
*/
this.game = game;
2018-01-26 14:35:09 +00:00
/**
* An array containing all added sounds.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#sounds
2018-02-18 16:16:19 +00:00
* @type {Phaser.Sound.BaseSound[]}
* @default []
2018-02-12 12:25:30 +00:00
* @private
* @since 3.0.0
*/
this.sounds = [];
2018-01-26 14:35:09 +00:00
/**
* Global mute setting.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#mute
* @type {boolean}
* @default false
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.mute = false;
2018-01-26 14:35:09 +00:00
/**
* Global volume setting.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#volume
* @type {number}
* @default 1
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.volume = 1;
2018-01-26 14:35:09 +00:00
/**
* Global playback rate at which all the sounds will be played.
* Value of 1.0 plays the audio at full speed, 0.5 plays the audio at half speed
* and 2.0 doubles the audio's playback speed.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#rate
* @type {number}
* @default 1
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.rate = 1;
2018-01-26 14:35:09 +00:00
2017-11-16 16:21:49 +00:00
/**
* Global detuning of all sounds in [cents](https://en.wikipedia.org/wiki/Cent_%28music%29).
* The range of the value is -1200 to 1200, but we recommend setting it to [50](https://en.wikipedia.org/wiki/50_Cent).
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#detune
* @type {number}
* @default 0
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2017-11-16 16:21:49 +00:00
*/
this.detune = 0;
2018-01-26 14:35:09 +00:00
/**
* Flag indicating if sounds should be paused when game looses focus,
2018-01-06 16:40:23 +00:00
* for instance when user switches to another tab/program/app.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#pauseOnBlur
* @type {boolean}
* @default true
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.pauseOnBlur = true;
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
game.events.on('blur', function ()
{
if (this.pauseOnBlur)
{
this.onBlur();
}
}, this);
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
game.events.on('focus', function ()
{
if (this.pauseOnBlur)
{
this.onFocus();
}
}, this);
2018-02-12 12:25:30 +00:00
game.events.once('destroy', this.destroy, this);
/**
* Property that actually holds the value of global playback rate.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#_rate
* @type {number}
* @private
* @default 1
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this._rate = 1;
2018-01-26 14:35:09 +00:00
/**
* Property that actually holds the value of global detune.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#_detune
* @type {number}
* @private
* @default 0
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this._detune = 0;
2018-01-26 14:35:09 +00:00
/**
* Mobile devices require sounds to be triggered from an explicit user action,
* such as a tap, before any sound can be loaded/played on a web page.
* Set to true if the audio system is currently locked awaiting user interaction.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#locked
* @type {boolean}
2018-02-18 16:16:46 +00:00
* @readOnly
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.locked = this.locked || false;
2018-01-26 14:35:09 +00:00
/**
* Flag used internally for handling when the audio system
* has been unlocked, if there ever was a need for it.
*
2018-02-12 12:25:30 +00:00
* @name Phaser.Sound.BaseSoundManager#unlocked
* @type {boolean}
* @default false
2018-02-18 16:17:04 +00:00
* @private
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
this.unlocked = false;
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
if (this.locked)
{
this.unlock();
}
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:42:20 +00:00
/**
* Adds a new sound into the sound manager.
*
* @method Phaser.Sound.BaseSoundManager#add
2018-02-12 12:25:30 +00:00
* @override
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:42:20 +00:00
* @param {string} key - Asset key for the sound.
2018-02-18 16:18:13 +00:00
* @param {SoundConfig} [config] - An optional config object containing default sound settings.
2018-02-18 16:16:19 +00:00
*
2018-02-18 16:18:13 +00:00
* @return {Phaser.Sound.BaseSound} The new sound instance.
2018-01-06 16:42:20 +00:00
*/
add: NOOP,
2018-01-26 14:35:09 +00:00
/**
* Audio sprite sound type.
*
* @typedef {Phaser.Sound.BaseSound} AudioSpriteSound
*
* @property {object} spritemap - Local reference to 'spritemap' object form json file generated by audiosprite tool.
*/
/**
2018-01-06 16:44:27 +00:00
* Adds a new audio sprite sound into the sound manager.
*
2018-01-06 16:44:27 +00:00
* @method Phaser.Sound.BaseSoundManager#addAudioSprite
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:44:27 +00:00
* @param {string} key - Asset key for the sound.
2018-02-18 16:19:24 +00:00
* @param {SoundConfig} [config] - An optional config object containing default sound settings.
2018-02-18 16:16:19 +00:00
*
2018-02-18 16:19:24 +00:00
* @return {AudioSpriteSound} The new audio sprite sound instance.
*/
2018-01-26 14:35:09 +00:00
addAudioSprite: function (key, config)
{
var sound = this.add(key, config);
2018-01-26 14:35:09 +00:00
sound.spritemap = this.game.cache.json.get(key).spritemap;
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
for (var markerName in sound.spritemap)
{
if (!sound.spritemap.hasOwnProperty(markerName))
{
continue;
}
2018-02-12 12:25:30 +00:00
2017-11-30 21:56:54 +00:00
var marker = sound.spritemap[markerName];
2018-02-12 12:25:30 +00:00
sound.addMarker({
name: markerName,
start: marker.start,
duration: marker.end - marker.start,
config: config
});
}
2018-02-12 12:25:30 +00:00
return sound;
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:47:41 +00:00
/**
* Enables playing sound on the fly without the need to keep a reference to it.
* Sound will auto destroy once its playback ends.
*
* @method Phaser.Sound.BaseSoundManager#play
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:47:41 +00:00
* @param {string} key - Asset key for the sound.
* @param {ISoundConfig | ISoundMarker} [extra] - An optional additional object containing settings to be applied to the sound. It could be either config or marker object.
2018-02-18 16:16:19 +00:00
*
2018-02-12 12:25:30 +00:00
* @return {boolean} Whether the sound started playing successfully.
2018-01-06 16:47:41 +00:00
*/
2018-01-26 14:35:09 +00:00
play: function (key, extra)
{
var sound = this.add(key);
2018-02-12 12:25:30 +00:00
sound.once('ended', sound.destroy, sound);
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
if (extra)
{
if (extra.name)
{
sound.addMarker(extra);
return sound.play(extra.name);
}
2018-01-26 14:35:09 +00:00
else
{
return sound.play(extra);
}
}
2018-01-26 14:35:09 +00:00
else
{
return sound.play();
}
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:48:31 +00:00
/**
* Enables playing audio sprite sound on the fly without the need to keep a reference to it.
* Sound will auto destroy once its playback ends.
*
* @method Phaser.Sound.BaseSoundManager#playAudioSprite
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:48:31 +00:00
* @param {string} key - Asset key for the sound.
* @param {string} spriteName - The name of the sound sprite to play.
* @param {ISoundConfig} [config] - An optional config object containing default sound settings.
2018-02-18 16:16:19 +00:00
*
2018-02-12 12:25:30 +00:00
* @return {boolean} Whether the audio sprite sound started playing successfully.
2018-01-06 16:48:31 +00:00
*/
2018-01-26 14:35:09 +00:00
playAudioSprite: function (key, spriteName, config)
{
var sound = this.addAudioSprite(key);
2018-02-12 12:25:30 +00:00
sound.once('ended', sound.destroy, sound);
2018-02-12 12:25:30 +00:00
return sound.play(spriteName, config);
},
2018-01-26 14:35:09 +00:00
/**
2018-01-06 16:49:17 +00:00
* Removes a sound from the sound manager.
* The removed sound is destroyed before removal.
*
2018-01-06 16:49:17 +00:00
* @method Phaser.Sound.BaseSoundManager#remove
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:49:17 +00:00
* @param {ISound} sound - The sound object to remove.
2018-02-18 16:16:19 +00:00
*
2018-02-12 12:25:30 +00:00
* @return {boolean} True if the sound was removed successfully, otherwise false.
*/
2018-01-26 14:35:09 +00:00
remove: function (sound)
{
2018-01-04 18:41:43 +00:00
var index = this.sounds.indexOf(sound);
2018-01-26 14:35:09 +00:00
if (index !== -1)
{
sound.destroy();
2018-01-04 18:41:43 +00:00
this.sounds.splice(index, 1);
return true;
}
return false;
},
2018-01-26 14:35:09 +00:00
/**
2018-01-06 16:52:34 +00:00
* Removes all sounds from the sound manager that have an asset key matching the given value.
* The removed sounds are destroyed before removal.
*
2018-01-06 16:52:34 +00:00
* @method Phaser.Sound.BaseSoundManager#removeByKey
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-01-06 16:52:34 +00:00
* @param {string} key - The key to match when removing sound objects.
2018-02-18 16:16:19 +00:00
*
2018-02-12 12:25:30 +00:00
* @return {number} The number of matching sound objects that were removed.
*/
2018-01-26 14:35:09 +00:00
removeByKey: function (key)
{
2018-01-04 18:43:41 +00:00
var removed = 0;
2018-01-26 14:35:09 +00:00
for (var i = this.sounds.length - 1; i >= 0; i--)
{
var sound = this.sounds[i];
2018-01-26 14:35:09 +00:00
if (sound.key === key)
{
sound.destroy();
2018-01-04 18:43:41 +00:00
this.sounds.splice(i, 1);
removed++;
}
}
return removed;
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:55:34 +00:00
/**
* Pauses all the sounds in the game.
*
* @method Phaser.Sound.BaseSoundManager#pauseAll
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-01-06 16:55:34 +00:00
*/
2018-01-26 14:35:09 +00:00
pauseAll: function ()
{
this.forEachActiveSound(function (sound)
{
2018-01-04 18:30:29 +00:00
sound.pause();
});
2018-01-26 14:35:09 +00:00
2018-01-26 13:56:33 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#pauseall
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
*/
this.emit('pauseall', this);
2018-01-04 18:30:29 +00:00
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:55:48 +00:00
/**
* Resumes all the sounds in the game.
*
* @method Phaser.Sound.BaseSoundManager#resumeAll
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-01-06 16:55:48 +00:00
*/
2018-01-26 14:35:09 +00:00
resumeAll: function ()
{
this.forEachActiveSound(function (sound)
{
2018-01-04 18:32:10 +00:00
sound.resume();
});
2018-01-26 14:35:09 +00:00
2018-01-26 13:57:01 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#resumeall
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
*/
this.emit('resumeall', this);
2018-01-04 18:32:10 +00:00
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:56:05 +00:00
/**
* Stops all the sounds in the game.
*
* @method Phaser.Sound.BaseSoundManager#stopAll
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-01-06 16:56:05 +00:00
*/
2018-01-26 14:35:09 +00:00
stopAll: function ()
{
this.forEachActiveSound(function (sound)
{
2018-01-04 14:59:44 +00:00
sound.stop();
});
2018-01-26 14:35:09 +00:00
2018-01-26 13:57:18 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#stopall
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
*/
this.emit('stopall', this);
2018-01-04 14:59:44 +00:00
},
2018-01-26 14:35:09 +00:00
/**
* Method used internally for unlocking audio playback on devices that
* require user interaction before any sound can be played on a web page.
*
2018-01-26 13:55:49 +00:00
* Read more about how this issue is handled here in [this article](https://medium.com/@pgoloskokovic/unlocking-web-audio-the-smarter-way-8858218c0e09).
*
2018-02-12 12:25:30 +00:00
* @method Phaser.Sound.BaseSoundManager#unlock
* @override
* @protected
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
unlock: NOOP,
2018-01-26 14:35:09 +00:00
/**
2018-01-07 21:09:11 +00:00
* Method used internally for pausing sound manager if
* Phaser.Sound.BaseSoundManager#pauseOnBlur is set to true.
*
2018-02-12 12:25:30 +00:00
* @method Phaser.Sound.BaseSoundManager#onBlur
* @override
* @protected
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
onBlur: NOOP,
2018-01-26 14:35:09 +00:00
/**
2018-01-07 21:09:44 +00:00
* Method used internally for resuming sound manager if
* Phaser.Sound.BaseSoundManager#pauseOnBlur is set to true.
*
2018-02-12 12:25:30 +00:00
* @method Phaser.Sound.BaseSoundManager#onFocus
* @override
* @protected
2018-02-12 12:25:30 +00:00
* @since 3.0.0
*/
onFocus: NOOP,
2018-01-26 14:35:09 +00:00
/**
* Update method called on every game step.
2018-01-06 16:58:28 +00:00
* Removes destroyed sounds and updates every active sound in the game.
*
2018-01-06 16:58:28 +00:00
* @method Phaser.Sound.BaseSoundManager#update
2018-02-12 12:25:30 +00:00
* @protected
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
* @param {number} time - The current timestamp as generated by the Request Animation Frame or SetTimeout.
* @param {number} delta - The delta time elapsed since the last frame.
*/
2018-01-26 14:35:09 +00:00
update: function (time, delta)
{
if (this.unlocked)
{
2018-01-17 17:11:27 +00:00
this.unlocked = false;
this.locked = false;
2018-01-26 14:35:09 +00:00
2018-01-26 13:57:46 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#unlocked
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
*/
2018-01-17 17:11:27 +00:00
this.emit('unlocked', this);
}
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
for (var i = this.sounds.length - 1; i >= 0; i--)
{
if (this.sounds[i].pendingRemove)
{
this.sounds.splice(i, 1);
}
}
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
this.sounds.forEach(function (sound)
{
sound.update(time, delta);
});
},
2018-01-26 14:35:09 +00:00
2018-01-06 16:59:43 +00:00
/**
* Destroys all the sounds in the game and all associated events.
*
* @method Phaser.Sound.BaseSoundManager#destroy
2018-02-12 12:25:30 +00:00
* @since 3.0.0
2018-01-06 16:59:43 +00:00
*/
2018-01-26 14:35:09 +00:00
destroy: function ()
{
this.removeAllListeners();
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
this.forEachActiveSound(function (sound)
{
sound.destroy();
});
2018-02-12 12:25:30 +00:00
this.sounds.length = 0;
this.sounds = null;
this.game = null;
},
2018-01-26 14:35:09 +00:00
/**
2018-01-07 21:10:02 +00:00
* Method used internally for iterating only over active sounds and skipping sounds that are marked for removal.
*
2018-01-06 17:10:19 +00:00
* @method Phaser.Sound.BaseSoundManager#forEachActiveSound
2018-02-12 12:25:30 +00:00
* @private
* @since 3.0.0
2018-02-18 16:16:19 +00:00
*
2018-02-12 12:25:30 +00:00
* @param {function} callbackfn - Callback function. (sound: ISound, index: number, array: ISound[]) => void
* @param [scope] - Callback context.
*/
2018-02-12 12:25:30 +00:00
forEachActiveSound: function (callbackfn, scope)
2018-01-26 14:35:09 +00:00
{
var _this = this;
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
this.sounds.forEach(function (sound, index)
{
if (!sound.pendingRemove)
{
2018-02-12 12:25:30 +00:00
callbackfn.call(scope || _this, sound, index, _this.sounds);
}
});
}
2018-02-12 12:25:30 +00:00
});
2018-01-26 14:35:09 +00:00
/**
* Global playback rate.
2018-01-06 17:10:48 +00:00
*
* @name Phaser.Sound.BaseSoundManager#rate
2018-02-12 12:25:30 +00:00
* @type {number}
* @since 3.0.0
*/
Object.defineProperty(BaseSoundManager.prototype, 'rate', {
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
get: function ()
{
return this._rate;
},
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
set: function (value)
{
this._rate = value;
2018-01-26 14:35:09 +00:00
this.forEachActiveSound(function (sound)
{
sound.setRate();
});
2018-01-26 14:35:09 +00:00
2018-01-26 14:00:15 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#rate
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
* @param {number} value - An updated value of Phaser.Sound.BaseSoundManager#rate property.
*/
this.emit('rate', this, value);
}
2018-02-12 12:25:30 +00:00
});
2018-01-26 14:35:09 +00:00
/**
* Global detune.
2018-01-06 17:11:03 +00:00
*
* @name Phaser.Sound.BaseSoundManager#detune
2018-02-12 12:25:30 +00:00
* @type {number}
* @since 3.0.0
*/
Object.defineProperty(BaseSoundManager.prototype, 'detune', {
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
get: function ()
{
return this._detune;
},
2018-02-12 12:25:30 +00:00
2018-01-26 14:35:09 +00:00
set: function (value)
{
this._detune = value;
2018-01-26 14:35:09 +00:00
this.forEachActiveSound(function (sound)
{
sound.setRate();
});
2018-01-26 14:35:09 +00:00
2018-01-26 14:00:38 +00:00
/**
* @event Phaser.Sound.BaseSoundManager#detune
* @param {Phaser.Sound.BaseSoundManager} soundManager - Reference to the sound manager that emitted event.
* @param {number} value - An updated value of Phaser.Sound.BaseSoundManager#detune property.
*/
this.emit('detune', this, value);
}
2018-02-12 12:25:30 +00:00
});
2018-02-12 12:25:30 +00:00
module.exports = BaseSoundManager;