Webaudio en 3D-soundscapes implementatie

In deze tutorial wikkelen we Web Audio in een eenvoudige API die zich richt op het afspelen van geluiden binnen een 3D-coördinaatruimte en die kan worden gebruikt voor meeslepende interactieve toepassingen, inclusief maar niet beperkt tot, 3D-games.

Deze tutorial is de tweede in een tweedelige serie. Als u de eerste zelfstudie in de serie niet hebt gelezen, moet u dit doen voordat u deze zelfstudie leest, omdat deze u kennis laat maken met de verschillende Web Audio-elementen die we hier zullen gebruiken.

Demonstratie

Voordat we aan de slag gaan, is hier een kleine demonstratie die de vereenvoudigde API gebruikt die we in deze zelfstudie bespreken. Geluiden (vertegenwoordigd door de witte vierkanten) worden willekeurig gepositioneerd en gespeeld in een 3D-coördinaatruimte met behulp van de aan het hoofd gerelateerde overdrachtfunctie (HRTF) die Web Audio ons biedt.

De bronbestanden voor de demonstratie zijn bij deze zelfstudie gevoegd.

Overzicht

Omdat de vereenvoudigde API (AudioPlayer) al is gemaakt voor deze zelfstudie en beschikbaar is om te downloaden, gaan we hier uitgebreid kijken naar de AudioPlayer-API en de code waarmee deze wordt aangedreven.

Lees voordat u verder gaat met deze tutorial de vorige tutorial in deze serie als je dat nog niet gedaan hebt en nieuw bent in de wereld van Web Audio.

Geluids speler

De Geluids speler klasse bevat onze vereenvoudigde API en wordt getoond op de venster object naast de standaard Web Audio-klassen als, en alleen als, Web Audio wordt ondersteund door de webbrowser. Dit betekent dat we moeten controleren of er een klasse bestaat voordat we het proberen te gebruiken.

if (window.AudioPlayer! == undefined) audioPlayer = nieuwe AudioPlayer ()

(We hadden kunnen proberen om een ​​nieuwe te maken Geluids speler object binnen een proberen te vangen verklaring, maar een eenvoudige voorwaardelijke controle werkt perfect.)

Achter de schermen, de geluids speler maakt een nieuw AudioContext object en een nieuw AudioGainNode object voor ons, en verbindt de GainNode bezwaar tegen de bestemming knoop blootgesteld door de AudioContext voorwerp.

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

Wanneer geluiden worden gemaakt en afgespeeld, worden ze verbonden met de m_gain knooppunt, dit stelt ons in staat om het volume (amplitude) van alle geluiden gemakkelijk te regelen.

De geluids speler configureert ook de audio luisteraar, blootgesteld door m_context, dus het komt overeen met het gemeenschappelijke 3D-coördinatensysteem dat wordt gebruikt met WebGL. Het positieve z as wijst naar de kijker (met andere woorden, het wijst naar het 2D-scherm), het positieve Y as wijst naar boven en het positieve X as wijst naar rechts.

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

De positie van de luisteraar is altijd nul; het bevindt zich in het midden van het audio-coördinatensysteem.

Geluiden laden

Voordat we geluiden kunnen maken of afspelen, moeten we de geluidsbestanden laden. gelukkig genoeg geluids speler zorgt voor al het harde werk voor ons. Het onthult een laden (...) functie die we kunnen gebruiken om de geluiden te laden, en drie event handlers waarmee we de voortgang van de lading kunnen volgen.

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

De reeks geluidsindelingen die wordt ondersteund, is browserafhankelijk. Chrome en Firefox ondersteunen bijvoorbeeld OGG Vorbis, maar Internet Explorer doet dit niet. Alle drie de browsers ondersteunen MP3, wat handig is, maar het probleem met MP3 is het ontbreken van een naadloze looping van het geluid - het MP3-formaat is er gewoon niet voor ontworpen. Echter, OGG Vorbis is, en kan loop-geluiden perfect.

Bij het bellen van de laden (...) meerdere keren functioneren, geluids speler zal de aanvragen in een wachtrij duwen en ze sequentieel laden. Wanneer alle in de wachtrij geplaatste geluiden zijn geladen (en zijn gedecodeerd) de gebeurtenishandler onLoadComplete gebeurtenishandler wordt gebeld.

Achter de schermen, geluids speler gebruikt een single XMLHttpRequest object om de geluiden te laden. De responseType van het verzoek is ingesteld op "Arraybuffer", en wanneer het bestand is geladen waarnaar de arraybuffer is verzonden m_context voor decodering.

// vereenvoudigd voorbeeld m_loader = nieuw XMLHttpRequest () m_queue = [] functie load () m_loader.open ("GET", m_queue [0]) m_loader.responseType = "arraybuffer" m_loader.onload = onLoad m_loader.send () function onLoad (event) var data = m_loader.response var status = m_loader.status m_loader.abort () // reset de loader als (status < 400)  m_context.decodeAudioData(data, onDecode)  

Als het laden en decoderen van een bestand succesvol is, geluids speler laadt het volgende bestand in de wachtrij (als de wachtrij niet leeg is) of laat ons weten dat alle bestanden zijn geladen.

Geluiden creëren

Nu we enkele geluidsbestanden hebben geladen, kunnen we onze geluiden maken en afspelen. We moeten het eerst vertellen geluids speler om de geluiden te creëren, en dit wordt gedaan met behulp van de maak (...) functie zichtbaar gemaakt door geluids speler.

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

We zijn vrij om zoveel geluiden te maken als we nodig hebben, zelfs als we maar één geluidsbestand hebben geladen.

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

Het geluidsbestandpad doorgegeven aan de maak (...) functie vertelt gewoon geluids speler welk bestand het gemaakte geluid zou moeten gebruiken. Als het opgegeven geluidsbestand niet is geladen wanneer het maak (...) functie wordt aangeroepen, wordt een runtime-fout gegenereerd.

Geluiden spelen

Wanneer we een of meer geluiden hebben gemaakt, zijn we vrij om die geluiden te spelen wanneer dat nodig is. Om een ​​geluid te spelen, gebruiken we de toepasselijke naam spelen (...) functie zichtbaar gemaakt door geluids speler.

audioPlayer.play (Geluid1)

Om te bepalen of een a moet worden afgespeeld gelust geluid, we kunnen ook een Boolean doorgeven aan de spelen (...) functie. Als de Boolean is waar, het geluid loopt continu door totdat het wordt gestopt.

audioPlayer.play (sound1, true)

Om een ​​geluid te stoppen, kunnen we de hou op(… ) functie.

audioPlayer.stop (Geluid1)

De speelt(… ) functie laat ons weten of een geluid speelt op dit moment.

if (audioPlayer.isPlaying (sound1)) ...

Achter de schermen, de geluids speler moet een verrassende hoeveelheid werk doen om een ​​geluid te krijgen, vanwege het modulaire karakter van Web Audio. Wanneer een geluid moet worden gespeeld,geluids speler moet nieuw maken AudioSourceBufferNode en PannerNode objecten, configureer en verbind ze en verbind vervolgens het geluid met de m_gain knooppunt. Gelukkig is Web Audio sterk geoptimaliseerd, dus het maken en configureren van nieuwe audioknooppunten veroorzaakt zelden merkbare overheadkosten.

sound.source = m_context.createBufferSource () sound.panner = m_context.createPanner () sound.source.buffer = sound.buffer sound.source.loop = loop sound.source.onended = onSoundEnded // Dit is een beetje een hack maar we moeten verwijzen naar het geluid // -object in de gebeurtenishandler onSoundEnded en dingen doen // op deze manier is optimaler dan het verbinden van de handler. sound.source.sound = geluid sound.panner.panningModel = "HRTF" sound.panner.distanceModel = "lineaire" sound.panner.setPosition (sound.x, sound.y, sound.z) sound.source.connect (geluid .panner) sound.panner.connect (m_gain) sound.source.start ()

Het spelen van geluiden is natuurlijk nuttig, maar het doel van geluids speler is om geluiden in een 3D-coördinatensysteem te spelen, dus we moeten waarschijnlijk de geluidsposities instellen voordat ze worden afgespeeld. geluids speler onthult een paar functies die ons in staat stellen om precies dat te doen.

Geluiden positioneren

  • De setX (...) en getX (...) functies zichtbaar gemaakt door geluids speler kan worden gebruikt om de positie van een geluid langs het coördinatensysteem in te stellen en te krijgen X as.
  • De setY (...) en getY (...) functies kunnen worden gebruikt om de positie van een geluid langs het coördinatensysteem in te stellen en te krijgen Y as.
  • De setZ (...) en getZ (...) functies kunnen worden gebruikt om de positie van een geluid langs het coördinatensysteem in te stellen en te krijgen z as.
  • Eindelijk, het behulpzame setPosition (...) functie kan worden gebruikt om de positie van een geluid langs de coördinatensystemen in te stellen X, Y, en z assen.
audioPlayer.setX (geluid1, 100) audioPlayer.setZ (geluid1, 200) console.log (audioPlayer.getX (geluid1)) // 100 console.log (audioPlayer.getZ (sound1)) // 200 audioPlayer.setPosition (sound1, 300, 0, 400) console.log (audioPlayer.getX (geluid1)) // 300 console.log (audioPlayer.getZ (sound1)) // 400

Hoe verder een geluid uit het midden van het coördinatensysteem komt, hoe rustiger het geluid zal zijn. Op een afstand van 10000 (de standaard Web Audio) klinkt een geluid volledig stil.

Volume

We kunnen het globale (master) volume van de geluiden regelen met behulp van de setVolume (...) en getVolume (...) functies zichtbaar gemaakt door geluids speler.

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

De setVolume (...) De functie heeft ook een tweede parameter die kan worden gebruikt om het volume over een bepaalde periode te vervagen. Om bijvoorbeeld het volume over een periode van twee seconden op nul te zetten, kunnen we het volgende doen:

audioPlayer.setVolume (0.0, 2.0)

De tutorial demo maakt hier gebruik van om de geluiden soepel in te voeren.

Achter de schermen, de geluids speler vertelt gewoon het m_gain knooppunt om de versterkingswaarde lineair te veranderen telkens wanneer het volume moet worden gewijzigd.

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 (volume, currentTime + tijd)

geluids speler dwingt een minimale fade-tijd van 0.01 seconden, om te zorgen dat steile volumeveranderingen geen hoorbare klikken of ploffen veroorzaken.

Conclusie

In deze zelfstudie hebben we een manier bekeken om Web Audio in een eenvoudige API te verwerken die zich richt op het afspelen van geluiden in een 3D-coördinaatruimte voor gebruik in (onder andere toepassingen) 3D-games.

Vanwege het modulaire karakter van Web Audio, kunnen programma's die gebruikmaken van Web Audio behoorlijk snel ingewikkeld worden, dus ik hoop dat deze tutorial nuttig voor u is geweest. Als je begrijpt hoe Web Audio werkt en hoe krachtig het is, weet ik zeker dat je er veel plezier mee zult hebben.

Vergeet niet dat de AudioPlayer en demonstratie bronbestanden beschikbaar zijn op GitHub en klaar om te downloaden. De broncode wordt redelijk goed becommentarieerd, dus het is de moeite waard de tijd te nemen om er snel naar te kijken.

Als u feedback of vragen heeft, kunt u hieronder een reactie plaatsen.

Middelen

  • W3C Web Audio-specificatie
  • MDN Web Audio-documentatie