Ljudhantering är mycket viktigt för många typer av Flash-applikationer, som interaktiva webbplatser och spel. Så länge du vill leverera en rik interaktiv upplevelse, kanske du vill överväga att använda ljudeffekter och bakgrundsmusik. I denna handledning presenterar jag en minimalistisk ljudhanteringsram som hanterar ljud i ljudspår. Och jag ska visa hur man integrerar ljudramverket med kommandorammen från mina tidigare handledning.
Jag har spelat spel med osynlig ljudhantering, och det försämrar användarupplevelsen. Har du någonsin spelat ett spel, säg, ett actionspel där karaktärens utropsteam spelas innan den föregående rösten slutar, överlappar varandra? Det är ett resultat av dålig ljudhantering: det borde inte finnas mer än en röst av samma karaktär som spelar i taget. Ljudhanteringsramen som jag håller på att täcka tar hand om det här problemet genom att hantera ljud med ljudspår.
Exemplen i den här handledningen använder kommandorammen och scenhanteringsramen från min tidigare handledning, tänkande i kommandon (del 1, del 2) och exemplen använder också datagentalklassen från att ladda data med kommandon. Jag rekommenderar starkt att du går igenom dessa handledning först innan du går vidare. Dessutom behöver du GreenSock Tweening Platform för att slutföra exemplen.
Ljudspåret vi pratar om här har inget att göra med spel- eller filmljudspår. Ett ljudspår är ett imaginärt "spår" som hör samman med en uppspelning av a enda ljud. Ett enda ljudspår tillåter inte att mer än ett ljud spelas åt gången. Om ett ljudspår spelar ett ljud, säger vi att det är ockuperade. Om ett annat ljud ska spelas på ett upptaget ljudspår, stoppas det ljud som spelas upp, och sedan spelas den nya på spåret. Det är således rimligt att spela en singelkaraktärs röst på ett enda ljudspår, för att undvika det överlappande problemet som nämnts tidigare. Också i de flesta fall borde det finnas bara ett spår för bakgrundsmusik.
Låt oss ta en titt på några konceptuella siffror. En enda applikation kan ha flera ljudspår.
Varje ljudspår kan innehålla ett enda ljud.
Om ett ljud ska spelas på ett upptaget spår, stoppas det "gamla" ljudet först och sedan spelas det "nya" ljudet på banan.
Ljudhanteringsramen består av två klasser, den Soundmanager klass och Ljudspår klass. Varje ljudspår tilldelas en unik nyckelsträng. Ett upptaget ljudspårs underliggande ljud är faktiskt ett inbyggt SoundChannel-objekt som erhålls från den inbyggda Sound.play () -metoden, och Soundmanager klass hanterar ljudspår och organiserar uppspelning av ljud.
Här är några snabba förhandsvisningar av användningen av ramverket. Följande kod spelar ett nytt ljud på ett ljudspår i samband med nyckelsträngen "musik", var MySound är en sund klass från biblioteket.
// spela ett ljud på "musik" spåret SoundManager.play ("music", nya MySound ());
Om samma kod kod körs igen innan uppspelningen är klar stoppas det ursprungliga ljudet och ett nytt ljud spelas på musikspåret.
// stoppa det ursprungliga ljudet på "musik" spåret och spela ett nytt SoundManager.play ("music", ny MySound ());
De SoundManager.stop () Metoden stoppar ett ljudspår i samband med en angiven nyckelsträng.
// stoppa "musik" ljudspåret SoundManager.stop ("music");
För att omvandla ljudet, precis som att justera volymen, behöver vi få en referens till en ljudspårs underliggande ljudkanal. Referensen kan erhållas genom att komma åt SoundTrack.channel fast egendom.
var kanal: SoundChannel = SoundManager.getSoundTrack ("music"). kanal; var transformera: SoundTransform = channel.soundTransform; transform.volume = 0,5; channel.soundTransform = transform;
Nog teori. Låt oss gå ner till kodningen. Vi ska använda olika nyckelsträngar för att skilja olika ljudspår. Här är Ljudspår klass, som väsentligen representerar ett nyckelkanalpar. Detaljer beskrivs i kommentarer.
paketljud importera flash.media.SoundChannel; / ** * Ett ljudspår representerar ett nyckelkanalpar. * / offentlig klass SoundTrack // skrivskyddad nyckeltal privat var _key: String; public function get key (): String return _key; // skrivskyddad ljudkanalreferens privat var _kanal: SoundChannel; allmän funktion få kanal (): SoundChannel return _channel; allmän funktion SoundTrack (tangent: String, kanal: SoundChannel) _key = key; _channel = channel; / ** * Stannar den underliggande ljudkanalen. * / public function stop (): void _channel.stop ();
Och här är det Soundmanager klass. Observera att associationen med nyckelspår hanteras med hjälp av Dictionary-klassen. Ett spår tömmes automatiskt om ett ljud har nått slutet.
paketljud importera flash.events.Event; importera flash.media.Sound; importera flash.media.SoundChannel; importera flash.media.SoundTransform; importera flash.utils.Dictionary; / ** * Med den här klassen kan du hantera ljud när det gäller ljudspår. * / public class SoundManager // en ordbok som håller spår av alla ljudspår privat statisk var _soundTracks: Dictionary = new Dictionary (); // en ordlista som kartlägger en ljudkanal till dess motsvarande nyckel för uppspelningshantering hantering av privat statisk var _soundKeys: Dictionary = new Dictionary (); / ** * Spelar ett ljud och returnerar ett motsvarande ljudspårobjekt. * / statisk funktion för statisk funktion (tangent: String, ljud: Ljud, startTime: int = 0, loopar: int = 0, transform: SoundTransform = null): SoundTrack // om ljudspåret är upptaget, stoppa det aktuella ljudspåret om (isPlaying (key)) stoppa (tangent); // spela ljudet, skapa en ny ljudkanal var kanal: SoundChannel = sound.play (startTime, loopar, transform); // lyssna på hela händelsen för ljudkanalkanalen.addEventListener (Event.SOUND_COMPLETE, onSoundComplete); // skapa ett nytt ljudspår var soundTrack: SoundTrack = ny SoundTrack (nyckel, kanal); // lägg till ljudspåret i ordlistan _soundTracks [key] = soundTrack; // lägg till kanal-nyckel kartläggning relation _soundKeys [channel] = key; returnera soundTrack; / ** * Returnerar en referens till ljudspåret som motsvarar den angivna nyckelsträngen. * / statisk statisk funktion getSoundTrack (nyckel: String): SoundTrack return _soundTracks [key]; / ** * Bestämmer om ett ljudspår spelas för tillfället. * / statisk statisk funktion isPlaying (nyckel: String): Boolean Return Boolean (_soundTracks [key]); / ** * Stannar ett ljudspår. * / statisk statisk funktionstopp (tangent: String): void var soundTrack: SoundTrack = _soundTracks [key]; // kontrollera om ljudspåret existerar om (soundtrack) // stoppa ljudspåret soundTrack.stop (); // och ta bort det från ordlistan ta bort _soundTracks [key]; // tillsammans med kanal-nyckel-förhållandet radera _soundKeys [soundTrack.channel]; / ** * Tar bort ett ljudspår när ljuduppspelningen är klar * / privat statisk funktion onSoundComplete (e: Event): void // kasta händelsespeditionen till ett ljudkanalobjekt var kanal: SoundChannel = SoundChannel (e. mål); // ta bort händelseläsenarkanalen.removeEventListener (Event.SOUND_COMPLETE, onSoundComplete); // extrahera motsvarande nyckel var nyckel: String = _soundKeys [channel]; // ta bort ljudspårstoppet (tangent);
Låt oss nu pröva vår ram för ljudhantering. Vi ska jämföra resultatet av upprepade förfrågningar för att spela ett ljud med och utan att använda ljudhanteraren.
Skapa ett nytt Flash-dokument (duh).
Skapa två knappar på scenen. Du kan rita din egen och konvertera dem till symboler, eller så kan du, som i mitt fall, dra två knappkomponenter från komponentpanelen. Namnge dem "boing_btn" och "managedBoing_btn".
Importera ljudet vi ska spela till biblioteket. Du kan hitta filen "Boing.wav" i exemplet källkatalogen.
Slutligen skapa en AS-fil för dokumentklassen. Koden är ganska enkel, så jag förklarar bara allt i kommentarerna.
paket import flash.display.Sprite; importera flash.events.MouseEvent; importera flash.media.Sound; importera ljud.SoundManager; allmän klass BoingPlayer utökar Sprite public function BoingPlayer () // lägg till klicklistorna för båda knapparna boing_btn.addEventListener (MouseEvent.CLICK, onBoing); managedBoing_btn.addEventListener (MouseEvent.CLICK, onManagedBoing); // spela ljudet direkt genom att aktivera metoden Sound.play (). privat funktion onBoing (e: MouseEvent): void var ljud: Ljud = ny Boing (); sound.play (); // spela ljudet med ljudhanteraren på "Boing" ljudspårens privata funktion onManagedBoing (e: MouseEvent): void var ljud: Ljud = nytt Boing (); SoundManager.play ("boing", ljud);
Var gjort. Tryck Ctrl + Enter för att testa filmen och försök snabbt att klicka på knapparna (kom ihåg att slå på högtalarna). För "Boing!" knappen, flera ljud överlappar när de spelas. När det gäller "Managed Boing!" knappen, som använder ljudhanteraren, måste ett ljud tvingas stoppa innan nästa spelas, så du hör inte ljuden blandas ihop tillsammans.
Milstolpe Visa det på nätetKommandon, kommandon, kommandon. Det är alltid trevligt att integrera ditt arbete med dina tidigare, eller hur? Nu ska vi integrera ljudhanteringsramverket med kommandorammen, tillsammans med scenhanteringsramen. Återigen, om du inte är bekant med kommandorammen och scenhanteringsramen, kan du bättre kolla in dem i mina tidigare handledning (del 1, del 2) innan du fortsätter.
Namnet på det här kommandot är ganska självförklarande: det spelar ett ljud med ljudhanteraren.
paketkommandon.sounds import commands.Command; importera flash.media.Sound; importera flash.media.SoundTransform; importera ljud.SoundManager; / ** * Detta kommando spelar ett ljud. * / Public Class PlaySound utökar kommandot public var-tangent: String; allmänheten var ljud: ljud; public var startTime: int; offentliga var slingor: int; public var transformer: SoundTransform; allmän funktion PlaySound (tangent: String, ljud: Ljud, startTime: int = 0, loopar: int = 0, transform: SoundTransform = null) this.key = key; this.sound = ljud; this.startTime = startTime; this.loops = loopar; this.transform = transform; åsidosätta skyddad funktion exekvera (): void // berätta ljudhanteraren för att spela ljudet SoundManager.play (tangent, ljud, startTime, loopar, transformera); // Slutför kommandot komplett ();
Detta är i grunden den onda kusinen till det föregående kommandot. Kommandot stoppar ett ljudspår genom att använda ljudhanteraren.
paketkommandon.sounds import commands.Command; importera ljud.SoundManager; / ** * Detta kommando stoppar ett ljudspår som motsvarar en given knapp. * / offentlig klass StopSound utökar kommandot public var-nyckel: String; offentlig funktion StopSound (nyckel: String) this.key = key; åsidosätta skyddad funktion exekvera (): void // berätta ljudhanteraren för att stoppa ljudspåret, hur ont>:] SoundManager.stop (key); // Slutför kommandot komplett ();
Detta kommando laddar en extern MP3-fil till a Ljud objekt. Inte tills laddningen är klar kommer kommandot att komplett() metod kallas. Detta gör det möjligt för oss att enkelt kombinera kommandot med andra kommandon, utan att behöva oroa sig för att hantera laddningsfärdigställandet.
paketkommandon.loading import commands.Command; importera flash.events.Event; importera flash.media.Sound; importera flash.net.URLRequest; / ** * Detta kommando laddar upp ett ljud. * / public class SoundLoad utökar kommando public var sound: Sound; public var url: URLRequest; offentlig funktion SoundLoad (ljud: Ljud, URL: URLRequest) this.sound = sound; this.url = url; åsidosätta skyddad funktion exekvera (): void // lägg till hela lyssnarens ljud.addEventListener (Event.COMPLETE, onComplete); // börja ladda sound.load (url); privat funktion onComplete (e: Event): void // ta bort hela lyssnarens ljud.removeEventListener (Event.COMPLETE, onComplete); // Slutför kommandot komplett ();
Integrationen är klar. Förbered dig på vårt slutliga exempel!
I det här exemplet kommer vi att tillåta användare att spela två musik på samma ljudspår. Om ett ljud ska spelas när ljudspåret är upptaget, förstörs den ursprungliga musiken först och sedan spelas den nya musiken. Fade-out hanteras av TweenMaxTo kommando, som internt använder den speciella egendomen volym tillhandahållen av TweenMax klass från GreenSock Tweening Platform. De två musikerna är externa MP3-filer som laddas under körtiden.
Observera att vi ska använda scenhanteringsramen. Om du vill uppdatera ditt minne, kolla här.
Gör en kopia av FLA-filen som användes i föregående exempel. Byt namn på knapparna till "music1_btn" och "music2_btn". Du kan också ändra knappens etiketter till "Musik 1" och "Musik 2". Och lägg till en extra knapp med namnet "stop_btn", vilket är för att stoppa musiken.
MP3-filerna finns i källkatalogen. Kopiera dem till samma mapp som FLA-filen.
Skapa en ny AS-fil för dokumentklassen i den nya FLA-filen. Instantiera en scenhanterare och initiera den till ett laddningstillstånd där de två MP3-filerna laddas.
paket import flash.display.Sprite; import scenes.SceneManager; public class MusicPlayer utökar Sprite public function MusicPlayer () // instansera ett scenhanteringsobjekt var scenManager: SceneManager = new SceneManager (); // Ange en laddningsplats som den ursprungliga scenen ScenManager.setScene (ny LoadingScene (this));
Laddningsplatsen instansierar två Ljud objekt för att ladda de två MP3-filerna. Knapparna är osynliga i början och kommer att ställas in igen när laddningen är klar. När laddningen är klar instruerar scenen omedelbart scenhanteraren att gå vidare till huvudplatsen, som skrivet i överskridandet onSceneSet () metod. Ytterligare detaljer beskrivs i kommentarerna.
paket import commands.Command; importera kommandon.data.RegisterData; importera kommandon.loading.SoundLoad; importera kommandon.ParallelCommand; import commands.SerialCommand; importera kommandon.utils.SetProperties; importera flash.events.Event; importera flash.media.Sound; importera flash.net.URLRequest; import scenes.Scene; public class LoadingScene utökar scenen // en hänvisning till dokumentrotsbehållaren private var container: MusicPlayer; allmän funktion LoadingScene (container: MusicPlayer) this.container = container; åsidosätta den offentliga funktionen createIntroCommand (): Command // skapa två ljudobjekt för att ladda de två MP3-filer var music1: Sound = new Sound (); var music2: Sound = new Sound (); var command: Command = new ParallelCommand (0, // gömma knapparna nya SetProperties (container.music1_btn, ala: 0, synlig: false), nya SetProperties (container.music2_btn, alfa: 0, synlig: false) , nya SetProperties (container.stop_btn, alpha: 0, visible: false), // registrera de två ljudobjekten till datahanteraren nya RegisterData ("music1", music1), nya RegisterData ("music2", music2) // börja ladda MP3-filerna med nya SoundLoad (music1, new URLRequest ("Music1.mp3")), ny SoundLoad (music2, ny URLRequest ("Music2.mp3"))); returnera kommandot; åsidosätta den offentliga funktionen onSceneSet (): void // berätta scenhanteraren för att växla till huvudplatsen direkt efter intro-kommandot är klar scenManager.setScene (ny MainScene (container));
Huvudplatsen återställer de dolda knapparna till synliga och registrerar spela musik() metod och stopMusic () metod som lyssnare för klickhändelsen. I spela musik() metod utförs ett seriellt kommando om "bgm" ljudspåret upptas. Kommandot avlägsnar först klicklistorna, försvinner den aktuella musiken, stoppar den aktuella musiken, spelar den nya musiken på det nuvarande "bgm" -ljudspåret och lägger till slut klicklistorna. De stopMusic Metoden gör i princip samma sak, bara att det inte finns någon ny musikuppspelning. Denna komplexa serie av åtgärder utförs i endast några få rader med ren kod. Ganska snyggt, va?
Observera att lägga till och ta bort lyssnare är vanliga åtgärder som finns i båda spela musik() metod och stopMusic () metod. Så de är fakturerade som två privata fastigheter, addListeners och removeListeners, initierad i konstruktorn.
paket import commands.Command; importera kommandon.events.AddEventListener; importera kommandon.events.RemoveEventListener; importera kommandon.greensock.TweenMaxTo; importera kommandon.ParallelCommand; import commands.SerialCommand; importera kommandon.sounds.PlaySound; importera kommandon.sounds.StopSound; importera data.DataManager; importera flash.display.Sprite; importera flash.events.Event; importera flash.events.MouseEvent; importera flash.media.Sound; importera flash.utils.Dictionary; import scenes.Scene; importera ljud.SoundManager; importera ljud. SoundTrack; / ** * Huvudplatsen visas när laddningen är klar. * / public class MainScene utökar scenen // en hänvisning till dokument root container privat var container: MusicPlayer; privat var btn1: Sprite; privat var btn2: Sprite; privat var btn3: Sprite; private var dataKeys: Dictionary = ny ordbok (); privat var addListeners: Command; privat var removeListeners: Command; allmän funktion MainScene (container: MusicPlayer) this.container = container; btn1 = container.music1_btn; btn2 = container.music2_btn; btn3 = container.stop_btn; // datatangenter som används för att hämta ljudobjekt från datahanteraren i metoden data playMusic () methodKeys [btn1] = "music1"; dataKeys [btn2] = "music2"; // detta kommando lägger till alla lyssnare addListeners = nya ParallelCommand (0, nya AddEventListener (btn1, MouseEvent.CLICK, playMusic), nya AddEventListener (btn2, MouseEvent.CLICK, playMusic), nya AddEventListener (btn3, MouseEvent.CLICK, stopMusic)) ; // detta kommando tar bort alla lyssnare removeListeners = nya ParallelCommand (0, nya RemoveEventListener (btn1, MouseEvent.CLICK, playMusic), nya RemoveEventListener (btn2, MouseEvent.CLICK, playMusic), nya RemoveEventListener (btn3, MouseEvent.CLICK, stopMusic)) ; överstiga den offentliga funktionen createIntroCommand (): Command varkommando: Command = new SerialCommand (0, // fade i knapparna nya ParallelCommand (0, nya TweenMaxTo (btn1, 1, autoAlpha: 1), nya TweenMaxTo (btn2, 1, autoAlpha: 1), ny TweenMaxTo (btn3, 1, autoAlpha: 1)), // lägg till klicka lyssnare addListeners); returnera kommandot; / ** * Spelar upp musiken. * / privat funktion playMusic (e: Event): void // hämta ljudobjektet som motsvarar en datatangent var musik: Ljud = DataManager.getData (dataKeys [e.target]); // kontrollera om BGM-ljudspåret spelas redan om (SoundManager.isPlaying ("bgm")) // hämta spårspåret var soundTrack: SoundTrack = SoundManager.getSoundTrack ("bgm"); var command: Command = new SerialCommand (0, // tillfälligt ta bort klicka lyssnare removeListeners, // fade ut det aktuella ljudspåret nytt TweenMaxTo (soundTrack.channel, 1, volume: 0), // och sedan stoppa ljudspåret ny StopSound ("bgm"), // spela ett nytt ljud på samma ljudspår nya PlaySound ("bgm", musik, 0, int.MAX_VALUE), // lägg till återkommande klicklistor addListeners); command.start (); annars // bara spela ljudet om ljudspåret är ledigt SoundManager.play ("bgm", musik, 0, int.MAX_VALUE); / ** * Stoppar den musik som spelas för närvarande. * / Private Function StopMusic (e: Event): void // kontrollera om BGM ljudspåret spelas redan om (SoundManager.isPlaying ("bgm")) // hämta spårspåret var soundTrack: SoundTrack = SoundManager. getSoundTrack ( "BGM"); var command: Command = new SerialCommand (0, // tillfälligt ta bort klicka lyssnare removeListeners, // fade ut det aktuella ljudspåret nytt TweenMaxTo (soundTrack.channel, 1, volume: 0), // och sedan stoppa ljudspåret ny StopSound ("bgm"), // re-add klicka lyssnare addListeners); command.start ();
Vi är redo att testa filmen. Tryck CTRL + ENTER för att testa den. När du klickar på en knapp börjar en musik att spela. Efter att ha klickat på en annan, börjar musiken ut och sedan börjar en ny från början.
Milstolpe Visa det på nätetDet är slutet på handledningen, jag vet. Men jag kunde inte motstå att visa detta för dig. Om du är en code jockey, jag vet att du redan har märkt att det finns många likheter i spela musik() metod och stopMusic () metod. Varför inte refactor dem till en enda? Om du inte är intresserad av den här jocky-versionen av musikspelaren kan du hoppa över till sammanfattningsdelen. Annars, fortsätt bara med att läsa!
Först ersätta alla spela musik och stopMusic i källkoden med handleMusic, vår nya evenemangslyttare. Därefter raderas spela musik och den stopMusic metod och lägg till följande handleMusic () metod i huvudscenen.
/ ** * Spelar eller stoppar musiken. Code jockey version. * / privatfunktionshandtagMusik (e: Event): void var musik: Ljud = DataManager.getData (dataKeys [e.target]); om (SoundManager.isPlaying ("bgm")) var soundTrack: SoundTrack = SoundManager.getSoundTrack ("bgm"); var command: Command = new SerialCommand (0, removeListeners, new TweenMaxTo (soundTrack.channel, 1, volym: 0), ny StopSound ("bgm"), // avgöra om vi ska spela en annan musik )? (ny PlaySound ("bgm", musik, 0, int.MAX_VALUE)): (nya Dummy ()), addListeners); command.start (); annars om (musik) SoundManager.play ("bgm", musik, 0, int.MAX_VALUE);
Du märker att den enda stora skillnaden mellan den här metoden och de ursprungliga lyssnarna är följande bit av kod:
(musik)? (ny PlaySound ("bgm", musik, 0, int.MAX_VALUE)): (ny Dummy ()),
Vad i helvete är det i alla fall? Detta är faktiskt den?: Villkorlig operatör. Det är en ternär operatör, vilket innebär att det kräver tre operander, A, B och C. Uttrycket "A? B: C" utvärderar till B om A är sant, eller C annars. De musik variabel ska innehålla en hänvisning till a Ljud objekt, så att variabeln utvärderas till sann. Om händelsespeditionsmålet är "stop_btn" -knappen innehåller variabeln ett nullvärde, vilket utvärderar till falskt i den ternära operatören. Så, om de två musikknapparna klickas, anses ovanstående kodbit som den enkla raden av kod nedan.
ny PlaySound ("bgm", musik, 0, int.MAX_VALUE)
Annars, om stoppknappen är klickad, betraktas kodbiten som ett dummy-kommando, vilket helt enkelt inte gör någonting.
ny Dummy ()
Bara en sak att märka. Följande kodrad
SoundManager.play ("bgm", musik, 0, int.MAX_VALUE);
ändras till
om (musik) SoundManager.play ("bgm", musik, 0, int.MAX_VALUE);
Detta är för att hantera undantaget att ljudspåret är tomt. Om du kan förstå koden ovanför är jag ganska säker på att du kan ta reda på vad den här raden handlar om.
Testa filmen genom att trycka på Ctrl + Enter, så får du se exakt samma resultat som det sista exemplet. Du kan betrakta det som en uppfyllelse av en kodjockey kodningsfärdighet.
I denna handledning har du lärt dig hur du hanterar ljud med ljudspår. Ett ljudspår låter bara ett ljud spelas av en gång, vilket är idealiskt för att representera en singelpersons röst eller bakgrundsmusik. Du har också sett hur du integrerar ljudhanteringsramverket med kommandorammen, vilket ger dig ett stort underhåll och flexibilitetshöjning på dina applikationer.
Detta är slutet på handledningen. Jag hoppas att du tyckte om det. Tack så mycket för att du läser!