Erste Schritte mit Web Audio API

Boris Smus
Boris Smus

Vor dem HTML5-Element <audio> war Flash oder ein anderes Plug-in erforderlich die Stille des Webs zu brechen. Während Audio im Web nicht mehr erfordert ein Plug-in, bringt das Audio-Tag erhebliche Einschränkungen die Implementierung anspruchsvoller Spiele und interaktiver Anwendungen.

Das Web Audio API ist ein JavaScript-API auf höchster Ebene zur Verarbeitung und dem Synthetisieren von Audioinhalten in Webanwendungen. Ziel dieser API ist es, Funktionen moderner Spiel-Audio-Engines sowie einige Kombinieren, Verarbeiten und Filtern von Aufgaben, die in modernen Desktop- Anwendungen zur Audioproduktion. Im Folgenden stellen wir Ihnen mit dieser leistungsstarken API.

Erste Schritte mit AudioContext

Ein AudioContext dient zum Verwalten und Abspielen aller Töne. Um zu produzieren einen Ton mit der Web Audio API erstellen, eine oder mehrere Tonquellen erstellen und verbinde sie mit dem Ziel der Audioausgabe von AudioContext. Instanz. Es muss keine direkte Verbindung sein und sie kann eine beliebige Anzahl an Zwischen-AudioNodes, die als Verarbeitungsknoten fungieren Module für das Audiosignal. Dieses routing wird in größerer in der Web Audio-Spezifikation.

Eine einzelne Instanz von AudioContext kann mehrere Toneingaben unterstützen und komplexe Audiodiagramme, sodass wir für jedes Audio-App entwickelt.

Mit dem folgenden Snippet wird ein AudioContext erstellt:

var context;
window.addEventListener('load', init, false);
function init() {
    try {
    context = new AudioContext();
    }
    catch(e) {
    alert('Web Audio API is not supported in this browser');
    }
}

Verwenden Sie für ältere WebKit-basierte Browser das Präfix webkit wie bei webkitAudioContext.

Viele der interessanten Web Audio API-Funktionen, wie die Erstellung AudioNodes und das Decodieren von Audiodateidaten sind Methoden von AudioContext.

Töne werden geladen

Das Web Audio API verwendet einen AudioBuffer für kurze bis mittellange Musik. Geräusche. Der grundlegende Ansatz besteht in der Verwendung von XMLHttpRequest das Abrufen von Sounddateien.

Die API unterstützt das Laden von Audiodateidaten in mehreren Formaten, z. B. WAV, MP3, AAC, OGG und andere Browser-Unterstützung für verschiedene Audioformate variieren.

Das folgende Snippet zeigt, wie ein Sound-Sample geladen wird:

var dogBarkingBuffer = null;
var context = new AudioContext();

function loadDogSound(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    // Decode asynchronously
    request.onload = function() {
    context.decodeAudioData(request.response, function(buffer) {
        dogBarkingBuffer = buffer;
    }, onError);
    }
    request.send();
}

Die Audiodateidaten sind binär (kein Text). Daher wird responseType festgelegt. der Anfrage an 'arraybuffer'. Weitere Informationen zu ArrayBuffers finden Sie in diesem Artikel über XHR2.

Sobald die (nicht decodierten) Audiodateidaten empfangen wurden, können sie beibehalten werden. für eine spätere Decodierung oder kann sofort mit dem AudioContext-Methode decodeAudioData(). Diese Methode verwendet den ArrayBuffer der Audiodateidaten in request.response und decodiert es asynchron, ohne die Hauptausführung von JavaScript zu blockieren. Thread).

Wenn decodeAudioData() beendet ist, wird eine Callback-Funktion aufgerufen, die stellt die decodierten PCM-Audiodaten als AudioBuffer bereit.

Ton wiedergeben

<ph type="x-smartling-placeholder">
</ph> Ein einfaches Audiodiagramm
Einfaches Audiodiagramm

Sobald mindestens ein AudioBuffers geladen ist, kann es losgehen Geräusche. Angenommen, wir haben gerade ein AudioBuffer-Element mit dem Ton eines Hundegebells und dass das Laden abgeschlossen ist. Dann können wir mit dem folgenden Code in diesen Puffer ein.

var context = new AudioContext();

function playSound(buffer) {
    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.noteOn(0);                          // play the source now
}

Diese playSound()-Funktion könnte jedes Mal aufgerufen werden, wenn jemand eine Taste drückt oder mit der Maus auf etwas klickt.

Mit der noteOn(time)-Funktion ist es ganz einfach, präzise Geräusche zu planen. für Spiele und andere zeitkritische Anwendungen. Um jedoch dass diese Planung ordnungsgemäß funktioniert, sollten Sie sicherstellen, vorab geladen.

Web Audio API abstrahieren

Natürlich wäre es besser, ein allgemeineres Ladesystem zu erstellen, der nicht hartcodiert ist, um diesen speziellen Ton zu laden. Es gibt viele mit den vielen kurzen bis mittellangen Tönen umgehen, eine Audioanwendung oder ein Audiospiel verwenden würde. Hier ist eine Möglichkeit mit einem BufferLoader (nicht Teil des Webstandards).

Das folgende Beispiel zeigt, wie Sie die Klasse BufferLoader verwenden können. Lassen Sie uns zwei AudioBuffers erstellen. Sobald sie geladen sind, spielen wir sie gleichzeitig ab.

window.onload = init;
var context;
var bufferLoader;

function init() {
    context = new AudioContext();

    bufferLoader = new BufferLoader(
    context,
    [
        '../sounds/hyper-reality/br-jam-loop.wav',
        '../sounds/hyper-reality/laughter.wav',
    ],
    finishedLoading
    );

    bufferLoader.load();
}

function finishedLoading(bufferList) {
    // Create two sources and play them both together.
    var source1 = context.createBufferSource();
    var source2 = context.createBufferSource();
    source1.buffer = bufferList[0];
    source2.buffer = bufferList[1];

    source1.connect(context.destination);
    source2.connect(context.destination);
    source1.noteOn(0);
    source2.noteOn(0);
}

Der richtige Umgang mit der Zeit: Klänge mit Rhythmus spielen

Mit dem Web Audio API können Entwickler die Wiedergabe genau planen. Bis Erstellen wir einen einfachen Rhythmus-Track. Wahrscheinlich bekanntestes Schlagzeugmuster:

<ph type="x-smartling-placeholder">
</ph> Einfaches Rock-Schlagzeugmuster
Einfaches Rock-Schlagzeugmuster

bei dem auf jeder achten Note ein Hihat gespielt wird und Kick and Snare vierteljährlich gespielt werden.

Wenn wir die Zwischenspeicher kick, snare und hihat geladen haben, ist das ganz einfach:

for (var bar = 0; bar < 2; bar++) {
    var time = startTime + bar * 8 * eighthNoteTime;
    // Play the bass (kick) drum on beats 1, 5
    playSound(kick, time);
    playSound(kick, time + 4 * eighthNoteTime);

    // Play the snare drum on beats 3, 7
    playSound(snare, time + 2 * eighthNoteTime);
    playSound(snare, time + 6 * eighthNoteTime);

    // Play the hi-hat every eighth note.
    for (var i = 0; i < 8; ++i) {
    playSound(hihat, time + i * eighthNoteTime);
    }
}

Hier führen wir nur eine Wiederholung anstelle der Endlosschleife aus, Noten. Die Funktion playSound ist eine Methode, die eine wie im Folgenden gezeigt:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

Lautstärke eines Tons ändern

Eine der grundlegendsten Operationen, die du für einen Ton durchführen möchtest, die Lautstärke ändern. Mit dem Web Audio API können wir unsere Quelle an folgende URL weiterleiten: sein Ziel über einen AudioGainNode zu ermitteln, um den Parameter Lautstärke:

<ph type="x-smartling-placeholder">
</ph> Audiodiagramm mit Verstärkungsknoten
Audiografik mit einem Verstärkungsknoten

Diese Verbindungseinrichtung kann so erfolgen:

// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);

Nachdem die Grafik eingerichtet wurde, können Sie das Diagramm ändern Sie den gainNode.gain.value so:

// Reduce the volume.
gainNode.gain.value = 0.5;

Überblenden zwischen zwei Tönen

Nehmen wir nun ein etwas komplexeres Szenario, in dem wir mehrere Töne abspielen, aber zwischen ihnen überblenden möchten. Dies ist ein Bei einer DJ-ähnlichen App gibt es zwei Plattenspieler und von einer Tonquelle zur anderen schwenken möchten.

Dazu können Sie das folgende Audiodiagramm verwenden:

<ph type="x-smartling-placeholder">
</ph> Audiodiagramm mit zwei Quellen, die über Verstärkungsknoten verbunden sind
Audiografik mit zwei Quellen, die über Verstärkungsknoten verbunden sind

Dafür erstellen wir einfach zwei AudioGainNodes und verbinden jede Quelle über die Knoten zu erstellen. Verwenden Sie dazu in etwa folgende Funktion:

function createSource(buffer) {
    var source = context.createBufferSource();
    // Create a gain node.
    var gainNode = context.createGainNode();
    source.buffer = buffer;
    // Turn on looping.
    source.loop = true;
    // Connect source to gain.
    source.connect(gainNode);
    // Connect gain to destination.
    gainNode.connect(context.destination);

    return {
    source: source,
    gainNode: gainNode
    };
}

Überblendung bei gleichmäßiger Stärke

Bei einem naiven linearen Crossfade-Ansatz zeigt sich beim Schwenken ein Rückgang der Lautstärke. zwischen den Stichproben.

<ph type="x-smartling-placeholder">
</ph> Lineare Überblendung
Lineare Überblendung

Um dieses Problem zu lösen, verwenden wir eine gleichmäßige Leistungskurve, bei der die entsprechende Verstärkungskurven sind nicht linear und schneiden sich bei höherer Amplitude Dadurch werden Lautstärkeeinbrüche zwischen Audioregionen minimiert, sodass in einer gleichmäßigeren Überblendung zwischen Regionen dargestellt, die leicht und eine andere Ebene haben.

<ph type="x-smartling-placeholder">
</ph> Überblenden mit gleicher Stärke.
Gleiche Überblendung

Überblenden von Playlists

Eine weitere gängige Crossfader-Anwendung ist eine Musikplayer-Anwendung. Wenn sich ein Song ändert, wird der aktuelle Titel aus- und der um lästige Übergänge zu vermeiden. Dazu planen Sie eine in die Zukunft überblenden. Dafür könnten wir setTimeout verwenden. angezeigt wird, ist dies nicht genau. Mit dem Web Audio API können Sie mithilfe der Schnittstelle AudioParam zukünftige Werte für Parameter wie den Verstärkungswert eines AudioGainNode.

Bei einer Playlist können wir also zwischen Tracks wechseln, indem wir einen für den aktuell wiedergegebenen Titel und einen Anstieg für den nächsten Titel ein, beide kurz vor dem Ende des aktuellen Titels:

function playHelper(bufferNow, bufferLater) {
    var playNow = createSource(bufferNow);
    var source = playNow.source;
    var gainNode = playNow.gainNode;
    var duration = bufferNow.duration;
    var currTime = context.currentTime;
    // Fade the playNow track in.
    gainNode.gain.linearRampToValueAtTime(0, currTime);
    gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
    // Play the playNow track.
    source.noteOn(0);
    // At the end of the track, fade it out.
    gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
    gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
    // Schedule a recursive track change with the tracks swapped.
    var recurse = arguments.callee;
    ctx.timer = setTimeout(function() {
    recurse(bufferLater, bufferNow);
    }, (duration - ctx.FADE_TIME) - 1000);
}

Die Web Audio API bietet praktische RampToValue-Methoden zum den Wert eines Parameters allmählich ändern, z. B. linearRampToValueAtTime und exponentialRampToValueAtTime.

Die Übergangszeitfunktion kann aus den integrierten linearen und exponentiellen Einsen (wie oben) können Sie auch Ihren eigenen Wert über ein Array von Werten mit der Funktion setValueCurveAtTime.

Einen einfachen Filtereffekt auf einen Ton anwenden

<ph type="x-smartling-placeholder">
</ph> Ein Audiodiagramm mit einem BiquadFilterNode
Ein Audiodiagramm mit einem BiquadFilterNode

Mit der Web Audio API können Sie Ton von einem Audioknoten in einen anderen übertragen, Erstellen einer potenziell komplexen Kette von Prozessoren, um komplexe auf deine Soundformen anwenden.

Eine Möglichkeit dafür besteht darin, BiquadFilterNodes zwischen den Quelle und Ziel. Diese Art von Audioknoten kann Low-Order-Filtern, mit denen grafische Equalizer und sogar auch komplexere Effekte. Dabei geht es vor allem um die Auswahl der Frequenzspektrum des zu betonenden und zu unterdrückenden Tons.

Folgende Filtertypen werden unterstützt:

  • Tiefpassfilter
  • Hochpassfilter
  • Band pass filter
  • Low-Shelf-Filter
  • High-Shelf-Filter
  • Hochwertfilter
  • Notch filter
  • Filter für alle Karten/Tickets

Alle Filter enthalten Parameter, mit denen die Anzahl der gain: die Häufigkeit, mit der der Filter angewendet wird, und ein Qualitätsfaktor Der Tiefpassfilter behält den unteren Frequenzbereich bei, verwirft aber hohe Frequenzen. Der Verbindungspunkt wird durch den Frequenzwert bestimmt, und der Q-Faktor keine Einheit ist und die Form des Diagramm. Die Verstärkung wirkt sich nur auf bestimmte Filter aus, z. B. und nicht diesen Tiefpassfilter.

Richten wir einen einfachen Tiefpassfilter ein, um nur die Basen aus einem Soundbeispiel:

// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);

Im Allgemeinen müssen die Häufigkeitseinstellungen für eine logarithmische Skala, da das menschliche Hören nach demselben Prinzip (A4 hat also 440 Hz und A5 hat 880 Hz). Weitere Informationen finden Sie in der FilterSample.changeFrequency im Quellcodelink oben.

Beachten Sie, dass Sie mithilfe des Beispielcodes die Verbindung Filter, der das AudioContext-Diagramm dynamisch ändert. Wir können die Verbindung AudioNodes aus dem Diagramm durch Aufrufen von node.disconnect(outputNumber). Um beispielsweise die Route für die Grafik von einem Filter auf einen Direktverbindung herstellen, können wir Folgendes tun:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

Weitere Hörgewohnheiten

Wir haben die API-Grundlagen behandelt, wie das Laden und Abspielen von Audioinhalten. Proben. Wir haben Audiodiagramme mit Verstärkungsknoten und Filtern erstellt Töne und Audioparameter angepasst, um gängige Töne zu ermöglichen Effekte. Jetzt sind Sie bereit, Ihre Web-App zu gestalten, Audio-Apps!

Viele Entwickler haben bereits tolle Arbeit mit der Web Audio API. Einige meiner Lieblingsspiele umfassen:

  • AudioJedit, ein browsereigenes Tool zum Erstellen von Sounds, das SoundCloud-Permalinks
  • ToneCraft, ein Sound Sequencer, bei dem Töne von 3D-Blöcke zu stapeln.
  • Plink, ein Spiel zum gemeinsamen Musikmachen mit Web Audio und Web Sockets