Animating the waveform

We're now going to add a mask to the "sample black" layer and reveal it as the sound is streaming from the first frame to the last. This will result in a waveform that starts out grey and swipes to black as the media plays.

Select the "sample black" layer and insert a layer. Name the new layer "sample mask". Select the "sample mask" layer and use the "Modify > Layer..." command to call up the Layer Properties dialog and select the Mask Type for the layer like this:

Select OK and we're nearly there.

Click and drag the "sample black" layer. When you get it right, the dotted green oval will go dark green.

And release the drag so that it is under the "sample mask" layer.

Now anything that is drawn on the "sample mask" layer will reveal anything that is indented under it (with the dotted green page icons). So, to reveal the black waveform, we are going to Shape Tween a box in the mask layer to slowly cover (and thus, reveal) the black waveform. Let's do it.

Select the "sample mask" layer. Select the Rectangle Tool (R) and draw a rectangle. Double-click on the rectangle frame and delete it (it's not really useful to us for this). Click to select the rectangle fill (it will get dots on it). Click and drag the top left corner of the rectangle until the drag outline snaps to the top left corner of the waveform sample.

Now select the Free Transform Tool (Q) and drag the bottom right corner of the rectangle so that it snaps to the bottom right corner of the waveform sample. If you lock the masked layers (click on the dots under the padlock in the timeline) you will only see the black waveform. If you unlock the layers again and drag the right hand side of the rectangle to about half its width, you will see left half black and right half grey when you lock them again.

OK. Unlock the layers and drag the right hand side of the rectangle back again. Now add a keyframe in the last frame of your "sample mask" layer and select frame 1.

In your Properties panel for frame 1 choose a Shape Tween. Free Transform the right hand side of the rectangle to the left until you can't see the rectangle any more (you may need to zoom in to do this properly - try Ctrl+Shift-rolling your mouse wheel to zoom in). Transforming the rectangle like this should give it a width of zero.

Now lock the mask layers again. If you click and drag the playhead you should hear the sound you imported playing and the waveform will transform from grey to black as you scrub back and forth.

I have also added a playhead indicator using shape tweening, but I will leave that as an exercise for you to try at home!

Save now. Have a break. Next up we are going to do some coding!

Part 2: Planning our controls

What do we want to end up with? For the "Western Australia: Land and People" exhibition we needed a movie control that we could just drag and drop on any file that had audio in it (we had over 80 files to add controls to, so it had to be quick and simple). This means that everything has to be put in to a single movie so that we can just drag and drop the controls onto our sound. So first up:

  1. We needed a movie clip to contain our controls. We called this movie "media-controls".

Within that we needed Play/Pause and Rewind buttons, giving:

  1. Play button, "play".
  2. Pause button, "pause".
  3. Rewind button, "rew".

A slider would be nice, for selecting a point to play from:

  1. We wanted a movie clip to contain the slider, "progress-bar".

And the slider should have a Drag button for positioning the playhead:

  1. Drag button, "dragger".

The slider should probably also contain a preload or streaming indicator to give some feedback on how much of the sound has been loaded so far:

  1. Movie clip to indicate preload, "frames-loaded".

This image shows the slider (on the left) with the dragger and a light grey preload indicator. On the right are the rew, play, and pause buttons. All these elements will be contained in a single movie clip ready for us to drop on to a sound.

That'll do for now. We're going to build and test the controls in test.fla and then, when it all works, we'll rip out the control movie and dump it into a media-controller.fla where we will make it available as a shared resource. That way, we'll be able to use it anywhere.

All right, easy bits first.

Getting the sound to play, pause and rewind

First, we'll get the container movie out of the way. Create a layer in test.fla for our controls. I put the layer at the bottom and named it "controller". Select the "Insert > New Symbol..." command (Ctrl+F8) and name it "media-controls", giving it a Behaviour of Movie Clip.

Select the controller layer you created and drag the media-controls movie clip on to the stage just under your waveform image. All right, you may put it wherever you want. It doesn't make much difference for now.

Open your media-controls movie and create three buttons: "play", "pause", and "rew". We gave them some standard-looking icons and some clicky sounds for mouse-overs and presses. And I'm not going into details about how to make buttons. That stuff is in your help files - just don't use components. They are rather large (too many bytes) and tend to load in the first frame.

Put your buttons on layers named "play", "pause", and "rew", and align them with your sample waveform as you like! This is what I did:

I have placed the buttons on the right to leave room for the slider that we will add later.

Now for the button code. Because our sound is streaming on the timeline of our parent movie, all we have to do to control our sound is to control our parent movie as we would for any other movie.

Paste this code on the Rewind button:

on (release) {  //rew
        _parent.gotoAndStop(1);
}

Paste this code on the Pause button:

on (release) {  //pause
        _parent.stop();
}

Paste this code on the Play button:

on (release) {  //play
        _parent.play();
}

All of this is standard movie clip control code.

Test your movie (Ctrl+Enter). The sound should play and animate the waveform as it goes along. And the three buttons should operate as expected. This will give you the basic functionality, but we can do better than this. Let's make the Play/Pause buttons seem like one button that toggles between play and pause states, and get the Rewind button to rewind-and-play (if the sound is currently playing) or rewind-and-stop (if the sound is paused).

What we will do is hide the Play button (set its _visible property to false) if the sound is playing or show the Play button (set its _visible property to true) if the sound is paused. If we align the Play and Pause buttons so that the Play covers the Pause, we will get our toggling effect!

Because we will be accessing the Play button's _visible property, we need to give our Play button an Instance Name. Select the Play button on the stage and look at your Properties panel. You will see a text box with "" greyed out. Click on the grey text and type in the name for the button that you want to use in your code to access the button's properties and methods. We named it "playBTN".

Make the changes to the code for our three buttons.

Paste this code on the Rewind button:

on (release) {  //rew
        if (playBTN._visible) {  //paused
                _parent.gotoAndStop(1);
        } else {  //playing
                _parent.gotoAndPlay(1);
        }
}

Paste this code on the Pause button:

on (release) {  //pause
        _parent.stop();
        playBTN._visible = true;
}

Paste this code on the Play button:

on (release) {  //play
        _parent.play();
        playBTN._visible = false;
}

Then realign the buttons by dragging the Play button over the Pause button.

Now test it. Working? Really? Here's the problem: the parent movie (our sound) starts out playing, but the Play/Pause button defaults to a state that is appropriate for a paused parent movie.

We have two options: (1) only add code to stop the parent movie and just leave the rest as it is, or (2) put the Pause button above the Play button in the layers and recode to show and hide the Pause button. I chose the first option, added an actions layer and put a stop command in frame 1 to stop the parent movie from playing. Like this:

Test it now! And save.

Adding a load indicator

We could use a preloader for this, but that wouldn't make the best use of Flash's streaming capabilities. What we are going to do is show how much has loaded and then let the visitor play any part that Flash has cached. We thought this would be best achieved with a slider. So let's make one.

Our first task is to create a progress bar movie clip to contain our slider.

Create a layer in the media-controls movie clip in test.fla for our slider. I put the layer between the actions and rew layers and named it "slider". Select the "Insert > New Symbol..." command (Ctrl+F8) and name it "progress-bar", giving it a Behaviour of Movie Clip.

Select the slider layer you created and drag the progress-bar movie clip on to the stage to the left of the buttons we have completed (or put it anywhere else you feel is appropriate).

Open the progress-bar movie clip and now draw the background rectangle that will form the basis for our slider. I made mine with a black fill and border. My dimensions for the rectangle were 171 wide and 4 high. Rename "Layer 1" to "base" ('cos it's the base of our slider). You should have this (the stage is zoomed in a bit):

Now for the highlight that will grow within this base as more and more of the audio file is loaded.

Create a layer above the base layer and name it "highlight". On the highlight layer, draw a rectangle (any colour you like), select and delete the rectangle's border, and align it with the start of the base layer's rectangle. Like this:

To be able to change the size of this in code, it needs to be a movie clip, and it needs to have an Instance Name.

Select your grey (or whatever colour you chose) rectangle and use the "Insert > Convert to Symbol..." command (F8). Name it "frames-loaded" and give it a Behaviour of Movie Clip.

Make sure that your new frames-loaded movie clip is selected. In the Properties panel, set the to "progressBar".

Time for a slight diversion.

The code for the slider will get a little tricky. To minimise confusion and to make code maintenance easier, we are going to keep as much of our code in one place as we can. That place will be a functions layer that will contain our ActionScript code. So create a new layer at the top of the timeline and name it "functions".

Out of habit, we usually have some standard layers in all of our FLA files:

Layer name

What we use it for

labels

Contains a whole bunch of labelled keyframes and nothing else. This makes it easy to see where our labels are and what they are called.

actions

ActionScript that needs to be executed as the movie is playing. We don't put any functions in this layer, just code that needs to be executed on a keyframe.

functions

This is for ActionScript functions that we call from the actions layer or in event handlers on movie clips or buttons. When we have the majority of code in one place, it makes debugging much easier because it is easy to find the code.

That's it. Diversion over. Back to our progressBar code.

Our first function will set the size of the progress bar according to the sound download progress. This code needs to go into your newly created "functions" layer:

function setProgressBar() {
        var parentLoaded = 170 * _parent._parent._framesloaded / _parent._parent._totalframes;
        progressBar._width = parentLoaded;
}

We are setting the _width of the progressBar movie clip to a fraction of the width of our rectangle on the base layer. I made the rectangle 171 wide and indented the progressBar movie clip by 0.5 from the left, so I want the same distance in from the right, giving 171 - 0.5 - 0.5 = 170. The fraction of 170 that we want is given by the usual _framesloaded / _totalframes. No new stuff here! The only tricky thing is that we have to go up far enough so that _framesloaded and _totalframes will refer to the Flash movie that has the sound streaming on the timeline.

The following table shows what code accesses which movie clip:

Variable

Movie clip

_framesloaded

progress-bar

_parent._framesloaded

media-controls

_parent._parent._framesloaded

Main timeline

And now back to the progressBar movie clip. This is the code that needs to go on it to call the setProgressBar function:

onClipEvent (enterFrame) {
        _parent.setProgressBar();
}

Test this (Ctrl+Enter) with streaming (Ctrl+Enter again). As Flash simulates the download, you should see the grey bar in the black slider background growing until all of your sound has loaded.

Save now.