Spiel-Audio vereinfacht

Die Web-Audio-API ist ein mächtiger Verbündeter für alle, die JavaScript-Spiele erstellen, aber mit dieser Fähigkeit ist Komplexität verbunden. Web Audio ist ein modulares System. Audioknoten können zu komplexen Diagrammen zusammengefügt werden, um alles von der Wiedergabe eines einzelnen Sounds bis zu einer voll ausgestatteten Musiksequenzierungsanwendung zu verarbeiten. Das ist beeindruckend, um es gelinde auszudrücken.

Bei der Programmierung von Spielen wünschen die meisten Entwickler jedoch eine grundlegende API, die Sounds einfach lädt und wiedergibt und Optionen zum Ändern der Lautstärke, der Tonhöhe und des Pan (Stereoposition) dieser Sounds bietet. Dieses Lernprogramm bietet eine elegante Lösung, indem die Web Audio-API schnell und leicht verarbeitet wird Klingen Klasse, die alles für Sie erledigt.

Hinweis: Dieses Tutorial richtet sich in erster Linie an JavaScript-Programmierer. Die zum Mischen und Bearbeiten von Audiomaterial im Code verwendeten Techniken können jedoch auf nahezu jede Programmierumgebung angewendet werden, die Zugriff auf eine Low-Level-Sound-API bietet.

Live Demo

Bevor wir loslegen, werfen Sie einen Blick auf die Live-Demo der Klingen Klasse in Aktion. Sie können auf die Schaltflächen in der Demo klicken, um Sounds abzuspielen:

  • SFX 01 ist ein Single-Shot-Sound mit Standardeinstellungen. 
  • SFX 02 ist ein Single-Shot-Sound, dessen Pan (Stereoposition) und Lautstärke bei jeder Wiedergabe zufällig eingestellt werden. 
  • SFX 03 ist ein Schleifengeräusch; Durch Klicken auf die Schaltfläche wird der Ton ein- und ausgeschaltet, und die Position des Mauszeigers innerhalb der Schaltfläche passt die Tonhöhe an.

Hinweis: Wenn Sie keine Sounds hören, unterstützt der von Ihnen verwendete Webbrowser die Audio-Streams von Web Audio API oder OGG Vorbis nicht. Die Verwendung von Chrome oder Firefox sollte das Problem lösen.

Das Leben kann einfacher sein

Das folgende Bild veranschaulicht ein grundlegendes Web-Audio-Knotendiagramm:

Visuelles Beispiel eines Web-Audio-Knotendiagramms.

Wie Sie sehen, gibt es in der Grafik einige Audioknoten, um die Wiedergabe von vier Sounds auf spielerische Weise zu steuern. Die Panner- und Gain-Knoten beschäftigen sich mit dem Panning und der Lautstärke. Es gibt ein paar dynamische Kompressor-Knoten, die dazu beitragen, hörbare Artefakte (Clips, Pops usw.) zu vermeiden, wenn der Graph durch lautes Audio überlastet wird.

In der Lage zu sein, Audio-Knotendiagramme wie in JavaScript zu erstellen, ist großartig, aber das permanente Erstellen, Verbinden und Trennen dieser Knoten kann zu einer echten Belastung werden. Wir werden die Dinge vereinfachen, indem wir das Mischen und die Manipulation von Audio programmatisch mit einem einzigen Skriptprozessorknoten durchführen.

Visuelles Beispiel eines vereinfachten Web-Audio-Knotendiagramms.

Ja, das ist definitiv viel einfacher - und es vermeidet auch den Verarbeitungsaufwand, der beim Erstellen, Verbinden und Trennen einer Last von Audioknoten entsteht, wenn ein Sound abgespielt werden muss. 

Es gibt andere Macken in der Web-Audio-API, die die Dinge schwierig machen können. Der Panner-Knoten ist z. B. speziell für Sounds konzipiert, die im 3D-Raum und nicht im 2D-Raum positioniert sind. Audiopuffer-Quellknoten (im vorherigen Bild mit "Sound" bezeichnet) können nur einmal abgespielt werden, daher müssen sie ständig erstellt werden und verbinden Sie diese Knotentypen.

Der einzelne Skriptprozessorknoten, der von verwendet wird Klingen In der Klasse werden regelmäßig Klangproben angefordert, die von JavaScript an sie übergeben werden. Das macht uns die Sache sehr viel einfacher. Wir können Sound-Samples sehr schnell und einfach in JavaScript mischen und bearbeiten, um die Lautstärke-, Tonhöhen- und Panning-Funktion zu erzeugen, die wir für 2D-Spiele benötigen.

Die Soundklasse

Anstatt durch die Schöpfung des Babys zu gehen Klingen Klasse werden wir einen Blick auf die Kernteile des Codes werfen, die in direktem Zusammenhang mit der Web Audio API und der Bearbeitung von Sound-Samples stehen. Die Demo-Quelldateien enthalten die voll funktionsfähige Klingen Klasse, die Sie frei studieren und in Ihren eigenen Projekten verwenden können.

Sounddateien laden

Das Klingen Klasse lädt Tondateien als Array-Puffer über ein Netzwerk XMLHttpRequest Objekte. Die Array-Puffer werden dann durch ein Audiokontextobjekt in Rohton-Samples dekodiert.

request.open ("GET", "sound.ogg"); request.onload = decodieren; request.responseType = "arraybuffer"; request.open (); Funktion decode () if (request.response! == null) audioContext.decodeAudioData (request.response, done);  Funktion erledigt (AudioBuffer) …

Offensichtlich gibt es keine Fehlerbehandlung in diesem Code, aber es zeigt, wie die Sounddateien geladen und dekodiert werden. Das Audiopuffer an den übergeben erledigt() Funktion enthält die rohen Klangbeispiele aus der geladenen Klangdatei.

Mischen und Bearbeiten von Klangbeispielen

Um die geladenen Sound-Samples zu mischen und zu bearbeiten, drücken Sie die Klingen Klasse hängt einen Listener an einen Skriptprozessorknoten an. Dieser Listener wird regelmäßig aufgerufen, um weitere Klangbeispiele anzufordern.

// Berechne eine Puffergröße. // Dies erzeugt einen sinnvollen Wert, der die // // Latenzzeit und die CPU-Auslastung für Spiele mit 60 Hz ausgleicht. var v = audioContext.sampleRate / 60; var n = 0; während (v> 0) v >> = 1; n ++;  v = Math.pow (2, n); // Puffergröße // Erzeuge den Script-Prozessor. Prozessor = audioContext.createScriptProcessor (v); // Den Listener anhängen. processor.onaudioprocess = processSamples; Funktion processSamples (Ereignis) …

Die Häufigkeit bei der processSamples () Die aufgerufene Funktion variiert je nach Hardware-Setup, ist jedoch normalerweise etwa 45 Mal pro Sekunde. Das hört sich nach viel an, aber es ist erforderlich, die Audio-Latenz so niedrig zu halten, dass sie in modernen Spielen verwendet werden kann, die normalerweise mit 60 Frames pro Sekunde laufen. Wenn die Audio-Latenzzeit zu hoch ist, werden die Sounds zu spät gehört, um sich mit den Vorgängen auf dem Bildschirm zu synchronisieren. Dies wäre für jeden, der ein Spiel spielt, ein erschütterndes Erlebnis.

Trotz der Häufigkeit, mit der die processSamples () Wenn die Funktion aufgerufen wird, bleibt die CPU-Auslastung niedrig. Machen Sie sich also keine Sorgen, dass der Spielelogik und dem Rendering zu viel Zeit genommen wird. Bei meiner Hardware (Intel Core i3, 3 GHz) liegt die CPU-Auslastung selten über 2%, selbst wenn viele Sounds gleichzeitig abgespielt werden.

Das processSamples () Funktion enthält tatsächlich das Fleisch von Klingen Klasse; Hier werden die Sound-Samples gemischt und bearbeitet, bevor sie über Web-Audio zur Hardware übertragen werden. Der folgende Code veranschaulicht, was in der Funktion passiert:

// Schnapp dir das Klangbeispiel. sampleL = samplesL [soundPosition >> 0]; sampleR = samplesR [soundPosition >> 0]; // Erhöhen Sie die Abspielposition des Sounds. soundPosition + = soundScale; // Wende die globale Lautstärke an (betrifft alle Sounds). sampleL * = globalVolume; sampleR * = globalVolume; // Wende die Lautstärke des Sounds an. sampleL * = soundVolume; sampleR * = soundVolume; // Wenden Sie den Schwenk des Sounds an (Stereoposition). sampleL * = 1,0 - soundPan; sampleR * = 1,0 + soundPan;

Das ist mehr oder weniger alles. Das ist der Zauber: Eine Handvoll einfacher Bedienungsvorgänge ändert die Lautstärke, die Tonhöhe und die Stereoposition eines Sounds.

Wenn Sie ein Programmierer sind und sich mit dieser Art der Soundverarbeitung auskennen, denken Sie vielleicht: "Das kann nicht alles sein", und Sie wären richtig: Die Sound-Klasse muss die Sound-Instanzen, Sample-Puffer und die Protokollierung überwachen ein paar andere Dinge tun - aber das ist alles ganz normal!

Verwenden der Soundklasse

Der folgende Code veranschaulicht die Verwendung der Sound-Klasse. Sie können auch die Quelldateien für die Live-Demo dieses Tutorials herunterladen.

// Erstellen Sie ein paar Sound-Objekte. var boom = neuer Sound ("boom.ogg"); var tick = neuer Sound ("tick.ogg"); // Optional einen Listener an die Sound-Klasse übergeben. Sound.setListener (Listener); // Dadurch werden alle neu erstellten Sound-Objekte geladen. Sound.load (); // Der Zuhörer. Funktionslistener (Sound, Status) if (state === Sound.State.LOADED) if (Sound === Häkchen) setInterval (playTick, 1000);  else if (sound === boom) setInterval (playBoom, 4000);  else if (state === Sound.State.ERROR) console.warn ("Soundfehler:% s", sound.getPath ());  // Spielt den Tick-Sound ab. function playTick () tick.play ();  // Spielt den Boom-Sound ab. function playBoom ​​() boom.play (); // Randomize der Tonhöhe und Lautstärke des Sounds. boom.setScale (0,8 + 0,4 * Math.random ()); boom.setVolume (0,2 + 0,8 * Math.random ()); 

Schön und einfach.

Eine Anmerkung: Es spielt keine Rolle, ob die Web-Audio-API in einem Browser nicht verfügbar ist, und es spielt keine Rolle, wenn der Browser kein bestimmtes Soundformat abspielen kann. Sie können noch anrufen abspielen() und halt() Funktionen auf einem Klingen Objekt ohne Fehler geworfen. Das ist absichtlich; Sie können Ihren Spielcode wie gewohnt ausführen, ohne sich um Probleme mit der Browser-Kompatibilität zu kümmern oder Ihren Code zu verzweigen, um diese Probleme zu lösen. Das Schlimmste, was passieren kann, ist Stille.

Die Sound Class API

  • abspielen()
  • halt()
  • getPath (): Ruft den Dateipfad des Sounds ab.
  • getState ()
  • getPan ()
  • setPan (Wert): Legt die Panorama-Position des Sounds fest (Stereoposition).
  • getScale ()
  • setScale (Wert): Legt die Tonleiter (Tonhöhe) des Sounds fest.
  • getVolume ()
  • setVolume (Wert): Stellt die Lautstärke ein.
  • isPending ()
  • ladet()
  • isLoaded ()
  • isLooped ()

Die Sound-Klasse enthält auch die folgenden statischen Funktionen.

  • Belastung(): Lädt neu erstellte Sounds.
  • halt(): Stoppt alle Sounds.
  • getVolume ()
  • setVolume (Wert): Legt die globale Lautstärke (Master) fest.
  • getListener ()
  • setListener (Wert): Verfolgt den Ladevorgang beim Laden usw.
  • canPlay (Format): Überprüft, ob verschiedene Soundformate abgespielt werden können.

Dokumentation finden Sie im Quellcode der Demo.

Fazit

Das Abspielen von Soundeffekten in einem JavaScript-Spiel sollte einfach sein. Dieses Tutorial macht es so, indem es die leistungsstarke Web-Audio-API in eine schnelle, leichte Sound-Klasse packt, die alles für Sie erledigt.

Ähnliche Resourcen

Wenn Sie mehr über Klangbeispiele und deren Manipulation erfahren möchten, habe ich für Tuts + eine Serie geschrieben, die Sie eine Weile beschäftigen sollte…

  1. Erstellen eines Synthesizers - Einführung
  2. Erstellen eines Synthesizers - Core Engine
  3. Erstellen eines Synthesizers - Audioprozessoren

Die folgenden Links beziehen sich auf die standardisierten Spezifikationen von W3C und Khronos, die in direktem Zusammenhang mit der Web-Audio-API stehen:

  • Web-Audio-API
  • Typisierte Arrays