A Flash Developer Resource Site

Results 1 to 16 of 16

Thread: [Source/Feedback] AS2 sound management class

Hybrid View

  1. #1
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477

    [Source/Feedback] AS2 sound management class

    Hey guys. For the first time in ages I've got the point of putting sound in a game (oh my!), so I decided to actually go all out and use a 'proper' sound management class for the event. I started by searching the various internets, including FK for clues or hell, even a full blown source, and found that there was surprisingly little out there on the subject.

    So I wrote my own.

    I figure if I had trouble finding resources, especially specific for games, then some of you guys must also. So I'm going to share my code with you guys, feel free to make use of it and if you make any changes or additions I'd appreciate if you posted them here, if that's not too much to ask

    Keep in mind this is only designed to handle sound effects, not music loops, cross fading or anything like that. There's plenty more you could add to this, but I wanted to keep it at just the basics for now: play, volume and pan. It's written as an AS2 class, so to use it just copy from below, paste into soundEngine.as and put it with the rest of your classes.

    the code:
    Code:
    class soundEngine {
    	// declaring our variables
    	// soundsArray: this array stores all the soundFX used in the game
    	private var soundsArray:Array = [];
    	// soundChannels: this array stores the currently playing sounds and acts
    	// as a cache for recently played sounds
    	private var soundChannels:Array = [];
    	// number of channels: this is how many sound channels we will use, you can
    	// add more or less as per your needs. I'm going with 16.
    	private var noOfChannels:Number = 16;
    	
    	
    	// function is called to initiate the sound engine
    	public function initSounds (){
    		// populate soundsArray the first value is the sound's linkageID the second value
    		// is the name we will refer to it in the engine. This allows us to swap out sound 
    		// effects without affecting previously written code.
    		soundsArray = [["door_00.WAV","doorToggle"],["switch_00.WAV","switchToggle"]]; // and so on...
    		// clear the channels
    		soundChannels = [];
    		
    		// create the channel MC's. Because flash needs a unique MC container to give 
    		// sounds objects their own pan/volume/etc properties. go figure.
    		_root.createEmptyMovieClip("sounds", 9);
    		for (var i=0; i < noOfChannels; i++){
    			_root.sounds.createEmptyMovieClip("channel"+i, i);
    		}
    			
    		
    	}
    	
    	// this will be the function we call to play a sound. Pramaters required are:
    	// id: this is the name we give each soundFX in the soundsArray (see above)
    	// vol and pan are pretty straight forward
    	public function playSound (id,vol,pan){
    		var done:Boolean = false;
    		// first we'll check if there's a copy of the desired sound in an open channel
    		for (var i in soundChannels){
    			if (soundChannels[i][0] == id & soundChannels[i][1].position == soundChannels[i][1].duration){
    				// we've found one and it's not playing, excellent, all we have to
    				// do is play it and we're done.
    				doSound(i,vol,pan);
    				done=true;
    				break
    			}
    		}
    		if (!done){
    			// well we didn't find the sound already, lets create one.
    			// first we'll make sure there's room in the channels array
    			if (soundChannels.length >= noOfChannels) {
    				// there's not, so we remove the first one which isn't playing
    				for (var j in soundChannels){
    					if(soundChannels[j][1].position == soundChannels[j][1].duration){
    						soundChannels.splice(j,1);
    						done=true;
    						break
    					}
    				}				
    			}
    			// start by finding the desired soundFX in the soundsArray
    			for (var i in soundsArray){
    				if (soundsArray[i][1] == id){
    					// it's there, now we create the new sound
    					var n = soundChannels.push([id]) - 1; // push it's name to the channels array
    					soundChannels[n][1]= new Sound(_root.sounds["channel"+n]); // create the sound
    					soundChannels[n][1].attachSound(soundsArray[i][0]); // and attach it
    					doSound(n,vol,pan); // now play
    					break
    				}
    			}
    		}
    	}
    	
    	// function sets the volume, pan then plays the desired sound
    	private function doSound(i,vol,pan){
    		var channel = soundChannels[i][1];
    		channel.setVolume(vol ? vol : 100);
    		channel.setPan(pan ? pan : 0);
    		channel.start();
    	}
    	
    	
    	
    }
    and now a code snipet of how to use it:
    Code:
    // on your main timeline
    var soundUtil = new soundEngine;
    soundUtil.initSounds();
    Code:
    // and later on in your game code
    
    _root.soundUtil.playSound("doorToggle",100,0);
    // and so on...
    This will play the sound we titled "doorToggle" at 100 volume paned to the center. Easy

  2. #2
    2KHeroes / Sylvaniah designer luxregina's Avatar
    Join Date
    Jul 2001
    Location
    Somewhere between Kirlundin and Anskaven
    Posts
    1,273
    Very nice : I was in the process of writting one for my own use, but I'll use yours and let you know how it behaves

  3. #3
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    Excellent! I did find a bug however, it's nothing big but will cause problems if you really push the system. It's caused by having every channel utilized when trying to open a new channel, since the cleanup code I have in there won't remove anything it will then try to add the sound to a non-existant channel. Luckily it's easily fixed.

    I won't post all the code again, just the fix (that non-editing rule is annoying):

    find the line that say:
    if (soundChannels.length >= noOfChannels) {

    and replace that entire statement with:
    Code:
    			// first we'll make sure there's room in the channels array
    			if (soundChannels.length >= noOfChannels) {
    				// there's not, so we remove the first one which isn't playing
    				done = false;
    				for (var j in soundChannels){
    					if(soundChannels[j][1].position == soundChannels[j][1].duration){
    						soundChannels.splice(j,1);
    						done=true;
    						break
    					}
    				}
    				// this is for when all the channels are currently in use. we stop the one at the 
    				// beginning and remove it. it's probably been there the longest anyway.
    				if(!done){
    					soundChannels[0][1].stop();
    					soundChannels.shift();
    				}
    			}

  4. #4
    M.D. mr_malee's Avatar
    Join Date
    Dec 2002
    Location
    Shelter
    Posts
    4,139
    nice, in my SoundUtility i don't scan channels and things like that. I keep the Utility very manual, in my experience I've had very different uses for sound in my games and there was a time when the Utility was changing to accommodate these weird needs, the solution was to minimize automation.

    You might want to consider adding a Boolean to test whether you actually want a sound to be searched, things like bullet fire could slow your code down with all the looping and array shifting/ pushing. But i do like the idea, never thought of it before.

    Here's mine if you care.
    lather yourself up with soap - soap arcade

  5. #5
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    Quote Originally Posted by mr_malee
    You might want to consider adding a Boolean to test whether you actually want a sound to be searched, things like bullet fire could slow your code down with all the looping and array shifting/ pushing. But i do like the idea, never thought of it before.
    I thought bullet fire would be a problem too, but because I'm (kind of) caching sounds in the channelsArray, after the first burst with a new weapon there will several copies of the sound ready to use. The next time you shoot, all it has to do is iterate through the channelsArray until it finds one not currently playing. No shifting or pushing involved.

    If that makes any sense. It sounds a lot more clearer in my head, but I know it works. I have a gun in my engine that fires on every second frame and on each fire it plays a short sound without any slowdown or additional code overhead.

    Edit: Sorry ever, I have no idea how to help you with that. As mr malee said, why use html for sound? what are the benefits?

  6. #6
    Truimagz.com everfornever's Avatar
    Join Date
    Sep 2006
    Location
    St. Louis
    Posts
    1,306
    I think this is great for in game sounds. Thanks a bunch, I will certainly look itno it.

    One question though, I am using sound loops in an external library, and communicating with them to play through the html page itslef, instead of storing them in game, for all world sounds I am speaking of.

    Do you know of a way when using sounds in this way to send commands to the music.mp3 to increase or decreas its fvolume? I am having a hrad time with this.

    I have different background sounds that play for many different parts of a zone, and I dont like that it just cuts off when the new one is loaded. I need a way to fade out.

    Just to clarify, I do know how to accomplish this with html operations and or javascript, but I cant figure out how to do it through flash to tell it to, I have tried using vars and using sendandload to set there parameters, but this gets pretty icky when usin alot of sounds, I hate having to sendand load var sound = false, var sound2 =false; var sound3 = false var sound4 = true, everytime I need a new sound fade out function. I have about 50 so far.
    Last edited by everfornever; 03-21-2007 at 07:32 PM.

  7. #7
    M.D. mr_malee's Avatar
    Join Date
    Dec 2002
    Location
    Shelter
    Posts
    4,139
    why do you need to use the HTML?

    i can't help there, but usually i use an external swf which is exported with the SoundClass inside at and all the sounds needed, then from the Main swf:

    load sounds.swf into soundClip

    play sound like:

    soundClip.soundUtility.playSound("sound")

    or reference the function which is faster

    _global.playSound = soundClip.soundUtility.playSound
    lather yourself up with soap - soap arcade

  8. #8
    M.D. mr_malee's Avatar
    Join Date
    Dec 2002
    Location
    Shelter
    Posts
    4,139
    nice, its a good idea, never thought about caching sounds.

    Sounds are something i think about and deal with last in any project. Don't know why?
    lather yourself up with soap - soap arcade

  9. #9
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    Quote Originally Posted by mr_malee
    Sounds are something i think about and deal with last in any project. Don't know why?
    Probably because handling sound in flash is so clunky and unwieldy. What I really don't get is why we have to attach sound files to a movie clip in order to give them their own properties. I'm curious if any AS3 adventurers can tell me if it's much more usable in AS3?

  10. #10
    Member
    Join Date
    Mar 2007
    Location
    Long Beach, CA
    Posts
    38
    scripting sound is fantastic.

  11. #11
    Member
    Join Date
    Mar 2007
    Location
    Long Beach, CA
    Posts
    38
    Man, I can't wait to get my hands on those synth capabilities, and equalizing and all that nonsense.

    Just being able to play sounds reliably and in perfect sync will be nice.

  12. #12
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    @Rockytastic: Tell me about it! I just hope it fast enough to use in a real world environment, unlike many of flash's other 'features'

    In other news, I'm polishing off a pretty big update to the code above which fixes many bugs and streamlines the caching system. I've also figured out a neat and very simple way of handling looping sounds (such as bullets, not music handling.. that will come soon): the playSound() function now has an extra "loop" boolean parameter, and will return the channel that the requested sound has been assigned. To stop a looping sound, your code simply has to keep track of which channel is playing the loop and call a function in the soundUtil to stop that channel.

    And lastly, a general sound question. When panning sound, should sound be based on where the character is, or where the camera is? For example, if the character is located on the far left side of the screen and a sound occurs right on top of him should it come from the center of the pan, or to the left? Is this different for scrolling games vs fixed camera games? Something to think about...

  13. #13
    Member
    Join Date
    Mar 2007
    Location
    Long Beach, CA
    Posts
    38
    I think the sound ought to be relative to the camera. It will help the player use their own sense of direction.

  14. #14
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    As promised, an update!

    I've included a small demonstration of how to use the sound manager, fixed plenty of bugs and optimised the system quite a bit. See the attached file.

    Any questions will be gladly answered and feel free to may use of this in any way you wish!
    Attached Files Attached Files

  15. #15
    Student
    Join Date
    Apr 2001
    Location
    -
    Posts
    4,756
    would you mind posting the code here as well - so that it gets even easier and faster to get it- even in the future when that attachment maybe isn´t valid any more, thx anyway- might check it once it if I get back to something that involves sound

  16. #16
    Filthy Gorgeous DancingOctopus's Avatar
    Join Date
    Sep 2003
    Location
    Sunny Australia.
    Posts
    477
    no probs mate.

    Code:
    class soundEngine {
    	// noOfChannels, change this variable to the number of channels you wish to utilize.
    	// a higher value here means more sounds will be able to play at once, and also means
    	// more sounds will be cached, reducing to number of lookups to the soundsArray.
    	private var noOfChannels:Number = 24;
    	private var soundsArray:Array = [];
    	private var soundChannels:Array = [];
    	private var j:Number=0;
    	
    	// function is called to initiate the sound engine
    	public function initSounds (){
    		soundsArray = [["sfx_bell.wav","sound1"],
    						["sfx_powerup.wav","sound2"],
    						["sfx_loop.mp3","loop"]];
    		
    		soundChannels = [];
    		
    		_root.createEmptyMovieClip("sounds", 9);
    		for (var i=0; i < noOfChannels; i++){
    			_root.sounds.createEmptyMovieClip("channel"+i, i);
    		}
    			
    		
    	}
    	
    	// this will be the function we call to play a sound. Pramaters required are:
    	// id: this is the name we give each soundFX in the soundsArray (see above)
    	// vol and pan are pretty straight forward
    	// returns the channel the requested sound is assigned
    	public function playSound (id:String,vol:Number,pan:Number,loop:Boolean):Number{
    		var done:Boolean = false;
    		if(loop){ var loopVar = 9999;
    		} else { var loopVar = 0; }
    		// check if there's a spare cached instance
    		for (var i in soundChannels){
    			if (soundChannels[i][0] == id & soundChannels[i][1].position == soundChannels[i][1].duration){
    				//trace ("cache")
    				doSound(i,vol,pan,loopVar);
    				done=true;
    				var returnNum = i;
    				break
    			}
    		}
    		// means we didn't find one, lets make some room and add our new sound
    		if (!done){
    			//trace ("new")
    			var n:Number;
    			if (soundChannels.length >= noOfChannels) {
    				for (var k=0; k < noOfChannels; k++){
    					if(soundChannels[j][1].position == soundChannels[j][1].duration & !soundChannels[j][2]){
    						delete soundChannels[j];
    						n = j;
    						break
    					}
    					//that was our final check, since there's no spare channels, we just 
    					// cut off and remove the very next sound
    					if (k == noOfChannels-1 & !n){
    						//trace ("forceDel");
    						soundChannels[j][1].stop();
    						delete soundChannels[j]
    						n = j;
    					}
    					if(++j >= noOfChannels) j=0;
    				}
    			} else {
    				j = (n = soundChannels.length)+1;
    				if(++j >= noOfChannels) j=0;
    			}
    			for (var i in soundsArray){ // search the sound array
    				if (soundsArray[i][1] == id){ // to find the desired sound
    					soundChannels[n]=[id];
    					soundChannels[n][1]= new Sound(_root.sounds["channel"+n]); // create the sound
    					soundChannels[n][1].attachSound(soundsArray[i][0]); // and attach it
    					doSound(n,vol,pan,loopVar); // now play
    					var returnNum = n;
    					break
    				}
    			}
    		}
    		//trace (returnNum)
    		return returnNum;
    	}
    	
    	// function to stop all sounds
    	public function killAll(humans):Void{
    		for (var i in soundChannels){
    			soundChannels[i][1].stop();
    			delete soundChannels[i]
    		}
    	}
    	
    	// function to stop a specific channel
    	public function stopChannel (i:Number):Void{
    		soundChannels[i][1].stop();
    		delete soundChannels[i]
    	}
    	
    	// function to update a specific channel
    	public function updateChannel (i:Number, vol:Number, pan:Number):Void{
    		var channel = soundChannels[i][1];
    		if (vol!=null){ channel.setVolume(vol) }
    		if (pan!=null){ channel.setPan(pan) }
    	}
    		
    	
    	// internals
    	// function sets the volume, pan then plays the desired sound
    	private function doSound(i:Number,vol:Number,pan:Number,loopVar:Boolean):Void{
    		soundChannels[i][2]=false;
    		if(loopVar){ soundChannels[i][2]=true; }
    		
    		var channel = soundChannels[i][1];
    		channel.setVolume(vol ? vol : 100);
    		channel.setPan(pan ? pan : 0);
    		channel.start(0,loopVar);
    	}
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  




Click Here to Expand Forum to Full Width

HTML5 Development Center