Gaming Your Way

May contain nuts.

The sound of silience ...

Back again!

After a good deal of time I finally have something to post about - or let's face it moan about.

As the headling slightly might suggest I'm dealing with sound today.
I think that sound handling in AS3 is a nightmare compared to the ease of it in AS1/2 and I'm not the only one asking WTF?

So in order to play your sound you have to instanciate it, if it's exported CS3 kindly creates a class for you so you can easily use it ... (loading it from an external source is another story)

This is what the CS3 help gives us for embeded sounds ("working with embeded sounds"):

var drum:DrumSound = new DrumSound();
var channel:SoundChannel = drum.play();

My first question was: what do I need the SoundChannel for if I just want to play the sound?

Well, the rocket scientists at Adobe thought that it would be a good idea to add a play() command to the Sound, but not a stop(), so in order to stop our sound playing we *need* the SoundChannel - so we better store it for later use.

Anyway, to make my life easier I converted my SoundUtil class from AS2, basically it deals with the sounds so I don't have to think about it, it has a few usefull commands like playSFX (plays a sound effect, once), playMusic (which allows fading), crossfade ...
I usually used attached sounds (or from an external swf, but the SoundUtil dealt with it ...)
So in order to play music for the menu I'd just do:

SoundUtil.getInstance().playMusic("musicName", 2); // 2 would do a 2 sec. fade in

The AS3 version should work the same, although it uses static functions which then call the singleton's method.

Oh wait. We need to have a class to start the embeded sound ...

To get over that I wrote the add method, which basically takes the name of the sound (or the classname) and then does it's magic.

        public function add (strSound:String, bIsMusic:Boolean):void {
            
            var refClass:Class = getDefinitionByName(strSound) as Class;
            var sndTmp:Sound = new refClass();
                        
            var iTmp:int = this._aSound.length;
            
            this._aSound.push(sndTmp);
            this._objSound[strSound] = { id:iTmp, bIsMusic: bIsMusic };
            
            if (bIsMusic) {
                this._objSound[strSound].spDummy = new Sprite();
            }
            
        }

Ha! that was easy ...

As you see the sounds name gets stored in an object (I just use it as dictionairy), I store an Object with some more values along with the name. And you surely might ask WHY on earth I did create a Sprite for music files ...
Well I'm a lamer, I use the Sprite to attach an onEnterFrameTo it for things like fading :)

Fast forward ...

k. Let's say we play some music, and only wont it to play 2 times, after that the sound should be removed from memory. Luckily we have the onSoundComplete Event, it should return (CS3 help): "The Sound object on which a sound has finished playing."

For me it reads like it returns the Sound that is playing. FAIL!

It does however return a SoundChannel, which of course HAS no information (prove me wrong) about the Sound it belongs to ...
So how can I unload/cleanup a Sound when an onSoundComplete occurs, if I don't know which Sound is playing (and don't want to write a seperate Listner for each sound)?

Oh lucky me...

Thank fuck I store a lot of things in my information object (not only what is shown in the add method), for instance I store the SoundChannel I got from Sound's play() command and I store if a Sound is playing ...

After a few hours of using our favorite search engine I came up with something so stupid it might even be brilliant ...

private function onSoundComplete (e:Event):void {
            
      var strKey:String;
            
      for (strKey in this._objSound) {
           if (this._objSound[strKey].bIsMusic) {
               if (this._objSound[strKey].chChannel == e.target) {
                   this._objSound[strKey].chChannel.removeEventListener(Event.SOUND_COMPLETE, this.onSoundComplete);
                   this._objSound[strKey].bIsPlaying = false;
                   // do some cleanup
               }
           }
       }
            
}

Basically I loop over all music "files" that are playing and *compare* their SoundChannel with the one returned by the Event.
That's so insanely stupid! But it works. Sweet.

Maybe it helps some of you ...

nGFX

Comments (2) -

  • John Cotterell

    10/29/2008 7:34:23 PM |

    I had fun with this on my last project.

    It was very confusing at first, especially as you also have SoundTransform as well as Sound and SoundChannel.

    It makes sense if you consider that you might want to have the same Sound object managed in two different SoundChannel objects. By keeping Sound, SoundChannel and SoundTransform separate, it gives your code more flexibility and consistency.

    BitmapData works the same way - you have the Bitmap Class, but you may want to use it in more than one place, hence the BitmapData. Filters would be the equivalent of SoundTransform.

    Unfortunately like everything in AS3, it requires twice as much code.

  • nGFX

    10/29/2008 11:14:02 PM |

    Yeah, it is in some AS3 twisted sort of way logical, *but*, why for instance can't I find out which Sound belongs to a SoundChannel without jumping through hoops?

    Why dosn't a Sound have a stop() as well ...

    Working with sound is so much easier in .NET, basically in every other languge, but hey we choose our own fate :)

    nGFX

Comments are closed