Flash MX MP3 player version 2, Pg.4
source: http://www.thegoldenmean.com
4 — Modify the .fla… again
If you got to this page directly from a search engine or other link, be advised that the tutorial you are currently reading extends an earlier article which you can (and need to) read here
finesse
Here is the finished version 2 Player. It adheres to this tutorial series’ policy of keeping the appearance austere. Teaching you how to make it work is my task. How it ends up looking on your web site is yours!
This paragraph should be replaced by a Flash movie if you have at least version 6 of the Flash Player and your browser has Javascript enabled.
project files
You may download the project files for version 2b of the Player by clicking this link.
(the updated songList PHP script is available separately here. Please recall the note on page three about the modified directory structure for this new version of the script.)
Updated project files for MX 2004 are available, although you are going to have to figure out how they work from the comments in the code. They are not terribly different from the ActionScript outlined in this tutorial but there are differences, so read the comments closely. These updated project files do include the updated songList script which utilizes the modified directory structure so be sure to read the remarks at the bottom of page 3.
- demo 4b
- an alternate version that has a slide open pane which reveals the listbox
what changed?
- When I made the first version of the Player I was tickled by the hundredths of a second whizzing past in the text fields. Over time (no pun intended) I decided that was a bit of a distraction.
- By popular demand, there is a new text field that displays title/album/artist. This would have been a pain in the older, hand-coded XML file. It’s so easy to get that data now that it seems a natural evolution
- Using Button Components for the next, previous and pause/play controls always bothered me a bit and, considering the new version 2 Component architecture making version 1 Components less compatible with newer versions of Flash I decided to make those controls MovieClips instead of Button Components.
1) format time
Simply shorten the last block of the formatTime() function so it reads as follows:
//display result nicely outputData = elapsedM + ":" + elapsedS; return outputData;
2) info_txt
the text field
I found the dimensions of the previous movie were too small to add a longish text field with any hope of reading it, so the first thing I did was increase the dimensions of the movie. The demo movie is 350px wide by 240px high now. You can obviously adjust yours to suit your own needs. (Even at 350px it cuts off some text. You might consider using a pixel font to get more text in less space. There are numerous sources for pixel fonts. One I can recommend warmly from first–hand experience is Fonts for Flash.) You will probably need to adjust the placement of the existing interface elements when the movie size changes.
Then I added a new layer to keep things organized. In the new layer I created a new dynamic text field, and gave that new text field an instance name of “info_txt”.
modify the code
We need to write some code to insert data in the new text field. The most natural place to put this code is in the doPlay() function. The easiest way to illustrate the necessary changes and their context in the function is just to quote the bulk of that function and mark the changed material in bold:
function doPlay (theSong) { //we always want to start with a fresh Sound object, //so intialize the environment: clearInterval (streamingID); clearInterval (playingID); if (typeof myTunes == "object") { myTunes.stop (); delete myTunes; } info_txt.text = ""; displayDuration_txt.text = "--"; displayPosition_txt.text = "--"; //reset pause/play button to default in case user had //paused sound and then selected another track pausePlay_pb.setClickHandler ("pauseSound"); pausePlay_pb.setLabel ("pause"); //grab data parameter from ListBox var theSong = songList_lb.getSelectedItem ().data; var plIndex = songList_lb.getSelectedIndex(); myTunes = new Sound (); myTunes.loadSound (theSong, true); myTunes.setVolume (Math.floor(volume_mc.slider_mc._x/2)); //the setIntervals that display time data streamingID = setInterval (displayStreaming, 200, myTunes); playingID = setInterval (displayPosition, 200, myTunes); //display info in composite text field var node = songList_xml.firstChild.childNodes[plIndex]; info_txt.text = node.attributes.title+"/"+ node.attributes.artist+"/"+ node.attributes.album; //what to do when tune ends //clean up stuff from previous song myTunes.onSoundComplete = function () { //part one - if single track mode if (!mainTL.continuous) { //clean up from previous song //clear the text fields displayDuration_txt.text = ""; displayPosition_txt.text = ""; info_txt.text = " ";
You will notice that when a new tune is loaded (as part of the initialization routine) or when the current tune ends the text field is cleared of any content it might have had by setting its text to nothing:
info_txt.text = " ".
The information we want is in an XML node, but which one? The index of the item selected in the ListBox corresponds to the XML node we are interested in. We have a ListBox Component method that will return the selected item. Perfect. A new local variable is declared to store that index number, plIndex. Another local variable, node, travels up the XML tree to the specific node that contains this song’s records (stored in attributes). We get to the XML child node we want by first stepping up one level from the root (songList_xml.firstChild), and then asking for the child node that matches the ListBox selection (.childNodes[plIndex]).
Here’s an example. If the first track is selected in the ListBox Component, its index number is 0 (zero, since it uses a zero-based numbering system). Addressing the corresponding XML node would be as follows:
songList_xml.firstChild.childNodes[0]
Sounds insanely complicated but that’s how you navigate in an XML document.
After finally arriving at the XML node that corresponds to the selected tune, the data from the various attributes is handed over to the info_txt text field. That completes the second enhancement. On to the interface buttons.
3) movieclips as buttons
Version one of this Player utilized Button Components for the Next, Previous and Pause/Play User Interface Controls. That made developing the Player much easier, and the focus of that discussion was primarily on other issues, so it seemed like a good trade-off.
The price you pay for that convenience is inflexiblity. You can’t easily make a Button Component look like much but a Button Component. Add to that the incompatibilities with the new Version 2.0 Component method calls and it seemed like it was time to move to MovieClips for those three User Interface elements.
next and previous
Not a whole lot to say here. Make two new MovieClip symbols. Make them as simple or as elaborate as your muse dictates. (In keeping with the general de-emphasis of aesthetics in this tutorial I made mine as plain and uninspired as possible in the demo file so they would stand out!) Delete the next_pb and prev_pb Button Components from the stage and in their place drag instances of your new symbols from the Library. Give them instance names “prev_mc” and “next_mc”.
Select the “Button Code” layer in your main timeline and simply change the “_pb” to “_mc” like so:
//code for next and previous prev_mc.onRelease = function(){ prevTrack(); }; next_mc.onRelease = function(){ nextTrack(); };
pause/play
This is just a little bit trickier, since the pause/play button needs to change “state” to reflect what its current status is. (If you click it, will it pause the sound or will it play the sound?) In addition, doPlay() needs to reset the button as part of the initialization routine. (If the user pauses a song and then selects a new song, the pause/play button needs to return to its default state.)
We will exploit one of Flash’s unique capabilities for this — the ability to use frames in a timeline to represent a MovieClip’s “state”.
Before we make the pause_play_mc, we need to make individual MovieClip symbols, pause_mc and play_mc. These are essentially the same, just with different symbols or icons. Again, feel free to make them as visually elaborate as your project’s aesthetics dictate.
Now create a new MovieClip symbol. Name it “pause_play_mc”. In the timeline of your new symbol, double click the first (default) layer and change its name to “buttons”. Press the F6 function key to create a new keyframe. Create a new layer, named “script”, and ensure that it too has two keyframes (not just two frames - they need to be keyframes because they need to be separated from one another). Click the first frame of the buttons layer. Drag an instance of your pause_mc symbol from the library and give it an instance name. Select the second keyframe of the buttons layer and do the same thing with the play_mc symbol, remembering to give it an instance name.
We’ve got two “states” now, represented visually by the two frames. The state frame one represents assumes music is playing and a click of the button will pause it. The state represented by frame two assumes the music is paused and a click of the button will resume play. The visual representation doesn’t make anything happen though — time to add the code that corresponds to each state.
You know from the first tutorial in this series that my preference is to keep all code on the main timeline. It is just so much easier to organize and locate that way. However in the case of the pause_play_mc that approach turns out to be a pain. We will therefore place that button’s code on its own timeline. (You might want to remind yourself of that with a comment in the “Button Code” layer of the main timeline. While you are there, you can delete the code that was directed at the PushButton Component used previously.) Select the first keyframe of the scripts layer and insert the following:
pause_mc.onRelease = function () { mainTL.pauseSound (); play (); }; stop();
Select the second keyframe of the scripts layer and insert:
play_mc.onRelease = function () { mainTL.resumeSound (); play (); }; stop();
Recall from version one that mainTL is a global variable that resolves to the main timeline of this movie. We need that reference because otherwise the functions in pause_play_mc wouldn’t be able to “find” the functions out in the main timeline. I avoid using _root because it causes all manner of problems if you load this swf into another movie.
Each time the button is clicked it calls a function and then advances the playhead using play();. But each frame also has a stop(); method to prevent the thing from looping endlessly. So each click effectively toggles the button’s state.
Return to the main timeline. Delete the PushButton Component and replace it with your new pause_play_mc symbol, being sure to give it an instance name so you can refer to it with ActionScript.
One final step remains — initializing the new button’s state when a new track begins to play. Select the “Functions” layer of the main timeline, scroll down to the doPlay() function and locate the lines of code which read:
//reset pause/play button to default in case user had //paused sound and then selected another track pausePlay_pb.setClickHandler ("pauseSound"); pausePlay_pb.setLabel ("pause");
Change them to read:
//reset pause/play button to default in case user had //paused sound and then selected another track pause_play_mc.gotoAndStop(1);
You’re done!
inspiration
Now that you are an expert at making Flash MX play your MP3 files…
Now that you have spent hours pecking away at your keyboard and cussing at your computer…
Now that you are feeling pretty good about what you have accomplished…
You might enjoy looking at some Players others have made. Really, the variety is astonishing. Maybe you’ll get some good ideas for how to “skin” your own player. I’ll share several that I have run across to give you a sense of just how creative and inventive Flash developers can be. Maybe you will get some satisfaction from knowing you understand how they work under the hood now!
- Eric Dolecki’s dazzling work of art.
- FlashLoaded’s SoundPlayer (commercial component)
- FlamPlayer - wow! A very polished commerical endeavor, free if you are willing to keep their branding in place. Available without thier branding for a fee. Uses MySQL and PHP. You get the .swf but not the .fla. Extraordinary work.
- Jeroen Wijering’s elegant Player. This appears to be a Component not an .fla so there's only so much you can do to customize it, but it certainly is beautiful and appears quite solid and flexible.
- WimpyPlayer (also commercial, but not a component. very impressive - sold as a swf that gets all of its configuration instructions from FlashVars embedded in the html! Also very sophisticated use of PHP)
- Bleep (a sweet minimalistic widget by Warp Records (a music retailer) - waveforms generated by FlashAmp I’m almost positive)
- PixelPlay’s intriguing iPod (only availble when his iPod is plugged in evidently, so you might need to try later if it’s not connected)
- Senocular’s example, used as a demo in his exhaustive XML tutorial
- A playful one at NASA’s SpacePlace
Finally, you may be interested in a tutorial by Shane Rebenschied, author of Flash MX 2004: Beyond the Basics Hands On Training. (It was pointed out to me in May, 2008 that the original link to this tutorial no longer points to anything meaningful. You might try this link to a PDF sample chapter of his book.) Mr. Rebenschied’s Player is significantly different from mine (for one thing it does not use a“playlist” navigation), but he goes into greater detail than I about technical topics such as streaming, compression and ID3 tags, and he uses MX 2004. I recommend reading Mr. Rebenschied’s tutorial to fill in some of the gaps mine leaves, and to consider a very different design approach.
That wraps up version two of the Flash Player. You have the basics for producing a very flexible module. I can’t wait to see how you implement it!
- page one - the introduction
- page two - minor modifications to the Flash movie
- page three - the PHP magic
--top--