1. Trang chủ
  2. » Công Nghệ Thông Tin

Học Actionscript 3.0 - p 33 ppsx

10 329 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Nội dung

Internal and External Sounds Chapter 11: Sound 299 Once you’ve provided a linkage class name, you can create an instance of the sound the same way you instantiate a movie clip: var snd:Sound = new ClaireElmo(); Thereafter, you can manage the instance of this sound by referring to the vari- able snd. This creation of a Sound class instance and the use of one method to load a sound file are the only basic differences between using internal and external sounds. All play, pause, stop, and transform operations are identical, regardless of the sound source. If you prefer to use internal sounds, the using_internal_sound.fla source file demonstrates the playing and stopping of a short imported sound using the basic syntax explained in the “Playing, Stopping, and Pausing Sounds” sec- tion later in this chapter. However, we recommend using external sounds for most uses. Loading External Sounds Using internal sounds, creating an instance of the Sound class and populating it with audio occur in one step. To load a sound from an external MP3, we need to use the load() method of the Sound class, so we must first explicitly create an instance of the class. This is shown in line 1 of the following code, found in the loading_external_sound.fla source file. 1 var snd:Sound = new Sound(); 2 3 var req:URLRequest = new URLRequest("song.mp3"); 4 snd.load(req); As discussed in prior chapters, you also need to create an instance of the URLRequest class (line 3) any time you load something. Although it has addi- tional purposes for advanced URL processing, the URLRequest class is also used to standardize the loading process. This allows you to load many asset types in a consistent manner. Finally, we load the sound in line 4. Once you’ve completed this process, you’re again ready to start manipulating the snd instance. When working with external sounds, however, there are additional factors that warrant a little extra attention. The steps we’ll describe aren’t required, but they’re recommended. You’ll find that they improve not only your own development efforts (such as when listening for errors when loading a sound), but also the user experience (such as providing feedback during loading). It’s not uncommon when loading external assets to encounter a missing file problem. The path to the file may be old or incorrect, or the loaded asset or your SWF may have been moved. In this case, a runtime error like this may occur: Error #2044: Unhandled IOErrorEvent:. text=Error #2032: Stream Error. N OT E Another difference between working with internal and external sounds is that you can buffer a loaded sound to improve playback experience. Buffering a sound means that your playback won’t begin until a specified amount of sound data has loaded. This allows the back- ground loading of the rest of the sound to stay ahead of your playback and is discussed in the “Buffering Sounds” sec- tion later in this chapter. Download from Wow! eBook <www.wowebook.com> Part IV: Sound and Video 300 Internal and External Sounds So, it’s a good idea to plan for this possibility and listen for an IO_ERROR (input/output error). If you do encounter an error, you can present it to the viewer as part of the user experience (through an alert or warning icon, for example), or just try to correct it during the authoring process by tracing to the Output panel. The following listener function traces a message, as well as the descriptive text sent with the event. 5 //listen for error 6 snd.addEventListener(IOErrorEvent.IO_ERROR, onIOError, 7 false, 0, true); 8 function onIOError(evt:IOErrorEvent):void { 9 trace("Error occurred when loading sound:", evt.text); 10 } The next enhancement to include when working with external sounds is to provide feedback to the user during the loading process. Again we add a listener to the sound instance, this time listening for a PROGRESS event, which is dispatched whenever loading progress occurs. When this happens, you can update the user by, for example, increasing the width of a sprite to create a progress bar. Lines 12 through 24 of the following code create the progress bar. Line 12 passes a color to the drawBar() function (lines 17 through 24), which creates a sprite, draws a rectangle that is 1 pixel wide and 10 pixels tall, and returns the sprite. Lines 13 and 14 position the progress bar and line 15 adds it to the display list. Lines 26 through 30 contain the listener function. The event captured by the listener function carries with it information, including the total number of bytes in the target object (in this scenario the sound being loaded), as well as the number of bytes loaded at the moment the event was fired. By dividing the latter by the former, you end up with a fraction. For example, if 500 bytes of a total 1,000 bytes have loaded, the progress is 500/1,000 or 0.5, indicating that the object is 50-percent loaded. By multiplying with a desired width of the progress bar, the bar will increase to the final desired size when the file is 100-percent loaded. 11 //track loading progress 12 var loadBar:Sprite = drawBar(0x000099); 13 loadBar.x = 20; 14 loadBar.y = 15; 15 addChild(loadBar); 16 17 function drawBar(col:uint):Sprite { 18 var bar:Sprite = new Sprite(); 19 var g:Graphics = bar.graphics; 20 g.beginFill(col, 1); 21 g.drawRect(0, 0, 1, 10); 22 g.endFill(); 23 return bar; 24 } 25 26 snd.addEventListener(ProgressEvent.PROGRESS, onLoadingProgress, 27 false, 0, true); N OT E In this example, the progress bar will reach 100 pixels wide when the process finishes or will stay at its original 1-pixel width if the function fails. Keeping these start and finish sizes in mind when test- ing locally is useful because loading even very large files from a local hard drive happens very quickly, and it’s quite common not to see the progress bar move at all with small sound files. Download from Wow! eBook <www.wowebook.com> Playing, Stopping, and Pausing Sounds Chapter 11: Sound 301 28 function onLoadingProgress(evt:ProgressEvent):void { 29 loadBar.width = 100 * (evt.bytesLoaded / evt.bytesTotal); 30 } The last option we’ll introduce here is for responding to the completion of the sound loading process. The structure is similar to the prior two event listener examples, this time using the Event.COMPLETE event to trigger the listener function. 31 //react to successful load 32 var sndLength:Number = 0; 33 snd.addEventListener(Event.COMPLETE, onCompleteLoad, 34 false, 0, true); 35 function onCompleteLoad(evt:Event):void { 36 sndLength = snd.length; 37 trace("Sound length:", sndLength); 38 } After creating the variable in line 32, this example stores the length of the sound in milliseconds in sndLength, and traces that value as a quick indica- tion that the process is complete. This code is the starting point for an audio player exercise that runs throughout the chapter. Soon, we’ll use the sound’s length to update a progress bar during playback of the sound. First, however, let’s look closely at the playback syntax. Playing, Stopping, and Pausing Sounds The simple syntax of the Sound class’s play() method can be a bit deceiving because it’s only part of the picture. To play a sound, all you need to do is call the method from the Sound class instance. However, all this does is start play- back and, without additional infrastructure, you can’t do much else. Instead, you should play the sound into an instance of the SoundChannel class, which allows you to stop, pan, or adjust the volume of the channel. This is different from prior versions of ActionScript, in which all sound control rested with the Sound object. To emphasize this idea, let’s think again about a recording studio. To move a sound from the left speaker to the right speaker in a mix, a sound engineer would twist a knob at the mixing desk, not ask a musician to run from one side of the studio to another. Similarly, although musicians often handle volume subtleties, fading a sound up or down is typically accomplished by adjusting a sound channel’s volume slider. In other words, the playback of the sound is typically separated from the manipulation of that sound in the mixing process. The same is true in ActionScript 3.0. We’ll begin with simple examples and then we’ll add these features to our ongoing audio player. Download from Wow! eBook <www.wowebook.com> Part IV: Sound and Video 302 Playing, Stopping, and Pausing Sounds Playing a Sound To place a sound into a channel, all you need to do is create the channel and then set it equal to the result of the sound’s play() method. var channel:SoundChannel; = new SoundChannel(); channel = snd.play(); This associates the sound with the specified channel, the same way you would plug a guitar into a channel in our metaphorical recording studio’s mixing desk. Once the sound is in the channel, you’ll be able to adjust its volume, set its pan, and stop its playback—all of which we’ll discuss in a moment. But how soon can you play the sound? If you’re using imported audio files, you can typically play the sound right away. However, when working with external files, you must consider the loading process. If you invoke the load() method to start loading an external sound file and then immediately attempt to play the sound, your attempt will likely fail because the sound will prob- ably still be loading. However, we saw in the previous section that a COMPLETE event is dispatched when a Sound instance’s MP3 is finished loading. So, an event listener listening for that event can play the sound without any problem. The following snippet shows syntax for playing a sound immediately after it’s loaded. This snippet assumes a Sound instance of snd, and a SoundChannel instance of channel. snd.addEventListener(Event.COMPLETE, onLoadComplete, false, 0, true); function onLoadComplete(evt:Event):void { channel = snd.play(); } This approach is used to play a sound as soon after loading as possible. This is useful for things like background audio that begins playing right away. Perhaps the most common way to play a sound, however, is by clicking a but- ton or through similar user interaction. We’ll set up just such a button when we return to our ongoing sound player project. Stopping a Sound Stopping a single sound in a channel requires only the stop() method. Unlike playing the sound, however, this method is invoked from the channel, not from the sound itself. Again assuming you’ve previously created a new instance of the SoundChannel, named channel, the syntax looks like this: channel.stop(); It’s also possible to stop all sounds using the SoundMixer class. As in the real world, multichannel playback funnels through a master sound mixer. Just as you can kill that master channel in a studio, you can stop all sounds using the SoundMixer class and it’s stopAll() method. Download from Wow! eBook <www.wowebook.com> Playing, Stopping, and Pausing Sounds Chapter 11: Sound 303 Unlike the previous methods discussed, stopAll() is static. This means an instance of the SoundMixer class does not need to be created using the new keyword. Instead, the method is called directly from the class. Therefore, to stop playing the sounds in all channels, you need only write: SoundMixer.stopAll(); Pausing Sounds and Resuming Playback Pausing a sound is a bit different. Currently, there is no dedicated pause method in ActionScript 3.0. Instead, you must rely on an optional parameter of the play() method that allows you to play the sound starting from a par- ticular number of seconds offset from the beginning of the sound. To use this feature to pause playback, you must first store the current posi- tion of a sound as it’s playing. Having retrieved this value from the channel’s aptly named position property, you can then stop playback in that channel. Later, you can resume playback by playing the sound from the stored posi- tion. Assuming the ongoing use of the snd and channel instance names, here are the first and second steps of the process: var pausePosition:Number = channel.position; channel.stop(); Then, at some later point, you can resume playback from where you left off: channel = snd.play(pausePosition); Applying the Syntax Now let’s put these concepts into practice and pick up from the source file we started in the “Loading External Sounds” section. The following code, added to the previous example, forms the player_basic.fla source file. If you want to look ahead, here’s a quick description of all the code related to playing a sound, spanning lines 40 through 75, so we know what we’re trying to accomplish. Lines 44 through 54 contain an event listener that will be triggered when a user clicks on the play button we’ll soon create. Lines 56 through 65 include the code required to show a progress bar during playback. Finally, lines 67 through 75 contain the function triggered when the playback is complete. Now let’s focus on the function that plays the sound and its accompanying variables. Line 40 creates a Boolean variable that we’ll use to check whether the sound is already playing. Because we’ll be using a button to play our sound, it will now be possible to play the sound multiple times. However, this also means that rapid repeated clicks of the button will play the sound over and over itself, layering the sound. This is fine for simulating a musical instrument or an echo, for which multiple simultaneous occurrences of the sound are acceptable, but it’s less desirable for playing spoken dialog or other sounds that typically would not be layered over themselves. N OT E You can control the volume and pan of the master mix by using the SoundMixer class, which we’ll dem- onstrate later on. We’ll also use the SoundMixer to visualize a sound during playback later in the chapter. Download from Wow! eBook <www.wowebook.com> Part IV: Sound and Video 304 Playing, Stopping, and Pausing Sounds Line 41 creates a variable that will store the most recent playback position of the song in its channel, when the pause button is pressed. Remember that we’re adding extra functionality to the play process to support pausing play- back, so we’ll need to pass this value to the play() method. Because we’re using one play() method to play from a stop and from a pause, it’s very important to initialize the value of this variable to zero so that the sound starts playing at the beginning when first played. Similarly, when we code the stop behavior later on, we’ll need to reset this variable to zero after stop- ping the sound, to avoid restarting from any previous pause position when replaying. Lines 44 through 54 make up the onPlaySound() function, which will be called by a play button that we’ll add later on. Line 45 checks to see whether the sound is not already playing. If that test passes, the isPlaying variable is set to true to prevent the sound from playing more than once simultane- ously. Lines 47 and 48 add a listener to the main timeline (the scope of the script) that will fire upon every enter frame event. We’ll use this listener to update the playback progress bar in just a moment. Lines 49 through 51 add a listener to the channel to trigger when the sound playback is complete. We’ll use that to reset things so the sound can be played again. Finally, line 52 plays the sound. The first time it plays, it will play from the beginning because of the initial 0 value of the soundPosition variable in line 41. 39 //play 40 var isPlaying:Boolean = false; 41 var soundPosition:Number = 0; 42 var channel:SoundChannel = new SoundChannel(); 43 44 function onPlaySound(evt:MouseEvent):void { 45 if (!isPlaying) { 46 isPlaying = true; 47 addEventListener(Event.ENTER_FRAME, onPlayProgress, 48 false, 0, true); 49 channel.addEventListener(Event.SOUND_COMPLETE, 50 onPlayComplete, 51 false, 0, true); 52 channel = snd.play(soundPosition); 53 } 54 } Next, we’ll setup the playback progress bar. Lines 56 through 59 create the bar, position it and add it to the display list. The drawBar() function is the same function found earlier in the file, spanning lines 17 through 24 and discussed in the “Loading External Sounds” section earlier in this chapter. It simply creates a sprite and draws a 1 × 10-pixel rectangle. The function in lines 61 through 65 updates the width of the progress bar. It’s called every enter frame event because of the listener created in lines 47 and 48. Dividing the playback position of the sound in the channel by the total length of the sound gives us a percentage. For example, if the position is 5000 and the length of the sound clip is 10,000 milliseconds (10 seconds), the playback is Download from Wow! eBook <www.wowebook.com> Playing, Stopping, and Pausing Sounds Chapter 11: Sound 305 50-percent complete. That percentage is then multiplied by the desired width of the bar, 100 pixels, and the width of the bar is set to this value. Later on in the chapter, we’ll drop two function calls into lines 63 and 64 to control the volume and pan of the sound, and we’ll update peak meter graphics on the stage that show the amplitude of the sound during playback. 55 //play progress bar 56 var playBar:Sprite = drawBar(0x0000FF); 57 playBar.x = 20; 58 playBar.y = 15; 59 addChild(playBar); 60 61 function onPlayProgress(evt:Event):void { 62 playBar.width = 100 * (channel.position / sndLength); 63 //future home of volume and pan adjustment 64 //future home of amplitude meter adjustment; 65 } The onPlayComplete() listener function (added in line 48) is triggered after the sound has finished playing. Lines 68 and 69 remove both listeners added when playback began. Once the sound is finished playing, there is no longer a need to update its playback progress or listen for a SOUND_COMPLETE event. Removing the listeners is not only efficient, but also allows us to set the play- back progress bar width to 0. If not removed, the enter frame event would continue to set the bar’s width to 100. (The position of the sound is at the end of the file when playback is complete.) The remainder of the function stops the sound, resets the soundPosition variable to 0, the width of the play progress bar to 0, and the isPlaying vari- able to false. All of this allows us to play the sound anew. 66 //playback complete listener function 67 function onPlayComplete(evt:Event):void { 68 removeEventListener(Event.ENTER_FRAME, onPlayProgress); 69 channel.removeEventListener(Event.SOUND_COMPLETE, 70 onPlayComplete); 71 channel.stop(); 72 soundPosition = 0; 73 playBar.width = 0; 74 isPlaying = false; 75 } Now that we have the play functionality complete, we need a button to trig- ger it. We’ll be creating three buttons by the time we’re done, so rather than repeating the button creation code three times, let’s set up a function to do the work for us. This takes less code but, more importantly, it means that if a future edit is required, you have to edit the code in only one place, not three. We’re going to draw our buttons dynamically, using the RoundRectButton class we created in Chapter 8, so line 77 imports the class. Remember that this material is presented in chunks for clarity. Import statements are typical- ly consolidated at the top of your script and you should feel free to reorganize your code any way you see fit. N OT E One idea behind using the RoundRectButton class to draw but- tons dynamically is to give you contin- ued practice using packages and classes. However, you’ll also find when we’re done that this entire file will contain no imported assets. As such, the entire audio player is less than 5 KB! This is a best-case scenario because we kept the interface simple—both so you didn’t need to rely on library assets and so a more complex interface didn’t intrude on the sound tutorial. The idea, however, is good. You could use the Graphics class to draw additional interface artwork, for example, and still keep the file size low. Download from Wow! eBook <www.wowebook.com> Part IV: Sound and Video 306 Playing, Stopping, and Pausing Sounds The createButton() function in lines 79 through 87 instantiates a button, positions it on the stage, adds a mouse click event listener, and adds the but- ton to the display list. This later lets us create buttons with only one line of code, as seen with the first button in line 89. With three or more buttons, this approach can really be economical. The function takes three arguments: the y coordinate to place the button on the stage, the label for the button, and the function that will be triggered when the user clicks on the button. Although this book has shown how to pass numbers and strings into functions, this is the first time we’ve used a function as an argument. This is a handy process that’s not only expedient, but also emphasizes the fact that functions are objects too, just like numbers and strings. As mentioned, line 89 creates the first button, passing a y-position of 40, a label of “Play” and the onPlaySound() function, created earlier, as the func- tion to execute when the button is clicked. 76 //playback complete listener function 77 import com.learningactionscript3.ui.RoundRectButton; 78 79 function createButton(yLoc:Number, labl:String, 80 func:Function):void { 81 var btn:RoundRectButton = 82 new RoundRectButton(100,20,10,2,0x000099,labl,0xFFFFFF); 83 btn.x = 20; 84 btn.y = yLoc; 85 btn.addEventListener(MouseEvent.CLICK, func, false, 0, true); 86 addChild(btn); 87 } 88 89 createButton(40, "Play", onPlaySound); The remainder of the script is dedicated to the pause and stop buttons. Lines 91 through 98 create the pause button, setting its y location to 65 and adding the onPauseSound() function as its mouse click listener. In line 93, the func- tion checks to see whether the sound is playing and, if so, stores the current playback position in the soundPosition variable. It then stops the sound and sets the isPlaying variable to false so the sound can play again later. Lines 99 through 102 follow the same process but are even simpler. The stop button is created in line 99, which places the button below the previously cre- ated play and pause buttons, and adds the onStopSound() method as the but- ton’s mouse click listener. The functionality required by manually stopping the sound is the same as the functionality required when the sound stops on its own in the onPlayComplete() function (lines 67 through 75). Therefore, all that’s required here is to call that function. However, because onPlayComplete() is a listener function, it expects an event argument. Calling the function without supplying the expected event argument will cause an argument count mismatch error. We can get around Download from Wow! eBook <www.wowebook.com> Buffering Sounds Chapter 11: Sound 307 this by sending null to the function to stand in for the event. The null value will satisfy the type checking at the listener function because null is the default value for all events. As long as your listener function doesn’t rely on specific information from the event, such as mouse coordinates or keyboard key code values, this technique makes it possible to use listener functions not only when an event occurs, but also manually. 90 //playback complete listener function 91 createButton(65, "Pause", onPauseSound); 92 function onPauseSound(evt:MouseEvent):void { 93 if (isPlaying) { 94 soundPosition = channel.position; 95 channel.stop(); 96 isPlaying = false; 97 } 98 } 99 createButton(90, "Stop", onStopSound); 100 function onStopSound(evt:MouseEvent):void { 101 onPlayComplete(null); 102 } At this point, you should be able play, pause, and stop your sound, and both the load and play progress bar should reach 100 pixels in width upon their respective completions. Buffering Sounds Waiting to play a sound until it’s fully loaded will prevent errors or stutters that might otherwise occur during the loading process. This method, howev- er, does suffer from the drawback of having to wait. An alternative approach is to preload only a portion of the sound prior to playback, and then play the sound while it continues to download progressively to Flash Player in the background. The principle behind this approach is to preload a buffer that can stay ahead of playback during the time required to download the remain- der of the sound. You’ll still need to wait, but not as much. How much of the sound should you buffer? That depends on how you plan to distribute your project. Theoretically, if you have no load time, you need no buffer time because the sound loads instantly. This is usually true of local files, when you are not loading the sound from the Internet. For remote files, connection speeds can dictate how much sound needs to be preloaded. If you know you’ll only encounter broadband connection speeds, you can buffer less of the sound. If you’re worried about slower connections, you may want to buffer more of the sound to prevent the playback from catching up with the loading process and stalling playback. To specify the buffer time, you must use the SoundLoaderContext class at the time of sound loading. The number of milliseconds of sound to buffer is passed to the constructor when instantiating the class; otherwise, a default value of 1000 is used. After instantiating the class, you then pass the resulting N OT E For more information about listener functions and the argument count mismatch error, see the “Using Event Listeners” section of Chapter 3. Download from Wow! eBook <www.wowebook.com> Part IV: Sound and Video 308 Changing Sound Volume and Pan instance into the sound load() method, as a second parameter following the URLRequest object. The following example adapts the start of our audio player by inserting line 2, and modifying line 4. It buffers 5 seconds of the loaded sound before the play() method will execute. This modification can be found in the player_ buffering.fla source file. 1 var snd:Sound = new Sound(); 2 var context:SoundLoaderContext = new SoundLoaderContext(5000); 3 var req:URLRequest = new URLRequest("song.mp3"); 4 snd.load(req, context); Changing Sound Volume and Pan During playback, it’s possible to manipulate the volume and pan of indi- vidual channels, as well as the global mixer containing all sounds. Doing so requires the SoundTransform class. The process involves starting with a SoundTransform instance (either by creating a new instance or by storing a reference to the existing transform object of the channel or mixer), setting the volume and/or pan setting of that instance, and then applying the transformation to the channel. For example, this snippet will set the volume of a SoundChannel instance called channel to 50 percent. var trans:SoundTransform = new SoundTransform(); trans.volume = 0.5; channel.soundTransform = trans; This syntax will set the volume of a channel to half of what it currently is: var trans:SoundTransform = channel.soundTransform; trans.volume *= 0.5; channel.soundTransform = trans; Notice that the first example sets the volume of a new SoundTransform instance to 0.5, while the second example multiplies the volume of an exist- ing SoundTransform instance by 0.5. The first example will set the volume to 50 percent, regardless of its prior setting, but the second example will cut the current volume in half. For example, if the second volume was originally 50 percent, it would then be 25 percent. Most ActionScript 3.0 settings that require percentage values use a unit range of 0 to 1. For example, volume is expressed as a range of 0 (muted) to 1 (full volume) with any interim value expressed as a percentage of full volume. To determine a value that describes a pan setting between left and right stereo channels, both a percentage left and a percentage right are required. Therefore, the units are expressed as a range of –1 (full left) through 0 (cen- tered) to 1 (full right). Negative interim values reflect some degree of pan left, N OT E Remember that buffering will have little effect when testing locally because the loading process will complete very quickly. You may wish to upload your test files to a server, and perhaps even use a very large sound file, to test your efforts. Download from Wow! eBook <www.wowebook.com> . you end up with a fraction. For example, if 500 bytes of a total 1 ,00 0 bytes have loaded, the progress is 500 /1 ,00 0 or 0. 5, indicating that the object is 5 0- percent loaded. By multiplying with. during playback. 55 //play progress bar 56 var playBar:Sprite = drawBar(0x 000 0FF); 57 playBar.x = 20; 58 playBar.y = 15; 59 addChild(playBar); 60 61 function onPlayProgress(evt:Event):void { 62 playBar.width. <www.wowebook.com> Playing, Stopping, and Pausing Sounds Chapter 11: Sound 30 5 5 0- percent complete. That percentage is then multiplied by the desired width of the bar, 100 pixels, and the width of

Ngày đăng: 06/07/2014, 18:20

TỪ KHÓA LIÊN QUAN

w