All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.flixel.FlxSound Maven / Gradle / Ivy

The newest version!
package org.flixel;

import org.flixel.system.gdx.audio.GdxMusic;
import org.flixel.system.gdx.audio.GdxSound;

import flash.events.Event;
import flash.events.IEventListener;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;

/**
 * This is the universal flixel sound object, used for streaming, music, and sound effects.
 * 
 * @author Ka Wing Chin
 * @author Thomas Weston
 */
public class FlxSound extends FlxBasic
{
	/**
	 * Automatically determine the type of file.
	 */
	static public final int AUTO = 0;
	/**
	 * A short audio clip.
	 */
	static public final int SFX = 1;
	/**
	 * A large music file.
	 */
	static public final int MUSIC = 2;

	/**
	 * The X position of this sound in world coordinates.
	 * Only really matters if you are doing proximity/panning stuff.
	 */
	public float x;
	/**
	 * The Y position of this sound in world coordinates.
	 * Only really matters if you are doing proximity/panning stuff.
	 */
	public float y;
	/**
	 * Whether or not this sound should be automatically destroyed when you switch states.
	 */
	public boolean survive;
	/**
	 * The ID3 song name.  Defaults to null.  Currently only works for streamed sounds.
	 */
	public String name;
	/**
	 * The ID3 artist name.  Defaults to null.  Currently only works for streamed sounds.
	 */
	public String artist;
	/**
	 * Stores the average wave amplitude of both stereo channels
	 */
	public float amplitude;
	/**
	 * Just the amplitude of the left stereo channel
	 */
	public float amplitudeLeft;
	/**
	 * Just the amplitude of the left stereo channel
	 */
	public float amplitudeRight;
	/**
	 * Whether to call destroy() when the sound has finished.
	 */
	public boolean autoDestroy;

	/**
	 * Internal tracker for a Flash sound object.
	 */
	protected Sound _sound;
	/**
	 * Internal tracker for a Flash sound channel object.
	 */
	protected SoundChannel _channel;
	/**
	 * Internal tracker for a Flash sound transform object.
	 */
	protected SoundTransform _transform;
	/**
	 * Internal tracker for the position in runtime of the music playback.
	 */
	protected float _position;
	/**
	 * Internal tracker for how loud the sound is.
	 */
	protected float _volume;
	/**
	 * Internal tracker for total volume adjustment.
	 */
	protected float _volumeAdjust;
	/**
	 * Internal tracker for how fast or how slow the sound is.
	 */
	private float _pitch;
	/**
	 * Internal tracker for whether the sound is looping or not.
	 */
	protected boolean _looped;
	/**
	 * Internal tracker for whether the sound is paused by focus lost.
	 */
	boolean _isPausedOnFocusLost;
	/**
	 * Internal tracker for the sound's "target" (for proximity and panning).
	 */
	protected FlxObject _target;
	/**
	 * Internal tracker for the maximum effective radius of this sound (for proximity and panning).
	 */
	protected float _radius;
	/**
	 * Internal tracker for whether to pan the sound left and right.  Default is false.
	 */
	protected boolean _pan;
	/**
	 * Internal timer used to keep track of requests to fade out the sound playback.
	 */
	protected float _fadeOutTimer;
	/**
	 * Internal helper for fading out sounds.
	 */
	protected float _fadeOutTotal;
	/**
	 * Internal flag for whether to pause or stop the sound when it's done fading out.
	 */
	protected boolean _pauseOnFadeOut;
	/**
	 * Internal timer for fading in the sound playback.
	 */
	protected float _fadeInTimer;
	/**
	 * Internal helper for fading in sounds.
	 */
	protected float _fadeInTotal;

	/**
	 * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet.
	 */
	public FlxSound()
	{
		super();
		createSound();
	}

	/**
	 * An internal function for clearing all the variables used by sounds.
	 */
	protected void createSound()
	{
		destroy();
		x = 0;
		y = 0;
		if(_transform == null)
			_transform = new SoundTransform();
		_transform.pan = 0f;
		_sound = null;
		_position = 0;
		_volume = 1.0f;
		_volumeAdjust = 1.0f;
		_pitch = 1.0f;
		_looped = false;
		_target = null;
		_radius = 0;
		_pan = false;
		_fadeOutTimer = 0;
		_fadeOutTotal = 0;
		_pauseOnFadeOut = false;
		_fadeInTimer = 0;
		_fadeInTotal = 0;
		exists = false;
		active = false;
		visible = false;
		name = null;
		artist = null;
		amplitude = 0;
		amplitudeLeft = 0;
		amplitudeRight = 0;
		autoDestroy = false;
		survive = false;
	}

	/**
	 * Clean up memory.
	 */
	public void destroy()
	{
		kill();

		_transform = null;
		_sound = null;
		_channel = null;
		_target = null;
		name = null;
		artist = null;
		
		super.destroy();
	}

	/**
	 * Handles fade out, fade in, panning, proximity, and amplitude operations each frame.
	 */
	@Override
	public void update()
	{
		float radial = 1.0f;
		float fade = 1.0f;
		
		//Distance-based volume control
		if(_target != null)
		{
			radial = 1 - FlxU.getDistance(new FlxPoint(_target.x,_target.y),new FlxPoint(x,y))/_radius;
			if(radial < 0) radial = 0;
			if(radial > 1) radial = 1;

			if(_pan)
			{
				float d = (x-_target.x)/_radius;
				if(d < -1) d = -1;
				else if(d > 1) d = 1;
				_transform.pan = d;
			}
		}

		//Cross-fading volume control
		if(_fadeOutTimer > 0)
		{
			_fadeOutTimer -= FlxG.elapsed;
			if(_fadeOutTimer <= 0)
			{
				if(_pauseOnFadeOut)
					pause();
				else
					stop();
			}
			fade = _fadeOutTimer/_fadeOutTotal;
			if(fade < 0) fade = 0;
		}
		else if(_fadeInTimer > 0)
		{
			_fadeInTimer -= FlxG.elapsed;
			fade = _fadeInTimer/_fadeInTotal;
			if(fade < 0) fade = 0;
			fade = 1 - fade;
		}

		_volumeAdjust = radial*fade;
		updateTransform();

		if((_transform.volume > 0) && (_channel != null))
		{
			amplitudeLeft = _channel.getLeftPeak()/_transform.volume;
			amplitudeRight = _channel.getRightPeak()/_transform.volume;
			amplitude = (amplitudeLeft+amplitudeRight)*0.5f;
		}
	}

	@Override
	public void kill()
	{
		super.kill();
		if(_channel != null)
			stop();
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from an embedded MP3.
	 * 
	 * @param	EmbeddedSound	An embedded Class object representing an MP3 file.
	 * @param	Looped			Whether or not this sound should loop endlessly.
	 * @param	AutoDestroy		Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default.
	 * @param	Type			Whether this sound is a sound effect or a music track.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadEmbedded(String EmbeddedSound, boolean Looped, boolean AutoDestroy, int Type)
	{
		stop();
		createSound();

		switch(Type)
		{
			case SFX:
				_sound = new GdxSound(EmbeddedSound);
				break;

			case MUSIC:
				_sound = new GdxMusic(EmbeddedSound);
				break;

			case AUTO:
			default:
				//If the type is not specified, make a guess based on the file size.
				FileHandle file = Gdx.files.internal(EmbeddedSound);
				Type = file.length() < 24576 ? SFX : MUSIC;
				return loadEmbedded(EmbeddedSound, Looped, AutoDestroy, Type);
		}

		//NOTE: can't pull ID3 info from embedded sound currently
		_looped = Looped;
		autoDestroy = AutoDestroy;
		updateTransform();
		exists = true;
		return this;
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from an embedded MP3.
	 * 
	 * @param	EmbeddedSound	An embedded Class object representing an MP3 file.
	 * @param	Looped			Whether or not this sound should loop endlessly.
	 * @param	AutoDestroy		Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadEmbedded(String EmbeddedSound, boolean Looped, boolean AutoDestroy)
	{
		return loadEmbedded(EmbeddedSound, Looped, AutoDestroy, AUTO);
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from an embedded MP3.
	 * 
	 * @param	EmbeddedSound	An embedded Class object representing an MP3 file.
	 * @param	Looped			Whether or not this sound should loop endlessly.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadEmbedded(String EmbeddedSound, boolean Looped)
	{
		return loadEmbedded(EmbeddedSound, Looped, false, AUTO);
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from an embedded MP3.
	 * 
	 * @param	EmbeddedSound	An embedded Class object representing an MP3 file.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadEmbedded(String EmbeddedSound)
	{
		return loadEmbedded(EmbeddedSound, false, false, AUTO);
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from a URL.
	 * 
	 * @param	SoundURL	A string representing the URL of the MP3 file you want to play.
	 * @param	Looped		Whether or not this sound should loop endlessly.
	 * @param	AutoDestroy	Whether or not this FlxSound instance should be destroyed when the sound finishes playing.  Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	// TODO: Load a sound from an URL
	public FlxSound loadStream(String SoundURL, boolean Looped, boolean AutoDestroy)
	{
		stop();
		createSound();
		// _sound = new Sound();
		// _sound.addEventListener(Event.ID3, gotID3);
		// _sound.load(new URLRequest(SoundURL));
		_looped = Looped;
		autoDestroy = AutoDestroy;
		updateTransform();
		exists = true;
		return this;
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from a URL.
	 * 
	 * @param	SoundURL	A string representing the URL of the MP3 file you want to play.
	 * @param	Looped		Whether or not this sound should loop endlessly.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadStream(String SoundURL, boolean Looped)
	{
		return loadStream(SoundURL, Looped, false);
	}

	/**
	 * One of two main setup functions for sounds, this function loads a sound from a URL.
	 * 
	 * @param	SoundURL	A string representing the URL of the MP3 file you want to play.
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound loadStream(String SoundURL)
	{
		return loadStream(SoundURL, false, false);
	}

	/**
	 * Call this function if you want this sound's volume to change
	 * based on distance from a particular FlxCore object.
	 * 
	 * @param	X		The X position of the sound.
	 * @param	Y		The Y position of the sound.
	 * @param	Object	The object you want to track.
	 * @param	Radius	The maximum distance this sound can travel.
	 * @param	Pan		Whether the sound should pan in addition to the volume changes (default: true).
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound proximity(float X,float Y,FlxObject Object,float Radius,boolean Pan)
	{
		x = X;
		y = Y;
		_target = Object;
		_radius = Radius;
		_pan = Pan;
		return this;
	}

	/**
	 * Call this function if you want this sound's volume to change
	 * based on distance from a particular FlxCore object.
	 * 
	 * @param	X		The X position of the sound.
	 * @param	Y		The Y position of the sound.
	 * @param	Object	The object you want to track.
	 * @param	Radius	The maximum distance this sound can travel.
	 * @param	Pan		Whether the sound should pan in addition to the volume changes (default: true).
	 * 
	 * @return	This FlxSound instance (nice for chaining stuff together, if you're into that).
	 */
	public FlxSound proximity(float X,float Y,FlxObject Object,float Radius)
	{
		return proximity(X,Y,Object,Radius,true);
	}

	/**
	 * Call this function to play the sound - also works on paused sounds.
	 * 
	 * @param	ForceRestart	Whether to start the sound over or not.  Default value is false, meaning if the sound is already playing or was paused when you call play(), it will continue playing from its current position, NOT start again from the beginning.
	 */
	public void play(boolean ForceRestart)
	{
		if(_position < 0)
			return;
		if(ForceRestart)
		{
			boolean oldAutoDestroy = autoDestroy;
			autoDestroy = false;
			stop();
			autoDestroy = oldAutoDestroy;
		}
		if(_looped)
		{
			if(_position == 0)
			{
				if(_channel == null)
					_channel = _sound.play(0f,9999,_transform);
				if(_channel == null)
					exists = false;
				else
					_channel.addEventListener(Event.SOUND_COMPLETE, stoppedListener);
			}
			else
				_channel.resume();
		}
		else
		{
			if(_position == 0)
			{
				if(_channel == null)
				{
					_channel = _sound.play(0f,0,_transform);
					if(_channel == null)
						exists = false;
					else
						_channel.addEventListener(Event.SOUND_COMPLETE, stoppedListener);
				}
			}
			else
				_channel.resume();
		}

		active = (_channel != null);
		_position = 0;
	}

	/**
	 * Call this function to play the sound - also works on paused sounds.
	 */
	public void play()
	{
		play(false);
	}

	/**
	 * Unpause a sound. Only works on sounds that have been paused.
	 */
	public void resume()
	{
		if(_position <= 0)
			return;
		_channel.resume();
		active = (_channel != null);
	}

	/**
	 * Call this function to pause this sound.
	 */
	public void pause()
	{
		if(_channel == null)
		{
			_position = -1;
			return;
		}
		_position = 1;
		_channel.pause();
		active = false;
	}

	/**
	 * Call this function to stop this sound.
	 */
	public void stop()
	{
		_position = 0;
		if(_channel != null)
		{
			_channel.stop();
			stopped();
		}
	}

	/**
	 * Call this function to make this sound fade out over a certain time interval.
	 * 
	 * @param	Seconds			The amount of time the fade out operation should take.
	 * @param	PauseInstead	Tells the sound to pause on fadeout, instead of stopping.
	 */
	public void fadeOut(float Seconds,boolean PauseInstead)
	{
		_pauseOnFadeOut = PauseInstead;
		_fadeInTimer = 0;
		_fadeOutTimer = Seconds;
		_fadeOutTotal = _fadeOutTimer;
	}

	/**
	 * Call this function to make this sound fade out over a certain time interval.
	 * 
	 * @param	Seconds			The amount of time the fade out operation should take.
	 */
	public void fadeOut(float Seconds)
	{
		fadeOut(Seconds,false);
	}

	/**
	 * Call this function to make a sound fade in over a certain
	 * time interval (calls play() automatically).
	 * 
	 * @param	Seconds		The amount of time the fade-in operation should take.
	 */
	public void fadeIn(float Seconds)
	{
		_fadeOutTimer = 0;
		_fadeInTimer = Seconds;
		_fadeInTotal = _fadeInTimer;
		play();
	}

	/**
	 * Set volume to a value between 0 and 1 to change how this sound is.
	 */
	public float getVolume()
	{
		return _volume;
	}

	/**
	 * Set volume to a value between 0 and 1 to change how this sound is.
	 * 
	 * @param	Volume	The volume of the sound.
	 */
	public void setVolume(float Volume)
	{
		_volume = Volume;
		if(_volume < 0)
			_volume = 0;
		else if(_volume > 1)
			_volume = 1;
		updateTransform();
	}

	/**
	 * Returns the currently selected "real" volume of the sound (takes fades
	 * and proximity into account).
	 * 
	 * @return	The adjusted volume of the sound.
	 */
	public float getActualVolume()
	{
		return _volume*_volumeAdjust;
	}

	/**
	 * Set the pitch multiplier, 1 == default, >1 == faster, <1 == slower.
	 * The value has to be between 0.5 and 2.0.
	 * 
	 * @param	Pitch	The pitch multiplier.
	 */
	public void setPitch(float Pitch)
	{
		_pitch = Pitch;
		if(_pitch < 0)
			_pitch = 0;
		else if(_pitch > 2)
			_pitch = 2;
		updateTransform();
	}

	/**
	 * Returns the current pitch of the sound.
	 * 
	 * @return	The pitch of the sound.
	 */
	public float getPitch()
	{
		return _pitch;
	}

	/**
	 * Call after adjusting the volume to update the sound channel's settings.
	 */
	protected void updateTransform()
	{
		_transform.volume = (FlxG.mute?0:1)*FlxG.getVolume()*_volume*_volumeAdjust;
		_transform.pitch = _pitch;
		if(_channel != null)
			_channel.setSoundTransform(_transform);
	}

	/**
	 * An internal helper function used to help Flash clean up and re-use finished sounds.
	 */
	protected void stopped()
	{
		_channel.removeEventListener(Event.SOUND_COMPLETE,stoppedListener);
		_channel = null;
		active = false;
		_isPausedOnFocusLost = false;
		if(autoDestroy)
			destroy();
	}

	/**
	 * Internal event handler for ID3 info (i.e. fetching the song name).
	 */
	// TODO: ID3 info
	protected void gotID3()
	{
		/*
		 * FlxG.log("got ID3 info!"); if(_sound.id3.songName.length > 0) name =
		 * _sound.id3.songName; if(_sound.id3.artist.length > 0) artist =
		 * _sound.id3.artist; _sound.removeEventListener(Event.ID3, gotID3);
		 */
	}
	
	/**
	 * Internal event listener.
	 */
	protected final IEventListener stoppedListener = new IEventListener()
	{
		@Override
		public void onEvent(Event e)
		{
			stopped();
		}
	};
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy