Sound.loop even when set for WebAudio wouldn't use the AudioContext loop property because Sound.start was being invoked with an offset and duration. Now if loop is true and no marker is being used it will use the native Web Audio loop support (#1431)

SoundManager.setDecodedCallback lets you specify a list of Sound files, or keys, and a callback. Once all of the Sound files have finished decoding the callback will be invoked. The amount of time spent decoding depends on the codec used and file size. If all of the files given have already decoded the callback is triggered immediately.

Sound.loopFull is a new method that will start playback of the Sound and set it to loop in its entirety.
This commit is contained in:
photonstorm 2015-02-11 05:17:53 +00:00
parent 18ff04810f
commit dfee82834d
3 changed files with 131 additions and 12 deletions

View file

@ -98,6 +98,8 @@ Thanks to @pnstickne for vast majority of this update.
* Tilemap.createFromObjects now checks for a `rotation` property on the Object and if present will set it as the Sprite.angle (#1433)
* If for whatever reason you wish to hide the Phaser banner in the console.log you can set `window.PhaserGlobal.hideBanner` to `true` and it will skip the output. Honestly I'd rather if you didn't, but the option is now there.
* TilemapLayer.setScale will allow you to apply scaling to a specific Tilemap layer, i.e. `layer.setScale(2)` would double the size of the layer. The way the Camera responds to the layer is adjusted accordingly based on the scale, as is Arcade collision (thanks @mickez #1605)
* SoundManager.setDecodedCallback lets you specify a list of Sound files, or keys, and a callback. Once all of the Sound files have finished decoding the callback will be invoked. The amount of time spent decoding depends on the codec used and file size. If all of the files given have already decoded the callback is triggered immediately.
* Sound.loopFull is a new method that will start playback of the Sound and set it to loop in its entirety.
### Updates
@ -140,6 +142,7 @@ Thanks to @pnstickne for vast majority of this update.
* Sprite.loadTexture and Image.loadTexture now no longer call `updateTexture` if the texture given is a RenderTexture. This fixes issues with RetroFonts in IE11 WebGL as well as other RenderTexture related IE11 problems (#1310 #1381 #1523)
* You can now tint animated Sprites in Canvas mode. Or change the texture atlas frame of a tinted Sprite or Image. Please note that this is pretty expensive (depending in the browser), as the tint is re-applied every time the *frame changes*. The Pixi tint cache has also been removed to allow for subtle tint color shifts and to avoid blowing up memory. So use this feature sparingly! But at least it does now work (#1070)
* ArcadePhysics.moveToPointer no longer goes crazy if the maxTime parameter is given and the Sprite is positioned in a larger game world (thanks @AnderbergE #1472)
* Sound.loop even when set for WebAudio wouldn't use the AudioContext loop property because Sound.start was being invoked with an offset and duration. Now if `loop` is true and no marker is being used it will use the native Web Audio loop support (#1431)
For changes in previous releases please see the extensive [Version History](https://github.com/photonstorm/phaser/blob/master/CHANGELOG.md).

View file

@ -405,6 +405,8 @@ Phaser.Sound.prototype = {
{
if (this.loop)
{
// console.log('Sound update loop: ' + this.currentTime + ' m: ' + this.currentMarker);
// won't work with markers, needs to reset the position
this.onLoop.dispatch(this);
@ -442,13 +444,27 @@ Phaser.Sound.prototype = {
}
},
/**
* Loops this entire sound. If you need to loop a section of it then use Sound.play and the marker and loop parameters.
*
* @method Phaser.Sound#loopFull
* @param {number} [volume=1] - Volume of the sound you want to play. If none is given it will use the volume given to the Sound when it was created (which defaults to 1 if none was specified).
* @return {Phaser.Sound} This sound instance.
*/
loopFull: function (volume) {
this.play(null, 0, volume, true);
},
/**
* Play this sound, or a marked section of it.
*
* @method Phaser.Sound#play
* @param {string} [marker=''] - If you want to play a marker then give the key here, otherwise leave blank to play the full sound.
* @param {number} [position=0] - The starting position to play the sound from - this is ignored if you provide a marker.
* @param {number} [volume=1] - Volume of the sound you want to play. If none is given it will use the volume given to the Sound when it was created (which defaults to 1 if none was specified).
* @param {boolean} [loop=false] - Loop when it finished playing?
* @param {boolean} [loop=false] - Loop when finished playing? If not using a marker / audio sprite the looping will be done via the WebAudio loop property, otherwise it's time based.
* @param {boolean} [forceRestart=true] - If the sound is already playing you can set forceRestart to restart it from the beginning.
* @return {Phaser.Sound} This sound instance.
*/
@ -574,6 +590,11 @@ Phaser.Sound.prototype = {
this._sound.connect(this.gainNode);
}
if (this.loop && marker === '')
{
this._sound.loop = true;
}
this.totalDuration = this._sound.buffer.duration;
if (this.duration === 0)
@ -583,22 +604,22 @@ Phaser.Sound.prototype = {
this.durationMS = this.totalDuration * 1000;
}
if (this.loop && marker === '')
{
this._sound.loop = true;
}
// Useful to cache this somewhere perhaps?
if (typeof this._sound.start === 'undefined')
{
this._sound.noteGrainOn(0, this.position, this.duration);
// this._sound.noteGrainOn(0, this.position, this.duration / 1000);
//this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it
}
else
{
// this._sound.start(0, this.position, this.duration / 1000);
this._sound.start(0, this.position, this.duration);
if (this.loop && marker === '')
{
this._sound.start(0);
}
else
{
this._sound.start(0, this.position, this.duration);
}
}
this.isPlaying = true;

View file

@ -64,6 +64,30 @@ Phaser.SoundManager = function (game) {
*/
this._sounds = [];
/**
* @property {Phaser.ArraySet} _watchList - An array set containing all the sounds being monitored for decoding status.
* @private
*/
this._watchList = new Phaser.ArraySet();
/**
* @property {boolean} _watching - Is the SoundManager monitoring the watchList?
* @private
*/
this._watching = false;
/**
* @property {function} _watchCallback - The callback to invoke once the watchlist is clear.
* @private
*/
this._watchCallback = null;
/**
* @property {object} _watchContext - The context in which to call the watchlist callback.
* @private
*/
this._watchContext = null;
/**
* @property {AudioContext} context - The AudioContext being used for playback.
* @default
@ -301,7 +325,7 @@ Phaser.SoundManager.prototype = {
},
/**
* Decode a sound by its assets key.
* Decode a sound by its asset key.
*
* @method Phaser.SoundManager#decode
* @param {string} key - Assets key of the sound to be decoded.
@ -313,6 +337,8 @@ Phaser.SoundManager.prototype = {
var soundData = this.game.cache.getSoundData(key);
// console.log(key, 'soundData', soundData);
if (soundData)
{
if (this.game.cache.isSoundDecoded(key) === false)
@ -322,9 +348,10 @@ Phaser.SoundManager.prototype = {
var that = this;
this.context.decodeAudioData(soundData, function (buffer) {
that.game.cache.decodedSound(key, buffer);
if (sound)
if (buffer)
{
that.game.cache.decodedSound(key, buffer);
that.onSoundDecode.dispatch(key, sound);
}
});
@ -333,6 +360,53 @@ Phaser.SoundManager.prototype = {
},
/**
* This method allows you to give the SoundManager a list of Sound files, or keys, and a callback.
* Once all of the Sound files have finished decoding the callback will be invoked.
* The amount of time spent decoding depends on the codec used and file size.
* If all of the files given have already decoded the callback is triggered immediately.
*
* @method Phaser.SoundManager#setDecodedCallback
* @param {string|array} files - An array containing either Phaser.Sound objects or their key strings as found in the Phaser.Cache.
* @param {Function} callback - The callback which will be invoked once all files have finished decoding.
* @param {Object} callbackContext - The context in which the callback will run.
*/
setDecodedCallback: function (files, callback, callbackContext) {
if (typeof files === 'string')
{
files = [ files ];
}
this._watchList.reset();
for (var i = 0; i < files.length; i++)
{
if (files[i] instanceof Phaser.Sound && !this.game.cache.isSoundDecoded(files[i].key))
{
this._watchList.add(files[i].key);
}
else if (!this.game.cache.isSoundDecoded(files[i]))
{
this._watchList.add(files[i]);
}
}
// All decoded already?
if (this._watchList.total === 0)
{
this._watching = false;
callback.call(callbackContext);
}
else
{
this._watching = true;
this._watchCallback = callback;
this._watchContext = callbackContext;
}
},
/**
* Updates every sound in the game.
*
@ -359,6 +433,27 @@ Phaser.SoundManager.prototype = {
this._sounds[i].update();
}
if (this._watching)
{
var key = this._watchList.first;
while (key)
{
if (this.game.cache.isSoundDecoded(key))
{
this._watchList.remove(key);
}
key = this._watchList.next;
}
if (this._watchList.total === 0)
{
this._watching = false;
this._watchCallback.call(this._watchContext);
}
}
},
/**