Web Audio og 3D Soundscapes Implementering

I denne opplæringen vil vi pakke inn Web Audio i en enkel API som fokuserer på spillelyder innenfor et 3D-koordinatrom, og kan brukes til omfattende interaktive applikasjoner, inkludert, men ikke begrenset til, 3D-spill.

Denne opplæringen er den andre i en todelt serie. Hvis du ikke har lest den første opplæringen i serien, bør du gjøre det før du leser denne opplæringen fordi den introduserer deg til de ulike Web Audio-elementene vi skal bruke her.

Demonstrasjon

Før vi kommer i gang, er det en liten demonstrasjon som bruker den forenklede API som vi skal dekke i denne opplæringen. Lyder (representert ved de hvite torgene) er tilfeldig plassert og spilt i et 3D-koordinatrom ved hjelp av den overordnede overføringsfunksjonen (HRTF) som Web Audio gir oss.

Kildefilene for demonstrasjonen er vedlagt denne opplæringen.

Oversikt

Fordi den forenklede API (AudioPlayer) allerede er opprettet for denne opplæringen og tilgjengelig for nedlasting, er det vi skal gjøre her, en bred titt på AudioPlayer API og koden som driver den.

Før du fortsetter denne opplæringen, vennligst les den forrige opplæringen i denne serien hvis du ikke har gjort det allerede, og er nytt for verden av Web Audio.

Audioplayer

De Audioplayer klassen inneholder vår forenklede API og er eksponert på vindu objekt sammen med standard Web Audio klasser hvis, og bare hvis, Web Audio støttes av nettleseren. Dette betyr at vi bør sjekke eksistensen av klassen før vi prøver å bruke den.

hvis (window.AudioPlayer! == undefined) audioPlayer = new AudioPlayer ()

(Vi kunne ha forsøkt å skape en ny Audioplayer objekt innen a prøv ... fange uttalelse, men en enkel betinget sjekk fungerer perfekt.)

Bak kulissene, den Audioplayer skaper en ny AudioContext objekt og en ny AudioGainNode objekt for oss, og kobler til GainNode protestere mot mål node eksponert av AudioContext gjenstand.

var m_context = ny AudioContext () var m_gain = m_context.createGain () ... m_gain.connect (m_context.destination)

Når lyder opprettes og spilles, blir de koblet til m_gain node, dette tillater oss å kontrollere volumet (amplitude) av alle lydene enkelt.

De Audioplayer Konfigurerer også lyden lytteren, eksponert av m_context, så det samsvarer med det vanlige 3D-koordinatsystemet som brukes med WebGL. Den positive z aksepunkter på betrakteren (med andre ord, det peker ut av 2D-skjermen), den positive y akse peker opp, og den positive x akse peker til høyre.

m_context.listener.setOrientation (0, 0, -1, 0, 1, 0)

Stillingen av lytteren er alltid null; den sitter i midten av lydkoordinatsystemet.

Laster inn lyder

Før vi kan lage eller spille noen lyder, må vi laste lydfilene; heldigvis nok Audioplayer tar vare på alt det harde arbeidet for oss. Det avslører a last (...) funksjon som vi kan bruke til å laste inn lydene, og tre hendelseshåndterere som tillater oss å holde oversikt over lastprogresjonen.

audioPlayer.onloadstart = funksjon () ... audioPlayer.onloaderror = funksjon () ... audioPlayer.onloadcomplete = funksjon () ... audioPlayer.load ("sound-01.ogg") audioPlayer.load ("sound-02 .ogg ") audioPlayer.load (" sound-03.ogg ")

Sett med lydformater som støttes, er nettleseravhengig. For eksempel støtter Chrome og Firefox OGG Vorbis, men ikke Internet Explorer. Alle tre nettleserne støtter MP3, noe som er praktisk, men problemet med MP3 er mangelen på sømløs lydsløyfe. MP3-formatet er ganske enkelt ikke designet for det. OGG Vorbis er imidlertid, og kan loop lykke perfekt.

Når du ringer til last (...) funksjon flere ganger, Audioplayer vil presse forespørslene inn i en kø og laste dem i rekkefølge. Når alle lydene i kø er lastet inn (og dekodet) onloadcomplete Event Handler vil bli kalt.

Bak scenen, Audioplayer bruker en enkelt XMLHttpRequest motsette seg å laste lydene. De responseType av forespørselen er satt til "Arraybuffer", og når filen har lastet matrisbufferen, sendes den til m_context for dekoding.

// forenklet eksempel m_loader = ny XMLHttpRequest () m_queue = [] funksjonsbelastning () m_loader.open ("GET", m_queue [0]) m_loader.responseType = "arraybuffer" m_loader.onload = onLad m_loader.send () funksjon onLoad (event) var data = m_loader.response var status = m_loader.status m_loader.abort () // tilbakestiller lasteren hvis (status < 400)  m_context.decodeAudioData(data, onDecode)  

Hvis lasting og dekoding av en fil er vellykket, Audioplayer vil enten laste inn den neste filen i køen (hvis køen ikke er tom) eller la oss få vite at alle filene er lastet inn.

Opprette lyder

Nå som vi har lastet inn noen lydfiler, kan vi lage og spille av våre lyder. Vi må først fortelle Audioplayer å lage lydene, og dette gjøres ved å bruke skape(… ) funksjon eksponert av Audioplayer.

var sound1 = audioPlayer.create ("sound-01.ogg") var sound2 = audioPlayer.create ("sound-02.ogg") var sound3 = audioPlayer.create ("sound-03.ogg")

Vi er fri til å lage så mange lyder som vi trenger, selv om vi bare har lastet inn en enkelt lydfil.

var a = audioPlayer.create ("beep.ogg") var b = audioPlayer.create ("beep.ogg") var c = audioPlayer.create ("beep.ogg")

Lydfilbanen passerte til skape(… ) funksjon forteller bare Audioplayer hvilken fil den opprettede lyden skal bruke. Hvis den angitte lydfilen ikke er lastet inn når skape(… ) funksjon kalles, vil en runtime feil bli kastet.

Spille av lyder

Når vi har opprettet en eller flere lyder, er vi fri til å spille disse lydene når vi trenger det. For å spille en lyd bruker vi den passende navnet spille(… ) funksjon eksponert av Audioplayer.

audioPlayer.play (SOUND1)

For å avgjøre om du skal spille en loopet lyd, vi kan også passere en boolsk til spille(… ) funksjon. Hvis den boolske er ekte, lyden løper kontinuerlig til den stoppes.

audioPlayer.play (sound1, true)

For å stoppe en lyd kan vi bruke Stoppe(… ) funksjon.

audioPlayer.stop (SOUND1)

De spiller(… ) funksjonen lar oss vite om en lyd spiller for øyeblikket.

hvis (audioPlayer.isPlaying (lyd1)) ...

Bak kulissene, den Audioplayer må gjøre en overraskende mengde arbeid for å få lyd til å spille, på grunn av den modulære naturen til Web Audio. Når en lyd må spilles,Audioplayer må skape nye AudioSourceBufferNode og PannerNode objekter, konfigurere og koble dem til, og koble deretter lyden til m_gain node. Heldigvis, Web Audio er svært optimalisert, slik at opprettelsen og konfigurasjonen av nye lydnoder gir sjelden noen merkbare overhead.

sound.source = m_context.createBufferSource () sound.panner = m_context.createPanner () sound.source.buffer = sound.buffer sound.source.loop = loop sound.source.onended = onSoundEnded // Dette er litt av et hack men vi må referere til lyd // objektet i hendelseshåndtereren onSoundEnded, og gjøre ting // denne måten er mer optimal enn å binde handleren. sound.source.sound = lyd sound.panner.panningModel = "HRTF" sound.panner.distanceModel = "lineær" sound.panner.setPosition (lyd.x, sound.y, sound.z) sound.source.connect (lyd .panner) sound.panner.connect (m_gain) sound.source.start ()

Å spille lyd er åpenbart nyttig, men hensikten med Audioplayer er å spille lyder i et 3D-koordinatsystem, så vi bør sannsynligvis stille lydposisjonene før du spiller dem. Audioplayer utsetter noen få funksjoner som tillater oss å gjøre nettopp det.

Plassering av lyder

  • De setX (...) og getX (...) funksjoner eksponert av Audioplayer kan brukes til å sette og få posisjonen til en lyd langs koordinatsystemet x akser.
  • De setY (...) og getY (...) Funksjoner kan brukes til å sette og få posisjonen til en lyd langs koordinatsystemet y akser.
  • De setZ (...) og getZ (...) Funksjoner kan brukes til å sette og få posisjonen til en lyd langs koordinatsystemet z akser.
  • Til slutt, det hjelpsomme setPosition (...) funksjonen kan brukes til å angi posisjonen til en lyd langs koordinatsystemet x, y, og z akser henholdsvis.
audioPlayer.setX (lyd1, 100) audioPlayer.setZ (lyd1, 200) console.log (audioPlayer.getX (lyd1)) // 100 console.log (audioPlayer.getZ (lyd1)) // 200 audioPlayer.setPosisjon (lyd1, 300, 0, 400) console.log (audioPlayer.getX (lyd1)) // 300 console.log (audioPlayer.getZ (lyd1)) // 400

Jo lenger en lyd er fra sentrum av koordinatsystemet, desto roligere blir lyden. På en avstand av 10000 (Web Audio Standard) En lyd blir helt stille.

Volum

Vi kan kontrollere det globale (master) volumet av lydene ved å bruke setVolume (...) og getVolume (...) funksjoner eksponert av Audioplayer.

audioPlayer.setVolume (0.5) // 50% console.log (audioPlayer.getVolume ()) // 0.5

De setVolume (...) funksjonen har også en andre parameter som kan brukes til å fade volumet over en tidsperiode. For eksempel å fade volumet til null over en to sekunders periode, kan vi gjøre følgende:

audioPlayer.setVolume (0.0, 2.0)

Tutorial-demoen utnytter dette for å fade inn lydene jevnt.

Bak kulissene, den Audioplayer forteller bare m_gain knutepunktet for å endre gevinstverdien lineært når volumet må endres.

var currentTime = m_context.currentTime var currentVolume = m_gain.gain.value m_gain.gain.cancelScheduledValues ​​(0.0) m_gain.gain.setValueAtTime (currentVolume, currentTime) m_gain.gain.linearRampToValueAtTime (volum, nåværende tid + tid)

Audioplayer håndhever en minimumsfadingstid på 0.01 sekunder, for å sikre at bratte volumendringer ikke forårsaker noen hørbare klikk eller dukker opp.

Konklusjon

I denne veiledningen tok vi en titt på en måte å pakke inn Web Audio i en enkel API som fokuserer på spillelyder innenfor et 3D-koordinatrom for bruk i (blant andre programmer) 3D-spill.

På grunn av den modulære naturen til Web Audio, kan programmer som bruker Web Audio, bli komplekse ganske raskt, så jeg håper denne opplæringen har vært til nytte for deg. Når du forstår hvordan Web Audio fungerer, og hvor kraftig det er, er jeg sikker på at du vil ha det mye moro med det.

Ikke glem lydspilleren og demonstrasjonskildefilene er tilgjengelige på GitHub og klar for nedlasting. Kildekoden kommenteres ganske bra, så det er verdt å ta deg tid til å se på det raskt.

Hvis du har tilbakemelding eller spørsmål, vær så snill å poste en kommentar nedenfor.

ressurser

  • W3C Web Audio Specification
  • MDN Web Audio Documentation