API Docs for: 1.4.0
Show:

File: src\sound\AudioManager.ts

/**
* The namespace that holds all of the assets and functionality when dealing with Audio.
* 
* @module Kiwi
* @submodule Sound
* @main Sound 
* 
*/

module Kiwi.Sound {

	/**
	* Manages the initialisation of assets necessary when dealing with audio in the game, either through Audio Tags or the Web Audio API. 
	* Also provides global sound controls that will be applyed to all Audio objects at the same time, within the Game this manager is a part of. 
	* 
	* @class AudioManager
	* @constructor
	* @namespace Kiwi.Sound
	* @param game {Kiwi.Game} The game that this audio manager belongs to.
	* @return {Kiwi.Sound.AudioManager}
	*/
	export class AudioManager {

		constructor(game: Kiwi.Game) {

			this._game = game;

		}

		/**
		* The type of object that this is.
		* @method objType
		* @return {String} "AudioManager"
		* @public
		*/
		public objType() {
			return "AudioManager";
		}

		/**
		* The game that this manager belongs to.
		* @property _game
		* @type Kiwi.Game
		* @private
		*/
		private _game: Kiwi.Game;

		/**
		* The global volume of all of the games audio.
		* @property _volume
		* @type number
		* @default 1
		* @private
		*/
		private _volume: number;

		/**
		* A boolean that controls whether the whole games audio should be muted or not.
		* @property _muted
		* @type boolean
		* @default false
		* @private
		*/
		private _muted: boolean;

		/**
		* An array containing all of the sounds on the game.
		* @property _sounds
		* @type Array
		* @private
		*/
		private _sounds: any;

		/**
		* The number of channels that are supported.
		* Not in much use at this point in time.
		* @property channels
		* @type number
		* @public
		*/
		public channels: number;

		/**
		* If the current game has audio support or not.
		* Useful because audio support is spotty in mobile browsers.
		* @property noAudio
		* @type boolean
		* @public
		*/
		public noAudio: boolean = false;

		/**
		* If the game is currently using the Web Audio API for the sound.
		* @property usingWebAudio
		* @type boolean
		* @public
		*/
		public usingWebAudio: boolean = false;

		/**
		* If the game is using audio tags for the sound. This is the fallback if the web audio api is not supported. 
		* @property usingAudioTag
		* @type boolean
		* @public
		*/
		public usingAudioTag: boolean = false;

		/**
		* Web Audio API ONLY - The audio context that is used for decoding audio, e.t.c. 
		* @property context
		* @type any
		* @public
		*/
		public context: any = null;

		/**
		* Web Audio API ONLY - The master gain node through which all sounds play.
		* @property masterGain
		* @type any
		* @public
		*/
		public masterGain: any = null;

		/**
		* The volume of the audio before it was muted. This is used so that when the audio is unmuted the volume will resume at this point.
		* @property _muteVolume
		* @type number
		* @private
		*/
		private _muteVolume: number;

		/**
		* Indicates if the sounds is currently 'locked' or not. 
		* If it is 'locked' then no audio can play until a user touch's the device.  
		* @property _locked
		* @type boolean
		* @private
		*/
		private _locked: boolean;

		/**
		* Sound buffer
		* @property _unlockedSource
		* @type {any}
		* @private
		*/
		private _unlockedSource: any = null;

		/**
		* Returns a boolean indicating whether the device has been touched or not. READ ONLY.
		* @property locked
		* @type boolean
		* @public
		*/
		public get locked():boolean {
			return this._locked;
		}

		/**
		* The boot method is executed when all of the DOM elements needed for the game are loaded and the game is ready to 'start' up.
		*
		* @method boot
		* @public
		*/
		public boot() {

			this._volume = 1;
			this._muted = false;
			this._sounds = [];

			//check to see if it is an iOS device and if it doesn't support webAudio
			if (Kiwi.DEVICE.iOS && Kiwi.DEVICE.webaudio == false) {
				this.channels = 1;
			}

			//Add mouse event here to 'unlock' the device.
			if (Kiwi.DEVICE.iOS && this._game.deviceTargetOption !== Kiwi.TARGET_COCOON) {
				this._locked = true;
				this._game.input.onUp.addOnce(this._unlocked, this);
				
				Kiwi.Log.log('Kiwi.AudioManager: Audio is currently Locked until at touch event.', '#audio', '#locked');
			} else {
				this._locked = false;
			}

			this.usingWebAudio = true;  //we hope for the best....
			this.usingAudioTag = false;

			if (!!window['AudioContext']) {//if audio context is supported
				this.context = new window['AudioContext']();
			}
			else if (!!window['webkitAudioContext']) {//if webkit audio context is supported
				this.context = new window['webkitAudioContext']();
			}
			else if (!!window['Audio']) {//no awesome audio support...so maybe the audio tags?
				this.usingWebAudio = false;
				this.usingAudioTag = true;
			}
			else {//they have no audio support........no sound for you :(
				this.usingWebAudio = false;
				this.noAudio = true; //prepared for the worst :(
			}

			if (this.context !== null) {

				if (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);
			}

		}

		/**
		* Is executed when a mouse event is fired on the device. This is used to enabled playback of sounds on the current device if they were awaiting for a user event.
		* @method _unlocked
		* @private
		*/
		private _unlocked() {
			Kiwi.Log.log('Kiwi.AudioManager: Audio now Unlocked.', '#audio', '#unlocked');

			if (this.usingAudioTag) {
				this._locked = false;
				for (var i = 0; i < this._sounds.length; i++) {
					this._sounds[i].playable = true;
				}
			} else {
				// Create empty buffer and play it
				var buffer = this.context.createBuffer(1, 1, 22050);
				this._unlockedSource = this.context.createBufferSource();
				this._unlockedSource.buffer = buffer;
                this._unlockedSource.connect(this.context.destination);

                if (this._unlockedSource.start === undefined) {
                    this._unlockedSource.noteOn(0);
                } else {
                    this._unlockedSource.start(0);
                }
			}
		}

		/**
		* Used to mute the audio on the device, or to check to see if the device is muted.
		* This will not stop audio from being played, will just mean that the audio is silent.
		* 
		* @property mute
		* @type boolean
		* @default false
		* @public
		*/
		public set mute(value: boolean) {
			
			if (value === true) {
				if (this._muted) return;
				this._muted = true;

				//mute the sounds 
				if (this.usingWebAudio) {
					this._muteVolume = this.masterGain.gain.value;
					this.masterGain.gain.value = 0;

				} else if (this.usingAudioTag) {
					for (var i = 0; i < this._sounds.length; i++) {
						this._sounds[i].mute = true;
					}
				}

			} else {
				if (this._muted == false) return;
				this._muted = false;

				if (this.usingWebAudio) {
					this.masterGain.gain.value = this._muteVolume;

				} else if (this.usingAudioTag) {
					for (var i = 0; i < this._sounds.length; i++) {
						this._sounds[i].mute = false;
					}
				}
			}

		}

		public get mute(): boolean {
			return this._muted;
		}

		/**
		* Global setting and getting of the volume.
		* A number between 0 (silence) and 1 (full volume).
		*
		* @property volume
		* @type number  
		* @default 1
		* @public
		*/
		public set volume(value: number) {

			if (value !== undefined) {

				value = Kiwi.Utils.GameMath.clamp(value, 1, 0);
				this._volume = value;

				if (this._muted) {
					this._muteVolume = this._volume;
				}

				if (this.usingWebAudio) {
					this.masterGain.gain.value = value;

				} else if (this.usingAudioTag) {
					for (var i = 0; i < this._sounds.length; i++) {
						//for each sound tag to update.
						this._sounds[i].volume = this._sounds[i].volume;
					}
				}

			}
		}
		public get volume(): number {

			return this._volume;

		}

		/**
		* Indicates whether or not an Audio Object is registered with this Audio Manager or not. For Kiwi Internal use only.
		* @method isRegistered
		* @param sound {Audio} The Audio object you are checking for.
		* @return {Boolean} If the piece of audio is registered or not.
		* @public
		*/
		public isRegistered(sound: Kiwi.Sound.Audio):boolean {
			if (this.noAudio) return; 

			if (this._sounds.indexOf(sound) !== -1) {
				return true;
			} else {
				return false;
			}
		}

		/**
		* Registers an Audio Object with this audio manager. Also assign's the audio piece a unique ID to identify it by. Internal Kiwi use only.
		* @method registerSound
		* @param sound {Audio} The audio piece you are wanting to register. 
		* @return { Boolean } Indication of if the sound was successfully added or not.
		* @public
		*/
		public registerSound(sound:Kiwi.Sound.Audio):boolean {
			if (this.isRegistered(sound) === false) {
				sound.id = this._game.rnd.uuid();
				this._sounds.push(sound);
				return true;
			}
			return false;
		}

		/**
		* Used to create a new sound on the audio manager. Returns the newly created sound.
		* 
		* @method add
		* @param key {string} The key for the audio file that is to be loaded from the AudioLibrary.
		* @param [volume=1] {number} The volume for the piece of audio.
		* @param [loop=false] {boolean} If the audio should keep repeat when it gets to the end.
		* @return {Kiwi.Sound.Audio}
		* @public
		*/
		public add(key: string, volume: number = 1, loop: boolean = false): Kiwi.Sound.Audio {//rename to create

			if (this.noAudio) return;

			var sound: Kiwi.Sound.Audio = new Kiwi.Sound.Audio(this._game, key, volume, loop);
			return sound;

		}

		/**
		* Removes the passed sound from the AudioManager.
		* If you remove a Audio Object from the AudioManager, then that Audio Object will not have a update loop.
		*
		* @method remove
		* @param sound {Kiwi.Sound.Audio} The Audio object that you want to remove.
		* @param [destory=true] If the Audio Object should be removed or not.
		* @public
		*/
		public remove(sound: Kiwi.Sound.Audio, destroy:boolean=true) {

			for (var i = 0; i < this._sounds.length; i++) {

				if (sound.id == this._sounds[i].id) {


					if (this.usingWebAudio && sound.gainNode) sound.gainNode.disconnect();

					if (destroy == true) sound.destroy();

					this._sounds.splice(i, 1);

					return;
				}

			}

		}

		/**
		* Plays all of the sounds listed in the AudioManager.
		* @method playAll
		* @public
		*/
		public playAll() {
			for (var i = 0; i < this._sounds.length; i++) {

				if (this._sounds[i]) {
					this._sounds[i].play();
				}

			}
		}

		/**
		* Stops all of the sounds that are listed in the AudioManager from playing.
		* @method stopAll
		* @public
		*/
		public stopAll() {

			for (var i = 0; i < this._sounds.length; i++) {

				if (this._sounds[i]) {
					this._sounds[i].stop();
				}

			}

		}

		/**
		* Pauses all of the sounds listed in the AudioManager.
		* @method pauseAll
		* @public
		*/
		public pauseAll() {

			for (var i = 0; i < this._sounds.length; i++) {

				if (this._sounds[i]) {
					this._sounds[i].pause();
				}

			}

		}

		/**
		* Resumes all of the sounds listed in the AudioManager. 
		* @method resumeAll
		* @public
		*/
		public resumeAll() {

			for (var i = 0; i < this._sounds.length; i++) {

				if (this._sounds[i]) {
					this._sounds[i].resume();
				}

			}

		}

		/**
		* The update loop that is executed every frame.
		* @method update
		* @public
		*/
		public update() {
			
			if (this._locked) {

				if (this.usingWebAudio && this._unlockedSource !== null) {
					if ((this._unlockedSource.playbackState === this._unlockedSource.PLAYING_STATE || this._unlockedSource.playbackState === this._unlockedSource.FINISHED_STATE))
					{
						this._locked = false;
						this._unlockedSource = null;
						for (var i = 0; i < this._sounds.length; i++) {
							this._sounds[i].playable = true;
						}
					}
				}

			}

			if (!this.noAudio) {
				for (var i = 0; i < this._sounds.length; i++) {
					this._sounds[i].update();
				}
			}

		}

	}

}