Skip to content

Commit adad60f

Browse files
committed
SoundManager.onVolumeChange is a new signal that is dispatched whenever the global volume changes. The new volume is passed as the only parameter to your callback.
SoundManager.onMute is a new signal that is dispatched when the SoundManager is globally muted, either directly via game code or as a result of the game pausing. SoundManager.onUnMute is a new signal that is dispatched when the SoundManager is globally un-muted, either directly via game code or as a result of the game resuming from a pause.
1 parent 1a7450e commit adad60f

1 file changed

Lines changed: 88 additions & 54 deletions

File tree

src/sound/SoundManager.js

Lines changed: 88 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ Phaser.SoundManager = function (game) {
3030
*/
3131
this.onSoundDecode = new Phaser.Signal();
3232

33+
/**
34+
* This signal is dispatched whenever the global volume changes. The new volume is passed as the only parameter to your callback.
35+
* @property {Phaser.Signal} onVolumeChange
36+
*/
37+
this.onVolumeChange = new Phaser.Signal();
38+
39+
/**
40+
* This signal is dispatched when the SoundManager is globally muted, either directly via game code or as a result of the game pausing.
41+
* @property {Phaser.Signal} onMute
42+
*/
43+
this.onMute = new Phaser.Signal();
44+
45+
/**
46+
* This signal is dispatched when the SoundManager is globally un-muted, either directly via game code or as a result of the game resuming from a pause.
47+
* @property {Phaser.Signal} onUnMute
48+
*/
49+
this.onUnMute = new Phaser.Signal();
50+
3351
/**
3452
* @property {boolean} _codeMuted - Internal mute tracking var.
3553
* @private
@@ -146,19 +164,6 @@ Phaser.SoundManager.prototype = {
146164
this.channels = 1;
147165
}
148166

149-
if (!this.game.device.cocoonJS && this.game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock))
150-
{
151-
this.game.input.touch.callbackContext = this;
152-
this.game.input.touch.touchStartCallback = this.unlock;
153-
this.game.input.mouse.callbackContext = this;
154-
this.game.input.mouse.mouseDownCallback = this.unlock;
155-
this.touchLocked = true;
156-
}
157-
else
158-
{
159-
this.touchLocked = false;
160-
}
161-
162167
// PhaserGlobal overrides
163168
if (window['PhaserGlobal'])
164169
{
@@ -237,33 +242,54 @@ Phaser.SoundManager.prototype = {
237242
this.masterGain.connect(this.context.destination);
238243
}
239244

245+
if (!this.noAudio)
246+
{
247+
// On mobile we need a native touch event before we can play anything, so capture it here
248+
if (!this.game.device.cocoonJS && this.game.device.iOS || (window['PhaserGlobal'] && window['PhaserGlobal'].fakeiOSTouchLock))
249+
{
250+
this.setTouchLock();
251+
}
252+
}
253+
254+
},
255+
256+
/**
257+
* Sets the Input Manager touch callback to be SoundManager.unlock.
258+
* Required for iOS audio device unlocking. Mostly just used internally.
259+
*
260+
* @method Phaser.SoundManager#setTouchLock
261+
*/
262+
setTouchLock: function () {
263+
264+
this.game.input.touch.callbackContext = this;
265+
this.game.input.touch.touchStartCallback = this.unlock;
266+
this.touchLocked = true;
267+
240268
},
241269

242270
/**
243271
* Enables the audio, usually after the first touch.
272+
*
244273
* @method Phaser.SoundManager#unlock
245274
*/
246275
unlock: function () {
247276

248-
if (this.touchLocked === false)
277+
if (this.noAudio || !this.touchLocked || this._unlockSource !== null)
249278
{
250279
return;
251280
}
252281

253282
// Global override (mostly for Audio Tag testing)
254-
if (this.game.device.webAudio === false || (window['PhaserGlobal'] && window['PhaserGlobal'].disableWebAudio === true))
283+
if (this.usingAudioTag)
255284
{
256-
// Create an Audio tag?
257285
this.touchLocked = false;
258286
this._unlockSource = null;
259-
this.game.input.touch.callbackContext = null;
260-
this.game.input.touch.touchStartCallback = null;
261-
this.game.input.mouse.callbackContext = null;
262-
this.game.input.mouse.mouseDownCallback = null;
263287
}
264-
else
288+
else if (this.usingWebAudio)
265289
{
266290
// Create empty buffer and play it
291+
// The SoundManager.update loop captures the state of it and then resets touchLocked to false
292+
267293
var buffer = this.context.createBuffer(1, 1, 22050);
268294
this._unlockSource = this.context.createBufferSource();
269295
this._unlockSource.buffer = buffer;
@@ -279,6 +305,10 @@ Phaser.SoundManager.prototype = {
279305
}
280306
}
281307

308+
// We can remove the event because we've done what we needed (started the unlock sound playing)
309+
this.game.input.touch.callbackContext = null;
310+
this.game.input.touch.touchStartCallback = null;
311+
282312
},
283313

284314
/**
@@ -360,8 +390,6 @@ Phaser.SoundManager.prototype = {
360390

361391
var soundData = this.game.cache.getSoundData(key);
362392

363-
// console.log(key, 'soundData', soundData);
364-
365393
if (soundData)
366394
{
367395
if (this.game.cache.isSoundDecoded(key) === false)
@@ -434,24 +462,22 @@ Phaser.SoundManager.prototype = {
434462
},
435463

436464
/**
437-
* Updates every sound in the game.
465+
* Updates every sound in the game, checks for audio unlock on mobile and monitors the decoding watch list.
438466
*
439467
* @method Phaser.SoundManager#update
468+
* @protected
440469
*/
441470
update: function () {
442471

443-
if (this.touchLocked)
472+
if (this.noAudio)
444473
{
445-
if (this.game.device.webAudio && this._unlockSource !== null)
446-
{
447-
if ((this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE))
448-
{
449-
this.touchLocked = false;
450-
this._unlockSource = null;
451-
this.game.input.touch.callbackContext = null;
452-
this.game.input.touch.touchStartCallback = null;
453-
}
454-
}
474+
return;
475+
}
476+
477+
if (this.touchLocked && this._unlockSource !== null && (this._unlockSource.playbackState === this._unlockSource.PLAYING_STATE || this._unlockSource.playbackState === this._unlockSource.FINISHED_STATE))
478+
{
479+
this.touchLocked = false;
480+
this._unlockSource = null;
455481
}
456482

457483
for (var i = 0; i < this._sounds.length; i++)
@@ -584,6 +610,11 @@ Phaser.SoundManager.prototype = {
584610
*/
585611
play: function (key, volume, loop) {
586612

613+
if (this.noAudio)
614+
{
615+
return;
616+
}
617+
587618
var sound = this.add(key, volume, loop);
588619

589620
sound.play();
@@ -622,6 +653,8 @@ Phaser.SoundManager.prototype = {
622653
}
623654
}
624655

656+
this.onMute.dispatch();
657+
625658
},
626659

627660
/**
@@ -653,6 +686,8 @@ Phaser.SoundManager.prototype = {
653686
}
654687
}
655688

689+
this.onUnMute.dispatch();
690+
656691
},
657692

658693
/**
@@ -736,35 +771,34 @@ Object.defineProperty(Phaser.SoundManager.prototype, "volume", {
736771

737772
get: function () {
738773

739-
if (this.usingWebAudio)
740-
{
741-
return this.masterGain.gain.value;
742-
}
743-
else
744-
{
745-
return this._volume;
746-
}
774+
return this._volume;
747775

748776
},
749777

750778
set: function (value) {
751779

752-
this._volume = value;
753-
754-
if (this.usingWebAudio)
755-
{
756-
this.masterGain.gain.value = value;
757-
}
758-
else
780+
if (this._volume !== value)
759781
{
760-
// Loop through the sound cache and change the volume of all html audio tags
761-
for (var i = 0; i < this._sounds.length; i++)
782+
this._volume = value;
783+
784+
if (this.usingWebAudio)
785+
{
786+
this.masterGain.gain.value = value;
787+
}
788+
else
762789
{
763-
if (this._sounds[i].usingAudioTag)
790+
// Loop through the sound cache and change the volume of all html audio tags
791+
for (var i = 0; i < this._sounds.length; i++)
764792
{
765-
this._sounds[i].volume = this._sounds[i].volume * value;
793+
if (this._sounds[i].usingAudioTag)
794+
{
795+
this._sounds[i].volume = this._sounds[i].volume * value;
796+
}
766797
}
767798
}
799+
800+
this.onVolumeChange.dispatch(value);
801+
768802
}
769803

770804
}

0 commit comments

Comments
 (0)