var Class = require('../../utils/Class'); var BaseSound = require('../BaseSound'); var SoundEvent = require('../SoundEvent'); var SoundValueEvent = require('../SoundValueEvent'); var HTML5AudioSound = new Class({ Extends: BaseSound, initialize: function HTML5AudioSound(manager, key, config) { if (config === void 0) { config = {}; } /** * An array containing all HTML5 Audio tags that could be used for individual * sound's playback. Number of instances depends on the config value passed * to the Loader#audio method call, default is 1. * * @private * @property {HTMLAudioElement[]} tags */ this.tags = manager.game.cache.audio.get(key); if (!this.tags) { console.error('No audio loaded in cache with key: \'' + key + '\'!'); return; } /** * Reference to an HTML5 Audio tag used for playing sound. * * @private * @property {HTMLAudioElement} audio * @default null */ this.audio = null; /** * Audio tag's playback position recorded on previous * update method call. Set to 0 if sound is not playing. * * @private * @property {number} previousTime * @default 0 */ this.previousTime = 0; /** * Timestamp as generated by the Request Animation Frame or SetTimeout * representing the time at which the delayed sound playback should start. * Set to 0 if sound playback is not delayed. * * @private * @property {number} startTime * @default 0 */ this.startTime = 0; this.duration = this.tags[0].duration; this.totalDuration = this.tags[0].duration; BaseSound.call(this, manager, key, config); }, play: function (markerName, config) { if (!BaseSound.prototype.play.call(this, markerName, config)) { return false; } // \/\/\/ isPlaying = true, isPaused = false \/\/\/ if (!this.pickAndPlayAudioTag()) { return false; } this.events.dispatch(new SoundEvent(this, 'SOUND_PLAY')); return true; }, pause: function () { if (this.startTime > 0) { return false; } if (!BaseSound.prototype.pause.call(this)) { return false; } // \/\/\/ isPlaying = false, isPaused = true \/\/\/ this.currentConfig.seek = this.audio.currentTime - (this.currentMarker ? this.currentMarker.start : 0); this.stopAndReleaseAudioTag(); this.events.dispatch(new SoundEvent(this, 'SOUND_PAUSE')); return true; }, resume: function () { if (this.startTime > 0) { return false; } if (!BaseSound.prototype.resume.call(this)) { return false; } // \/\/\/ isPlaying = true, isPaused = false \/\/\/ if (!this.pickAndPlayAudioTag()) { return false; } this.events.dispatch(new SoundEvent(this, 'SOUND_RESUME')); return true; }, stop: function () { if (!BaseSound.prototype.stop.call(this)) { return false; } // \/\/\/ isPlaying = false, isPaused = false \/\/\/ this.stopAndReleaseAudioTag(); this.events.dispatch(new SoundEvent(this, 'SOUND_STOP')); return true; }, pickAndPlayAudioTag: function () { if (!this.pickAudioTag()) { this.reset(); return false; } var seek = this.currentConfig.seek; var delay = this.currentConfig.delay; var offset = (this.currentMarker ? this.currentMarker.start : 0) + seek; this.previousTime = this.audio.currentTime = offset; this.applyConfig(); if (delay === 0) { this.startTime = 0; if (this.audio.paused) { this.audio.play(); } } else { this.startTime = window.performance.now() + delay; if (!this.audio.paused) { this.audio.pause(); } } this.resetConfig(); return true; }, pickAudioTag: function () { if (!this.audio) { for (var i = 0; i < this.tags.length; i++) { var audio = this.tags[i]; if (audio.dataset.used === 'false') { audio.dataset.used = 'true'; this.audio = audio; return true; } } if (!this.manager.override) { return false; } var otherSounds_1 = []; this.manager.forEachActiveSound(function (sound) { if (sound.key === this.key && sound.isPlaying) { otherSounds_1.push(sound); } }); otherSounds_1.sort(function (a1, a2) { if (a1.loop === a2.loop) { return a2.seek - a1.seek; } return a1.loop ? 1 : -1; }); var selectedSound = otherSounds_1[0]; this.audio = selectedSound.audio; selectedSound.reset(); selectedSound.audio = null; selectedSound.previousTime = 0; selectedSound.startTime = 0; } return true; }, stopAndReleaseAudioTag: function () { this.audio.pause(); this.audio.dataset.used = 'false'; this.audio = null; this.previousTime = 0; this.startTime = 0; }, reset: function () { BaseSound.prototype.stop.call(this); }, update: function (time, delta) { // TODO include play method call delay if (this.isPlaying) { // handling delayed playback if (this.startTime > 0) { if (this.startTime < time) { this.previousTime = this.audio.currentTime += (time - this.startTime) / 1000; this.startTime = 0; this.audio.play(); } return; } // handle looping and ending var startTime = (this.currentMarker ? this.currentMarker.start : 0); var endTime = startTime + this.duration; var currentTime = this.audio.currentTime; if (currentTime >= endTime) { if (this.currentConfig.loop) { currentTime = this.audio.currentTime = startTime + (currentTime - endTime); } else { this.reset(); this.stopAndReleaseAudioTag(); this.events.dispatch(new SoundEvent(this, 'SOUND_ENDED')); return; } } else if (currentTime < startTime) { currentTime = this.audio.currentTime += startTime; } if (currentTime < this.previousTime) { this.events.dispatch(new SoundEvent(this, 'SOUND_LOOP')); } this.previousTime = currentTime; } }, destroy: function () { BaseSound.prototype.destroy.call(this); // TODO release all HTML5 Audio tag related stuff }, setMute: function () { if (this.audio) { this.audio.muted = this.currentConfig.mute || this.manager.mute; } }, setVolume: function () { if (this.audio) { this.audio.volume = this.currentConfig.volume * this.manager.volume; } }, setRate: function () { BaseSound.prototype.setRate.call(this); if (this.audio) { this.audio.playbackRate = this.totalRate; } } }); /** * Mute setting. * * @name Phaser.Sound.HTML5AudioSound#mute * @property {boolean} mute */ Object.defineProperty(HTML5AudioSound.prototype, 'mute', { get: function () { return this.currentConfig.mute; }, set: function (value) { this.currentConfig.mute = value; this.setMute(); this.events.dispatch(new SoundValueEvent(this, 'SOUND_MUTE', value)); } }); /** * Volume setting. * * @name Phaser.Sound.HTML5AudioSound#volume * @property {number} volume */ Object.defineProperty(HTML5AudioSound.prototype, 'volume', { get: function () { return this.currentConfig.volume; }, set: function (value) { this.currentConfig.volume = value; this.setVolume(); this.events.dispatch(new SoundValueEvent(this, 'SOUND_VOLUME', value)); } }); /** * Current position of playing sound. * * @name Phaser.Sound.HTML5AudioSound#seek * @property {number} seek */ Object.defineProperty(HTML5AudioSound.prototype, 'seek', { get: function () { if (this.isPlaying) { return this.audio.currentTime; } else if (this.isPaused) { return this.currentConfig.seek; } else { return 0; } }, set: function (value) { if (this.startTime > 0) { return; } if (this.isPlaying || this.isPaused) { value = Math.min(Math.max(0, value), this.duration); if (this.isPlaying) { this.previousTime = this.audio.currentTime = value; } else if (this.isPaused) { this.currentConfig.seek = value; } this.events.dispatch(new SoundValueEvent(this, 'SOUND_SEEK', value)); } } }); /** * Property indicating whether or not * the sound or current sound marker will loop. * * @name Phaser.Sound.HTML5AudioSound#loop * @property {boolean} loop */ Object.defineProperty(HTML5AudioSound.prototype, 'loop', { get: function () { return this.currentConfig.loop; }, set: function (value) { this.currentConfig.loop = value; if (this.audio) { this.audio.loop = value; } } }); module.exports = HTML5AudioSound;