La gestione del suono è molto importante per molti tipi di applicazioni Flash, come siti Web e giochi interattivi. Finché si desidera offrire una ricca esperienza interattiva, è possibile prendere in considerazione l'utilizzo di effetti sonori e musica di sottofondo. In questo tutorial presenterò una struttura di gestione del suono minimalista che gestisce i suoni in tracce sonore. E mostrerò come integrare il framework audio con il framework dei comandi dai miei tutorial precedenti.
Ho giocato a giochi con una gestione audio incauta e questo degrada l'esperienza dell'utente. Hai mai giocato a un gioco, ad esempio un gioco d'azione, in cui la voce esclamativa del personaggio viene riprodotta prima che la voce precedente si concluda, sovrapponendosi a vicenda? Questo è il risultato di una cattiva gestione del suono: non ci dovrebbe essere più di una voce dello stesso personaggio che gioca in un momento. La struttura di gestione del suono che sto per affrontare si occuperà di questo problema gestendo i suoni con le tracce audio.
Gli esempi in questo tutorial fanno uso del quadro comandi e del framework di gestione delle scene del mio precedente tutorial, Thinking in Commands (Parte 1, Parte 2), e gli esempi usano anche la classe data manager da Loading Data with Commands. Consiglio vivamente di passare attraverso questi tutorial prima di andare avanti. Inoltre, avrai bisogno della piattaforma Tweening GreenSock per completare gli esempi.
La colonna sonora di cui stiamo parlando non ha nulla a che fare con le tracce audio di giochi o film. Una colonna sonora è una "traccia" immaginaria associata a una riproduzione di a singolo suono. Una singola traccia audio non consente più di una riproduzione audio alla volta. Se una traccia audio sta attualmente riproducendo un suono, diciamo che lo è occupato. Se un altro suono deve essere riprodotto su una traccia audio occupata, il suono attualmente in riproduzione viene interrotto, e quindi il nuovo brano viene riprodotto sulla traccia. È quindi ragionevole suonare le voci di un singolo personaggio su una singola traccia audio, in modo da evitare il problema di sovrapposizione di suoni menzionato in precedenza. Inoltre, nella maggior parte dei casi, dovrebbe esserci una sola traccia per la musica di sottofondo.
Diamo un'occhiata ad alcune figure concettuali. Una singola applicazione può avere più tracce audio.
Ogni traccia audio può contenere un singolo suono di riproduzione.
Se un suono deve essere riprodotto su una traccia occupata, il suono "vecchio" viene prima arrestato e quindi il suono "nuovo" viene riprodotto sulla traccia.
Il framework Sound Management consiste di due classi, la SoundManager classe e il Colonna sonora classe. Ad ogni traccia audio viene assegnata una stringa di tasti univoca. Il suono di sottofondo di un brano musicale occupato è in realtà un oggetto SoundChannel nativo ottenuto dal metodo nativo Sound.play () e il SoundManager classe gestisce le tracce sonore e organizza la riproduzione dei suoni.
Ecco alcune anteprime rapide sull'utilizzo del framework. Il seguente codice riproduce un nuovo suono su una traccia sonora associata alla stringa di tasti "musica", dove MySound è una classe sonora dalla libreria.
// riproduce un suono sulla traccia "music" SoundManager.play ("music", new MySound ());
Se la stessa riga di codice viene eseguita nuovamente prima della fine della riproduzione, il suono originale viene interrotto e viene riprodotto un nuovo suono sulla traccia "musicale".
// ferma il suono originale sulla traccia "music" e riproduci uno nuovo SoundManager.play ("music", new MySound ());
Il SoundManager.stop () metodo interrompe una traccia sonora associata a una stringa di tasti specificata.
// ferma la traccia audio "music" SoundManager.stop ("music");
Per trasformare il suono, come per regolare il volume, è necessario ottenere un riferimento al canale sonoro sottostante di una traccia audio. Il riferimento può essere ottenuto accedendo al SoundTrack.channel proprietà.
var channel: SoundChannel = SoundManager.getSoundTrack ("music"). channel; var transform: SoundTransform = channel.soundTransform; transform.volume = 0,5; channel.soundTransform = transform;
Basta teoria. Passiamo alla codifica. Useremo diversi stringhe chiave per distinguere diverse tracce audio. Ecco il Colonna sonora classe, che rappresenta essenzialmente una coppia chiave-canale. I dettagli sono descritti nei commenti.
pacchetto sounds import flash.media.SoundChannel; / ** * Una colonna sonora rappresenta una coppia di canali chiave. * / public class SoundTrack // valore della chiave di sola lettura private var _key: String; get funzione pubblica (): String return _key; // riferimento del canale audio di sola lettura private var _channel: SoundChannel; funzione pubblica get channel (): SoundChannel return _channel; public function SoundTrack (key: String, channel: SoundChannel) _key = key; _canale = canale; / ** * Arresta il canale audio sottostante. * / public function stop (): void _channel.stop ();
Ed ecco il SoundManager classe. Si noti che l'associazione delle tracce chiave viene gestita utilizzando la classe Dizionario. Una traccia si svuota automaticamente se un suono di riproduzione ha raggiunto la sua fine.
pacchetto sounds import flash.events.Event; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundTransform; import flash.utils.Dictionary; / ** * Questa classe ti consente di gestire i suoni in termini di tracce audio. * / public SoundManager // un dizionario che tiene traccia di tutte le tracce audio private static var _soundTracks: Dictionary = new Dictionary (); // un dizionario che mappa un canale audio alla sua chiave corrispondente per il completamento della riproduzione che gestisce la variabile statica privata _soundKeys: Dictionary = new Dictionary (); / ** * Riproduce un suono e restituisce un oggetto traccia audio corrispondente. * / public static function play (tasto: String, suono: Sound, startTime: int = 0, loop: int = 0, transform: SoundTransform = null): SoundTrack // se la traccia audio è occupata, ferma la traccia audio corrente se (isPlaying (chiave)) stop (tasto); // riproduce il suono, creando un nuovo canale audio var channel: SoundChannel = sound.play (startTime, loops, transform); // ascolta l'evento completo del canale audio channel.addEventListener (Event.SOUND_COMPLETE, onSoundComplete); // crea una nuova traccia audio var soundTrack: SoundTrack = new SoundTrack (chiave, canale); // aggiungi la colonna sonora al dizionario _soundTracks [chiave] = soundTrack; // aggiungi la relazione di mappatura dei canali dei canali _soundKeys [canale] = chiave; return soundTrack; / ** * Restituisce un riferimento alla colonna sonora corrispondente alla stringa chiave fornita. * / public static function getSoundTrack (key: String): SoundTrack return _soundTracks [key]; / ** * Determina se una traccia sonora è attualmente in riproduzione. * / public static function isPlaying (key: String): Boolean return Boolean (_soundTracks [key]); / ** * Interrompe una traccia audio. * / public static function stop (key: String): void var soundTrack: SoundTrack = _soundTracks [chiave]; // controlla se la traccia sonora esiste se (soundTrack) // interrompe il suono della traccia soundTrack.stop (); // e rimuoverlo dal dizionario delete _soundTracks [key]; // insieme alla relazione chiave-canale cancella _soundKeys [soundTrack.channel]; / ** * Rimuove una colonna sonora quando la riproduzione audio è completa * / funzione statica privata onSoundComplete (e: Event): void // lancia il dispatcher di eventi su un oggetto canale audio var channel: SoundChannel = SoundChannel (e. bersaglio); // rimuove il listener di eventi channel.removeEventListener (Event.SOUND_COMPLETE, onSoundComplete); // estrae la chiave var key corrispondente: String = _soundKeys [canale]; // rimuove la fermata della traccia sonora (tasto);
Ora testiamo la nostra struttura di gestione del suono. Confronteremo il risultato di richieste ripetute per riprodurre un suono con e senza utilizzare il gestore audio.
Crea un nuovo documento Flash (duh).
Crea due pulsanti sul palco. Puoi disegnare i tuoi e convertirli in simboli, oppure puoi, come nel mio caso, trascinare due componenti Button dal pannello Componenti. Chiamali "boing_btn" e "managedBoing_btn".
Importa il suono che suoneremo nella libreria. È possibile trovare il file "Boing.wav" nella cartella di origine di esempio.
Infine, crea un file AS per la classe del documento. Il codice è piuttosto semplice, quindi spiego tutto nei commenti.
pacchetto import flash.display.Sprite; import flash.events.MouseEvent; import flash.media.Sound; suoni importati. SoundManager; public class BoingPlayer estende Sprite public function BoingPlayer () // aggiungi i listener dei clic per entrambi i pulsanti boing_btn.addEventListener (MouseEvent.CLICK, onBoing); managedBoing_btn.addEventListener (MouseEvent.CLICK, onManagedBoing); // riproduce direttamente il suono invocando il metodo Sound.play (). funzione privata onBoing (e: MouseEvent): void var sound: Sound = new Boing (); sound.play (); // riproduce il suono con il sound manager sulla funzione privata della traccia audio "boing" suManagedBoing (e: MouseEvent): void var sound: Sound = new Boing (); SoundManager.play ("boing", suono);
Sono stati fatti. Premi Ctrl + Invio per testare il film e prova a fare rapidamente clic sui pulsanti (ricorda di accendere i diffusori). Per il "Boing!" pulsante, più suoni si sovrappongono durante la riproduzione. Per quanto riguarda il "Managed Boing!" pulsante, che fa uso del gestore del suono, un suono è forzato a fermarsi prima di suonare il prossimo, quindi non sentirete i suoni mescolati insieme.
Pietra miliare Visualizza onlineComandi, comandi, comandi. È sempre bello integrare il tuo lavoro con i tuoi precedenti, giusto? Ora integreremo il framework di gestione del suono con il framework dei comandi, insieme al framework di gestione delle scene. Anche in questo caso, se non si ha familiarità con il quadro dei comandi e il framework di gestione delle scene, è meglio controllarli nelle mie precedenti esercitazioni (Parte 1, Parte 2) prima di andare avanti.
Il nome di questo comando è piuttosto auto-esplicativo: suona un suono con il sound manager.
pacchetto commands.sounds import commands.Command; import flash.media.Sound; import flash.media.SoundTransform; suoni importati. SoundManager; / ** * Questo comando riproduce un suono. * / public class PlaySound estende Command public var key: String; suono var pubblico: Suono; public var startTime: int; var var in pubblico: int; var var pubblico: SoundTransform; funzione pubblica PlaySound (tasto: String, suono: Sound, startTime: int = 0, loop: int = 0, transform: SoundTransform = null) this.key = key; this.sound = suono; this.startTime = startTime; this.loops = loop; this.transform = transform; override protected function execute (): void // indica al sound manager di riprodurre il suono SoundManager.play (key, sound, startTime, loops, transform); // completa il comando complete ();
Questo è fondamentalmente il cugino cattivo del comando precedente. Questo comando arresta una traccia audio utilizzando il gestore audio.
pacchetto commands.sounds import commands.Command; suoni importati. SoundManager; / ** * Questo comando arresta una colonna sonora corrispondente ad un dato tasto. * / public class StopSound estende Command public var key: String; funzione pubblica StopSound (key: String) this.key = key; override protected function execute (): void // indica al gestore del suono di interrompere la traccia audio, come diabolica>:] SoundManager.stop (chiave); // completa il comando complete ();
Questo comando carica un file MP3 esterno in a Suono oggetto. Non fino a quando il caricamento è completo sarà il comando completare() metodo essere chiamato. Questo ci consente di concatenare facilmente il comando con altri comandi, senza doversi preoccupare di gestire il completamento del caricamento.
pacchetto commands.loading import commands.Command; import flash.events.Event; import flash.media.Sound; import flash.net.URLRequest; / ** * Questo comando carica un suono. * / public class SoundLoad estende Command public var sound: Sound; url var pubblico: URLRequest; funzione pubblica SoundLoad (suono: Sound, url: URLRequest) this.sound = sound; this.url = url; override protected function execute (): void // aggiungi il listener completo sound.addEventListener (Event.COMPLETE, onComplete); // inizia a caricare sound.load (url); funzione privata onComplete (e: Event): void // rimuove il listener completo sound.removeEventListener (Event.COMPLETE, onComplete); // completa il comando complete ();
Integrazione completata. Preparati per il nostro ultimo esempio!
In questo esempio, consentiremo agli utenti di riprodurre due brani musicali sulla stessa traccia audio. Se un suono deve essere riprodotto quando la traccia sonora è occupata, la musica originale viene prima disattivata, quindi viene riprodotta la nuova musica. La dissolvenza è gestita dal TweenMaxTo comando, che utilizza internamente la proprietà speciale volume fornito dal TweenMax classe dalla piattaforma Tweening di GreenSock. Le due musiche sono file MP3 esterni caricati durante l'esecuzione.
Nota che useremo il framework di gestione delle scene. Se vuoi rinfrescare la memoria, vai a controllare qui.
Crea una copia del file FLA utilizzato nell'esempio precedente. Rinominare i pulsanti su "music1_btn" e "music2_btn". È inoltre possibile modificare le etichette dei pulsanti su "Musica 1" e "Musica 2". E aggiungi un pulsante extra chiamato "stop_btn", che serve per fermare la musica.
I file MP3 possono essere trovati nella cartella sorgente. Copiali nella stessa cartella del file FLA.
Creare un nuovo file AS per la classe di documento del nuovo file FLA. Crea un'istanza di un gestore scene e inizializza lo stato di caricamento, in cui vengono caricati i due file MP3.
pacchetto import flash.display.Sprite; import scene.SceneManager; public class MusicPlayer estende Sprite public function MusicPlayer () // crea un'istanza di un oggetto Scene Manager var SceneManager: SceneManager = new SceneManager (); // imposta una scena di caricamento come scena scena inizialeManager.setScene (new LoadingScene (this));
La scena di caricamento istanzia due Suono oggetti per il caricamento dei due file MP3. I pulsanti sono invisibili all'inizio e saranno nuovamente visibili quando il caricamento è terminato. Una volta completato il caricamento, la scena istruisce immediatamente il responsabile della scena a passare alla scena principale, come scritto nell'override onSceneSet () metodo. Ulteriori dettagli sono descritti nei commenti.
package import commands.Command; import commands.data.RegisterData; import commands.loading.SoundLoad; import commands.ParallelCommand; import commands.SerialCommand; import commands.utils.SetProperties; import flash.events.Event; import flash.media.Sound; import flash.net.URLRequest; scene import.Scene; public class LoadScene estende Scene // un riferimento al contenitore var per il contenitore radice del documento: MusicPlayer; funzione pubblica LoadingScene (container: MusicPlayer) this.container = container; override public function createIntroCommand (): Command // crea due oggetti sonori per caricare i due file MP3 var music1: Sound = new Sound (); var music2: Sound = new Sound (); var command: Command = new ParallelCommand (0, // nasconde i pulsanti new SetProperties (container.music1_btn, alpha: 0, visible: false), new SetProperties (container.music2_btn, alpha: 0, visible: false) , nuove SetProperties (container.stop_btn, alpha: 0, visible: false), // registrano i due oggetti sonori nel nuovo DataDatabase di Data Manager ("music1", music1), new RegisterData ("music2", music2), // inizia il caricamento dei file MP3 nuovo SoundLoad (music1, new URLRequest ("Music1.mp3")), nuovo SoundLoad (music2, new URLRequest ("Music2.mp3"))); comando di ritorno; override public function onSceneSet (): void // indica al gestore scene di passare alla scena principale direttamente dopo che il comando di introduzione è completo. SceneManager.setScene (new MainScene (container));
La scena principale riporta i pulsanti nascosti a visibili e registra i file suonare() metodo e il stopMusic () metodo come ascoltatori per l'evento click. Nel suonare() metodo, un comando seriale viene eseguito se la traccia sonora "bgm" è occupata. Il comando rimuove temporaneamente gli ascoltatori dei clic, attenua la musica corrente, interrompe la musica corrente, riproduce la nuova musica sulla traccia audio "bgm" ora vuota e infine aggiunge nuovamente gli ascoltatori dei clic. Il stopMusic il metodo fondamentalmente fa la stessa cosa, solo che non c'è una nuova riproduzione musicale. Questa complessa serie di azioni viene eseguita in poche righe di codice pulito. Abbastanza pulito, eh?
Si noti che l'aggiunta e la rimozione degli ascoltatori sono azioni comuni presenti in entrambi suonare() metodo e il stopMusic () metodo. Quindi vengono considerati come due proprietà private, addListeners e removeListeners, inizializzato nel costruttore.
package import commands.Command; import commands.events.AddEventListener; import commands.events.RemoveEventListener; import commands.greensock.TweenMaxTo; import commands.ParallelCommand; import commands.SerialCommand; import commands.sounds.PlaySound; import commands.sounds.StopSound; import data.DataManager; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.media.Sound; import flash.utils.Dictionary; scene import.Scene; suoni importati. SoundManager; import sounds.SoundTrack; / ** * La scena principale viene visualizzata al termine del caricamento. * / public class MainScene estende Scene // un riferimento al container var contenitore del documento root privato: MusicPlayer; private var btn1: Sprite; private var btn2: Sprite; private var btn3: Sprite; private var dataKeys: Dictionary = new Dictionary (); private var addListeners: Command; private var removeListeners: Command; funzione pubblica MainScene (container: MusicPlayer) this.container = container; btn1 = container.music1_btn; btn2 = container.music2_btn; btn3 = container.stop_btn; // chiavi dati utilizzate per recuperare oggetti sonori dal gestore dati nel metodo playMusic () dataKeys [btn1] = "music1"; dataKeys [btn2] = "music2"; // questo comando aggiunge tutti gli ascoltatori addListeners = new ParallelCommand (0, nuovo AddEventListener (btn1, MouseEvent.CLICK, playMusic), nuovo AddEventListener (btn2, MouseEvent.CLICK, playMusic), nuovo AddEventListener (btn3, MouseEvent.CLICK, stopMusic)) ; // questo comando rimuove tutti i listener removeListeners = new ParallelCommand (0, new RemoveEventListener (btn1, MouseEvent.CLICK, playMusic), new RemoveEventListener (btn2, MouseEvent.CLICK, playMusic), new RemoveEventListener (btn3, MouseEvent.CLICK, stopMusic)) ; override public function createIntroCommand (): Command var command: Command = new SerialCommand (0, // fade nei pulsanti new ParallelCommand (0, new TweenMaxTo (btn1, 1, autoAlpha: 1), new TweenMaxTo (btn2, 1, autoAlpha: 1), new TweenMaxTo (btn3, 1, autoAlpha: 1)), // aggiungi click lister addListeners); comando di ritorno; / ** * Riproduce la musica. * / private function playMusic (e: Event): void // richiama l'oggetto sonoro corrispondente a una chiave dati var music: Sound = DataManager.getData (dataKeys [e.target]); // controlla se la sound track di BGM è già in esecuzione se (SoundManager.isPlaying ("bgm")) // recupera la traccia audio di riproduzione var soundTrack: SoundTrack = SoundManager.getSoundTrack ("bgm"); comando var: Comando = new SerialCommand (0, // rimuovi temporaneamente i listener di clic removeListeners, // dissolve la nuova traccia audio attuale TweenMaxTo (soundTrack.channel, 1, volume: 0), // e quindi interrompe la traccia audio nuovo StopSound ("bgm"), // riproduce un nuovo suono sulla stessa traccia audio nuovo PlaySound ("bgm", music, 0, int.MAX_VALUE), // aggiunge nuovamente click lister addListeners); command.start (); else // riproduce il suono solo se la colonna sonora è inattiva SoundManager.play ("bgm", music, 0, int.MAX_VALUE); / ** * Interrompe la riproduzione della musica attualmente in riproduzione. * / private function stopMusic (e: Event): void // controlla se la sound track di BGM è già in riproduzione se (SoundManager.isPlaying ("bgm")) // recupera la traccia audio di riproduzione var soundTrack: SoundTrack = SoundManager. getSoundTrack ( "BGM"); comando var: Comando = new SerialCommand (0, // rimuove temporaneamente i listener di clic removeListeners, // dissolve la nuova traccia audio attuale TweenMaxTo (soundTrack.channel, 1, volume: 0), // e quindi interrompe la traccia audio nuovo StopSound ("bgm"), // riaggiungi i listener di ascolto addListeners); command.start ();
Siamo pronti a testare il film. Premere CTRL + INVIO per verificarlo. Quando fai clic su un pulsante, inizia la riproduzione di una musica. Dopo aver cliccato su un altro, la musica si attenua, e poi una nuova parte dall'inizio.
Pietra miliare Visualizza onlineÈ la fine del tutorial, lo so. Ma non potevo resistere dal mostrarti questo. Se sei un code jockey, scommetto che hai già notato che ci sono molte somiglianze nel suonare() metodo e il stopMusic () metodo. Perché non li refactoring in uno solo? Se non sei interessato a questa versione jocky di codice del lettore musicale, puoi saltare alla sezione di riepilogo. Altrimenti, continua a leggere!
In primo luogo, sostituire tutto il suonare e stopMusic nel codice sorgente con handleMusic, il nostro nuovo ascoltatore di eventi. Quindi, eliminare il suonare e il stopMusic metodo e aggiungere quanto segue handleMusic () metodo nella classe di scena principale.
/ ** * Riproduce o ferma la musica. Versione di jockey di codice. * / private function handleMusic (e: Event): void var music: Sound = DataManager.getData (dataKeys [e.target]); if (SoundManager.isPlaying ("bgm")) var soundTrack: SoundTrack = SoundManager.getSoundTrack ("bgm"); comando var: Command = new SerialCommand (0, removeListeners, new TweenMaxTo (soundTrack.channel, 1, volume: 0), nuovo StopSound ("bgm"), // determina se stiamo per suonare un'altra musica (musica )? (nuovo PlaySound ("bgm", music, 0, int.MAX_VALUE)): (nuovo Dummy ()), addListeners); command.start (); else if (musica) SoundManager.play ("bgm", music, 0, int.MAX_VALUE);
Noterai che l'unica grande differenza tra questo metodo e gli ascoltatori originali è il seguente blocco di codice:
(musica)? (nuovo PlaySound ("bgm", music, 0, int.MAX_VALUE)): (nuovo Dummy ()),
Che diavolo è questo comunque? Questo è in realtà l'operatore?: Condizionale. È un operatore ternario, il che significa che richiede tre operandi, A, B e C. L'istruzione "A? B: C" restituisce B se A è vero o C altrimenti. Il musica la variabile dovrebbe contenere un riferimento a a Suono oggetto, in modo che la variabile valuti su true. Tuttavia, se la destinazione del dispatcher dell'evento è il pulsante "stop_btn", la variabile contiene un valore nullo, che viene valutato come falso nell'operatore ternario. Quindi, se si fa clic sui due pulsanti musicali, il chunk di codice sopra è considerato come la singola riga di codice sottostante.
nuovo PlaySound ("bgm", music, 0, int.MAX_VALUE)
Altrimenti, se si fa clic sul pulsante stop, il chunk di codice viene considerato come un comando fittizio, che semplicemente non fa nulla.
nuovo manichino ()
Solo un'altra cosa da notare. La seguente riga di codice
SoundManager.play ("bgm", music, 0, int.MAX_VALUE);
è cambiato in
if (musica) SoundManager.play ("bgm", music, 0, int.MAX_VALUE);
Questo è per la gestione dell'eccezione che la traccia audio è attualmente vuota. Se riesci a capire il chunk di codice qui sopra, sono abbastanza sicuro che potresti capire di cosa tratta questa linea.
Prova il film premendo Ctrl + Invio, vedrai lo stesso risultato dell'ultimo esempio. Puoi considerarlo un compimento della vanità del codice di un fantino del codice.
In questo tutorial, hai imparato come gestire i suoni con le tracce audio. Una traccia audio consente di riprodurre un solo suono alla volta, quindi ideale per rappresentare la voce di un singolo personaggio o la musica di sottofondo. Inoltre, hai visto come integrare il framework di gestione del suono con il quadro dei comandi, che ti offre un'enorme manutenibilità e una maggiore flessibilità nelle tue applicazioni.
Questa è la fine del tutorial. Spero ti sia piaciuto. Grazie mille per la lettura!