Any good sound management class available?

11 posts

Flag Post

I’m working on a game and Adobe’s AS3 sound management is a royal pain to work with. Anybody know a good class that makes it easy to play, loop, add, mute, and control the volume of multiple sounds easily? Thankies so much!

 
Flag Post

Shoop-de-woop

package dpbrad.utils 
{
	import com.greensock.plugins.SoundTransformPlugin;
	import dpbrad.math.FastMath;
	import flash.events.Event;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.utils.Dictionary;
	/**
	 * ...
	 * @author DPbrad
	 */
	public class Sfx 
	{
		
		public static var DEBUG:Boolean = false;
		
		public var complete:Function;
		
		public function Sfx(source:*, type:String=null, complete:Function=null):void
		{
			_type = type;
			if (source is Class) {
				_sound = _sounds[source];
				if (!_sound) _sound = _sounds[source] = new source;
			}
			else if (source is Sound) _sound = source;
			else throw new Error("Sfx source needs to be of type Class or Sound!");
			this.complete = complete;
		}
		
		/**
		 * Plays the sound once
		 * @param	vol			Volume factor, a value from 0 -> 1
		 * @param	pan			Panning factor, a value from -1 -> 1
		 */
		public function play(vol:Number = 1, pan:Number = 0):void
		{
			if (_channel) stop();
			_pan = FastMath.clampNumber(pan, -1, 1);
			_vol = vol < 0 ? 0: vol;
			_filteredPan = FastMath.clampNumber(_pan + getPan(_type), -1, 1);
			_filteredVol = Math.max(0, _vol * getVolume(_type));
			_transform.pan = _filteredPan;
			_transform.volume = _filteredVol;
			_channel = _sound.play(0, 0, _transform);
			if (_channel) {
				addPlaying();
				_channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
			}
			_looping = false;
			_position = 0;
			if (DEBUG) trace("play(" + vol + "," + pan + ")");
		}
		
		/**
		 * Will play the sound continuously until stop() or play() is called
		 * @param	vol
		 * @param	pan
		 */
		public function loop(vol:Number = 1, pan:Number = 0):void
		{
			play(vol, pan);
			_looping = true;
		}
		
		/**
		 * Stops the sound if it is playing
		 * @return Whether the music was stopped.
		 */
		public function stop():Boolean
		{
			if (!_channel) return false;
			removePlaying();
			_position = _channel.position;
			_channel.removeEventListener(Event.SOUND_COMPLETE, onComplete);
			_channel.stop();
			_channel = null
			return true;
		}
		
		/**
		 * Resume the sound from the last stop() position
		 */
		public function resume():void
		{
			_channel = _sound.play(_position, 0, _transform);
			if (_channel) {
				addPlaying();
				_channel.addEventListener(Event.SOUND_COMPLETE, onComplete);
			}
			_position = 0;
		}
		
		/** @private Event handler for sound completion. */
		private function onComplete(e:Event = null):void
		{
			if (_looping) loop(_vol, _pan);
			else stop();
			_position = 0;
			if (complete != null) complete();
		}
		
		/** @private Add the sound to the global list. */
		private function addPlaying():void
		{
			if (!_typePlaying[_type]) _typePlaying[_type] = new Dictionary();
			_typePlaying[_type][this] = this;
		}
		
		/** @private Removes the sound from the global list */
		private function removePlaying():void
		{
			if (_typePlaying[_type]) delete _typePlaying[_type][this];
		}
		
		/**
		 * Alter the sounds volume
		 */
		public function get volume():Number { return _vol; }
		public function set volume(value:Number):void
		{
			if (!_channel) return;
			if (value < 0) value = 0;
			var filteredVol:Number = value * getVolume(_type);
			if (filteredVol < 0) filteredVol = 0;
			if (_filteredVol === filteredVol) return;
			_vol = value;
			_filteredVol = _transform.volume = filteredVol;
			_channel.soundTransform = _transform;
		}
		
		/**
		 * Alter the sounds panning
		 */
		public function get pan():Number { return _pan; }
		public function set pan(value:Number):void
		{
			if (!_channel) return;
			value = FastMath.clampNumber(value, -1, 1);
			var filteredPan:Number = FastMath.clampNumber(value + getPan(_type), -1, 1);
			if (_filteredPan === filteredPan) return;
			_pan = value;
			_filteredPan = _transform.pan = filteredPan;
			_channel.soundTransform = _transform;
		}
		
		/**
		 * Change the sound type
		 */
		public function get type():String { return _type; }
		public function set type(value:String):void
		{
			if (_type == value) return;
			if (_channel) {
				removePlaying();
				_type = value;
				addPlaying();
				pan = pan;
				volume = volume;
			} else {
				_type = value;
			}
		}
		
		/**
		 * Returns true if the sound is playing
		 */
		public function get playing():Boolean { return _channel != null; }
		
		/**
		 *  Returns the position of the Sfx object in seconds
		 */
		public function get position():Number { return (_channel ? _channel.position : _position) / 1000;}
		
		/**
		 * Returns the length of the Sfx object in seconds
		 */
		public function get length():Number { return _sound.length / 1000; }
		
		/**
		 * Returns the pan of a Sfx type
		 * @param	type	Type to check the pan of
		 * @return	Pan of the type
		 */
		static public function getPan(type:String):Number
		{
			var transform:SoundTransform = _typeTransforms[type];
			return transform ? transform.pan : 0;
		}
		
		/**
		 * Returns the volume of a Sfx type
		 * @param	type	Type to check the volume of
		 * @return	Volume of the type
		 */
		static public function getVolume(type:String):Number
		{
			var transform:SoundTransform = _typeTransforms[type];
			return transform ? transform.volume : 1;
		}
		
		/**
		 * Set the global pan for a Sfx type
		 * @param	type		Type to set the pan of
		 * @param	pan			Pan to set
		 */
		static public function setPan(type:String, pan:Number):void
		{
			var transform:SoundTransform = _typeTransforms[type];
			if (!transform) transform = _typeTransforms[type] = new SoundTransform();
			transform.pan = FastMath.clampNumber(pan, -1, 1);
			for each(var sfx:Sfx in _typePlaying[type]) {
				sfx.pan = sfx.pan;
			}
		}
		
		/**
		 * Set the global volume for a Sfx type
		 * @param	type		Type to set the volume of
		 * @param	vol			Volume to set
		 */
		static public function setVolume(type:String, volume:Number):void
		{
			var transform:SoundTransform = _typeTransforms[type];
			if (!transform) transform = _typeTransforms[type] = new SoundTransform();
			transform.volume = volume < 0? 0: volume;
			for each(var sfx:Sfx in _typePlaying[type]) {
				sfx.volume = sfx.volume;
			}
		}
		
		/**
		 * Stops all sounds of the type
		 * @param	type	Type of sounds to stop
		 * @return	Returns the amount of sounds stopped
		 */
		static public function stopType(type:String):int
		{
			var c:int = 0;
			for each(var sfx:Sfx in _typePlaying[type]) {
				if (sfx.playing) { sfx.stop(); c++ }
			}
			return c;
		}
		
		
		
		private var _type:String;
		private var _vol:Number = 1;
		private var _pan:Number = 0;
		private var _filteredVol:Number;
		private var _filteredPan:Number;
		private var _sound:Sound;
		private var _channel:SoundChannel;
		private var _transform:SoundTransform = new SoundTransform();
		private var _position:Number = 0;
		private var _looping:Boolean;
		
		private static var _sounds:Dictionary = new Dictionary;
		private static var _typePlaying:Dictionary = new Dictionary;
		private static var _typeTransforms:Dictionary = new Dictionary;
		
	}

}

This is my own personal Sound class. You use it like so;

([Embed] SoundWhatever) public static var SOUND_OMFG:Class;
var music:Sfx = new Sfx(SOUND_OMFG);

You can define a “type” of the sound too, so say you made a load of sound effects, you can pass a parameter called “SoundFX” for example, then use a static function Sfx.setVolume(“SoundFX”,.5) to set all the sounds of type “SoundFX” to .5

I made it a while ago so can’t fully remember all the stuff it can do. Feel free to modify it.

EDIT: Oops, contains two external libraries. You get the idea though….I hope.

 
Flag Post

Actually im going to clean this up tomorrow because I actually have a new version in my DropBox but I need to clean it up a bit. Still, if you know what your doing, it would only take 10/15 minutes to get this working nicely.

 
Flag Post

Thanks a ton! Could I get the FastMath.as by any chance?

 
Flag Post

Voila

package dpbrad.math 
{
	/**
	 * ...
	 * @author DPbrad
	 */
	public class FastMath 
	{
		
		public static function isBetween(value:int, min:int, max:int):Boolean
		{
			if ((value >= min) && (value < max)) return true;
			else return false;
		}
		
		public static function randomInt(min:int, max:int):int
		{
			return (Math.floor(Math.random() * (max - min + 1)) + min);  
		}
		
		public static function clampNumber(value:Number, min:Number, max:Number):Number
		{
			if (value < min) return min;
			if (value > max) return max;
			return value;
		}
		
		public static function clampInt(value:int, min:int, max:int):int
		{
			if (value < min) return min;
			if (value > max) return max;
			return value;
		}
		
		public static function wrapInt(value:int, min:int, max:int):int
		{
			var modulo:int = value % max;
			return min + modulo;
		}
		
	}

}
 
Flag Post

You are my hero, this works perfectly for me. <3

 
Flag Post

cool, what’s the wrapInt function doing?

 
Flag Post

It would wrap a value around until it is back inside the min/max range.
I have no idea what I used it for before.

 
Flag Post

nope

trace(wrapInt(5, 6, 7)); // 11
trace(wrapInt(15, 10, 20)); // 25 
trace(wrapInt(110, 100, 101)); // 109
 
Flag Post
Originally posted by NineFiveThree:

nope

trace(wrapInt(5, 6, 7)); // 11
trace(wrapInt(15, 10, 20)); // 25 
trace(wrapInt(110, 100, 101)); // 109

Well it’s useless then! Haha, well some poor client has a broken application that doesnt wrap integers properly. Oh dear. :)

EDIT: Also, with regards to the Sfx class above, I am in the process of updating it right now to get rid of dependencies on other classes, and also improve some stuff and add new things.

 
Flag Post

Maybe something like this:

function wrapInt(value:int, min:int, max:int):int {
	if(value < min) return min + (max - min) % value;
	else if(value > max) return min + value % (max - min)
	else return value
}