diff --git a/src/sound/BaseSound.js b/src/sound/BaseSound.js index e2bd3cedb..11f521e28 100644 --- a/src/sound/BaseSound.js +++ b/src/sound/BaseSound.js @@ -128,7 +128,8 @@ var BaseSound = new Class({ detune: 0, seek: 0, loop: false, - delay: 0 + delay: 0, + pan: 0 }; @@ -217,7 +218,8 @@ var BaseSound = new Class({ detune: 0, seek: 0, loop: false, - delay: 0 + delay: 0, + pan: 0 } }, marker); @@ -418,6 +420,7 @@ var BaseSound = new Class({ this.rate = this.currentConfig.rate; this.detune = this.currentConfig.detune; this.loop = this.currentConfig.loop; + this.pan = this.currentConfig.pan; }, /** diff --git a/src/sound/events/PAN_EVENT.js b/src/sound/events/PAN_EVENT.js new file mode 100644 index 000000000..c73c44ea5 --- /dev/null +++ b/src/sound/events/PAN_EVENT.js @@ -0,0 +1,27 @@ +/** + * @author pi-kei + * @copyright 2020 Photon Storm Ltd. + * @license {@link https://opensource.org/licenses/MIT|MIT License} + */ + +/** + * The Sound Pan Event. + * + * This event is dispatched by both Web Audio and HTML5 Audio Sound objects when their pan changes. + * + * Listen to it from a Sound instance using `Sound.on('pan', listener)`, i.e.: + * + * ```javascript + * var sound = this.sound.add('key'); + * sound.on('pan', listener); + * sound.play(); + * sound.setPan(0.5); + * ``` + * + * @event Phaser.Sound.Events#PAN + * @since 3.0.0 + * + * @param {(Phaser.Sound.WebAudioSound|Phaser.Sound.HTML5AudioSound)} sound - A reference to the Sound that emitted the event. + * @param {number} pan - The new pan of the Sound. + */ +module.exports = 'pan'; diff --git a/src/sound/events/index.js b/src/sound/events/index.js index 139bb2461..212777d00 100644 --- a/src/sound/events/index.js +++ b/src/sound/events/index.js @@ -32,6 +32,7 @@ module.exports = { STOP_ALL: require('./STOP_ALL_EVENT'), STOP: require('./STOP_EVENT'), UNLOCKED: require('./UNLOCKED_EVENT'), - VOLUME: require('./VOLUME_EVENT') + VOLUME: require('./VOLUME_EVENT'), + PAN: require('./PAN_EVENT') }; diff --git a/src/sound/html5/HTML5AudioSound.js b/src/sound/html5/HTML5AudioSound.js index 950345cb0..c5918f9f6 100644 --- a/src/sound/html5/HTML5AudioSound.js +++ b/src/sound/html5/HTML5AudioSound.js @@ -916,6 +916,49 @@ var HTML5AudioSound = new Class({ { this.loop = value; + return this; + }, + + /** + * Gets or sets the pan of this sound, a value between -1 (full left pan) and 1 (full right pan). + * Has no effect on HTML5 Audio Sound. + * + * @name Phaser.Sound.HTML5AudioSound#pan + * @type {number} + * @default 0 + * @fires Phaser.Sound.Events#PAN + * @since 3.0.0 + */ + pan: { + + get: function () + { + return this.currentConfig.pan; + }, + + set: function (value) + { + this.currentConfig.pan = value; + + this.emit(Events.PAN, this, value); + } + }, + + /** + * Sets the pan of this Sound. Has no effect on HTML5 Audio Sound. + * + * @method Phaser.Sound.HTML5AudioSound#setPan + * @fires Phaser.Sound.Events#PAN + * @since 3.4.0 + * + * @param {number} value - The pan of the sound. + * + * @return {Phaser.Sound.HTML5AudioSound} This Sound instance. + */ + setPan: function (value) + { + this.pan = value; + return this; } diff --git a/src/sound/noaudio/NoAudioSound.js b/src/sound/noaudio/NoAudioSound.js index 952d17e79..a2d0fc64e 100644 --- a/src/sound/noaudio/NoAudioSound.js +++ b/src/sound/noaudio/NoAudioSound.js @@ -70,7 +70,8 @@ var NoAudioSound = new Class({ detune: 0, seek: 0, loop: false, - delay: 0 + delay: 0, + pan: 0 }, config); this.currentConfig = this.config; @@ -80,6 +81,7 @@ var NoAudioSound = new Class({ this.detune = 0; this.seek = 0; this.loop = false; + this.pan = 0; this.markers = {}; this.currentMarker = null; this.pendingRemove = false; @@ -178,7 +180,9 @@ var NoAudioSound = new Class({ setSeek: returnThis, - setLoop: returnThis + setLoop: returnThis, + + setPan: returnThis }); diff --git a/src/sound/typedefs/SoundConfig.js b/src/sound/typedefs/SoundConfig.js index 25b15aa5d..22746120c 100644 --- a/src/sound/typedefs/SoundConfig.js +++ b/src/sound/typedefs/SoundConfig.js @@ -11,4 +11,5 @@ * @property {number} [seek=0] - Position of playback for this sound, in seconds. * @property {boolean} [loop=false] - Whether or not the sound or current sound marker should loop. * @property {number} [delay=0] - Time, in seconds, that should elapse before the sound actually starts its playback. + * @property {number} [pan=0] - A value between -1 (full left pan) and 1 (full right pan). 0 means no pan. */ diff --git a/src/sound/webaudio/WebAudioSound.js b/src/sound/webaudio/WebAudioSound.js index fd8e25fdd..cc104058d 100644 --- a/src/sound/webaudio/WebAudioSound.js +++ b/src/sound/webaudio/WebAudioSound.js @@ -91,6 +91,15 @@ var WebAudioSound = new Class({ */ this.volumeNode = manager.context.createGain(); + /** + * Panner node responsible for controlling this sound's pan. + * + * @name Phaser.Sound.WebAudioSound#pannerNode + * @type {StereoPannerNode} + * @private + */ + this.pannerNode = manager.context.createStereoPanner(); + /** * The time at which the sound should have started playback from the beginning. * Based on BaseAudioContext.currentTime value. @@ -165,7 +174,9 @@ var WebAudioSound = new Class({ this.muteNode.connect(this.volumeNode); - this.volumeNode.connect(manager.destination); + this.volumeNode.connect(this.pannerNode); + + this.pannerNode.connect(manager.destination); this.duration = this.audioBuffer.duration; @@ -493,6 +504,8 @@ var WebAudioSound = new Class({ this.muteNode = null; this.volumeNode.disconnect(); this.volumeNode = null; + this.pannerNode.disconnect(); + this.pannerNode = null; this.rateUpdates.length = 0; this.rateUpdates = null; }, @@ -892,6 +905,49 @@ var WebAudioSound = new Class({ { this.loop = value; + return this; + }, + + /** + * Gets or sets the pan of this sound, a value between -1 (full left pan) and 1 (full right pan). + * + * @name Phaser.Sound.WebAudioSound#pan + * @type {number} + * @default 0 + * @fires Phaser.Sound.Events#PAN + * @since 3.0.0 + */ + pan: { + + get: function () + { + return this.pannerNode.pan.value; + }, + + set: function (value) + { + this.currentConfig.pan = value; + this.pannerNode.pan.setValueAtTime(value, this.manager.context.currentTime); + + this.emit(Events.PAN, this, value); + } + }, + + /** + * Sets the pan of this Sound. + * + * @method Phaser.Sound.WebAudioSound#setPan + * @fires Phaser.Sound.Events#PAN + * @since 3.4.0 + * + * @param {number} value - The pan of the sound. + * + * @return {Phaser.Sound.WebAudioSound} This Sound instance. + */ + setPan: function (value) + { + this.pan = value; + return this; }