Archive for February, 2010

Creating and Seamless Audio Stream in AS3

Thursday, February 18th, 2010

I was frustrated that there was no ActionScript3 equivalent for setting an audio clip to Stream as you can inside of Flash. I wanted to create a seamless audio loop that was synced with a corresponding looping MovieClip. Because the audio never quite lined up on the last frame of the MovieClip inside Flash CS4, there was always an audible seam. I created a class called AudioStreamController which takes a MovieClip and a Sound object as parameters. On ENTER_FRAME the playback of the MovieClip seeks to the frame of the corresponding Sound position (assuming that the MovieClip is roughly the length of your Sound).

package
{
    import flash.events.*;
    import flash.media.*;
    import flash.display.*;
   
    public class AudioStreamController extends EventDispatcher
    {
        /** The sound channel to control sound playback */
        private var __channel:SoundChannel;
       
        /** The sound object */
        private var __sound:Sound;
       
        /** The number of times to loop the animation */
        public var loops:int = 1;
       
        /** The animatino to sync to audio */
        public var __clip:MovieClip;
       
        /** The total number of frames */
        public var __frames:int;
       
        /** The last paused position */
        public var __lastPosition:int;
       
        /**
        *   The purpose of this class is to replicate the syncing that
        *   you can do in Flash using the audio "Stream" setting
        *   @param clip The animation you want to sync
        *   @param sound The sound object you want to play
        */

        public function AudioStreamController(clip:MovieClip, sound:Sound): void
        {
            __sound = sound;
            __clip = clip;
            __frames = clip.totalFrames;
           
            stop();
        }
       
        /**
        *   Start the playback of the audio and listen for new frame enter
        *   to seek to the correct frame of the animation
        */

        public function play(): void
        {
            __channel = __sound(__lastPosition, loops);
            __clip.addEventListener(Event.ENTER_FRAME, update);
        }
       
        /**
        *   Stop and return to the beginning
        */

        public function stop(): void
        {
            if (__channel)
            {
                __channel.stop();
            }
            __lastPosition = 0;
            __clip.gotoAndStop(1);
            __clip.removeEventListener(Event.ENTER_FRAME, update);
        }
       
        /**
        *   Pause the playback and save the current position
        */

        public function pause(): void
        {
            __clip.removeEventListener(Event.ENTER_FRAME, update);
            __lastPosition = __channel.position;
            __channel.stop();
        }
       
        /**
        *   Update event called on entering new frame of the animation
        *   @param ev Enter frame event
        */

        protected function update(ev:Event): void
        {
            // This is the percentage of progress of all the loops
            var percent:Number = __channel.position / (__sound.length * loops);
           
            // The current frame over all the loops
            var f:int = Math.round((__frames - 1) * loops * percent) + 1;
           
            // The current frame gets converted into a frame on a single loop
            f = f - int(f / __frames) * __frames;

            __clip.gotoAndStop(f);

            if (percent > 0.999)
            {
                __clip.removeEventListener(Event.ENTER_FRAME, update);
                __clip.gotoAndStop(__frames);
               
                dispatchEvent(new Event(Event.COMPLETE));
            }
        }
       
        /**
        *   Destroy this controller object
        *   don't use after this point
        */

        public function destroy(): void
        {
            __clip.removeEventListener(Event.ENTER_FRAME, update);
           
            stop();
           
            __clip = null;
            __sound = null;
            __channel = null;
        }
    }
}