Web Audio och 3D Soundscapes Implementation

I den här handledningen kommer vi att sätta ihop webbljud i ett enkelt API som fokuserar på att spela ljud inom ett 3D-koordinatutrymme och kan användas för immersiva interaktiva applikationer inklusive, men inte begränsat till, 3D-spel.

Denna handledning är den andra i en tvådelad serie. Om du inte har läst den första handledningen i serien bör du göra det innan du läser den här handledningen eftersom den introducerar dig till de olika Web Audio-elementen vi kommer att använda här.

Demonstration

Innan vi börjar, här är en liten demonstration som använder det förenklade API som vi kommer att täcka i den här handledningen. Ljud (representerade av de vita rutorna) slumpmässigt placeras och spelas i ett 3D-koordinatutrymme med den huvudrelaterade överföringsfunktionen (HRTF) som Web Audio tillhandahåller för oss.

Källfilerna för demonstrationen bifogas denna handledning.

Översikt

Eftersom det förenklade API-skivan (AudioPlayer) redan har skapats för denna handledning och kan laddas ner, är det vi ska göra här en bred titt på AudioPlayer API och koden som driver den.

Innan du fortsätter denna handledning läs du den tidigare handledningen i den här serien om du inte redan har gjort det och är nya i världen av Web Audio.

Ljudspelare

De Ljudspelare klassen innehåller vårt förenklade API och exponeras på fönster objekt bredvid de vanliga webbljudklasserna om och endast om webljud stöds av webbläsaren. Det betyder att vi bör kontrollera förekomsten av klassen innan vi försöker använda den.

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

(Vi kunde ha försökt att skapa en ny Ljudspelare objekt inom a försök fånga uttalande, men en enkel villkorlig kontroll fungerar bra.)

Bakom kulisserna, den ljudspelare skapar en ny AudioContext objekt och en ny AudioGainNode objekt mot oss och ansluter GainNode protestera mot destination nod utsatt av AudioContext objekt.

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

När ljud skapas och spelas kommer de att anslutas till m_gain nod gör det möjligt för oss att enkelt styra volymen (amplituden) av alla ljud.

De ljudspelare konfigurerar även ljudet lyssnare, exponeras av m_context, så det matchar det vanliga 3D-koordinatsystemet som används med WebGL. Den positiva z axelpunkter på betraktaren (med andra ord, det pekar på 2D-skärmen), den positiva y axeln pekar upp och den positiva x axeln pekar åt höger.

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

Positionen hos lyssnare är alltid noll; den sitter i mitten av ljudkoordinatsystemet.

Laddar ljud

Innan vi kan skapa eller spela några ljud behöver vi ladda ljudfilerna. lyckligtvis nog ljudspelare tar hand om allt det hårda arbetet för oss. Det exponerar a ladda (...) funktion som vi kan använda för att ladda upp ljudet och tre händelsehanterare som gör att vi kan hålla koll på lastförloppet.

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

Den uppsättning ljudformat som stöds är beroende av webbläsare. Till exempel, Chrome och Firefox stöder OGG Vorbis men Internet Explorer gör det inte. Alla tre webbläsare stöder MP3, vilket är praktiskt, men problemet med MP3 är bristen på sömlös ljudlåsning. MP3-formatet är helt enkelt inte konstruerat för det. Men OGG Vorbis är och kan slinga ljud perfekt.

När du ringer till ladda (...) funktion flera gånger, ljudspelare kommer att driva förfrågningarna i en kö och ladda dem i följd. När alla ljud i kö är laddade (och avkodas) onLoadComplete händelsehanteraren kommer att kallas.

Bakom kulisserna, ljudspelare använder en enda XMLHttpRequest motsätta sig att ladda ljuden. De responseType av begäran är inställd på "Arraybuffer", och när filen har laddats matrisbufferten skickas till m_context för avkodning.

// förenklat exempel m_loader = ny XMLHttpRequest () m_queue = [] funktionslast () m_loader.open ("GET", m_queue [0]) m_loader.responseType = "arraybuffer" m_loader.onload = onLadad m_loader.send () funktion onLoad (händelse) var data = m_loader.response var status = m_loader.status m_loader.abort () // återställer loader om (status < 400)  m_context.decodeAudioData(data, onDecode)  

Om lastningen och avkodningen av en fil lyckas, ljudspelare kommer antingen ladda nästa fil i kön (om köen inte är tom) eller låt oss veta att alla filer har laddats.

Skapa ljud

Nu när vi har laddat upp några ljudfiler kan vi skapa och spela våra ljud. Vi måste först tala ljudspelare för att skapa ljuden, och detta görs genom att använda skapa(… ) funktion exponerad av ljudspelare.

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

Vi kan skapa så många ljud som vi behöver, även om vi bara har laddat en enda ljudfil.

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

Ljudfilbanan gick vidare till skapa(… ) funktion berättar bara ljudspelare vilken fil det skapade ljudet ska använda. Om den angivna ljudfilen inte har laddats när skapa(… ) funktion kallas, kommer ett runtime fel att kastas.

Spelar ljud

När vi har skapat ett eller flera ljud kan vi spela de ljuden när vi behöver. För att spela ett ljud använder vi den lämpliga namnet spela(… ) funktion exponerad av ljudspelare.

audioPlayer.play (Ljud1)

För att avgöra om du ska spela en looped ljud, vi kan också passera en booleska till spela(… ) fungera. Om den booleska är Sann, ljudet slingras kontinuerligt tills det är stoppat.

audioPlayer.play (sound1, true)

För att stoppa ett ljud kan vi använda sluta(… ) fungera.

audioPlayer.stop (Ljud1)

De spelar(… ) funktionen låter oss veta om ett ljud spelas för närvarande.

om (audioPlayer.isPlaying (sound1)) ...

Bakom kulisserna, den ljudspelare måste göra en överraskande mängd arbete för att få ett ljud att spela på grund av den modulära karaktären hos Web Audio. När ett ljud behöver spelas,ljudspelare måste skapa nya AudioSourceBufferNode och PannerNode objekt, konfigurera och anslut dem och anslut sedan ljudet till m_gain nod. Lyckligtvis är Web Audio optimerad så att skapandet och konfigurationen av nya ljudnoder orsakar sällan några märkbara kostnader.

sound.source = m_context.createBufferSource () sound.panner = m_context.createPanner () sound.source.buffer = sound.buffer sound.source.loop = loop sound.source.onended = onSoundEnded // Det här är lite av ett hack men vi behöver referera till ljudet // objektet i händelsehanteraren onSoundEnded och göra saker // det här sättet är mer optimalt än att binda hanteraren. sound.source.sound = ljud sound.panner.panningModel = "HRTF" sound.panner.distanceModel = "linjär" sound.panner.setPosition (sound.x, sound.y, sound.z) sound.source.connect (ljud .panner) sound.panner.connect (m_gain) sound.source.start ()

Att spela ljud är uppenbarligen användbart, men syftet med ljudspelare är att spela ljud inom ett 3D-koordinatsystem, så vi borde noga ställa in ljudpositionerna innan de spelas. ljudspelare utsätter några funktioner som gör det möjligt för oss att göra just det.

Placering av ljud

  • De setX (...) och getX (...) funktioner utsatta av ljudspelare kan användas för att ställa in och ta ställning till ett ljud längs koordinatsystemet x axel.
  • De setY (...) och getY (...) funktioner kan användas för att ställa in och få en ljudsignal längs koordinatsystemet y axel.
  • De setZ (...) och getZ (...) funktioner kan användas för att ställa in och få en ljudsignal längs koordinatsystemet z axel.
  • Slutligen, det hjälpsamma setPosition (...) funktionen kan användas för att ställa in ljudets position längs koordinatsystemet x, y, och z axlar respektive.
audioPlayer.setX (sound1, 100) audioPlayer.setZ (sound1, 200) console.log (audioPlayer.getX (sound1)) // 100 console.log (audioPlayer.getZ (sound1)) // 200 audioPlayer.setPosition (sound1, 300, 0, 400) console.log (audioPlayer.getX (sound1)) // 300 console.log (audioPlayer.getZ (sound1)) // 400

Ju längre ett ljud är från mitten av koordinatsystemet, desto tystare blir ljudet. På ett avstånd av 10 tusen (webljudsinställningen) ett ljud blir helt tyst.

Volym

Vi kan styra den globala (master) volymen av ljuden med hjälp av setVolume (...) och getVolume (...) funktioner utsatta av ljudspelare.

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

De setVolume (...) funktionen har också en andra parameter som kan användas för att minska volymen över en tidsperiod. För att tona volymen till noll under en period på två sekunder kan vi göra följande:

audioPlayer.setVolume (0.0, 2.0)

Tutorial-demo drar nytta av detta för att tona in ljuden smidigt.

Bakom kulisserna, den ljudspelare berättar helt enkelt m_gain nod för att linjärt ändra förstärkningsvärdet när volymen behöver ändras.

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 (volym, nuvarande tid + tid)

ljudspelare verkställer en minsta blektid av 0,01 sekunder, för att säkerställa att branta volymförändringar inte orsakar några hörbara klick eller poppar.

Slutsats

I den här handledningen tittade vi på ett sätt att sätta ihop webbljud i ett enkelt API som fokuserar på att spela ljud inom ett 3D-koordinatutrymme för användning i (bland andra program) 3D-spel.

På grund av den modulära karaktären hos Web Audio kan program som använder Web Audio få komplexa ganska snabbt, så jag hoppas att den här handledningen har varit till nytta för dig. När du förstår hur Web Audio fungerar, och hur kraftfull det är, är jag säker på att du kommer ha mycket roligt med det.

Glöm inte AudioPlayer och demonstrationskällfilerna är tillgängliga på GitHub och redo för nedladdning. Källkoden kommenteras ganska bra så det är värt att ta sig tid att ta en snabb titt på det.

Om du har några synpunkter eller frågor, var god och skriv en kommentar nedan.

Medel

  • W3C Web Audio Specification
  • MDN Web Audio Documentation