Chapter 17. How Do I Work with External Assets?

17.0 Introduction

Flash is capable of many tasks by only using assets that have been created or imported into the authoring environment. However, one of its greatest strengths is its ability to work with external assets at runtime. Flash Player can load several kinds of assets including plain text, XML, HTML, CSS, URL-encoded variables, images, sound, video, and other SWF files, to name some examples. It can even load raw binary data.

This chapter provides a brief overview of loading and unloading a few of these external asset types. You can do very different things with each asset type, depending on the asset, security restrictions, and the version of Flash Player you’re targeting. However, the recipes here should give you some idea of what’s possible, as well as warn you about some possible pitfalls, to help you along the way.

To improve your memory management efforts, and to save some possible debugging time, you should know a little about how Flash Player 9 and later flushes objects from RAM. In short, you can’t immediately remove something from memory. Instead, Flash Player uses a process called garbage collection. When an object is no longer in use, Flash Player marks it for collection. Subsequently, during an optimal low in processor demand, the garbage collector sweeps through and collects all objects previously marked for removal.

Unlike your neighborhood trash removal service, however, you can’t predict when garbage collection will occur, and you can’t reliably force the process. As long as you’re aware of how this system works, you should be able to maintain your code that improves your chances of efficient memory management and more effective garbage collection.

17.1 Loading and Applying a Cascading Style Sheet

Problem

You want to apply the same text styling to one or more text fields, particularly those containing HTML-based content from internal or external sources.

Solution

Use the URLLoader class to load an external Cascading Style Sheet (CSS) into an instance of the StyleSheet class, and apply the latter instance to text fields.

Discussion

The URLLoader class can load plain text, name/value pair variables (such as those in a URL), or binary data. As seen in the first block of the following script, this example uses two such loaders—one for an external HTML file and one for an external CSS document—and a StyleSheet instance to contain the data from the CSS file.

The following two functions are very similar, loading first the CSS document, then the HTML document. In both cases, event listeners for load complete and input/output (IO) errors are added, and the content’s loaded. If either process generates an IO error, the onIOErr() function at the end of the script is called.

When the CSS document is loaded, the onCSSLoaded() listener function creates a StyleSheet instance, parses the loaded CSS document to create the requisite styles, and then repeats the loading process for the HTML file.

When the HTML file is loaded, the onLoadHTML() listener function stores the incoming HTML in a local variable, creates a text field, and then sets several basic text field properties (discussed in Chapter 14). It also applies the style sheet, populates the htmlText property with the loaded HTML data, and adds the field to the display list. Finally, the function removes all listeners you don’t need any more.

var htmlFile:URLLoader;
var cssStyles:StyleSheet;
var cssFile:URLLoader;

function loadCSS() {
    cssFile = new URLLoader();
    cssFile.addEventListener(Event.COMPLETE, onCSSLoaded, false, 0, true);
    cssFile.addEventListener(IOErrorEvent.IO_ERROR, onIOErr, false, 0, true);
    cssFile.load(new URLRequest("demo.css"));
}
loadCSS();

function onCSSLoaded (evt:Event):void {
    cssStyles = new StyleSheet();
    cssStyles.parseCSS(evt.target.data);
    htmlFile = new URLLoader();
    htmlFile.addEventListener(Event.COMPLETE, onHTMLLoaded, false, 0, true);
    htmlFile.addEventListener(IOErrorEvent.IO_ERROR, onIOErr, false, 0, true);
    htmlFile.load(new URLRequest("demo.html"));
}

function onHTMLLoaded(evt:Event):void {
    var htmlData:String = evt.target.data;
    var txtFld = new TextField();
    txtFld.width = 550;
    txtFld.multiline = true;
    txtFld.wordWrap = true;
    txtFld.autoSize = TextFieldAutoSize.LEFT;
    txtFld.selectable = false;
    txtFld.styleSheet = cssStyles;
    txtFld.htmlText = htmlData;
    addChild(txtFld);

    cssFile.removeEventListener(Event.COMPLETE, onCSSLoaded);
    cssFile.removeEventListener(IOErrorEvent.IO_ERROR, onIOErr);
    htmlFile.removeEventListener(Event.COMPLETE, onHTMLLoaded);
    htmlFile.removeEventListener(IOErrorEvent.IO_ERROR, onIOErr);
}

function onIOErr(evt:IOErrorEvent):void {
    trace("A loading error occurred:", evt.text);
}

Warning

You must apply a StyleSheet instance to a text field before populating it with text.

Samples of HTML and CSS files required for this exercise to work, follow:

HTML: demo.html

<body>
<span class='heading'>Use CSS to Style Text</span><br/>
<span class='subheading'>A Simple Example</span><br/>
<p>Lorem ipsum dolor sit amet,sed do eiusmod tempor incididunt ut
 labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</body>

CSS: demo.css

body {
    font-family: Verdana;
    margin-left: 6px;
    margin-right: 6px;
}

p {
    text-indent: 20px;
}

.heading {
    font-size: 18px;
    font-weight: bold;
    letter-spacing: 1px;
    color: #FF6633;
}

.subheading {
    font-size: 14px;
    font-style: italic;
    text-align: right;
}

See Also

15.1 Creating a Text Field for creating a text field, 15.5 Populating a Text Field for populating a text field, 15.6 Automatically Sizing a Text Field for auto-sizing a text field, 15.10 Formatting Text Using HTML for supported HTML, 15.11 Formatting Text Using CSS for supported CSS.

17.2 Loading and Displaying an Image or SWF File

Problem

You want to display an image or SWF from an external source.

Solution

Use the Loader class to load an external JPG, GIF, PNG, or SWF file, and then add it to the display list.

Discussion

You can fairly simply load a display object, such as an image or SWF file. You need to start with an instance of the Loader class, and then use its load() method to load the content. Consistency is a hallmark of ActionScript 3.0. You must use a URLRequest instance for every URL.

Then you add the image or SWF file to the display list after the load is complete. You add it by using the addChild() method inside an event listener function that triggers when the Event.COMPLETE event is received. This event’s not available to the Loader instance, but rather to the related LoaderInfo class. You can immediately access an instance of this class by using the contentLoaderInfo property of the Loader you created, so you don’t have to instantiate another class.

After the load complete event is received, the image is added to the display list. Since the event listener was added to the contentLoaderInfo property, rather than the Loader itself, you must add the evt.target.content to the display list, rather than the more typical evt.target. For more information, see Chapter 14.

var ldr:Loader = new Loader();
ldr.load(new URLRequest("loadMe.jpg"));

ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, ¬
     onLoaded, false, 0, true);
function onLoaded(evt:Event):void {
    addChild(evt.target.content);
    evt.target.removeEventListener(Event.COMPLETE, onLoaded);
}

Although this example loads an image, loading a SWF file uses the same syntax. You need only change the path inside the URLRequest to resolve to a SWF file. However, you may wish to consider other additional features. For example, when loading a SWF file, you may want to delay display or further action until the SWF file has fully initialized. The next recipe demonstrates this approach.

See Also

17.3 Communicating with an ActionScript 3.0 Loaded SWF for information about working with loaded content after it is loaded or initialized.

17.3 Communicating with an ActionScript 3.0 Loaded SWF

Problem

Having loaded a SWF file that was coded using ActionScript 3.0, you want to communicate between the parent and the loaded SWF file.

Solution

Cast the content of a Loader instance as a movie clip and get or set properties, or invoke methods, of that movie clip.

Discussion

When you load a SWF file into a Loader instance, the content of that instance contains the SWF file and all data therein. Essentially, the main timeline of the loaded SWF file is a movie clip, so you can access the methods or properties of the loaded SWF file the same way you access similar attributes of a movie clip. Hereafter, the host, or parent, SWF file is called “Loader” and the loaded SWF is called “Loadee.”

Loadee: loadee.swf

Starting with the loaded content, you need a SWF file named loadee.swf. To demonstrate a variety of communication tasks, the file should include a precreated animated movie clip called anim on the stage and the following script.

The first line stops the movie clip from playing so that another SWF file may start it later. The first function traces a hard-coded string to the Output panel. The second function accepts a string argument from a function call, and traces a new string that includes that argument value. The last function also accepts an argument value, but this time it’s a number, and the function returns a new value (adding 1 to the incoming value) to the script from which it was called. None of the functions are called within the loadee.

anim.stop();

function sayHello():void {
    trace("Hello, from your loadee!");
}

function showMsg(msg:String=""):void {
    trace("Loadee here again. You said, '" + msg + "'");
}

function returnSum(num:Number=0):Number {
    return (num + 1);
}

Loader: loader.swf

The loadee is then loaded into a host, or parent, SWF file. To do so, you must create an instance of the Loader class, add it to the display list, and then load the desired content, as the first block of the following script shows.

In order to communicate with the new SWF file, it must be fully loaded. Listen for an Event.COMPLETE event before proceeding, to make sure the content is loaded.

var loader:Loader = new Loader();
addChild(loader);
loader.load(new URLRequest("loadee.swf"));

loader.contentLoaderInfo.addEventListener(Event.COMPLETE, callLoadee, ¬
     false, 0, true);
function callLoadee(evt:Event):void {
    var loadee:MovieClip = MovieClip(loader.content);
    loadee.anim.play();
    loadee.sayHello();
    loadee.showMsg("Hi, from Loader!");
    trace("Loader sent 1 to Loadee and got back:", loadee.returnSum(1));
    evt.target.removeEventListener(Event.INIT, callLoadee);
}

The first line of the callLoadee() function stores a reference to the loaded content, rather than the existing Loader instance. The casting to MovieClip is required because a Loader instance can also load images, for which such communication attempts don’t apply. As such, the compiler must know your property and method access is legal.

The second line targets the animated movie clip, and tells it to play. Because the clip was initially stopped, if it plays when loaded, then you know this method did its job.

The third line calls a function in the loaded SWF file. The result is the tracing of the string, “Hello, from your loadee!” The fourth line accomplishes a similar task but passes a value into the loadee to vary the outcome of the function. The result is the tracing of the modified string, “Loadee here again. You said, ‘Hi, from Loader!’”

Finally, the last line of callLoadee() also calls a function and passes in a value to affect its outcome. However, in this case, a value is returned to the parent SWF file. Passing in 1 returns a value of 2, at which point the parent traces, “Loader sent 1 to Loadee and got back: 2” to the Output panel. This demonstrates round-trip communication and getting data from a loaded SWF file.

Note

You can add a Loader instance without content to the display list without generating an error. However, if you prefer, you can add the instance to the display list with an event listener after the loader issues an Event.COMPLETE event (indicating the content has been loaded).

17.4 Communicating with an ActionScript 2.0 Loaded SWF

Problem

Having loaded a SWF file that was coded using ActionScript 2.0, you want to communicate between the parent and the loaded SWF file.

Solution

Use a LocalConnection object to communicate between host and loaded SWF file.

Discussion

Although ActionScript 3.0 can easily load SWF files created using ActionScript 2.0, the two versions of the language can’t coexist in the same file. ActionScript 3.0 was written from the ground up and exists in its own virtual machine—a player within a player, if you will—in Flash Player.

As such, an ActionScript 3.0 SWF file can’t communicate directly with an ActionScript 2.0 SWF file. One way you can get around this limitation is to use a LocalConnection object. Just as you can use local connections to communicate between multiple SWF files in a browser window, or between SWF file and projector, you can use the technique to communicate between ActionScript Virtual Machine 2 (AVM2, used for ActionScript 3.0) and AVM1 (ActionScript 1.0/2.0). Note that an ActionScript 3.0 SWF file can load an ActionScript 2.0 SWF file, but the reverse isn’t possible.

This example shows how to control an animation and trigger a function, while passing data into the AVM1 SWF file. To test all functionality, you need an animated movie clip in the loaded SWF file.

Starting with the host, or parent SWF file, the first step in this process is to load the SWF file. In this case, the loaded SWF file is named as2.swf, and the parent SWF file, although not referenced by filename in the script, is as3.swf.

In this example, the communication is triggered by a button click, but it’s still a good idea to enable this functionality only after the loaded content is ready. Otherwise, you may initiate communication prematurely and encounter an unresponsive button or even errors. Here, the local connection, button, and event listener are all created only after the receipt of the init event, as you see in the previous recipe. The listener’s contents are explained after the script.

Hereafter, the host, or parent, SWF file is called “Loader” and the loaded SWF file is called “Loadee.”

Loader: as3.swf

var loader:Loader = new Loader();
loader.load(new URLRequest("as2.swf"));
addChild(loader);

loader.contentLoaderInfo.addEventListener(Event.INIT, onInit, false, 0, true);
function onInit(evt:Event):void {
    var as3as2LC:LocalConnection = new LocalConnection();

    var sendBtn:Sprite = new Sprite();
    sendBtn.buttonMode = true;
    sendBtn.graphics.beginFill(0x000099);
    sendBtn.graphics.drawCircle(0, 0, 15);
    sendBtn.graphics.endFill();
    sendBtn.x = sendBtn.y = 30;
    addChild(sendBtn);

    sendBtn.addEventListener(MouseEvent.CLICK, onSendBtn);
    function onSendBtn(evt:MouseEvent):void {
        as3as2LC.send("crossVM","playClip");
        as3as2LC.send("crossVM","showMsg", "Hello, from AS3!");
    }
}

The first line of the event listener function initializes the local connection. The second block of the event listener function creates the button used to trigger the communication, and adds it to the display list.

The final block of the event listener function creates the button event listener. When you click the button, two messages go through the local connection. This step uses the send() method of the LocalConnection instance, as3as2LC. The first argument value is the name of the local connection. This connection is like the telephone number or radio channel over which the connected objects communicate, increasing security. Any participating parties must send or receive along this “channel” to successfully communicate. In this case, the host sends over the “crossVM” connection, and the loaded SWF file must connect to this same channel to receive instructions and respond.

The first message sent triggers a function called playClip(), and the second message triggers a function called showMsg() but also passes along the string argument “Hello, from AS3!”

Loadee: as2.swf

In the loaded SWF file, the first block of code stops the animated movie clip. The clip is played by instruction issued across the local connection. The variable name used to store the connection in the loadee doesn’t have to be the same as the variable used in the parent SWF file. Instead, the correct connection is established because the loadee connects to the same channel created by the loader, “crossVM”.

The last two blocks of code are simple functions, but one thing is atypical. Each block is a method of the LocalConnection instance. You can confine access from the connection to only those functions you wish to be executed from a connecting remote SWF file.

anim.stop();

var as3as2LC:LocalConnection = new LocalConnection();
as3as2LC.connect("crossVM");

as3as2LC.playClip = function():Void {
    anim.play();
};

as3as2LC.showMsg = function(msg:String):Void {
    if (msg == undefined) { msg = ""; }
    trace("Loadee here. You said, '" + msg + "'");
};

See Also

17.2 Loading and Displaying an Image or SWF File for loading a SWF file.

17.5 Unloading an Image or SWF File

Problem

You want to reduce RAM and performance overhead by unloading an image or SWF file.

Solution

Use the unload() method of the Loader class, but be sure to clean up first by stopping timers, closing streams, removing listeners, and more!

Discussion

The first part of this recipe is simple. A single method unloads a loaded image or SWF file, and the optional additional steps of removing the Loader instance from the display list and nullifying the variable reference are also demonstrated. Hereafter, the host, or parent, SWF file is called “Loader” and the loaded SWF file is called “Loadee.”

Loadee: loadee.swf

Beginning with the loadee, the first line of the SWF files frame script sets its frame rate to 1 frame per second. This step is only helpful from a tutorial standpoint because it reduces the number of times text will be traced to the Output panel later on during testing.

The next three lines simply draw a maroon circle into the main timeline at point (500, 40) to provide visual feedback to see when the file’s loaded and unloaded.

stage.frameRate = 1;

this.graphics.beginFill(0x990000);
this.graphics.drawCircle(500, 40, 20);
this.graphics.endFill();

Loader: loader.swf

Now look at this recipe’s host, or parent, SWF file. The first block of the following script loads and displays a SWF file as discussed in 17.2 Loading and Displaying an Image or SWF File. The second block creates and displays a sprite that serves as a button. The last block adds an event listener that calls its function when the button sprite is clicked.

The first line of the listener function unloads the Loader instance. The second line removes the instance from the display list, and the last line nullifies the instance so the garbage collector can collect it from memory.

var loader:Loader = new Loader();
addChild(loader);
loader.load(new URLRequest("loadee.swf"));

var unloadBtn:Sprite = new Sprite();
unloadBtn.buttonMode = true;
unloadBtn.graphics.beginFill(0x000099);
unloadBtn.graphics.drawCircle(0, 0, 15);
unloadBtn.graphics.endFill();
unloadBtn.x = unloadBtn.y = 30;
addChild(unloadBtn);

unloadBtn.addEventListener(MouseEvent.CLICK, onClick, false, 0, true);
function onClick(evt:MouseEvent):void {
  if (loader != null) {
    loader.unload();
    removeChild(loader);
    loader = null;
  }
}

But wait...there’s more!

This process appears to be straightforward and, as described, works with simple content such as loaded images in almost every case. However, when it comes to the average loaded SWF file, many problems arise. Put simply, many features, when used, prevent a SWF file from unloading. This recipe covers some of the most common examples of this problem.

This issue’s widely discussed, however, so if you run into a situation in which your content isn’t unloading, you may be able to find additional information on the web. Grant Skinner, for example, has written several posts about memory management and related topics in his blog. One in particular, covers many of the concerns discussed here, and links to other related topics in his archive: http://www.gskinner.com/blog/archives/2008/04/failure_to_unlo.html.

Enter frame events

One of the easiest problems to run into is also one of the easiest problems to miss. If a loaded SWF file contains an enter frame event listener that hasn’t been removed, the SWF file can’t be unloaded. You can see this in action by adding the following to the loadee script, republishing, and testing the loader/loadee relationship again. This code adds an event listener where none existed before, and then traces a simple string to the Output panel every time an enter frame event occurs.

addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
function onLoop(evt:Event):void {
    trace("loaded enter frame");
}

When you try to unload the SWF file, although the visual feedback appears to show that the content has unloaded, you notice the trace continues forever. You can’t unload the SWF file.

The workaround is to be certain you remove all enter frame event listeners before trying to unload the applicable SWF file. One approach to this problem is to add a “clean-up” function to your SWF files. Inside this routine, place any maintenance instructions, such as the removal of event listeners, and call the function before unloading. Here’s an example of the function that should be inserted into the loadee.

function cleanUp():void {
    removeEventListener(Event.ENTER_FRAME, onLoop);
}

The following snippet is an example of code you can add to the parent SWF file, in the existing event listener responsible for triggering the unloading process. The entire attempt is wrapped in a try..catch statement so that any resulting errors can be suppressed from end-user view. The clean-up process is invoked in the first two lines of the try segment.

First a local variable is created to reference the content of loader, as described in 17.3 Communicating with an ActionScript 3.0 Loaded SWF. Then the cleanUp() function you added to the loaded SWF file is called prior to unloading.

function onClick(evt:MouseEvent):void {
    try {
        var loadee:MovieClip = MovieClip(loader.content);
        loadee.cleanUp();
        loader.unload();
        removeChild(loader);
        loader = null;
    } catch (err:Error) {
        trace("Error unloading:", err.message);
    }
}

If you test this improved setup, you find that removing the enter frame event listener before unloading lets the SWF file fully unload. Not only do the visual elements disappear from view, but the tracing caused by the loaded SWF file ceases.

Timers

You can see the same scenario in action with a running timer. If you substitute the enter frame addition to the original loaded SWF file, loadee.swf, with this timer addition, then you witness the same behavior. This new code triggers a trace every second.

var tmr:Timer = new Timer(1000);
tmr.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
tmr.start();
function onTimer(evt:TimerEvent):void {
    trace("loaded timer");
}

The solution is similar, but the timer must be stopped prior to removing the listener. The garbage collector can’t ever collect a running timer. The timer reference should also be nullified after the listener’s removed.

function cleanUp():void {
    tmr.stop();
    tmr.removeEventListener(TimerEvent.TIMER, onTimer);
    tmr = null;
}

Since the host SWF file was set to call the cleanUp() function after its button was clicked, no further change to the host is required. Upon adding the preceding code to the loaded SWF file, you find that the tracing ceases and this version can also be collected from memory.

Streams and connections

Unfortunately, the list of steps to remove a SWF file from memory doesn’t stop at enter frame and timer listeners. Several other causes may prevent a SWF file from being unloaded, and here are a few of the most common solutions.

Loaded SWFs with Video

Pause and close all NetStream instances, remove all related event listeners, and then nullify the NetStream instances. Then close all NetConnection instances, remove all related event listeners, and nullify the NetConnection instances.

Loaded SWFs with Sound

Stop all sounds from playing, close all streams, remove all related listeners, and nullify any Sound, and SoundChannel instances.

Loaded SWFs with Local Connections

Close all LocalConnection instances, remove all related listeners, and nullify the LocalConnection instances.

A step in the right direction

Although stated at the outset of this chapter, this section bears repeating. This list of memory management issues and workarounds is by no means complete. However, this peek into the complex world of unloading content in ActionScript 3.0 may give you a head start when it comes to debugging your own projects.

See Also

17.2 Loading and Displaying an Image or SWF File for loading a SWF file.

17.6 Loading and Playing a Sound

Problem

You want to load and play an external MP3 file.

Solution

Create an instance of the Sound class to load and play the sound, and store the sound in an instance of the SoundChannel class for discrete control.

Discussion

ActionScript 3.0 introduces a much more granular level of control over sound playback. Previously, the Sound class did most of the heavy lifting, but ActionScript 3.0 introduces a few new classes to both add and distribute functionality.

You still begin working with an external MP3 file by creating an instance of the Sound class, loading a file, and playing the sound. People also often wait for the sound to load before playing the file. You do this step in ActionScript 3.0 with an event listener added to the Sound instance, as you see in the following script.

However, a new class is introduced into the equation before the sound is played. The SoundChannel class creates a discrete sound channel that you can control separately from other sound channels (up to 32). When the sound is played, it’s played into the new sound channel the way a single musical instrument is assigned to a specific channel in an audio mixing console.

var snd:Sound = new Sound();
snd.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
snd.load(new URLRequest("music.mp3"));

var channel:SoundChannel = new SoundChannel();
function onComplete(evt:Event):void {
    channel = snd.play();
    snd.removeEventListener(Event.COMPLETE, onComplete);
}

Finally, after the sound is played into a discrete channel, you no longer need its load complete listener, so it’s removed. You can also manipulate (17.7 Setting the Volume and Pan of a Sound) and visualize the sound (17.8 Visualizing the Amplitude of a Sound) without affecting, or being affected by, other sounds in the SWF file.

Note

17.6 Loading and Playing a Sound through 17.9 Unloading a Sound can be combined into a single script to demonstrate in one file all the sound features discussed in these recipes.

17.7 Setting the Volume and Pan of a Sound

Problem

You want to change the volume and/or pan (degree of the sound in each of the left and right stereo channels) of a sound.

Solution

Start with the SoundTransform property of the SoundChannel class, and set the values of the volume and pan properties.

Discussion

Building on 17.6 Loading and Playing a Sound, this recipe adds volume and pan control. The bolded lines in this recipe’s code can be added to the onComplete() function from 17.6 Loading and Playing a Sound to randomly assign a volume and pan level to the sound when it’s initially played.

The first bold line creates trans, an instance of another new sound class, SoundTransform, by querying the soundTransform property of the sound channel. Continuing the real-world metaphor started in 17.6 Loading and Playing a Sound, using the SoundTransform class (either directly or through a sound channel’s soundTransform property) is similar to adjusting the volume slider and/or pan knob on a single channel of an audio mixing console.

Note

These values were formerly in the ActionScript 2.0 Sound class and have been moved to make transforming sounds more consistent with other such alterations, like color transformations, in ActionScript 3.0. As with color transformations, when altering a sound’s volume or pan setting you must first edit a transformation instance (either newly created or retrieved from an existing channel, as in this example), and then apply (or reapply) the edited transformation to the sound channel.

function onComplete(evt:Event):void {
    channel = snd.play();
    snd.removeEventListener(Event.COMPLETE, onComplete);
    var trans:SoundTransform = channel.soundTransform;
    trans.volume = Math.random();
    trans.pan = Math.random() * 2 - 1;
    channel.soundTransform = trans;
}

The second bold line automatically sets the volume of trans to a random number between 0 and 1. The third bold line sets the pan of trans to a random number between −1 and 1.

Note

New to ActionScript 3.0, ranges similar to percentage values (0–100) are now 0 to 1. Values of the volume property range from 0 to 1, and values of the pan property range from −1 to 1. (−1 is far-left, 1 is far-right, and 0 is dead center).

Finally, trans is reapplied to the soundTransform property of channel, resulting in a random volume between mute and full, and a random pan between far-left and far-right each time the SWF file runs.

See Also

17.6 Loading and Playing a Sound for loading and playing a sound.

17.8 Visualizing the Amplitude of a Sound

Problem

You want to display the amplitude of a sound during playback.

Solution

Use the leftPeak and rightPeak properties of a sound’s channel to control the visual appearance of one or more display objects.

Discussion

This recipe builds on 17.6 Loading and Playing a Sound and 17.7 Setting the Volume and Pan of a Sound, and visualizes sound during playback. You can easily do this if you create traditional peak meters that increase in size with a sound’s amplitude. The first two blocks of this recipe’s script create these peak meters by drawing blue and green sprites (for the left and right stereo channels, respectively), 20 × 100 pixels in size, with a bottom-center registration point.

The last code block contains an event listener that sets the height of these sprites to the value of an ActionScript sound channel’s leftPeak and rightPeak properties. These properties contain the left and right stereo amplitudes, respectively, of any sound channel at query time. These values are always between 0 and 1, so multiplying them by 100 yields a maximum height of 100 pixels at full amplitude, and a minimum height of zero during silence.

var leftPeakSP:Sprite = createBar(0x0000FF);
leftPeakSP.x = 20;
leftPeakSP.y = 120;
addChild(leftPeakSP);
var rightPeakSP:Sprite = createBar(0x00FF00);
rightPeakSP.x = 50;
rightPeakSP.y = 120;
addChild(rightPeakSP);

function createBar(col:uint):Sprite {
    var sp:Sprite = new Sprite();
    var g:Graphics = sp.graphics;
    g.beginFill(col);
    g.drawRect(0, 0, 20, -100);
    g.endFill();
    return sp;
}

addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
function onLoop(evt:Event):void {
    leftPeakSP.height = channel.leftPeak * 100;
    rightPeakSP.height = channel.rightPeak * 100;
}

See Also

17.6 Loading and Playing a Sound for loading and playing a sound and 17.7 Setting the Volume and Pan of a Sound for setting the volume and pan of a sound.

17.9 Unloading a Sound

Problem

To minimize impact on performance and available memory, you want to unload a sound after it has served its purpose.

Solution

Stop the sound, close the sound stream, and nullify the Sound and SoundChannel instances.

Discussion

Building on 17.6 Loading and Playing a Sound through 17.8 Visualizing the Amplitude of a Sound, this recipe adds the ability to unload a sound. The first block of code creates a clickable sprite used to unload a sound, and the second block adds an event listener to the sprite that responds when the user clicks on the sprite.

The first step in the listener function is to stop sound playback using the stop() method of the SoundChannel instance. Next, the sound stream is closed. All sound files, whether they’re streaming from a server or from a local file, have a stream. This stream essentially refers to the background downloading of file content while the sound is playing.

Closing the stream means you can halt the download process even if data remains to be downloaded. This process is attempted in a try..catch statement because the stream may be fully loaded by the time this instruction is issued. A try..catch statement tries the requested commands, and catches any errors thrown so they can be suppressed from end-user view. For debugging purposes, the statement will trace the error to the Output panel in authoring mode only.

After the channel playback is stopped and the sound stream is closed, you can choose to nullify either or both variables, and remove the enter frame listener (created in the previous recipe) to allow everything to be collected. The remaining four lines are explained after the script passage.

var unloadBtn:Sprite = new Sprite();
unloadBtn.buttonMode = true;
unloadBtn.graphics.beginFill(0x990000);
unloadBtn.graphics.drawRect(0, 0, 20, 20);
unloadBtn.graphics.endFill();
unloadBtn.x = 520;
unloadBtn.y = 10;
addChild(unloadBtn);

unloadBtn.addEventListener(MouseEvent.CLICK, onUnloadBtn, false, 0, true);
function onUnloadBtn(evt:MouseEvent):void {
    channel.stop();
    try {
        snd.close();
    } catch (err:IOError) {
        trace("Close stream error:", err.message);
    }

    var trans:SoundTransform = channel.soundTransform;
    trans.volume = 0;
    channel.soundTransform = trans;
    SoundMixer.stopAll();

    channel = null;
    snd = null;

    removeEventListener(Event.ENTER_FRAME, onLoop);
}

Depending on how you write your code, you may sometimes find that a sound continues to play even after its stream has been closed, because the portion of the external file that’s already been streamed, up to the point of closing the stream, is still eligible for playback.

Stopping playback in the channel may be enough to prevent the sound from continuing to play. If not, you may want to try one or two additional steps. You may also want to mute the sound channel immediately after stopping it, so that if content continues to play it won’t be heard, and/or stop all sounds playing through the global SoundMixer class—the last of the new sound classes discussed in this chapter. The SoundMixer class is analogous to the master mixer on an audio mixing console. All discrete sound channels flow through the SoundMixer and, therefore, can be stopped all at once.

See Also

17.6 Loading and Playing a Sound for loading and playing a sound and 17.7 Setting the Volume and Pan of a Sound for setting the volume and pan of a sound, and 17.8 Visualizing the Amplitude of a Sound for visualizing the amplitude of a sound.

17.10 Loading and Playing a Video

Problem

You want to load and play an external FLV or H.264 video source.

Solution

Create NetConnection and NetStream instances, as well as a Video object for display, and then play the NetStream instance.

Discussion

You have a few ways to play videos in Flash, the simplest of which is to use one of the provided components designed for this purpose. However, in some situations you don’t want to commit to the memory/file size to use the components, or you may even be dissatisfied with a component’s functionality and want to create your own player.

In these cases, the functionality of a player you create can range from simple to robust, depending on how much time you want to put into coding its features. This recipe covers the bare essentials, adding a few features in descriptive segments. When you’ve completed this recipe, be sure to look at 17.11 Unloading a Video for information about unloading the video content.

The first step in creating your own ActionScript video player is to create an instance of the Video class, and add it to the display list, to provide you with a display object on which to watch your video. You see this in the first two lines of the following script segment. Describing a real-world parallel, this is like getting a television.

The second step is to instantiate a NetConnection object, as seen in the second two lines of the script. This part lets you connect to a streaming video server, but passing a value of null to the connect() method also lets you work with progressive download video sources. Using a NetConnection is loosely analogous to selecting which video on demand (VOD) service you wish to watch.

The next step is to create a NetStream instance, specifying the NetConnection instance as its argument, and attaching the stream to the Video instance, as seen in lines 5 and 6 of the following script. This step is somewhat akin to selecting which video category offered by the previously selected VOD service to watch (comedy, drama, and so on).

Finally, playing the video of your choice is like choosing and playing the video from the previously selected genre that you want to view.

var vid:Video = new Video();
addChild(vid);

var vidConnection:NetConnection = new NetConnection();
vidConnection.connect(null);

var vidStream:NetStream = new NetStream(vidConnection);
vid.attachNetStream(vidStream);

vidStream.play("<your_video_here>.flv");

Although the previous bare-bones example works, you may also want to handle automatically triggered events associated with the video. For instance, you may receive notifications or errors associated with metadata embedded or injected into the video, or even data from cue points added when encoding the video.

The following script segment creates an object designed to trap this information to prevent errors from appearing at runtime. This segment creates an object with functions attributed to each of the onMetaData and onCuePoint event handlers. This object is then passed to the client property of the NetStream instance.

As a result, the cited functions respond to incoming metadata and/or cue points. In this example, the duration metadata entry is traced to the Output panel, as well as any cue point text data that may be encoded into the video.

var infoClient:Object = new Object();
infoClient.onMetaData = onMetaData;
infoClient.onCuePoint = onCuePoint;
vidStream.client = infoClient;

function onMetaData(info:Object):void {
    trace("duration:", info.duration);
}
function onCuePoint(info:Object):void {
    trace("cuepoint:", info.parameters.text);
}

In addition to listening for metadata and cue point information, you may also want to react to any asynchronous errors that may occur when attempting to play a video. This error usually occurs when a server calls a method that the client hasn’t defined, so it’s handy to have active when scripting a no-frills video player.

The following segment adds event listeners for asynchronous event errors to both the NetConnection and NetStream objects.

vidConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬
     onAsyncError, false, 0, true);
vidStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬
     onAsyncError, false, 0, true);
function onAsyncError(evt:AsyncErrorEvent):void {
    trace(evt.text);
}

Finally, if you want your video to do more than just play, then you want to script an accompanying controller. This recipe provides one example of controller functionality by creating a button to toggle the pause state of the video.

var pauseBtn:Sprite = new Sprite();
pauseBtn.buttonMode = true;
pauseBtn.graphics.beginFill(0x000099);
pauseBtn.graphics.drawRect(0, 0, 20, 20);
pauseBtn.graphics.endFill();
pauseBtn.y = vid.y + vid.height + 5;
addChild(pauseBtn);

pauseBtn.addEventListener(MouseEvent.CLICK, onPauseToggle, false, ¬
     0, true);
function onPauseToggle(evt:MouseEvent):void {
    vidStream.togglePause();
}

See Also

17.11 Unloading a Video for information about unloading a video.

17.11 Unloading a Video

Problem

You want to unload a video after it has served its purpose.

Solution

Pause and close NetStream instances. Close NetConnection instances. Remove all listeners and nullify all stream and connection references.

Discussion

Building on 17.10 Loading and Playing a Video, this recipe provides a mechanism for unloading the video. The first block of code creates a button to click for unloading, and adds it to the display list. The second block of code creates the button’s event listener.

To unload a video, you must first pause and then close the NetStream instance (vidStream). You must then remove any event listeners added to this instance (such as the asynchronous error listener used in this example) and nullify the instance to let it be collected. You must then close the NetConnection instance (vidConnection), remove any listeners attached thereto, and nullify that instance to allow it, too, to be collected.

var unloadBtn:Sprite = new Sprite();
unloadBtn.buttonMode = true;
unloadBtn.graphics.beginFill(0x990000);
unloadBtn.graphics.drawRect(0, 0, 20, 20);
unloadBtn.graphics.endFill();
unloadBtn.x = vid.width - 20;
unloadBtn.y = vid.y + vid.height + 5;
addChild(unloadBtn);

unloadBtn.addEventListener(MouseEvent.CLICK, onUnloadBtn, false, 0, true);
function onUnloadBtn(evt:MouseEvent):void {
    vidStream.pause();
    vidStream.close();
    vidStream.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
    vidStream = null;
    vidConnection.close();
    vidConnection.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, ¬
     onAsyncError);
    vidConnection = null;
    //
    removeChild(vid);
    vid = null;
}

Only after all of these steps can the garbage collector remove the video. Finally, if you’re also finished with the video object used to display the video, then you can remove it from the display list, remove any relevant event listeners, and nullify that instance, as well.

See Also

17.10 Loading and Playing a Video for information regarding loading and playing a video.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.199.191