SoundManager overhaul nearly finished. Proper Web Audio / Audio tag split, working across desktop and mobile. Includes iOS touch unlock restriction handling, auto decoding, global volume management, audio sprite support (for both web audio and audio tags) and playhead monitoring.

This commit is contained in:
Richard Davey
2013-07-16 14:45:08 +01:00
parent d0e886259d
commit 72eb22128f
21 changed files with 2366 additions and 498 deletions
+4 -1
View File
@@ -14,6 +14,7 @@
/// <reference path="cameras/CameraManager.ts" />
/// <reference path="gameobjects/GameObjectFactory.ts" />
/// <reference path="sound/SoundManager.ts" />
/// <reference path="sound/Sound.ts" />
/// <reference path="Stage.ts" />
/// <reference path="Time.ts" />
/// <reference path="tweens/TweenManager.ts" />
@@ -298,12 +299,12 @@ module Phaser {
this.stage = new Stage(this, parent, width, height);
this.world = new World(this, width, height);
this.add = new GameObjectFactory(this);
this.sound = new SoundManager(this);
this.cache = new Cache(this);
this.load = new Loader(this, this.loadComplete);
this.time = new Time(this);
this.tweens = new TweenManager(this);
this.input = new Input(this);
this.sound = new SoundManager(this);
this.rnd = new RandomDataGenerator([(Date.now() * Math.random()).toString()]);
this.physics = new Physics.Manager(this);
@@ -391,6 +392,7 @@ module Phaser {
this.tweens.update();
this.input.update();
this.stage.update();
this.sound.update();
if (this.onPausedCallback !== null)
{
@@ -407,6 +409,7 @@ module Phaser {
this.tweens.update();
this.input.update();
this.stage.update();
this.sound.update();
this.physics.update();
this.world.update();
+3 -1
View File
@@ -61,6 +61,8 @@ module Phaser {
document.addEventListener('visibilitychange', (event) => this.visibilityChange(event), false);
document.addEventListener('webkitvisibilitychange', (event) => this.visibilityChange(event), false);
document.addEventListener('pagehide', (event) => this.visibilityChange(event), false);
document.addEventListener('pageshow', (event) => this.visibilityChange(event), false);
window.onblur = (event) => this.visibilityChange(event);
window.onfocus = (event) => this.visibilityChange(event);
@@ -216,7 +218,7 @@ module Phaser {
return;
}
if (event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true)
if (event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true)
{
if (this._game.paused == false)
{
+4
View File
@@ -94,6 +94,10 @@ module Phaser {
return <Sprite> this._world.group.add(new Sprite(this._game, x, y, key, frame, bodyType));
}
public audio(key: string, volume?: number = 1, loop?: bool = false) {
return <Sound> this._game.sound.add(key, volume, loop);
}
/**
* Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center.
*
+2 -2
View File
@@ -275,7 +275,7 @@ module Phaser {
* @property recordPointerHistory
* @type {Boolean}
**/
public recordPointerHistory: bool = true;
public recordPointerHistory: bool = false;
/**
* The rate in milliseconds at which the Pointer objects should update their tracking history
@@ -286,7 +286,7 @@ module Phaser {
/**
* The total number of entries that can be recorded into the Pointer objects tracking history.
* The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* @property recordLimit
* @type {Number}
*/
-2
View File
@@ -79,9 +79,7 @@ module Phaser {
* @param {Any} event
**/
private consumeTouchMove(event) {
event.preventDefault();
}
/**
+38 -4
View File
@@ -122,22 +122,40 @@ module Phaser {
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
public addSound(key: string, url: string, data) {
public addSound(key: string, url: string, data, webAudio: bool = true, audioTag: bool = false) {
this._sounds[key] = { url: url, data: data, decoded: false };
console.log('Cache addSound', key, url, webAudio, audioTag);
var decoded: bool = false;
if (audioTag) {
decoded = true;
}
this._sounds[key] = { url: url, data: data, isDecoding: false, decoded: decoded, webAudio: webAudio, audioTag: audioTag };
}
public updateSound(key: string, property: string, value) {
if (this._sounds[key])
{
this._sounds[key][property] = value;
}
}
/**
* Add a new decoded sound.
* @param key {string} Asset key for the sound.
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
public decodedSound(key: string, data) {
console.log('decoded sound', key);
this._sounds[key].data = data;
this._sounds[key].decoded = true;
this._sounds[key].isDecoding = false;
}
@@ -201,12 +219,28 @@ module Phaser {
}
/**
* Get sound by key.
* @param key Asset key of the sound you want.
* @return {object} The sound you want.
*/
public getSound(key: string) {
if (this._sounds[key])
{
return this._sounds[key];
}
return null;
}
/**
* Get sound data by key.
* @param key Asset key of the sound you want.
* @return {object} The sound data you want.
*/
public getSound(key: string) {
public getSoundData(key: string) {
if (this._sounds[key])
{
+77 -10
View File
@@ -218,14 +218,15 @@ module Phaser {
/**
* Add a new audio file loading request.
* @param key {string} Unique asset key of the audio file.
* @param url {string} URL of audio file.
* @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ]
* @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process.
*/
public audio(key: string, url: string) {
public audio(key: string, urls: string[], autoDecode: bool = true) {
if (this.checkKeyExists(key) === false)
{
this._queueSize++;
this._fileList[key] = { type: 'audio', key: key, url: url, data: null, buffer: null, error: false, loaded: false };
this._fileList[key] = { type: 'audio', key: key, url: urls, data: null, buffer: null, error: false, loaded: false, autoDecode: autoDecode };
this._keys.push(key);
}
@@ -327,11 +328,32 @@ module Phaser {
break;
case 'audio':
this._xhr.open("GET", file.url, true);
this._xhr.responseType = "arraybuffer";
this._xhr.onload = () => this.fileComplete(file.key);
this._xhr.onerror = () => this.fileError(file.key);
this._xhr.send();
file.url = this.getAudioURL(file.url);
if (file.url !== null)
{
// WebAudio or Audio Tag?
if (this._game.sound.usingWebAudio)
{
this._xhr.open("GET", file.url, true);
this._xhr.responseType = "arraybuffer";
this._xhr.onload = () => this.fileComplete(file.key);
this._xhr.onerror = () => this.fileError(file.key);
this._xhr.send();
}
else if (this._game.sound.usingAudioTag)
{
file.data = new Audio();
file.data.name = file.key;
file.data.onerror = () => this.fileError(file.key);
file.data.preload = 'auto';
file.data.src = file.url;
file.data.addEventListener('canplaythrough', () => this.fileComplete(file.key), false);
file.data.load();
}
}
break;
case 'text':
@@ -345,6 +367,27 @@ module Phaser {
}
private getAudioURL(urls): string {
var extension: string;
for (var i = 0; i < urls.length; i++)
{
extension = urls[i].toLowerCase();
extension = extension.substr((Math.max(0, extension.lastIndexOf(".")) || Infinity) + 1);
if (this._game.device.canPlayAudio(extension))
{
console.log('getAudioURL', urls[i]);
return urls[i];
}
}
return null;
}
/**
* Error occured when load a file.
* @param key {string} Key of the error loading file.
@@ -406,8 +449,32 @@ module Phaser {
break;
case 'audio':
file.data = this._xhr.response;
this._game.cache.addSound(file.key, file.url, file.data);
if (this._game.sound.usingWebAudio)
{
file.data = this._xhr.response;
this._game.cache.addSound(file.key, file.url, file.data, true, false);
if (file.autoDecode)
{
this._game.cache.updateSound(key, 'isDecoding', true);
var that = this;
var key = file.key;
this._game.sound.context.decodeAudioData(file.data, function (buffer) {
if (buffer)
{
that._game.cache.decodedSound(key, buffer);
}
});
}
}
else
{
this._game.cache.addSound(file.key, file.url, file.data, false, true);
}
break;
case 'text':
+337 -52
View File
@@ -13,56 +13,76 @@ module Phaser {
/**
* Sound constructor
* @param context {object} The AudioContext instance.
* @param gainNode {object} Gain node instance.
* @param data {object} Sound data.
* @param [volume] {number} volume of this sound when playing.
* @param [loop] {boolean} loop this sound when playing? (Default to false)
*/
constructor(context, gainNode, data, volume?: number = 1, loop?: bool = false) {
constructor(game: Phaser.Game, key: string, volume?: number = 1, loop?: bool = false) {
this._context = context;
this._gainNode = gainNode;
this._buffer = data;
this._volume = volume;
this.loop = loop;
this.game = game;
// Local volume control
if (this._context !== null)
this.usingWebAudio = this.game.sound.usingWebAudio;
this.usingAudioTag = this.game.sound.usingAudioTag;
this.key = key;
if (this.usingWebAudio)
{
this._localGainNode = this._context.createGainNode();
this._localGainNode.connect(this._gainNode);
this._localGainNode.gain.value = this._volume;
}
this.context = this.game.sound.context;
this.masterGainNode = this.game.sound.masterGain;
if (this._buffer === null)
{
this.isDecoding = true;
if (typeof this.context.createGain === 'undefined')
{
this.gainNode = this.context.createGainNode();
}
else
{
this.gainNode = this.context.createGain();
}
this.gainNode.gain.value = volume * this.game.sound.volume;
this.gainNode.connect(this.masterGainNode);
}
else
{
this.play();
this._sound = this.game.cache.getSoundData(key);
this.totalDuration = this._sound.duration;
}
this._volume = volume;
this.loop = loop;
this.markers = {};
this.onDecoded = new Phaser.Signal;
this.onPlay = new Phaser.Signal;
this.onPause = new Phaser.Signal;
this.onResume = new Phaser.Signal;
this.onLoop = new Phaser.Signal;
this.onStop = new Phaser.Signal;
this.onMute = new Phaser.Signal;
}
/**
* Local private reference to AudioContext.
* Local reference to the current Phaser.Game.
*/
private _context;
public game: Game;
/**
* Reference to AudioContext instance.
*/
public context = null;
/**
* Reference to gain node of SoundManager.
*/
private _gainNode;
public masterGainNode;
/**
* GainNode of this sound.
*/
private _localGainNode;
public gainNode;
/**
* Decoded data buffer.
* Decoded data buffer / Audio tag.
*/
private _buffer;
@@ -78,43 +98,247 @@ module Phaser {
private _muteVolume: number;
private _muted: bool = false;
private _tempPosition: number;
private _tempVolume: number;
private _tempLoop: bool;
private _tempMarker: string;
public usingWebAudio: bool = false;
public usingAudioTag: bool = false;
public name: string = '';
autoplay: bool = false;
totalDuration: number = 0;
startTime: number = 0;
currentTime: number = 0;
duration: number = 0;
stopTime: number = 0;
position: number;
paused: bool = false;
loop: bool = false;
duration: number;
isPlaying: bool = false;
isDecoding: bool = false;
key: string;
markers;
currentMarker: string = '';
public setDecodedBuffer(data) {
// events
public onDecoded: Phaser.Signal;
public onPlay: Phaser.Signal;
public onPause: Phaser.Signal;
public onResume: Phaser.Signal;
public onLoop: Phaser.Signal;
public onStop: Phaser.Signal;
public onMute: Phaser.Signal;
this._buffer = data;
this.isDecoding = false;
//this.play();
public pendingPlayback: bool = false;
public get isDecoding(): bool {
return this.game.cache.getSound(this.key).isDecoding;
}
public get isDecoded(): bool {
return this.game.cache.isSoundDecoded(this.key);
}
public addMarker(name: string, start: number, stop: number, volume: number = 1, loop: bool = false) {
this.markers[name] = { name: name, start: start, stop: stop, volume: volume, duration: stop - start, loop: loop };
}
public removeMarker(name: string) {
delete this.markers[name];
}
public update() {
if (this.pendingPlayback && this.game.cache.isSoundDecoded(this.key))
{
this.pendingPlayback = false;
this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop);
}
if (this.isPlaying)
{
this.currentTime = this.game.time.now - this.startTime;
if (this.currentTime >= this.duration)
{
if (this.usingWebAudio)
{
if (this.loop)
{
this.onLoop.dispatch(this);
this.currentTime = 0;
this.startTime = this.game.time.now;
}
else
{
this.stop();
}
}
else
{
if (this.loop)
{
this.onLoop.dispatch(this);
this.play(this.currentMarker, 0, this.volume, true, true);
}
else
{
this.stop();
}
}
}
}
}
/**
* Play this sound.
* Play this sound, or a marked section of it.
* @param marker {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
public play() {
public play(marker: string = '', position?: number = 0, volume?: number = 1, loop?: bool = false, forceRestart: bool = false) {
if (this._buffer === null || this.isDecoding === true)
if (this.isPlaying == true && forceRestart == false)
{
// Use Restart instead
return;
}
this._sound = this._context.createBufferSource();
this._sound.buffer = this._buffer;
this._sound.connect(this._localGainNode);
this.currentMarker = marker;
if (this.loop)
if (marker !== '' && this.markers[marker])
{
this._sound.loop = true;
this.loop = this.markers[marker].loop;
this.volume = this.markers[marker].volume;
this.position = this.markers[marker].start;
this.duration = this.markers[marker].duration * 1000;
}
else
{
this.loop = loop;
this.volume = volume;
this.position = position;
this.duration = 0;
}
this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it
if (this.usingWebAudio)
{
// Does the sound need decoding?
if (this.game.cache.isSoundDecoded(this.key))
{
// Do we need to do this every time we play? How about just if the buffer is empty?
this._buffer = this.game.cache.getSoundData(this.key);
this._sound = this.context.createBufferSource();
this._sound.buffer = this._buffer;
this._sound.connect(this.gainNode);
this.totalDuration = this._sound.buffer.duration;
this.duration = this._sound.buffer.duration;
this.isPlaying = true;
if (this.duration == 0)
{
this.duration = this.totalDuration * 1000;
}
if (this.loop)
{
this._sound.loop = true;
}
// Useful to cache this somewhere perhaps?
if (typeof this._sound.start === 'undefined')
{
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.isPlaying = true;
this.startTime = this.game.time.now;
this.currentTime = 0;
this.stopTime = this.startTime + this.duration;
this.onPlay.dispatch(this);
}
else
{
this._tempVolume = volume;
this._tempLoop = loop;
this._tempPosition = position;
this._tempMarker = marker;
this.pendingPlayback = true;
if (this.game.cache.getSound(this.key).isDecoding == false)
{
this.game.sound.decode(this.key, this);
}
}
}
else
{
if (this._sound.readyState == 4)
{
this._sound.currentTime = this.position;
this._sound.muted = this._muted;
this._sound.volume = this._volume;
this._sound.play();
this.isPlaying = true;
this.startTime = this.game.time.now;
this.currentTime = 0;
this.stopTime = this.startTime + this.duration;
this.onPlay.dispatch(this);
}
}
}
public restart(marker: string = '', position?: number = 0, volume?: number = 1, loop?: bool = false) {
this.play(marker, position, volume, loop, true);
}
public pause() {
if (this.isPlaying && this._sound)
{
this.stop();
this.isPlaying = false;
this.paused = true;
this.onPause.dispatch(this);
}
}
public resume() {
if (this.isPlaying == false && this._sound)
{
if (this.usingWebAudio)
{
if (typeof this._sound.start === 'undefined')
{
this._sound.noteGrainOn(0, this.position, this.duration);
//this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it
}
else
{
this._sound.start(0, this.position, this.duration);
}
}
else
{
this._sound.play();
}
this.isPlaying = true;
this.paused = false;
this.onResume.dispatch(this);
}
}
@@ -123,11 +347,29 @@ module Phaser {
*/
public stop() {
if (this.isPlaying === true)
if (this.isPlaying && this._sound)
{
if (this.usingWebAudio)
{
if (typeof this._sound.stop === 'undefined')
{
this._sound.noteOff(0);
}
else
{
this._sound.stop(0);
}
}
else if (this.usingAudioTag)
{
this._sound.pause();
this._sound.currentTime = 0;
}
this.isPlaying = false;
this.currentMarker = '';
this.onStop.dispatch(this);
this._sound.noteOff(0);
}
}
@@ -135,37 +377,56 @@ module Phaser {
/**
* Mute sounds.
*/
public get mute():bool {
public get mute(): bool {
return this._muted;
}
public set mute(value: bool) {
if (value && this._muted == false)
if (value)
{
this._muteVolume = this._localGainNode.gain.value;
this._localGainNode.gain.value = 0;
this._muted = true;
if (this.usingWebAudio)
{
this._muteVolume = this.gainNode.gain.value;
this.gainNode.gain.value = 0;
}
else if (this.usingAudioTag)
{
this._muteVolume = this._sound.volume;
this._sound.volume = 0;
}
}
else
{
this._localGainNode.gain.value = this._muteVolume;
this._muted = false;
if (this.usingWebAudio)
{
this.gainNode.gain.value = this._muteVolume;
}
else if (this.usingAudioTag)
{
this._sound.volume = this._muteVolume;
}
}
this.onMute.dispatch(this);
}
public set volume(value: number) {
this._volume = value;
if (this._muted)
if (this.usingWebAudio)
{
this._muteVolume = this._volume;
this.gainNode.gain.value = value;
}
else
else if (this.usingAudioTag)
{
this._localGainNode.gain.value = this._volume;
this._sound.volume = value;
}
}
@@ -174,6 +435,30 @@ module Phaser {
return this._volume;
}
/**
* Renders the Pointer.circle object onto the stage in green if down or red if up.
* @method renderDebug
*/
public renderDebug(x: number, y: number) {
this.game.stage.context.fillStyle = 'rgb(255,255,255)';
this.game.stage.context.font = '16px Courier';
this.game.stage.context.fillText('Sound: ' + this.key + ' Locked: ' + this.game.sound.touchLocked + ' Pending Playback: ' + this.pendingPlayback, x, y);
this.game.stage.context.fillText('Decoded: ' + this.isDecoded + ' Decoding: ' + this.isDecoding, x, y + 20);
this.game.stage.context.fillText('Total Duration: ' + this.totalDuration + ' Playing: ' + this.isPlaying, x, y + 40);
this.game.stage.context.fillText('Time: ' + this.currentTime, x, y + 60);
this.game.stage.context.fillText('Volume: ' + this.volume + ' Muted: ' + this.mute, x, y + 80);
this.game.stage.context.fillText('WebAudio: ' + this.usingWebAudio + ' Audio: ' + this.usingAudioTag, x, y + 100);
if (this.currentMarker !== '')
{
this.game.stage.context.fillText('Marker: ' + this.currentMarker + ' Duration: ' + this.duration, x, y + 120);
this.game.stage.context.fillText('Start: ' + this.markers[this.currentMarker].start + ' Stop: ' + this.markers[this.currentMarker].stop, x, y + 140);
this.game.stage.context.fillText('Position: ' + this.position, x, y + 160);
}
}
}
}
+201 -66
View File
@@ -17,48 +17,106 @@ module Phaser {
*/
constructor(game: Game) {
this._game = game;
this.game = game;
if (window['PhaserGlobal'] && window['PhaserGlobal'].disableAudio == true)
this._volume = 1;
this._muted = false;
this._sounds = [];
if (this.game.device.iOS && this.game.device.webAudio == false)
{
return;
this.channels = 1;
}
if (game.device.webaudio == true)
if (window['PhaserGlobal'])
{
if (!!window['AudioContext'])
// Check to see if all audio playback is disabled (i.e. handled by a 3rd party class)
if (window['PhaserGlobal'].disableAudio == true)
{
this._context = new window['AudioContext']();
}
else if (!!window['webkitAudioContext'])
{
this._context = new window['webkitAudioContext']();
this.usingWebAudio = false;
this.noAudio = true;
return;
}
if (this._context !== null)
// Check if the Web Audio API is disabled (for testing Audio Tag playback during development)
if (window['PhaserGlobal'].disableWebAudio == true)
{
this._gainNode = this._context.createGainNode();
this._gainNode.connect(this._context.destination);
this._volume = 1;
this.usingWebAudio = false;
this.usingAudioTag = true;
this.noAudio = false;
return;
}
}
if (this.game.device.iOS && this.game.device.webAudio)
{
this.game.input.touch.callbackContext = this;
this.game.input.touch.touchStartCallback = this.unlock;
this.touchLocked = true;
}
else
{
// What about iOS5?
this.touchLocked = false;
}
this.usingWebAudio = true;
this.noAudio = false;
if (!!window['AudioContext'])
{
this.context = new window['AudioContext']();
}
else if (!!window['webkitAudioContext'])
{
this.context = new window['webkitAudioContext']();
}
else if (!!window['Audio'])
{
this.usingWebAudio = false;
this.usingAudioTag = true;
}
else
{
this.usingWebAudio = false;
this.noAudio = true;
}
if (this.context !== null)
{
if (typeof this.context.createGain === 'undefined')
{
this.masterGain = this.context.createGainNode();
}
else
{
this.masterGain = this.context.createGain();
}
this.masterGain.gain.value = 1;
this.masterGain.connect(this.context.destination);
}
}
public usingWebAudio: bool = false;
public usingAudioTag: bool = false;
public noAudio: bool = false;
/**
* Local private reference to game.
* Local reference to the current Phaser.Game.
*/
private _game: Game;
public game: Game;
/**
* Reference to AudioContext instance.
*/
private _context = null;
public context = null;
/**
* Gain node created from audio context.
* The Master Gain node through which all sounds
*/
private _gainNode;
public masterGain;
/**
* Volume of sounds.
@@ -66,11 +124,37 @@ module Phaser {
*/
private _volume: number;
private _sounds: Phaser.Sound[];
private _muteVolume: number;
private _muted: bool = false;
public channels: number;
public touchLocked: bool = false;
private _unlockSource = null;
public unlock() {
if (this.touchLocked == false || this.game.device.iOS == false)
{
return;
}
// Create Audio tag?
// Create empty buffer and play it
var buffer = this.context.createBuffer(1, 1, 22050);
this._unlockSource = this.context.createBufferSource();
this._unlockSource.buffer = buffer;
this._unlockSource.connect(this.context.destination);
this._unlockSource.noteOn(0);
}
/**
* Mute sounds.
* A global audio mute toggle.
*/
public get mute():bool {
return this._muted;
@@ -78,104 +162,155 @@ module Phaser {
public set mute(value: bool) {
if (value && this._muted == false)
if (value)
{
this._muteVolume = this._gainNode.gain.value;
this._gainNode.gain.value = 0;
if (this._muted)
{
return;
}
this._muted = true;
if (this.usingWebAudio)
{
this._muteVolume = this.masterGain.gain.value;
this.masterGain.gain.value = 0;
}
// Loop through sounds
for (var i = 0; i < this._sounds.length; i++)
{
if (this._sounds[i].usingAudioTag)
{
this._sounds[i].mute = true;
}
}
}
else
{
this._gainNode.gain.value = this._muteVolume;
if (this._muted == false)
{
return;
}
this._muted = false;
if (this.usingWebAudio)
{
this.masterGain.gain.value = this._muteVolume;
}
// Loop through sounds
for (var i = 0; i < this._sounds.length; i++)
{
if (this._sounds[i].usingAudioTag)
{
this._sounds[i].mute = false;
}
}
}
}
/**
* The global audio volume. A value between 0 (silence) and 1 (full volume)
*/
public set volume(value: number) {
value = this.game.math.clamp(value, 1, 0);
this._volume = value;
if (this._muted)
if (this.usingWebAudio)
{
this._muteVolume = this._volume;
this.masterGain.gain.value = value;
}
else
// Loop through the sound cache and change the volume of all html audio tags
for (var i = 0; i < this._sounds.length; i++)
{
this._gainNode.gain.value = this._volume;
if (this._sounds[i].usingAudioTag)
{
this._sounds[i].volume = this._sounds[i].volume * value;
}
}
}
public get volume(): number {
return this._volume;
if (this.usingWebAudio)
{
return this.masterGain.gain.value;
}
else
{
return this._volume;
}
}
/**
* Decode a sound with its assets key.
* @param key {string} Assets key of the sound to be decoded.
* @param callback {function} This will be invoked when finished decoding.
* @param [sound] {Sound} its bufer will be set to decoded data.
*/
public decode(key: string, callback = null, sound?: Sound = null) {
public decode(key: string, sound?: Sound = null) {
var soundData = this._game.cache.getSound(key);
var soundData = this.game.cache.getSoundData(key);
if (soundData)
{
if (this._game.cache.isSoundDecoded(key) === false)
if (this.game.cache.isSoundDecoded(key) === false)
{
this.game.cache.updateSound(key, 'isDecoding', true);
var that = this;
this._context.decodeAudioData(soundData, function (buffer) {
that._game.cache.decodedSound(key, buffer);
this.context.decodeAudioData(soundData, function (buffer) {
that.game.cache.decodedSound(key, buffer);
if (sound)
{
sound.setDecodedBuffer(buffer);
that.onSoundDecode.dispatch(sound);
}
callback();
});
}
}
}
/**
* Play a sound with its assets key.
* @param key {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
public play(key: string, volume?: number = 1, loop?: bool = false): Sound {
public onSoundDecode: Phaser.Signal = new Phaser.Signal;
if (this._context === null)
public update() {
if (this.touchLocked && this._unlockSource !== null)
{
return;
}
var soundData = this._game.cache.getSound(key);
if (soundData)
{
// Does the sound need decoding?
if (this._game.cache.isSoundDecoded(key) === true)
if ((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE))
{
return new Sound(this._context, this._gainNode, soundData, volume, loop);
}
else
{
var tempSound: Sound = new Sound(this._context, this._gainNode, null, volume, loop);
// this is an async process, so we can return the Sound object anyway, it just won't be playing yet
this.decode(key, () => tempSound.play(), tempSound);
return tempSound;
this.touchLocked = false;
this._unlockSource = null;
this.game.input.touch.callbackContext = null;
this.game.input.touch.touchStartCallback = null;
}
}
for (var i = 0; i < this._sounds.length; i++)
{
this._sounds[i].update();
}
}
public add(key: string, volume?: number = 1, loop?: bool = false): Sound {
var sound: Phaser.Sound = new Sound(this.game, key, volume, loop);
this._sounds.push(sound);
return sound;
}
}
+58 -9
View File
@@ -191,41 +191,53 @@ module Phaser {
// Audio
/**
* Is audioData available?
* Are Audio tags available?
* @type {boolean}
*/
public audioData: bool = false;
/**
* Is webaudio available?
* Is the WebAudio API available?
* @type {boolean}
*/
public webaudio: bool = false;
public webAudio: bool = false;
/**
* Is ogg available?
* Can this device play ogg files?
* @type {boolean}
*/
public ogg: bool = false;
/**
* Is mp3 available?
* Can this device play opus files?
* @type {boolean}
*/
public opus: bool = false;
/**
* Can this device play mp3 files?
* @type {boolean}
*/
public mp3: bool = false;
/**
* Is wav available?
* Can this device play wav files?
* @type {boolean}
*/
public wav: bool = false;
/**
* Is m4a available?
* Can this device play m4a files?
* @type {boolean}
*/
public m4a: bool = false;
/**
* Can this device play webm files?
* @type {boolean}
*/
public webm: bool = false;
// Device
/**
@@ -355,7 +367,7 @@ module Phaser {
this.mobileSafari = true;
}
else if (/MSIE (\d+\.\d+);/.test(ua))
{
{
this.ie = true;
this.ieVersion = parseInt(RegExp.$1);
}
@@ -380,6 +392,33 @@ module Phaser {
}
public canPlayAudio(type: string): bool {
if (type == 'mp3' && this.mp3)
{
return true;
}
else if (type == 'ogg' && (this.ogg || this.opus))
{
return true;
}
else if (type == 'm4a' && this.m4a)
{
return true;
}
else if (type == 'wav' && this.wav)
{
return true;
}
else if (type == 'webm' && this.webm)
{
return true;
}
return false;
}
/**
* Check audio support.
* @private
@@ -387,7 +426,7 @@ module Phaser {
private _checkAudio() {
this.audioData = !!(window['Audio']);
this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']);
this.webAudio = !!(window['webkitAudioContext'] || window['AudioContext']);
var audioElement: HTMLAudioElement = <HTMLAudioElement> document.createElement('audio');
var result = false;
@@ -401,6 +440,11 @@ module Phaser {
this.ogg = true;
}
if (audioElement.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, ''))
{
this.opus = true;
}
if (audioElement.canPlayType('audio/mpeg;').replace(/^no$/, ''))
{
this.mp3 = true;
@@ -418,6 +462,11 @@ module Phaser {
{
this.m4a = true;
}
if (audioElement.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''))
{
this.webm = true;
}
}
} catch (e) { }
+11
View File
@@ -61,6 +61,11 @@ TODO:
* When you toggle visible of a button that is over, it doesn't disable that 'over' state (should it? probably yes)
* Stage.opaqueBackground = 'rgb()' or null, alpha, blendMode, filters, mask, rotation+XYZ,scaleXYZ,visible
* Need a way for Input event to redirect to audio to unlock playback
* AudioSprite object?
* How to get web audio to playback from an offset
* Stage lost to mute
V1.0.0
@@ -148,6 +153,12 @@ V1.0.0
* Fixed bug where the hand cursor would be reset on input up, even if the mouse was still over the sprite.
* Fixed bug where pressing down then moving out of the sprite area wouldn't properly reset the input state next time you moved over the sprite.
* Added the Sprite.tween property, really useful to avoid creating new tween vars in your local scope if you don't need them.
* Added support for pagehide and pageshow events to Stage, hooked in to the pause/resume game methods.
* Extended Device audio checks to include opus and webm.
* Updated Loader and Cache so they now support loading of Audio() tags as well as Web Audio.
* Set Input.recordPointerHistory to false, you now need to enable the pointer tracking if you wish to use it.
* SoundManager will now automatically handle iOS touch unlocking.
V0.9.6
+8
View File
@@ -56,6 +56,14 @@
<TypeScriptCompile Include="cameras\basic camera 1.ts" />
<TypeScriptCompile Include="buttons\basic button.ts" />
<TypeScriptCompile Include="buttons\basic button 2.ts" />
<TypeScriptCompile Include="audio\play sound 1.ts" />
<TypeScriptCompile Include="audio\audio sprites 1.ts" />
<Content Include="audio\audio sprites 1.js">
<DependentUpon>audio sprites 1.ts</DependentUpon>
</Content>
<Content Include="audio\play sound 1.js">
<DependentUpon>play sound 1.ts</DependentUpon>
</Content>
<Content Include="buttons\basic button 2.js">
<DependentUpon>basic button 2.ts</DependentUpon>
</Content>
Binary file not shown.
@@ -0,0 +1,84 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmpDM="http://ns.adobe.com/xmp/1.0/DynamicMedia/">
<xmpDM:Tracks>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<xmpDM:trackName>CuePoint Markers</xmpDM:trackName>
<xmpDM:trackType>Cue</xmpDM:trackType>
<xmpDM:frameRate>f44100</xmpDM:frameRate>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<xmpDM:trackName>Subclip Markers</xmpDM:trackName>
<xmpDM:trackType>InOut</xmpDM:trackType>
<xmpDM:frameRate>f44100</xmpDM:frameRate>
</rdf:li>
</rdf:Bag>
</xmpDM:Tracks>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
<xmp:MetadataDate>2013-07-16T02:39:59+01:00</xmp:MetadataDate>
<xmp:ModifyDate>2013-07-16T02:39:59+01:00</xmp:ModifyDate>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#">
<xmpMM:InstanceID>xmp.iid:CF0377343CEDE211B46AA47009D9EF1D</xmpMM:InstanceID>
<xmpMM:DocumentID>xmp.did:CF0377343CEDE211B46AA47009D9EF1D</xmpMM:DocumentID>
<xmpMM:OriginalDocumentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</xmpMM:OriginalDocumentID>
<xmpMM:History>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:CE0377343CEDE211B46AA47009D9EF1D</stEvt:instanceID>
<stEvt:when>2013-07-16T02:39:59+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Audition CS6 (Windows)</stEvt:softwareAgent>
<stEvt:changed>/metadata</stEvt:changed>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<stEvt:action>saved</stEvt:action>
<stEvt:instanceID>xmp.iid:CF0377343CEDE211B46AA47009D9EF1D</stEvt:instanceID>
<stEvt:when>2013-07-16T02:39:59+01:00</stEvt:when>
<stEvt:softwareAgent>Adobe Audition CS6 (Windows)</stEvt:softwareAgent>
<stEvt:changed>/</stEvt:changed>
</rdf:li>
</rdf:Seq>
</xmpMM:History>
<xmpMM:DerivedFrom rdf:parseType="Resource">
<stRef:instanceID>xmp.iid:CE0377343CEDE211B46AA47009D9EF1D</stRef:instanceID>
<stRef:documentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</stRef:documentID>
<stRef:originalDocumentID>xmp.did:CE0377343CEDE211B46AA47009D9EF1D</stRef:originalDocumentID>
</xmpMM:DerivedFrom>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:format>audio/ogg; codec="vorbis"</dc:format>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
+44
View File
@@ -0,0 +1,44 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//var PhaserGlobal = { disableWebAudio: true };
(function () {
var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render);
function init() {
game.load.audio('rabbit', [
'assets/mp3/peter_rabbit.m4a',
'assets/mp3/peter_rabbit.mp3',
'assets/mp3/peter_rabbit.ogg'
]);
game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71);
game.load.start();
}
audioSprite:
Phaser.Sound
button:
Phaser.Button
pause:
Phaser.Button
function create() {
this.audioSprite = game.add.audio('rabbit');
this.audioSprite.addMarker('title', 3.00, 5.00, 1, true);
this.audioSprite.addMarker('help', 6.00, 12.00);
this.audioSprite.addMarker('intro', 14.00, 19.00);
this.audioSprite.addMarker('peter', 20.00, 21.50);
this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0);
//this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0);
}
function playMusic() {
this.audioSprite.play('help');
}
function render() {
this.audioSprite.renderDebug(32, 32);
}
function togglePause() {
if(this.music.paused) {
this.music.resume();
} else {
this.music.pause();
}
}
})();
+58
View File
@@ -0,0 +1,58 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//var PhaserGlobal = { disableWebAudio: true };
(function () {
var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render);
function init() {
game.load.audio('rabbit', ['assets/mp3/peter_rabbit.m4a', 'assets/mp3/peter_rabbit.mp3', 'assets/mp3/peter_rabbit.ogg']);
game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71);
game.load.start();
}
audioSprite: Phaser.Sound;
button: Phaser.Button;
pause: Phaser.Button;
function create() {
this.audioSprite = game.add.audio('rabbit');
this.audioSprite.addMarker('title', 3.00, 5.00, 1, true);
this.audioSprite.addMarker('help', 6.00, 12.00);
this.audioSprite.addMarker('intro', 14.00, 19.00);
this.audioSprite.addMarker('peter', 20.00, 21.50);
this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0);
//this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0);
}
function playMusic() {
this.audioSprite.play('help');
}
function render() {
this.audioSprite.renderDebug(32, 32);
}
function togglePause() {
if (this.music.paused)
{
this.music.resume();
}
else
{
this.music.pause();
}
}
})();
+56
View File
@@ -0,0 +1,56 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//var PhaserGlobal = { disableWebAudio: true };
(function () {
var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render);
function init() {
//game.load.audio('wizball', ['assets/mp3/oedipus_wizball_highscore.ogg', 'assets/mp3/oedipus_wizball_highscore.mp3']);
game.load.audio('boden', [
'assets/mp3/bodenstaendig_2000_in_rock_4bit.mp3'
]);
//game.load.audio('boden', ['assets/mp3/puzzleover.mp3']);
game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71);
game.load.start();
}
button:
Phaser.Button
music:
Phaser.Sound
volumeUp:
Phaser.Button
volumeDown:
Phaser.Button
pause:
Phaser.Button
function create() {
this.music = game.add.audio('boden');
this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0);
this.volumeUp = game.add.button(0, 0, 'button', volUp, this, 2, 1, 0);
this.volumeDown = game.add.button(700, 0, 'button', volDown, this, 2, 1, 0);
this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0);
}
function render() {
this.music.renderDebug(0, 300);
}
function togglePause() {
if(this.music.paused) {
this.music.resume();
} else {
this.music.pause();
}
}
function volUp() {
//game.sound.volume += 0.1;
this.music.volume += 0.1;
console.log('vol up', game.sound.volume);
}
function volDown() {
//game.sound.volume -= 0.1;
this.music.volume -= 0.1;
console.log('vol down', game.sound.volume);
}
function playMusic() {
this.music.play();
}
})();
+73
View File
@@ -0,0 +1,73 @@
/// <reference path="../../Phaser/Game.ts" />
/// <reference path="../../Phaser/sound/SoundManager.ts" />
/// <reference path="../../Phaser/sound/Sound.ts" />
//var PhaserGlobal = { disableWebAudio: true };
(function () {
var game = new Phaser.Game(this, 'game', 800, 600, init, create, null, render);
function init() {
//game.load.audio('wizball', ['assets/mp3/oedipus_wizball_highscore.ogg', 'assets/mp3/oedipus_wizball_highscore.mp3']);
game.load.audio('boden', ['assets/mp3/bodenstaendig_2000_in_rock_4bit.mp3']);
//game.load.audio('boden', ['assets/mp3/puzzleover.mp3']);
game.load.spritesheet('button', 'assets/buttons/button_sprite_sheet.png', 193, 71);
game.load.start();
}
button: Phaser.Button;
music: Phaser.Sound;
volumeUp: Phaser.Button;
volumeDown: Phaser.Button;
pause: Phaser.Button;
function create() {
this.music = game.add.audio('boden');
this.button = game.add.button(game.stage.centerX, 400, 'button', playMusic, this, 2, 1, 0);
this.volumeUp = game.add.button(0, 0, 'button', volUp, this, 2, 1, 0);
this.volumeDown = game.add.button(700, 0, 'button', volDown, this, 2, 1, 0);
this.pause = game.add.button(200, 200, 'button', togglePause, this, 2, 1, 0);
}
function render() {
this.music.renderDebug(0, 300);
}
function togglePause() {
if (this.music.paused)
{
this.music.resume();
}
else
{
this.music.pause();
}
}
function volUp() {
//game.sound.volume += 0.1;
this.music.volume += 0.1;
console.log('vol up', game.sound.volume);
}
function volDown() {
//game.sound.volume -= 0.1;
this.music.volume -= 0.1;
console.log('vol down', game.sound.volume);
}
function playMusic() {
this.music.play();
}
})();
+601 -152
View File
File diff suppressed because it is too large Load Diff
+123 -55
View File
@@ -4299,9 +4299,10 @@ module Phaser {
/**
* Add a new audio file loading request.
* @param key {string} Unique asset key of the audio file.
* @param url {string} URL of audio file.
* @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ]
* @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process.
*/
public audio(key: string, url: string): void;
public audio(key: string, urls: string[], autoDecode?: bool): void;
/**
* Add a new text file loading request.
* @param key {string} Unique asset key of the text file.
@@ -4327,6 +4328,7 @@ module Phaser {
* Load files. Private method ONLY used by loader.
*/
private loadFile();
private getAudioURL(urls);
/**
* Error occured when load a file.
* @param key {string} Key of the error loading file.
@@ -4436,11 +4438,11 @@ module Phaser {
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
public addSound(key: string, url: string, data): void;
public addSound(key: string, url: string, data, webAudio?: bool, audioTag?: bool): void;
public updateSound(key: string, property: string, value): void;
/**
* Add a new decoded sound.
* @param key {string} Asset key for the sound.
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
public decodedSound(key: string, data): void;
@@ -4470,11 +4472,17 @@ module Phaser {
*/
public getFrameData(key: string): FrameData;
/**
* Get sound by key.
* @param key Asset key of the sound you want.
* @return {object} The sound you want.
*/
public getSound(key: string);
/**
* Get sound data by key.
* @param key Asset key of the sound you want.
* @return {object} The sound data you want.
*/
public getSound(key: string);
public getSoundData(key: string);
/**
* Check whether an asset is decoded sound.
* @param key Asset key of the sound you want.
@@ -6806,6 +6814,7 @@ module Phaser {
* @returns {Sprite} The newly created sprite object.
*/
public sprite(x: number, y: number, key?: string, frame?, bodyType?: number): Sprite;
public audio(key: string, volume?: number, loop?: bool): Sound;
/**
* Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center.
*
@@ -6938,27 +6947,28 @@ module Phaser {
class Sound {
/**
* Sound constructor
* @param context {object} The AudioContext instance.
* @param gainNode {object} Gain node instance.
* @param data {object} Sound data.
* @param [volume] {number} volume of this sound when playing.
* @param [loop] {boolean} loop this sound when playing? (Default to false)
*/
constructor(context, gainNode, data, volume?: number, loop?: bool);
constructor(game: Game, key: string, volume?: number, loop?: bool);
/**
* Local private reference to AudioContext.
* Local reference to the current Phaser.Game.
*/
private _context;
public game: Game;
/**
* Reference to AudioContext instance.
*/
public context;
/**
* Reference to gain node of SoundManager.
*/
private _gainNode;
public masterGainNode;
/**
* GainNode of this sound.
*/
private _localGainNode;
public gainNode;
/**
* Decoded data buffer.
* Decoded data buffer / Audio tag.
*/
private _buffer;
/**
@@ -6969,28 +6979,66 @@ module Phaser {
* The real sound object (buffer source).
*/
private _sound;
public loop: bool;
private _muteVolume;
private _muted;
private _tempPosition;
private _tempVolume;
private _tempLoop;
private _tempMarker;
public usingWebAudio: bool;
public usingAudioTag: bool;
public name: string;
public autoplay: bool;
public totalDuration: number;
public startTime: number;
public currentTime: number;
public duration: number;
public stopTime: number;
public position: number;
public paused: bool;
public loop: bool;
public isPlaying: bool;
public isDecoding: bool;
public setDecodedBuffer(data): void;
public key: string;
public markers;
public currentMarker: string;
public onDecoded: Signal;
public onPlay: Signal;
public onPause: Signal;
public onResume: Signal;
public onLoop: Signal;
public onStop: Signal;
public onMute: Signal;
public pendingPlayback: bool;
public isDecoding : bool;
public isDecoded : bool;
public addMarker(name: string, start: number, stop: number, volume?: number, loop?: bool): void;
public removeMarker(name: string): void;
public update(): void;
/**
* Play this sound.
* Play this sound, or a marked section of it.
* @param marker {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
public play(): void;
public play(marker?: string, position?: number, volume?: number, loop?: bool, forceRestart?: bool): void;
public restart(marker?: string, position?: number, volume?: number, loop?: bool): void;
public pause(): void;
public resume(): void;
/**
* Stop playing this sound.
*/
public stop(): void;
/**
* Mute the sound.
* Mute sounds.
*/
public mute(): void;
/**
* Enable the sound.
*/
public unmute(): void;
public mute : bool;
public volume : number;
/**
* Renders the Pointer.circle object onto the stage in green if down or red if up.
* @method renderDebug
*/
public renderDebug(x: number, y: number): void;
}
}
/**
@@ -7005,47 +7053,50 @@ module Phaser {
* Create a new <code>SoundManager</code>.
*/
constructor(game: Game);
public usingWebAudio: bool;
public usingAudioTag: bool;
public noAudio: bool;
/**
* Local private reference to game.
* Local reference to the current Phaser.Game.
*/
private _game;
public game: Game;
/**
* Reference to AudioContext instance.
*/
private _context;
public context;
/**
* Gain node created from audio context.
* The Master Gain node through which all sounds
*/
private _gainNode;
public masterGain;
/**
* Volume of sounds.
* @type {number}
*/
private _volume;
private _sounds;
private _muteVolume;
private _muted;
public channels: number;
public touchLocked: bool;
private _unlockSource;
public unlock(): void;
/**
* Mute sounds.
* A global audio mute toggle.
*/
public mute(): void;
public mute : bool;
/**
* Enable sounds.
* The global audio volume. A value between 0 (silence) and 1 (full volume)
*/
public unmute(): void;
public volume : number;
/**
* Decode a sound with its assets key.
* @param key {string} Assets key of the sound to be decoded.
* @param callback {function} This will be invoked when finished decoding.
* @param [sound] {Sound} its bufer will be set to decoded data.
*/
public decode(key: string, callback?, sound?: Sound): void;
/**
* Play a sound with its assets key.
* @param key {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
public play(key: string, volume?: number, loop?: bool): Sound;
public decode(key: string, sound?: Sound): void;
public onSoundDecode: Signal;
public update(): void;
public add(key: string, volume?: number, loop?: bool): Sound;
}
}
/**
@@ -8106,36 +8157,46 @@ module Phaser {
public safari: bool;
public webApp: bool;
/**
* Is audioData available?
* Are Audio tags available?
* @type {boolean}
*/
public audioData: bool;
/**
* Is webaudio available?
* Is the WebAudio API available?
* @type {boolean}
*/
public webaudio: bool;
public webAudio: bool;
/**
* Is ogg available?
* Can this device play ogg files?
* @type {boolean}
*/
public ogg: bool;
/**
* Is mp3 available?
* Can this device play opus files?
* @type {boolean}
*/
public opus: bool;
/**
* Can this device play mp3 files?
* @type {boolean}
*/
public mp3: bool;
/**
* Is wav available?
* Can this device play wav files?
* @type {boolean}
*/
public wav: bool;
/**
* Is m4a available?
* Can this device play m4a files?
* @type {boolean}
*/
public m4a: bool;
/**
* Can this device play webm files?
* @type {boolean}
*/
public webm: bool;
/**
* Is running on iPhone?
* @type {boolean}
*/
@@ -8170,6 +8231,7 @@ module Phaser {
* @private
*/
private _checkBrowser();
public canPlayAudio(type: string): bool;
/**
* Check audio support.
* @private
@@ -8405,6 +8467,9 @@ module Phaser {
* @return {Phaser.Pointer} This object.
*/
constructor(game: Game, id: number);
private _highestRenderOrderID;
private _highestRenderObject;
private _highestInputPriorityID;
/**
* Local private reference to game.
* @property game
@@ -8582,6 +8647,12 @@ module Phaser {
**/
public totalTouches: number;
/**
* The number of miliseconds since the last click
* @property msSinceLastClick
* @type {Number}
**/
public msSinceLastClick: number;
/**
* How long the Pointer has been depressed on the touchscreen. If not currently down it returns -1.
* @property duration
* @type {Number}
@@ -8610,9 +8681,6 @@ module Phaser {
*/
public start(event): Pointer;
public update(): void;
private _highestRenderOrderID;
private _highestRenderObject;
private _highestInputPriorityID;
/**
* Called when the Pointer is moved on the touchscreen
* @method move
@@ -9265,7 +9333,7 @@ module Phaser {
public recordRate: number;
/**
* The total number of entries that can be recorded into the Pointer objects tracking history.
* The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* @property recordLimit
* @type {Number}
*/
+584 -144
View File
@@ -7243,7 +7243,7 @@ var Phaser;
this.cameraView = new Phaser.Rectangle(x, y, this.width, this.height);
this.transform.setCache();
this.outOfBounds = false;
this.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_KILL;
this.outOfBoundsAction = Phaser.Types.OUT_OF_BOUNDS_PERSIST;
// Handy proxies
this.scale = this.transform.scale;
this.alpha = this.texture.alpha;
@@ -9134,19 +9134,22 @@ var Phaser;
Loader.prototype.audio = /**
* Add a new audio file loading request.
* @param key {string} Unique asset key of the audio file.
* @param url {string} URL of audio file.
* @param urls {Array} An array containing the URLs of the audio files, i.e.: [ 'jump.mp3', 'jump.ogg', 'jump.m4a' ]
* @param autoDecode {boolean} When using Web Audio the audio files can either be decoded at load time or run-time. They can't be played until they are decoded, but this let's you control when that happens. Decoding is a non-blocking async process.
*/
function (key, url) {
function (key, urls, autoDecode) {
if (typeof autoDecode === "undefined") { autoDecode = true; }
if(this.checkKeyExists(key) === false) {
this._queueSize++;
this._fileList[key] = {
type: 'audio',
key: key,
url: url,
url: urls,
data: null,
buffer: null,
error: false,
loaded: false
loaded: false,
autoDecode: autoDecode
};
this._keys.push(key);
}
@@ -9234,15 +9237,33 @@ var Phaser;
file.data.src = file.url;
break;
case 'audio':
this._xhr.open("GET", file.url, true);
this._xhr.responseType = "arraybuffer";
this._xhr.onload = function () {
return _this.fileComplete(file.key);
};
this._xhr.onerror = function () {
return _this.fileError(file.key);
};
this._xhr.send();
file.url = this.getAudioURL(file.url);
if(file.url !== null) {
// WebAudio or Audio Tag?
if(this._game.sound.usingWebAudio) {
this._xhr.open("GET", file.url, true);
this._xhr.responseType = "arraybuffer";
this._xhr.onload = function () {
return _this.fileComplete(file.key);
};
this._xhr.onerror = function () {
return _this.fileError(file.key);
};
this._xhr.send();
} else if(this._game.sound.usingAudioTag) {
file.data = new Audio();
file.data.name = file.key;
file.data.onerror = function () {
return _this.fileError(file.key);
};
file.data.preload = 'auto';
file.data.src = file.url;
file.data.addEventListener('canplaythrough', function () {
return _this.fileComplete(file.key);
}, false);
file.data.load();
}
}
break;
case 'text':
this._xhr.open("GET", file.url, true);
@@ -9257,6 +9278,18 @@ var Phaser;
break;
}
};
Loader.prototype.getAudioURL = function (urls) {
var extension;
for(var i = 0; i < urls.length; i++) {
extension = urls[i].toLowerCase();
extension = extension.substr((Math.max(0, extension.lastIndexOf(".")) || Infinity) + 1);
if(this._game.device.canPlayAudio(extension)) {
console.log('getAudioURL', urls[i]);
return urls[i];
}
}
return null;
};
Loader.prototype.fileError = /**
* Error occured when load a file.
* @param key {string} Key of the error loading file.
@@ -9306,8 +9339,22 @@ var Phaser;
}
break;
case 'audio':
file.data = this._xhr.response;
this._game.cache.addSound(file.key, file.url, file.data);
if(this._game.sound.usingWebAudio) {
file.data = this._xhr.response;
this._game.cache.addSound(file.key, file.url, file.data, true, false);
if(file.autoDecode) {
this._game.cache.updateSound(key, 'isDecoding', true);
var that = this;
var key = file.key;
this._game.sound.context.decodeAudioData(file.data, function (buffer) {
if(buffer) {
that._game.cache.decodedSound(key, buffer);
}
});
}
} else {
this._game.cache.addSound(file.key, file.url, file.data, false, true);
}
break;
case 'text':
file.data = this._xhr.response;
@@ -9492,22 +9539,38 @@ var Phaser;
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
function (key, url, data) {
function (key, url, data, webAudio, audioTag) {
if (typeof webAudio === "undefined") { webAudio = true; }
if (typeof audioTag === "undefined") { audioTag = false; }
console.log('Cache addSound', key, url, webAudio, audioTag);
var decoded = false;
if(audioTag) {
decoded = true;
}
this._sounds[key] = {
url: url,
data: data,
decoded: false
isDecoding: false,
decoded: decoded,
webAudio: webAudio,
audioTag: audioTag
};
};
Cache.prototype.updateSound = function (key, property, value) {
if(this._sounds[key]) {
this._sounds[key][property] = value;
}
};
Cache.prototype.decodedSound = /**
* Add a new decoded sound.
* @param key {string} Asset key for the sound.
* @param url {string} URL of this sound file.
* @param data {object} Extra sound data.
*/
function (key, data) {
console.log('decoded sound', key);
this._sounds[key].data = data;
this._sounds[key].decoded = true;
this._sounds[key].isDecoding = false;
};
Cache.prototype.addText = /**
* Add a new text data.
@@ -9555,6 +9618,17 @@ var Phaser;
return null;
};
Cache.prototype.getSound = /**
* Get sound by key.
* @param key Asset key of the sound you want.
* @return {object} The sound you want.
*/
function (key) {
if(this._sounds[key]) {
return this._sounds[key];
}
return null;
};
Cache.prototype.getSoundData = /**
* Get sound data by key.
* @param key Asset key of the sound you want.
* @return {object} The sound data you want.
@@ -13794,6 +13868,11 @@ var Phaser;
if (typeof bodyType === "undefined") { bodyType = Phaser.Types.BODY_DISABLED; }
return this._world.group.add(new Phaser.Sprite(this._game, x, y, key, frame, bodyType));
};
GameObjectFactory.prototype.audio = function (key, volume, loop) {
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
return this._game.sound.add(key, volume, loop);
};
GameObjectFactory.prototype.physicsSprite = /**
* Create a new Sprite with the physics automatically created and set to DYNAMIC. The Sprite position offset is set to its center.
*
@@ -13985,90 +14064,313 @@ var Phaser;
var Sound = (function () {
/**
* Sound constructor
* @param context {object} The AudioContext instance.
* @param gainNode {object} Gain node instance.
* @param data {object} Sound data.
* @param [volume] {number} volume of this sound when playing.
* @param [loop] {boolean} loop this sound when playing? (Default to false)
*/
function Sound(context, gainNode, data, volume, loop) {
function Sound(game, key, volume, loop) {
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
/**
* Reference to AudioContext instance.
*/
this.context = null;
this._muted = false;
this.usingWebAudio = false;
this.usingAudioTag = false;
this.name = '';
this.autoplay = false;
this.totalDuration = 0;
this.startTime = 0;
this.currentTime = 0;
this.duration = 0;
this.stopTime = 0;
this.paused = false;
this.loop = false;
this.isPlaying = false;
this.isDecoding = false;
this._context = context;
this._gainNode = gainNode;
this._buffer = data;
this.currentMarker = '';
this.pendingPlayback = false;
this.game = game;
this.usingWebAudio = this.game.sound.usingWebAudio;
this.usingAudioTag = this.game.sound.usingAudioTag;
this.key = key;
if(this.usingWebAudio) {
this.context = this.game.sound.context;
this.masterGainNode = this.game.sound.masterGain;
if(typeof this.context.createGain === 'undefined') {
this.gainNode = this.context.createGainNode();
} else {
this.gainNode = this.context.createGain();
}
this.gainNode.gain.value = volume * this.game.sound.volume;
this.gainNode.connect(this.masterGainNode);
} else {
this._sound = this.game.cache.getSoundData(key);
this.totalDuration = this._sound.duration;
}
this._volume = volume;
this.loop = loop;
// Local volume control
if(this._context !== null) {
this._localGainNode = this._context.createGainNode();
this._localGainNode.connect(this._gainNode);
this._localGainNode.gain.value = this._volume;
}
if(this._buffer === null) {
this.isDecoding = true;
} else {
this.play();
}
this.markers = {
};
this.onDecoded = new Phaser.Signal();
this.onPlay = new Phaser.Signal();
this.onPause = new Phaser.Signal();
this.onResume = new Phaser.Signal();
this.onLoop = new Phaser.Signal();
this.onStop = new Phaser.Signal();
this.onMute = new Phaser.Signal();
}
Sound.prototype.setDecodedBuffer = function (data) {
this._buffer = data;
this.isDecoding = false;
//this.play();
};
Object.defineProperty(Sound.prototype, "isDecoding", {
get: function () {
return this.game.cache.getSound(this.key).isDecoding;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Sound.prototype, "isDecoded", {
get: function () {
return this.game.cache.isSoundDecoded(this.key);
},
enumerable: true,
configurable: true
});
Sound.prototype.addMarker = function (name, start, stop, volume, loop) {
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
this.markers[name] = {
name: name,
start: start,
stop: stop,
volume: volume,
duration: stop - start,
loop: loop
};
};
Sound.prototype.removeMarker = function (name) {
delete this.markers[name];
};
Sound.prototype.update = function () {
if(this.pendingPlayback && this.game.cache.isSoundDecoded(this.key)) {
this.pendingPlayback = false;
this.play(this._tempMarker, this._tempPosition, this._tempVolume, this._tempLoop);
}
if(this.isPlaying) {
this.currentTime = this.game.time.now - this.startTime;
if(this.currentTime >= this.duration) {
if(this.usingWebAudio) {
if(this.loop) {
this.onLoop.dispatch(this);
this.currentTime = 0;
this.startTime = this.game.time.now;
} else {
this.stop();
}
} else {
if(this.loop) {
this.onLoop.dispatch(this);
this.play(this.currentMarker, 0, this.volume, true, true);
} else {
this.stop();
}
}
}
}
};
Sound.prototype.play = /**
* Play this sound.
* Play this sound, or a marked section of it.
* @param marker {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
function () {
if(this._buffer === null || this.isDecoding === true) {
function (marker, position, volume, loop, forceRestart) {
if (typeof marker === "undefined") { marker = ''; }
if (typeof position === "undefined") { position = 0; }
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
if (typeof forceRestart === "undefined") { forceRestart = false; }
if(this.isPlaying == true && forceRestart == false) {
// Use Restart instead
return;
}
this._sound = this._context.createBufferSource();
this._sound.buffer = this._buffer;
this._sound.connect(this._localGainNode);
if(this.loop) {
this._sound.loop = true;
this.currentMarker = marker;
if(marker !== '' && this.markers[marker]) {
this.loop = this.markers[marker].loop;
this.volume = this.markers[marker].volume;
this.position = this.markers[marker].start;
this.duration = this.markers[marker].duration * 1000;
} else {
this.loop = loop;
this.volume = volume;
this.position = position;
this.duration = 0;
}
if(this.usingWebAudio) {
// Does the sound need decoding?
if(this.game.cache.isSoundDecoded(this.key)) {
// Do we need to do this every time we play? How about just if the buffer is empty?
this._buffer = this.game.cache.getSoundData(this.key);
this._sound = this.context.createBufferSource();
this._sound.buffer = this._buffer;
this._sound.connect(this.gainNode);
this.totalDuration = this._sound.buffer.duration;
if(this.duration == 0) {
this.duration = this.totalDuration * 1000;
}
if(this.loop) {
this._sound.loop = true;
}
// Useful to cache this somewhere perhaps?
if(typeof this._sound.start === 'undefined') {
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.isPlaying = true;
this.startTime = this.game.time.now;
this.currentTime = 0;
this.stopTime = this.startTime + this.duration;
this.onPlay.dispatch(this);
} else {
this._tempVolume = volume;
this._tempLoop = loop;
this._tempPosition = position;
this._tempMarker = marker;
this.pendingPlayback = true;
if(this.game.cache.getSound(this.key).isDecoding == false) {
this.game.sound.decode(this.key, this);
}
}
} else {
if(this._sound.readyState == 4) {
this._sound.currentTime = this.position;
this._sound.muted = this._muted;
this._sound.volume = this._volume;
this._sound.play();
this.isPlaying = true;
this.startTime = this.game.time.now;
this.currentTime = 0;
this.stopTime = this.startTime + this.duration;
this.onPlay.dispatch(this);
}
}
};
Sound.prototype.restart = function (marker, position, volume, loop) {
if (typeof marker === "undefined") { marker = ''; }
if (typeof position === "undefined") { position = 0; }
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
this.play(marker, position, volume, loop, true);
};
Sound.prototype.pause = function () {
if(this.isPlaying && this._sound) {
this.stop();
this.isPlaying = false;
this.paused = true;
this.onPause.dispatch(this);
}
};
Sound.prototype.resume = function () {
if(this.isPlaying == false && this._sound) {
if(this.usingWebAudio) {
if(typeof this._sound.start === 'undefined') {
this._sound.noteGrainOn(0, this.position, this.duration);
//this._sound.noteOn(0); // the zero is vitally important, crashes iOS6 without it
} else {
this._sound.start(0, this.position, this.duration);
}
} else {
this._sound.play();
}
this.isPlaying = true;
this.paused = false;
this.onResume.dispatch(this);
}
this._sound.noteOn(0)// the zero is vitally important, crashes iOS6 without it
;
this.duration = this._sound.buffer.duration;
this.isPlaying = true;
};
Sound.prototype.stop = /**
* Stop playing this sound.
*/
function () {
if(this.isPlaying === true) {
if(this.isPlaying && this._sound) {
if(this.usingWebAudio) {
if(typeof this._sound.stop === 'undefined') {
this._sound.noteOff(0);
} else {
this._sound.stop(0);
}
} else if(this.usingAudioTag) {
this._sound.pause();
this._sound.currentTime = 0;
}
this.isPlaying = false;
this._sound.noteOff(0);
this.currentMarker = '';
this.onStop.dispatch(this);
}
};
Sound.prototype.mute = /**
* Mute the sound.
*/
function () {
this._localGainNode.gain.value = 0;
};
Sound.prototype.unmute = /**
* Enable the sound.
*/
function () {
this._localGainNode.gain.value = this._volume;
};
Object.defineProperty(Sound.prototype, "mute", {
get: /**
* Mute sounds.
*/
function () {
return this._muted;
},
set: function (value) {
if(value) {
this._muted = true;
if(this.usingWebAudio) {
this._muteVolume = this.gainNode.gain.value;
this.gainNode.gain.value = 0;
} else if(this.usingAudioTag) {
this._muteVolume = this._sound.volume;
this._sound.volume = 0;
}
} else {
this._muted = false;
if(this.usingWebAudio) {
this.gainNode.gain.value = this._muteVolume;
} else if(this.usingAudioTag) {
this._sound.volume = this._muteVolume;
}
}
this.onMute.dispatch(this);
},
enumerable: true,
configurable: true
});
Object.defineProperty(Sound.prototype, "volume", {
get: function () {
return this._volume;
},
set: function (value) {
this._volume = value;
this._localGainNode.gain.value = this._volume;
if(this.usingWebAudio) {
this.gainNode.gain.value = value;
} else if(this.usingAudioTag) {
this._sound.volume = value;
}
},
enumerable: true,
configurable: true
});
Sound.prototype.renderDebug = /**
* Renders the Pointer.circle object onto the stage in green if down or red if up.
* @method renderDebug
*/
function (x, y) {
this.game.stage.context.fillStyle = 'rgb(255,255,255)';
this.game.stage.context.font = '16px Courier';
this.game.stage.context.fillText('Sound: ' + this.key + ' Locked: ' + this.game.sound.touchLocked + ' Pending Playback: ' + this.pendingPlayback, x, y);
this.game.stage.context.fillText('Decoded: ' + this.isDecoded + ' Decoding: ' + this.isDecoding, x, y + 20);
this.game.stage.context.fillText('Total Duration: ' + this.totalDuration + ' Playing: ' + this.isPlaying, x, y + 40);
this.game.stage.context.fillText('Time: ' + this.currentTime, x, y + 60);
this.game.stage.context.fillText('Volume: ' + this.volume + ' Muted: ' + this.mute, x, y + 80);
this.game.stage.context.fillText('WebAudio: ' + this.usingWebAudio + ' Audio: ' + this.usingAudioTag, x, y + 100);
if(this.currentMarker !== '') {
this.game.stage.context.fillText('Marker: ' + this.currentMarker + ' Duration: ' + this.duration, x, y + 120);
this.game.stage.context.fillText('Start: ' + this.markers[this.currentMarker].start + ' Stop: ' + this.markers[this.currentMarker].stop, x, y + 140);
this.game.stage.context.fillText('Position: ' + this.position, x, y + 160);
}
};
return Sound;
})();
Phaser.Sound = Sound;
@@ -14088,46 +14390,147 @@ var Phaser;
* Create a new <code>SoundManager</code>.
*/
function SoundManager(game) {
this.usingWebAudio = false;
this.usingAudioTag = false;
this.noAudio = false;
/**
* Reference to AudioContext instance.
*/
this._context = null;
this._game = game;
if(window['PhaserGlobal'] && window['PhaserGlobal'].disableAudio == true) {
return;
this.context = null;
this._muted = false;
this.touchLocked = false;
this._unlockSource = null;
this.onSoundDecode = new Phaser.Signal();
this.game = game;
this._volume = 1;
this._muted = false;
this._sounds = [];
if(this.game.device.iOS && this.game.device.webAudio == false) {
this.channels = 1;
}
if(game.device.webaudio == true) {
if(!!window['AudioContext']) {
this._context = new window['AudioContext']();
} else if(!!window['webkitAudioContext']) {
this._context = new window['webkitAudioContext']();
if(window['PhaserGlobal']) {
// Check to see if all audio playback is disabled (i.e. handled by a 3rd party class)
if(window['PhaserGlobal'].disableAudio == true) {
this.usingWebAudio = false;
this.noAudio = true;
return;
}
if(this._context !== null) {
this._gainNode = this._context.createGainNode();
this._gainNode.connect(this._context.destination);
this._volume = 1;
// Check if the Web Audio API is disabled (for testing Audio Tag playback during development)
if(window['PhaserGlobal'].disableWebAudio == true) {
this.usingWebAudio = false;
this.usingAudioTag = true;
this.noAudio = false;
return;
}
}
if(this.game.device.iOS && this.game.device.webAudio) {
this.game.input.touch.callbackContext = this;
this.game.input.touch.touchStartCallback = this.unlock;
this.touchLocked = true;
} else {
// What about iOS5?
this.touchLocked = false;
}
this.usingWebAudio = true;
this.noAudio = false;
if(!!window['AudioContext']) {
this.context = new window['AudioContext']();
} else if(!!window['webkitAudioContext']) {
this.context = new window['webkitAudioContext']();
} else if(!!window['Audio']) {
this.usingWebAudio = false;
this.usingAudioTag = true;
} else {
this.usingWebAudio = false;
this.noAudio = true;
}
if(this.context !== null) {
if(typeof this.context.createGain === 'undefined') {
this.masterGain = this.context.createGainNode();
} else {
this.masterGain = this.context.createGain();
}
this.masterGain.gain.value = 1;
this.masterGain.connect(this.context.destination);
}
}
SoundManager.prototype.mute = /**
* Mute sounds.
*/
function () {
this._gainNode.gain.value = 0;
SoundManager.prototype.unlock = function () {
if(this.touchLocked == false || this.game.device.iOS == false) {
return;
}
// Create Audio tag?
// Create empty buffer and play it
var buffer = this.context.createBuffer(1, 1, 22050);
this._unlockSource = this.context.createBufferSource();
this._unlockSource.buffer = buffer;
this._unlockSource.connect(this.context.destination);
this._unlockSource.noteOn(0);
};
SoundManager.prototype.unmute = /**
* Enable sounds.
*/
function () {
this._gainNode.gain.value = this._volume;
};
Object.defineProperty(SoundManager.prototype, "volume", {
get: function () {
return this._volume;
Object.defineProperty(SoundManager.prototype, "mute", {
get: /**
* A global audio mute toggle.
*/
function () {
return this._muted;
},
set: function (value) {
if(value) {
if(this._muted) {
return;
}
this._muted = true;
if(this.usingWebAudio) {
this._muteVolume = this.masterGain.gain.value;
this.masterGain.gain.value = 0;
}
// Loop through sounds
for(var i = 0; i < this._sounds.length; i++) {
if(this._sounds[i].usingAudioTag) {
this._sounds[i].mute = true;
}
}
} else {
if(this._muted == false) {
return;
}
this._muted = false;
if(this.usingWebAudio) {
this.masterGain.gain.value = this._muteVolume;
}
// Loop through sounds
for(var i = 0; i < this._sounds.length; i++) {
if(this._sounds[i].usingAudioTag) {
this._sounds[i].mute = false;
}
}
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(SoundManager.prototype, "volume", {
get: function () {
if(this.usingWebAudio) {
return this.masterGain.gain.value;
} else {
return this._volume;
}
},
set: /**
* The global audio volume. A value between 0 (silence) and 1 (full volume)
*/
function (value) {
value = this.game.math.clamp(value, 1, 0);
this._volume = value;
this._gainNode.gain.value = this._volume;
if(this.usingWebAudio) {
this.masterGain.gain.value = value;
}
// Loop through the sound cache and change the volume of all html audio tags
for(var i = 0; i < this._sounds.length; i++) {
if(this._sounds[i].usingAudioTag) {
this._sounds[i].volume = this._sounds[i].volume * value;
}
}
},
enumerable: true,
configurable: true
@@ -14135,53 +14538,43 @@ var Phaser;
SoundManager.prototype.decode = /**
* Decode a sound with its assets key.
* @param key {string} Assets key of the sound to be decoded.
* @param callback {function} This will be invoked when finished decoding.
* @param [sound] {Sound} its bufer will be set to decoded data.
*/
function (key, callback, sound) {
if (typeof callback === "undefined") { callback = null; }
function (key, sound) {
if (typeof sound === "undefined") { sound = null; }
var soundData = this._game.cache.getSound(key);
var soundData = this.game.cache.getSoundData(key);
if(soundData) {
if(this._game.cache.isSoundDecoded(key) === false) {
if(this.game.cache.isSoundDecoded(key) === false) {
this.game.cache.updateSound(key, 'isDecoding', true);
var that = this;
this._context.decodeAudioData(soundData, function (buffer) {
that._game.cache.decodedSound(key, buffer);
this.context.decodeAudioData(soundData, function (buffer) {
that.game.cache.decodedSound(key, buffer);
if(sound) {
sound.setDecodedBuffer(buffer);
that.onSoundDecode.dispatch(sound);
}
callback();
});
}
}
};
SoundManager.prototype.play = /**
* Play a sound with its assets key.
* @param key {string} Assets key of the sound you want to play.
* @param [volume] {number} volume of the sound you want to play.
* @param [loop] {boolean} loop when it finished playing? (Default to false)
* @return {Sound} The playing sound object.
*/
function (key, volume, loop) {
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
if(this._context === null) {
return;
}
var soundData = this._game.cache.getSound(key);
if(soundData) {
// Does the sound need decoding?
if(this._game.cache.isSoundDecoded(key) === true) {
return new Phaser.Sound(this._context, this._gainNode, soundData, volume, loop);
} else {
var tempSound = new Phaser.Sound(this._context, this._gainNode, null, volume, loop);
// this is an async process, so we can return the Sound object anyway, it just won't be playing yet
this.decode(key, function () {
return tempSound.play();
}, tempSound);
return tempSound;
SoundManager.prototype.update = function () {
if(this.touchLocked && this._unlockSource !== null) {
if((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE)) {
this.touchLocked = false;
this._unlockSource = null;
this.game.input.touch.callbackContext = null;
this.game.input.touch.touchStartCallback = null;
}
}
for(var i = 0; i < this._sounds.length; i++) {
this._sounds[i].update();
}
};
SoundManager.prototype.add = function (key, volume, loop) {
if (typeof volume === "undefined") { volume = 1; }
if (typeof loop === "undefined") { loop = false; }
var sound = new Phaser.Sound(this.game, key, volume, loop);
this._sounds.push(sound);
return sound;
};
return SoundManager;
})();
@@ -14870,6 +15263,12 @@ var Phaser;
document.addEventListener('webkitvisibilitychange', function (event) {
return _this.visibilityChange(event);
}, false);
document.addEventListener('pagehide', function (event) {
return _this.visibilityChange(event);
}, false);
document.addEventListener('pageshow', function (event) {
return _this.visibilityChange(event);
}, false);
window.onblur = function (event) {
return _this.visibilityChange(event);
};
@@ -14917,7 +15316,7 @@ var Phaser;
if(this.disablePauseScreen) {
return;
}
if(event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) {
if(event.type == 'pagehide' || event.type == 'blur' || document['hidden'] == true || document['webkitHidden'] == true) {
if(this._game.paused == false) {
this.pauseGame();
}
@@ -15908,35 +16307,45 @@ var Phaser;
this.webApp = false;
// Audio
/**
* Is audioData available?
* Are Audio tags available?
* @type {boolean}
*/
this.audioData = false;
/**
* Is webaudio available?
* Is the WebAudio API available?
* @type {boolean}
*/
this.webaudio = false;
this.webAudio = false;
/**
* Is ogg available?
* Can this device play ogg files?
* @type {boolean}
*/
this.ogg = false;
/**
* Is mp3 available?
* Can this device play opus files?
* @type {boolean}
*/
this.opus = false;
/**
* Can this device play mp3 files?
* @type {boolean}
*/
this.mp3 = false;
/**
* Is wav available?
* Can this device play wav files?
* @type {boolean}
*/
this.wav = false;
/**
* Is m4a available?
* Can this device play m4a files?
* @type {boolean}
*/
this.m4a = false;
/**
* Can this device play webm files?
* @type {boolean}
*/
this.webm = false;
// Device
/**
* Is running on iPhone?
@@ -16041,13 +16450,27 @@ var Phaser;
this.webApp = true;
}
};
Device.prototype.canPlayAudio = function (type) {
if(type == 'mp3' && this.mp3) {
return true;
} else if(type == 'ogg' && (this.ogg || this.opus)) {
return true;
} else if(type == 'm4a' && this.m4a) {
return true;
} else if(type == 'wav' && this.wav) {
return true;
} else if(type == 'webm' && this.webm) {
return true;
}
return false;
};
Device.prototype._checkAudio = /**
* Check audio support.
* @private
*/
function () {
this.audioData = !!(window['Audio']);
this.webaudio = !!(window['webkitAudioContext'] || window['AudioContext']);
this.webAudio = !!(window['webkitAudioContext'] || window['AudioContext']);
var audioElement = document.createElement('audio');
var result = false;
try {
@@ -16055,6 +16478,9 @@ var Phaser;
if(audioElement.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, '')) {
this.ogg = true;
}
if(audioElement.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/, '')) {
this.opus = true;
}
if(audioElement.canPlayType('audio/mpeg;').replace(/^no$/, '')) {
this.mp3 = true;
}
@@ -16067,6 +16493,9 @@ var Phaser;
if(audioElement.canPlayType('audio/x-m4a;') || audioElement.canPlayType('audio/aac;').replace(/^no$/, '')) {
this.m4a = true;
}
if(audioElement.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, '')) {
this.webm = true;
}
}
} catch (e) {
}
@@ -16653,6 +17082,12 @@ var Phaser;
**/
this.totalTouches = 0;
/**
* The number of miliseconds since the last click
* @property msSinceLastClick
* @type {Number}
**/
this.msSinceLastClick = Number.MAX_VALUE;
/**
* The Game Object this Pointer is currently over / touching / dragging.
* @property targetObject
* @type {Any}
@@ -16720,6 +17155,8 @@ var Phaser;
this.withinGame = true;
this.isDown = true;
this.isUp = false;
// Work out how long it has been since the last click
this.msSinceLastClick = this.game.time.now - this.timeDown;
this.timeDown = this.game.time.now;
this._holdSent = false;
// This sets the x/y and other local values
@@ -17777,7 +18214,7 @@ var Phaser;
* @property recordPointerHistory
* @type {Boolean}
**/
this.recordPointerHistory = true;
this.recordPointerHistory = false;
/**
* The rate in milliseconds at which the Pointer objects should update their tracking history
* @property recordRate
@@ -17786,7 +18223,7 @@ var Phaser;
this.recordRate = 100;
/**
* The total number of entries that can be recorded into the Pointer objects tracking history.
* The the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
* @property recordLimit
* @type {Number}
*/
@@ -19029,6 +19466,7 @@ var Phaser;
/// <reference path="cameras/CameraManager.ts" />
/// <reference path="gameobjects/GameObjectFactory.ts" />
/// <reference path="sound/SoundManager.ts" />
/// <reference path="sound/Sound.ts" />
/// <reference path="Stage.ts" />
/// <reference path="Time.ts" />
/// <reference path="tweens/TweenManager.ts" />
@@ -19199,12 +19637,12 @@ var Phaser;
this.stage = new Phaser.Stage(this, parent, width, height);
this.world = new Phaser.World(this, width, height);
this.add = new Phaser.GameObjectFactory(this);
this.sound = new Phaser.SoundManager(this);
this.cache = new Phaser.Cache(this);
this.load = new Phaser.Loader(this, this.loadComplete);
this.time = new Phaser.Time(this);
this.tweens = new Phaser.TweenManager(this);
this.input = new Phaser.Input(this);
this.sound = new Phaser.SoundManager(this);
this.rnd = new Phaser.RandomDataGenerator([
(Date.now() * Math.random()).toString()
]);
@@ -19266,6 +19704,7 @@ var Phaser;
this.tweens.update();
this.input.update();
this.stage.update();
this.sound.update();
if(this.onPausedCallback !== null) {
this.onPausedCallback.call(this.callbackContext);
}
@@ -19277,6 +19716,7 @@ var Phaser;
this.tweens.update();
this.input.update();
this.stage.update();
this.sound.update();
this.physics.update();
this.world.update();
if(this._loadComplete && this.onUpdateCallback) {