Web-Audio-Updates in Chrome 49

Chris Wilson
Chris Wilson

Die Unterstützung der Web Audio API in Chrome wurde kontinuierlich verbessert. In Chrome 49 (Beta seit Februar 2016 und voraussichtlich im März 2016 stabil) haben wir mehrere Funktionen aktualisiert, um die Spezifikation zu verfolgen. Außerdem wurde ein neuer Knoten hinzugefügt.

„decodeAudioData()“ gibt jetzt ein Promise zurück.

Die Methode decodeAudioData() für AudioContext gibt jetzt ein Promise zurück, wodurch die Promise-basierte asynchrone Musterverarbeitung aktiviert wird. Für die Methode decodeAudioData() wurden Erfolgs- und Fehler-Callback-Funktionen immer als Parameter verwendet:

context.decodeAudioData( arraybufferData, onSuccess, onError);

Jetzt können Sie jedoch die Promise-Methode verwenden, um die asynchrone Natur der Decodierung von Audiodaten zu verarbeiten:

context.decodeAudioData( arraybufferData ).then(
        (buffer) => { /* store the buffer */ },
        (reason) => { console.log("decode failed! " + reason) });

Obwohl dies in einem einzigen Beispiel ausführlicher aussieht, wird die asynchrone Programmierung mit Promise-Objekten einfacher und einheitlicher. Aus Kompatibilitätsgründen werden die Callback-Funktionen für Erfolg und Fehler gemäß der Spezifikation weiterhin unterstützt.

„OfflineAudioContext“ unterstützt jetzt „pause()“ und „resume()“.

Auf den ersten Blick mag es seltsam vorkommen, dass wir „pause()“ für einen OfflineAudioContext verwenden. Schließlich wurde suspend() zu AudioContext hinzugefügt, um die Audiohardware in den Standby-Modus zu versetzen. Dies scheint in Szenarien beim Rendern in einen Zwischenspeicher sinnlos zu sein (wofür natürlich OfflineAudioContext vorgesehen ist). Der Sinn dieses Features besteht jedoch darin, jeweils nur einen Teil eines "Scores" zu erstellen, um die Speichernutzung zu minimieren. Sie können weitere Knoten erstellen, während der Vorgang mitten im Rendering angehalten wird.

Die Mondlichtsonate von Beethoven enthält etwa 6.500 Noten. Wahrscheinlich wird jede "Notiz" in mindestens zwei Audiodiagrammknoten zerlegt (z.B. einen AudioBuffer und einen Gain-Knoten). Wenn Sie die gesamten siebeneinhalb Minuten in einem Puffer mit OfflineAudioContext rendern möchten, sollten Sie nicht alle Knoten auf einmal erstellen. Stattdessen können Sie sie in Zeitblöcken erstellen:

var context = new OfflineAudioContext(2, length, sampleRate);
scheduleNextBlock();
context.startRendering().then( (buffer) => { /* store the buffer */ } );

function scheduleNextBlock() {
    // create any notes for the next blockSize number of seconds here
    // ...

    // make sure to tell the context to suspend again after this block;
    context.suspend(context.currentTime + blockSize).then( scheduleNextBlock );

    context.resume();
}

Auf diese Weise können Sie die Anzahl der Knoten, die zu Beginn des Renderings vorab erstellt werden müssen, minimieren und den Speicherbedarf verringern.

IIRFilterNode

In der Spezifikation wurde ein Knoten für Audiophile hinzugefügt, die ihre eigene, genau festgelegte infinite-impulse-response erstellen möchten: den IIRFilterNode. Dieser Filter ergänzt den BiquadFilterNode, ermöglicht jedoch die vollständige Spezifikation der Filterantwortparameter (im Gegensatz zum benutzerfreundlichen AudioParams des BiquadFilterNode für Typ, Häufigkeit, Q usw.). Der IIRFilterNode ermöglicht die präzise Spezifikation von Filtern, die zuvor nicht erstellt werden konnten, z. B. Filter für eine einzelne Reihenfolge. Die Verwendung des IIRFilterNode erfordert jedoch ein gewisses Verständnis der Funktionsweise von IIR-Filtern. Außerdem sind sie wie BiquadFilterNodes nicht planbar.

Vorherige Änderungen

Ich möchte auch einige Verbesserungen erwähnen, die in der Vergangenheit vorgenommen wurden: In Chrome 48 wurde die BiquadFilter-Knotenautomatisierung mit der Audiorate ausgeführt. Die API hat sich hierfür überhaupt nicht geändert, aber dies bedeutet jedoch, dass Ihre Filter-Sweeps noch gleichmäßiger klingen. Ebenfalls in Chrome 48 haben wir eine Verkettung zur Methode AudioNode.connect() hinzugefügt, indem der Knoten zurückgegeben wird, mit dem eine Verbindung hergestellt wird. Dies vereinfacht das Erstellen von Knotenketten, wie in diesem Beispiel:

sourceNode.connect(gainNode).connect(filterNode).connect(context.destination);

Das ist im Moment erstmal alles und rocke weiter!