Hack 60 A Custom Sound Transform Class

figs/expert.gif figs/hack60.gif

Create a custom class to perform sound fades and cross-fades.

You'll often want to perform sound fades or cross-fades (fading in one sound while fading out another). The common nature of the task suggests that it is a great candidate to be implemented as a custom class. The class can take care of all the grunt work, allowing us to perform a sound fade using a few simple calls to the custom class's methods.

Like the built-in Color class, the Sound class supports a setTransform( ) method. Earlier, we created a custom color transform class [Hack #10] and we can apply the same concepts to create a similar custom class for sound. We've chosen to implement the SoundTrans class as a subclass of the built-in Sound class. The custom subclass contains two methods:


SoundTrans.fadeIn(duration, loop)

Starts a sound and fades it in from 0% to 100% volume over duration milliseconds and plays it loop times


SoundTrans.fadeOut(duration)

Fades a sound to zero volume over duration milliseconds

Here is our object-oriented version, implemented as a custom SoundTrans class, which must be stored in an external SoundTrans.as file:

// This ActionScript 2.0 code must go in an external SoundTrans.as file

class SoundTrans extends Sound {

  // FADE_OUT fades out a sound.

  // FADE_IN fades in a sound.

  // RATE sets the rate the effects will run at, in ms.

  private static var FADE_OUT:Object = {ll:0, lr:0, rr:0, rl:0};

  private static var FADE_IN:Object = {ll:100, lr:0, rr:100, rl:0};

  private static var RATE:Number = 60;

  private var interval:Number;

  private var startTime:Number;



  // (Re)starts the sound and fades it in over duration ms

  // then loops the sound loop times.

  public function fadeIn(duration:Number, loop:Number):Void {

    // Invoke functions inherited from the Sound superclass.

    stop( );

    start(0, loop);

    setTransform(FADE_OUT);

    // Invoke a custom method defined for the subclass.

    applyTransform(FADE_IN, duration);

  }

  // Fades out the sound over duration ms,

  // stopping the sound at the end.

  public function fadeOut(duration:Number):Void {

    applyTransform(FADE_OUT, duration);

  }

  // Initiate a fade and set up an interval to complete it over time.

  private function applyTransform(transObject:Object, 

                                  duration:Number):Void {

    // Get the current sound transform object.

    var getTrans:Object = getTransform( );

    var diffTrans:Object = new Object( );

    if (duration < RATE) {

      duration = RATE;

    }

    startTime = getTimer( );

    for (var i in transObject) {

      diffTrans[i] = (transObject[i]-getTrans[i]) / (duration/RATE);

    }

    // First parameter is current object, this.

    // Second parameter is function to invoke (transition( )).

    // Third parameter is interval duration in ms.

    // Fourth, fifth, and sixth parameters get passed to transition( ).

    interval = setInterval(this, "transition", RATE, transObject, diffTrans,

    duration);

  }

  private function transition(transObject:Object, diffTrans:Object, 

  duration:Number):Void {

      var getTrans:Object = getTransform( );

      for (var i in diffTrans) {

        getTrans[i] += diffTrans[i];

      }

      setTransform(getTrans);

      if (getTimer( ) - startTime > duration) {

        // Cleanup

        setTransform(transObject);

        clearInterval(interval);

      }

      updateAfterEvent( );

    }

}

Note that our class doesn't add new methods directly to the Sound class (as was typical when using prototype-based inheritance in ActionScript 1.0). Instead, the SoundTrans class extends (i.e., subclasses) the built-in Sound class. This has the advantage of keeping the new sound functionality (in our SoundTrans class) separate from the existing Sound class, while at the same time working seamlessly with the Sound class.

You can see this in the following code example, which demonstrates how to use the SoundTrans class.

First, create an instance of the SoundTrans class. This code creates a SoundTrans object that uses the groovyLoop.wav file (which is presumed to be available in the Library with sound linkage identifier groovyLoop).

var groovy:SoundTrans = new SoundTrans(this);

groovy.attachSound("groovyLoop");

We can now use the methods of the SoundTrans class (or those of the Sound superclass) with our SoundTrans instance. The following causes the sound to start playing with the sound volume fading in from zero to 100% over three seconds. The sound then loops twice:

groovyTrans.fadeIn(3000, 2);

The following fades out the sound over six seconds:

groovyTrans.fadeOut(6000);

Consider enhancing this custom class to implement additional features, such as:

  • Making the RATE value user-definable.

  • Adding a method that can accept an array of fade points, allowing you to create a scripted version of the sound envelope.