
Se et eksempel på hvad du kommer til at lave
Download .fla filen
Indtil videre har vi oprettet et sound objekt med to knapper, en afspil/pause knap og en stop knap. Vi viser også brugeren hvor mange minutter og sekunder der er blevet afspillet samt hvor meget af sangen der er downloadet – i form af en preloader. Sidst men ikke mindst giver vi brugeren muligheden for, at skrue op og ned for lyden.
Hvis du ikke har læst de 3 foregående tutorials, kan du finde dem her.
XML
Det næste vi skal i gang med, er at give brugeren mulighed for at vælge mellem flere sange, og til den funktion, bruger vi et XML dokument til, at holde styr på sangene. Hvis du ikke er helt med på beatet, hvad angår XML, så tjek lige Thomatrix's video tutorials.
Jeg vil ikke gå I detaljer med hvordan et XML document struktureres, men bare nævne, at jeg har oprettet et XML dokument, der indeholder url og titel til tre sange. Hver sang har deres egen node, med to attributter.
1 2 3 4 5 |
<music> <song url="../music/ff_lifehouse_01.mp3" title="Somewhere Only We Know"></song> <song url="../music/ff_lifehouse_02.mp3" title="Blind"></song> <song url="../music/ff_lifehouse_03.mp3" title="Midnight in Philidelphia"></song> </music> |
Så opretter jeg et XML obejekt i Flash, som henter url og title ind i 2 arrays som holder styr på dem.
Igen, hvis du intet kender til XML, så se Thomatrix's video tutorial om emnet.
Koden til xml objektet ser således ud:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var playlist:XML = new XML(); playlist.ignoreWhite = true; playlist.onLoad = function (success) { if(success) { _global.numSongs = playlist.firstChild.childNodes.length; _global.songName = []; _global.songFile = []; for (var i=0; isyntax] Det interessante er, at jeg opretter 3 globale variabler, som kan tilgås over alt i flash. <code type="actionscript"> _global.numSongs = playlist.firstChild.childNodes.length; _global.songName = []; _global.songFile = []; |
Da disse værdier, er meget vigtige i flere sammenhæng, har jeg valgt at gøre dem til globale variabler. De indeholder, som navnet siger, antallet af sange, sangens titel og sangens url.
Den anden interessante ting er, at jeg kalder en funktion, når XML filen er indlæst der hedder handleNumbers(0); Denne er ikke lavet endnu, men vil starte den første sang. Derfor har den parametret 0 med i kaldet. Grunden til at 0 er den første sang, og ikke 1, er, at i for loopet vi lavede tidligere, hvor vi hentede data fra xml dokumentet, der starter vi med var i=0.
soundHandle funktion
Vores nuværende soundobjekt, er simpelt og perfekt til, at kalde en enkelt mp3 og afspille den.
1 2 3 4 5 6 7 8 9 10 11 |
var ff_lyd:Sound = new Sound(); ff_lyd.onLoad = function(success:Boolean) { if (success) { ff_lyd.start(); status_txt.text = "Lyden blev hentet korrekt"; } else { status_txt.text = "Der gik et eller andet galt..."; } }; ff_lyd.loadSound("ff_lifehouse.mp3", false); |
Men da vi vil have mulighed for, at afspille flere mp3 filer, bliver vi nødt til, at ændre lidt i strukturen. Derfor pakker vi sound objektet ind i en funktion som vi kalder for handleNumbers(song:Number) – den vi kaldte fra xml objektet. Vi vil gerne holde styr på alle de ting der skal ske, når brugeren skifter nummer på den ene eller anden måde.
For at bruge funktionen, skal vi bruge et nummer for den sang vi vil afspille.
Det første vi gør er, at splitte soundobjektet op, så vi initialiserer det uden for funktionen
1 |
var ff_lyd:Sound; |
og inde I funktionen føjer vi den så til den pågældende sang.
1 |
ff_lyd = new Sound(); |
Som du sikkert har lagt mærke til, har vi slettet onLoad metoden, da vi jo har lavet en preloader. Men der er derimod blevet tilføjet en onSoundComplete handler, som sørger for, at udføre en handling, når den pågældende sang er færdigspillet. I vores tilfælde vil vi gerne afspille den næste sang, når denne er færdig. Men hvis der ikke er flere tilbage, skal den bare stoppe med at tjekke. Derfor gør vi brug, af variablen currPlaying som er en variabel vi skal initialisere uden for funktionen, grunden er, at vi også vil bruge den senere hen. Denne holder værdien, af det nummer der spilles lige nu.
Den anden variabel vi skal bruge, er den globale variabel vi satte i forbindelse med vores XML objekt, hvor vi tjekkede antallet af sange – numSongs.
Grunden til, at jeg ligger 1 til currPlaying er, at den starter ved 0 og ikke 1 lige som numSongs gør.
1 2 3 4 5 6 7 8 |
ff_lyd.onSoundComplete = function() { if(currPlaying+1 == numSongs){ timeTxt.text = "00:00"; } else{ handleNumbers(currPlaying+1); } }; |
Det næste vi skal er, at loade vores sang. Og det gøres jo med loadSound og så får vi jo besked på hvilket nummer i arrayet vi skal loade i form af song derfor:
ff_lyd.loadSound(songFile[song], true);
Læg lige mærke til, at jeg har ændre værden for, om nummeret skal streames, fra false til true.
Når vi ved hvilken sang der skal hentes, kan vi lige så godt sætte preloaderen i gang med at hente sangen. Så derfor flytter vi preloader funktions kaldet op i denne funktion
preloader();
Og så er det jo på sin plads at opdater variablen, der holder styr på hvilken sang der spilles lige nu
currPlaying = song;
scrubber._visible = false; vender jeg tilbage til senere.
scrubber._visible = false;
Nu da vi har taget titlen med fra XML dokumentet, kan vi jo lige så godt skrive den ud i et tekstfelt. Og da vi allerede har et tekstfelt på scenen der hedder status_txt så lad os bare skrive titlen ud i det
status_txt.text = songName[song];
Til sidst har jeg lige fundet en fejl fra sidst, hvor vi sætter volumen lige over funktionen der styrer volumen på sound objektet. Det er jo logisk, at soundobjektet, skal eksistere for, at man kan sætte volumen for det, så derfor er metoden flyttet fra dens plads og ind i denne funktion
ff_lyd.setVolume(50);
Den samlede kode for funktionen der holder styr på sangene ser derfor således ud.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var currPlaying:Number; var ff_lyd:Sound; function handleNumbers(song:Number){ ff_lyd = new Sound(); ff_lyd.onSoundComplete = function() { if(currPlaying+1 == numSongs){ timeTxt.text = "00:00"; } else{ handleNumbers(currPlaying+1); } }; ff_lyd.loadSound(songFile[song], true); preloader(); currPlaying = song; scrubber._visible = false; status_txt.text = songName[song]; ff_lyd.setVolume(50); } |
Forrige og næste knap
Som en naturlig del af, at vores player nu kan spille flere forskellige numre, indsætter vi en Forrige og en Næste knap. Jeg opretter en knap der ser således ud (pilen der peger mod venstre). I ”Up” stadiet er den hvid, men med 60% alpha, ligesom pause og stop knapperne. Når brugeren føre musen hen over knappen går den til ”Over” stadiet, hvor den skifter til 100% alpha (stadig hvid).
Denne knap giver vi instance navnet prevBtn så kopierer vi den og placerer kopien til højre for ”stop knappen” og går op i Modify >> Transform >> Flip horizontal. Nu peger pil den modsatte vej, og så giver vi den instancenavnet nextBtn.
Se evt. billedet hvis du er I tvivl om placering.

Koden til de to knapper er simpel. prevBtn sørger for, at brugeren kan gå en sang tilbage. Derfor er det meget naturligt, at vi ser på hvilken sang der spiller nu currPlaying og trækker en fra. Så kalder vi vores handleNumbers funktion med det tal.
Dog skal vi lige sørge for, at vi ikke bare bliver ved med at træde et skridt tilbage, så vi får negative numre. For at sikre os at det ikke sker, laver vi en if sætning, der tjekker om den sang der spiller nu (currPlaying), er større end 0 (husk på, at 0 er det første nummer i vores array, og dermed den første sang) Hvis currPlaying f.eks. har værdien 1 kan vi jo godt gå et skridt tilbage og spille sangen der har plads 0 i vores sang array.
1 2 3 4 5 |
prevBtn.onRelease = function(){ if(currPlaying > 0){ handleNumbers(currPlaying-1); } } |
Princippet er sådan set det samme for nextBtn, som går et skridt frem og spiller den næste sang. Her ligger vi bare 1 til currPlaying variablen, og kalder handleNumbers funktionen med det tal. Dog skal vi sørge for, at vi ikke forsøger at finde en sang som ikke findes, derfor laver vi en if sætning, der tjekker for, om den nuværende sang er mindre end antallet af sange.
Du har sikkert lagt mærke til, at vi trækker 1 fra numSongs variablen i if sætning. Hvorfor gør vi det? Jo hvis vi har 3 sange i xml dokumentet, vil der være 3 noder og derfor vil numSongs have værdien 3 når vi tjekker for for length for vores childNodes. Men da vores songFile array starter ved 0 og slutter på 2 i det tilfælde, vil den sidste sang have værdien 2 og dermed bliver vi nødt til at trække 1 fra numSongs for at kunne sammenligne de to værdier.
1 2 3 4 5 |
nextBtn.onRelease = function(){ if(currPlaying < numSongs-1){ handleNumbers(currPlaying+1); } } |
Scrubber
Vi vil gerne lave en scrubber der illustrativt viser, hvor meget af nummeret der er blevet afspillet. Til det formål laver vi et scrubber mc som vi kalder for scrubber. Inde i det ligger der et nyt mc, vi kalder for scrubberBar. Det er en grøn linie, der er 1 px høj og 470px lang. Husk at den grønne linie i scrubberBar mc’et skal have registreringspunktet (0,0).

Oven på baren, ligger vi et andet mc der får instancenavnet scrubberBtn. Det indeholder en kasse der har størrelsen 470x10 og som er fuldstændig transparent.

Ideen er, at scrubber mc’et ligges oven på preloaderen, og først kommer til syne når mp3 filen er loadet færdig. Derefter viser den hvor meget af sangen der er blevet afspillet, og man kan hive i den så den fungere som en slags ”spole” knap.
Det først vi gør er, at lave en funktion der sørger for, at scrubberen viser hvor meget af sangen der er blevet afspillet. Funktionen kalder vi for playHead og den kaldes fra preloader funktionen, når mp3 filen er loadet færdig. Så find det sted hvor der står
if(numPercentLoaded > 99){
og tilføj playHead kaldet lige neden under
1 2 |
if(numPercentLoaded > 99){ playHead(); |
Godt, så kan vi gå videre med fylde kode ind i funktionen.
Da vi tidligere kaldte handleNumbers for at spille et nyt nummer satte vi
scrubber._visible = false;
for at den ikke var synlig mens preloaderen kørte. Men nu vil vi gerne have den synlig igen, så derfor sætter vi
scrubber._visible = true;
som det første i funktionen. Derefter starter vi en onEnterFrame event som udregner hvor meget af sangen der er blevet afspillet og sætter _width for scrubberBar’en. Det er sådan set meget simpelt, først finder vi ud af hvor lang scrubberBar’en kan blive
scrubber.scrubberBtn._width
det tal dividere vi med antallet af skunder (den globale variabel totalSec) for at finde værdien for et sekund. Og derefter ganger vi den værdi med det antal sekunder som er blevet afspillet indtil videre (den globale variabel currentSec)
(scrubber.scrubberBtn._width/totalSec)*currentSec;
Derfor kommer den samlede kode til funktionen til at se således ud
1 2 3 4 5 6 |
function playHead(){ scrubber._visible = true; this.scrubber.onEnterFrame = function(){ scrubber.scrubberBar._width = (scrubber.scrubberBtn._width/totalSec)*currentSec; } } |
Hvis du tester dit script nu, skulle du gerne kunne se, at den grønne bare flytter sig i takt med at sangen bliver afspillet.
Men vi vil jo gerne kunne trykke på vores grønne scrubber og hive i den for at ”Spole” frem i nummeret. Så nu laver vi en onPress på scrubberBtn, som er den transperante knap der ligger oven på scrubberBar'en.
scrubber.scrubberBtn.onPress = function(){
Når brugeren klikker på den og holder musseknappen ned, starter vi en ny onEnterFrame event så derfor sletter vi den ovenstående onEnterFrame som kører i playHead funktionen
delete _root.scrubber.onEnterFrame;
Når musseknappen er presse ned på scrubberBtn, kan der forekomme 3 scenarier. Det første er, at brugeren har ført musen væk fra knappen, så musens x-værdi er mindre end 0. Det andet er at brugeren har musen hen over knappen og musens x-værdi dermed er over scrubberBtn. Og til sidst kan musen være væk fra knappen men denne gang er x-værdien større end længden på scrubberBtn.
Først tjekker vi om musens x-værdi er mindre end 0, og hvis den er, sættes bredden på scrubberBar'en til 0.
1 2 3 |
if(this._xmouse < 0 ){ this._parent.scrubberBar._width = 0; } |
Hvis musens x-værdi derimod er større end bredden på knappen sættes bredden på scrubberBar'en til bredden på scrubberBtn.
1 2 3 |
else if(this._xmouse > this._parent.scrubberBtn._width){ this._parent.scrubberBar._width = this._width; } |
Til sidst er der muligheden for, at musen er hen over knappen. I det tilfælde skal vi bruger vi _xscale for scrubberBar og regner forholdet ud mellem musens x-værdi og længden på scrubberBtn i procent.
1 2 3 |
else{ this._parent.scrubberBar._xscale = ((this._xmouse*100)/this._parent.scrubberBtn._width); } |
Nu tænker du nok, jamen vi skal da også ”spole” frem eller tilbage i sangen, så den passer med scrubberen.
Ja men det gør vi først, når brugeren giver slip på musseknappen inden for eller uden for scrubberBtn
scrubber.scrubberBtn.onRelease = scrubber.scrubberBtn.onReleaseOutside = function() {
Først sletter vi lige vores onEnterFrame event
delete this.onEnterFrame;
Derefter laver vi en ny global variabel der holder styr på den nye tid, ud fra hvor vores scrubber er placeret. Den kalder vi for currentTime
_global.currentTime = (totalSec/this._width)*this._xmouse;
Her regner ud hvor hvor vi skal placere playheadet ud fra hvor scrubberen er placeret. Dvs. nummerets varighed i sekunder totalSec divideret med bredden på scrubberBtn. Så kender vi værdien for en pixel i sekunder. Derefter ganger vi det tal med musens x-værdi.
NB Det er vigtigt, at vi i soundTime() funktionen, der holder styr på tiden for mp3 filen, ændre variablerne
1 2 |
var totalSec var currentSec |
til globale variabler
1 2 |
_global.totalSec _global.currentSec |
Ellers kan vi ikke lave ovenstående udregning for currentTime variablen.
Når vi har værdien for currentTime kan vi bruge vores start() metoden for at starte nummeret på det rigtige sted.
ff_lyd.start(currentTime);
Til sidst kalder vi playHead() funktionen igen, for at scrubberen fortsætter med at vise hvor langt i nummeret man er nået.
playHead();
Den samlede kode kommer til at se således ud for scrubberen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
scrubber.scrubberBtn.onPress = function(){ delete _root.scrubber.onEnterFrame; this.onEnterFrame = function(){ if(this._xmouse < 0 ){ this._parent.scrubberBar._width = 0; } else if(this._xmouse > this._parent.scrubberBtn._width){ this._parent.scrubberBar._width = this._width; } else{ this._parent.scrubberBar._xscale = ((this._xmouse*100)/this._parent.scrubberBtn._width); } } } scrubber.scrubberBtn.onRelease = scrubber.scrubberBtn.onReleaseOutside = function() { delete this.onEnterFrame; _global.currentTime = (totalSec/this._width)*this._xmouse; ff_lyd.start(currentTime); playHead(); } |
Det var slut på denne tutorial for mp3 afspilleren. Hvis du har kommentarer eller spørgsmål til tutorialen er du velkommen til stille dem i denne tråd.
5 kommentarer
Hvis jeg har Æ, Ø eller Å i enten titlen eller stien på en sang i min XML fil bliver sangen ikke afspillet - hvad kan jeg gøre?
Du skal undlade at bruge Æ Ø Å i dine stier.
mineFIler/minSødeMappe/minKæmpeFil/sødÅl.mp3
skrive istedet
mineFiler/minSoedeMappe/MinkaempeFil/soedAal.mp3
Det er mest fordi at nogle kunstnere hedder "Trentemøller", og sange som "Op på hesten" og så også fordi at jeg så skulle til at ligge stierne om på samtlige af mine iTunes sange. så det var bare hvis der ikke var et script man kunne sætte ind eller noget
Jeg har lige test med æøå og kan godt få det til at spille, men det var et hurtigt AS3 eksempel
kan du ik lige prøve at indsætte dette i din allerførste frame i din _root
System.useCodepage = true;Jeg mener at det er serverafhængigt hvordan tegnene fortolkes.
Så opfind en anden metode til at navngive dine foldere og filer!
Jeg kan ikke ha' kunder eller CMS'er det tiladder crappy url'er. ... "Den mp3-afspiller du har programmeret virker ikke!".
... nej, måske skyldes det at din fil ligger her?:
/Files/SjåWe børnesange!/Se den lille(what) kattekylling?.mp3"
//pf