Game Audio Forenklet

Web Audio API er en kraftig alliert for alle som lager JavaScript-spill, men med den kraften kommer kompleksitet. Web Audio er et modulært system; lyd noder kan kobles sammen for å danne komplekse grafer for å håndtere alt fra avspilling av en enkelt lyd til et fullt utstyrt musikk sekvenseringsprogram. Dette er imponerende, minst sagt.

Men når det gjelder programmeringsspill, vil de fleste utviklere ha en grunnleggende API som bare laster og spiller lyder, og gir muligheter for å endre volum, tonehøyde og panorere (stereoposisjon) av lydene. Denne opplæringen gir en elegant løsning ved å pakke inn Web Audio API på en rask og lett måte Lyd klasse som håndterer alt for deg.

Merk: Denne opplæringen er primært rettet mot JavaScript-programmerere, men teknikkene som brukes til å mikse og manipulere lyd i koden, kan brukes til nesten hvilket som helst programmeringsmiljø som gir tilgang til en lavnivå lyd API.

Live demo

Før vi begynner, ta en titt på live demo av Lyd klasse i aksjon. Du kan klikke på knappene i demoen for å spille av lyder:

  • SFX 01 er en single-shot lyd med standardinnstillinger. 
  • SFX 02 er en single-shot lyd som har sin pan (stereo posisjon) og volumet randomisert hver gang det spilles. 
  • SFX 03 er en looping lyd; Ved å klikke på knappen vil lyden slå av og på, og musepekerenposisjonen i knappen vil justere lydens tonehøyde.

Merk: Hvis du ikke hører noen lyder som spilles, støtter nettleseren du bruker ikke Web Audio API eller OGG Vorbis lydstrømmer. Bruk av Chrome eller Firefox skal løse problemet.

Livet kan være enklere

Følgende bilde visualiserer en grunnleggende Web Audio Node Graph:

Visuelt eksempel på en Web Audio node graph.

Som du kan se er det ganske mange lydnoder i grafen for å håndtere avspilling av fire lyder på en måte som passer for spill. Pannere og forsterkningsnoder omhandler panorering og volum, og det er et par dynamikk kompressor noder der inne for å forhindre noen hørbare gjenstander (klipp, pops og så videre) hvis grafen ender med å bli overbelastet av høy lyd.

Å kunne lage lydknudegrafer som i JavaScript er kjempebra, men å måtte konstant lage, koble til og koble fra disse nodene kan bli en reell byrde. Vi skal forenkle ting ved å håndtere lydblanding og manipulering programmatisk, ved hjelp av en enkelt skriptprosessorkode.

Visuelt eksempel på en forenklet Web Audio node graph.

Yep, det er definitivt mye enklere - og det unngår også prosesseringskostnadene som er involvert i å opprette, koble til og koble fra en mengde lyd noder hver gang en lyd må spilles av. 

Det er andre quirks i Web Audio API som kan gjøre ting vanskelig. Pannerknudepunktet er for eksempel designet spesielt for lyder som er plassert i 3D-rom, ikke 2D mellomrom, og lydbufferkilde noder (merket "lyd" i det forrige bildet) kan kun spilles en gang, og derfor må det alltid opprettes og koble de nodene.

Den enkle skriptprosessorens nod som brukes av Lyd klassen krever periodisk lydprøver som skal sendes til det fra JavaScript, og det gjør det mye enklere for oss. Vi kan mikse og manipulere lydprøver veldig raskt og enkelt i JavaScript, for å produsere volum, tonehøyde og panorering-funksjonalitet vi trenger for 2D-spill.

Lydklassen

I stedet for baby-stepping gjennom etableringen av Lyd klassen, vil vi se på kjernedelen av koden som er direkte relatert til Web Audio API og manipulering av lydprøver. Demo kildefilene inkluderer den fullt funksjonelle Lyd klassen, som du fritt kan studere og bruke i dine egne prosjekter.

Laster inn lydfiler

De Lyd klassen laster lydfiler over et nettverk som array buffere bruker XMLHttpRequest objekter. Arraybufferne dekoderes deretter til rå lydprøver av et lydkontekstobjekt.

request.open ("GET", "sound.ogg"); request.onload = dekode; request.responseType = "arraybuffer"; request.open (); funksjon dekoder () if (request.response! == null) audioContext.decodeAudioData (request.response, ferdig);  funksjon ferdig (audioBuffer) ...

Tydeligvis er det ingen feilhåndtering i den koden, men det viser hvordan lydfiler lastes og dekodes. De audioBuffer passert til gjort () Funksjonen inneholder de rå lydprøver fra den lastede lydfilen.

Blanding og manipulering av lydprøver

For å blande og manipulere de lastede lydprøvene, vil Lyd klassen legger til en lytter til en skriptprosessorkode. Denne lytteren vil bli kalt periodisk for å be om flere lydprøver.

// Beregn en bufferstørrelse. // Dette vil gi en fornuftig verdi som balanserer lyd / latens og CPU bruk for spill som kjører ved 60 Hz. var v = audioContext.sampleRate / 60; var n = 0; mens (v> 0) v >> = 1; n ++;  v = Math.pow (2, n); // buffer størrelse // Lag skriptprosessoren. prosessor = audioContext.createScriptProcessor (v); // Fest lytteren. processor.onaudioprocess = processSamples; funksjonsprosessPrøver (hendelse) ...

Frekvensen som den processSamples () funksjonen kalles vil variere på forskjellige maskinvareoppsett, men det er vanligvis rundt 45 ganger per sekund. Det kan høres ut som mye, men det er nødvendig å holde lydforsinkelsen lav nok til å være brukbar i moderne spill som vanligvis kjører på 60 bilder per sekund. Hvis lyddisplayet er for høyt, vil lydene bli hørt for sent for å synkronisere med det som skjer på skjermen, og det ville være en fornærmende opplevelse for alle som spiller et spill.

Til tross for frekvensen hvor processSamples () Funksjonen kalles, CPU-forbruket forblir lavt, så vær ikke bekymret for at for mye tid blir tatt bort fra spilllogikken og gjengivelsen. På min maskinvare (Intel Core i3, 3 GHz) bruker CPU-bruken sjelden over 2%, selv når mange lyder spilles samtidig.

De processSamples () funksjonen inneholder faktisk kjøttet av Lyd klasse; Det er der lydprøvene blandes og manipuleres før de skyves gjennom web-lyd til maskinvaren. Følgende kode viser hva som skjer inne i funksjonen:

// Ta tak i lydprøven. sampleL = samplesL [soundPosition >> 0]; sampleR = samplesR [soundPosition >> 0]; // Øk lydens stillingsposisjon. lydposisjon + = lydSkala; // Bruk det globale volumet (påvirker alle lyder). sampleL * = globalVolume; sampleR * = globalVolume; // Bruk lydens volum. sampleL * = soundVolume; sampleR * = soundVolume; // Bruk lydens pan (stereo posisjon). sampleL * = 1.0 - soundPan; sampleR * = 1.0 + soundPan;

Det er mer eller mindre alt der er til det. Det er den magiske: En håndfull enkle operasjoner endrer lydstyrken, tonehøyde og stereo posisjon av en lyd.

Hvis du er programmerer og kjent med denne typen lydbehandling, kan du tenke, "det kan ikke være alt der er til det", og du ville være riktig: Sound-klassen må holde styr på lydinstanser, prøvebuffere og Gjør et par andre ting, men det er alt som går i gang!

Bruke lydklassen

Følgende kode viser hvordan du bruker lydklassen. Du kan også laste ned kildefilene for den levende demoen som følger med denne opplæringen.

// Lag et par lydobjekter. var boom = ny lyd ("boom.ogg"); var tick = ny lyd ("tick.ogg"); // Passer eventuelt en lytter til lydklassen. Sound.setListener (lytter); // Dette vil laste inn nyopprettede lydobjekter. Sound.load (); // Lytteren. funksjon lytter (lyd, tilstand) if (state === Sound.State.LOADED) hvis (lyd === tick) setInterval (playTick, 1000);  annet hvis (lyd === boom) setInterval (playBoom, 4000);  annet hvis (state === Sound.State.ERROR) console.warn ("Lydfeil:% s", sound.getPath ());  // Spiller avspillingslyden. funksjon playTick () tick.play ();  // Spiller bommen lyden. funksjon playBoom ​​() boom.play (); // Randomize lydens tonehøyde og volum. boom.setScale (0.8 + 0.4 * Math.random ()); boom.setVolume (0.2 + 0.8 * Math.random ()); 

Fint og enkelt.

En ting å merke seg: Det spiller ingen rolle om Web Audio API ikke er tilgjengelig i en nettleser, og det spiller ingen rolle om nettleseren ikke kan spille et bestemt lydformat. Du kan fortsatt ringe til spille() og Stoppe() Fungerer på en Lyd objekt uten at noen feil blir kastet. Det er forsettlig; det lar deg kjøre spillkoden som vanlig uten å bekymre deg om problemer med nettleserens kompatibilitet eller forgrene koden din for å håndtere disse problemene. Det verste som kan skje er stillhet.

The Sound Class API

  • spille()
  • Stoppe()
  • getPath (): Får lydens filbane.
  • getState ()
  • getPan ()
  • setPan (verdi): Setter lydens pan (stereo posisjon).
  • getScale ()
  • settSkala (verdi): Angir lydens skala (tonehøyde).
  • getVolume ()
  • setVolume (verdi): Angir lydens volum.
  • isPending ()
  • isLoading ()
  • isLoaded ()
  • isLooped ()

Lydklassen inneholder også følgende statiske funksjoner.

  • laste(): Masser nyopprettede lyder.
  • Stoppe(): Stopper alle lyder.
  • getVolume ()
  • setVolume (verdi): Angir det globale (master) volumet.
  • getListener ()
  • setListener (verdi): Holder oversikt over lydbelastning fremover osv.
  • canPlay (format): Kontrollerer om ulike lydformater kan spilles.

Dokumentasjon finnes i demonstras kildekoden.

Konklusjon

Spille av lydeffekter i et JavaScript-spill bør være enkelt, og denne opplæringen gjør det ved å pakke inn den kraftige Web Audio API i en rask, lett lydklass som håndterer alt for deg.

Beslektede ressurser

Hvis du er interessert i å lære mer om lydprøver og hvordan å manipulere dem, har jeg skrevet en serie for Tuts + som burde holde deg opptatt for en stund ...

  1. Opprette en synthesizer - Introduksjon
  2. Opprette en synthesizer - Core Engine
  3. Opprette en synthesizer - lydprosessorer

Følgende koblinger er for W3C og Khronos standardiserte spesifikasjoner som er direkte relatert til Web Audio API:

  • Web Audio API
  • Types Arrays