Start · Sprachen · JavaScript · Referenz · AudioWorkletGlobalScope

AudioWorkletGlobalScope

Schnittstelle

Globaler Ausführungskontext eines Audio-Worklets, in dem mit <code>registerProcessor()</code> eigene <code>AudioWorkletProcessor</code>-Klassen für die Audioverarbeitung registriert werden.

seit JavaScript Baseline: alle modernen Browser (seit 2021; Chrome Kategorie: webapi

Signatur

interface AudioWorkletGlobalScope extends WorkletGlobalScope

Beschreibung

AudioWorkletGlobalScope ist der globale Kontext (globalThis) eines Skripts, das über audioContext.audioWorklet.addModule() geladen wird. Der Code läuft nicht im Haupt-Thread, sondern im Audio-Rendering-Thread und kann Audiodaten deshalb mit geringer Latenz und ohne Unterbrechungen durch die Benutzeroberfläche Sample für Sample verarbeiten.

In diesem Kontext steht vor allem die Funktion registerProcessor(name, processorCtor) bereit, mit der man eine von AudioWorkletProcessor abgeleitete Klasse unter einem Namen registriert. Im Haupt-Thread erzeugt man danach mit new AudioWorkletNode(context, name) einen passenden Knoten. Außerdem gibt es die schreibgeschützten Eigenschaften currentFrame (Nummer des gerade verarbeiteten Sample-Frames, Typ number), currentTime (Zeit des aktuellen Blocks in Sekunden, entspricht AudioContext.currentTime) und sampleRate (Abtastrate des zugehörigen BaseAudioContext).

Der Kontext ist absichtlich stark eingeschränkt: Es gibt kein DOM, kein window, kein fetch, kein setTimeout und kein XMLHttpRequest. Die Kommunikation mit dem Haupt-Thread läuft über this.port (ein MessagePort) im Prozessor, über AudioParam-Werte oder über processorOptions beim Erzeugen des Knotens.

Einsatzgebiete sind etwa eigene Effekte (Verzerrung, Bitcrusher, Filter), Synthesizer, Pegelmesser oder die Anbindung von WebAssembly-DSP-Code. Das ersetzt den veralteten ScriptProcessorNode.

Parameter

Name Typ Default Beschreibung
$name Pflicht string Parameter von registerProcessor(): eindeutiger Name, unter dem der Prozessor registriert wird. Wird später bei new AudioWorkletNode(ctx, name) verwendet. Ein leerer oder bereits vergebener Name löst einen NotSupportedError aus.
$processorCtor Pflicht class extends AudioWorkletProcessor Parameter von registerProcessor(): Konstruktor der Prozessor-Klasse. Optional kann sie ein statisches parameterDescriptors-Getter liefern, der die AudioParams beschreibt.

Rückgabewert

Typ
void
Beschreibung
registerProcessor() gibt undefined zurück. Die Eigenschaften currentFrame, currentTime und sampleRate liefern jeweils einen number-Wert.

Beispiele

Prozessor registrieren (Datei gain-processor.js)

// Läuft im AudioWorkletGlobalScope
class GainProcessor extends AudioWorkletProcessor {
  static get parameterDescriptors() {
    return [{ name: 'gain', defaultValue: 0.5, minValue: 0, maxValue: 1 }];
  }

  process(inputs, outputs, parameters) {
    const input = inputs[0];
    const output = outputs[0];
    const gain = parameters.gain;

    for (let ch = 0; ch < input.length; ch++) {
      for (let i = 0; i < input[ch].length; i++) {
        // gain hat Länge 1 (konstant) oder 128 (automatisiert)
        const g = gain.length > 1 ? gain[i] : gain[0];
        output[ch][i] = input[ch][i] * g;
      }
    }
    return true; // Prozessor am Leben halten
  }
}

registerProcessor('gain-processor', GainProcessor);

Modul im Haupt-Thread laden und Knoten erzeugen

const startAudio = async () => {
  const ctx = new AudioContext();
  await ctx.audioWorklet.addModule('gain-processor.js');

  const osc = new OscillatorNode(ctx, { frequency: 440 });
  const gainNode = new AudioWorkletNode(ctx, 'gain-processor');

  gainNode.parameters.get('gain').setValueAtTime(0.2, ctx.currentTime);
  osc.connect(gainNode).connect(ctx.destination);
  osc.start();
};

// AudioContext erst nach Nutzerinteraktion starten (Autoplay-Regeln)
document.querySelector('#play').addEventListener('click', startAudio);

Globale Eigenschaften nutzen und über den Port melden

// meter-processor.js
class MeterProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.lastReport = 0;
    this.port.postMessage({ sampleRate }); // globale Eigenschaft des Scopes
  }

  process(inputs) {
    const channel = inputs[0][0];
    if (!channel) return true;

    // Alle 0,1 s den RMS-Pegel senden
    if (currentTime - this.lastReport >= 0.1) {
      let sum = 0;
      for (const s of channel) sum += s * s;
      this.port.postMessage({ rms: Math.sqrt(sum / channel.length), frame: currentFrame });
      this.lastReport = currentTime;
    }
    return true;
  }
}

registerProcessor('meter-processor', MeterProcessor);

// Haupt-Thread:
// const meter = new AudioWorkletNode(ctx, 'meter-processor');
// meter.port.onmessage = ({ data }) => console.log(data);
{ sampleRate: 48000 } { rms: 0.12, frame: 4864 }

// Wichtig · Fallstricke

  • Sicherer Kontext nötig: audioWorklet gibt es nur über HTTPS bzw. localhost. Das Modul unterliegt CORS und der CSP (script-src bzw. worker-src).
  • Keine blockierenden oder allokierenden Operationen in process(): Die Methode wird etwa alle 128 Frames (bei 48 kHz rund alle 2,7 ms) aufgerufen. Wer dort Objekte oder Arrays anlegt, riskiert Knackser durch die Garbage Collection. Puffer deshalb im Konstruktor anlegen.
  • Der Rückgabewert von process() steuert die Lebensdauer: Bei false darf der Browser den Prozessor abbauen, sobald keine Eingänge mehr aktiv sind.
  • Ein Name lässt sich nur einmal registrieren. Wird addModule() mehrfach mit demselben Namen ausgeführt, kommt es zu einem NotSupportedError.
  • Im Scope stehen kein DOM, kein fetch und keine Timer zur Verfügung. TextDecoder/TextEncoder fehlen je nach Browser ebenfalls. Daten müssen deshalb per port.postMessage() oder als SharedArrayBuffer übergeben werden, wobei Letzteres Cross-Origin-Isolation voraussetzt.
  • Das Laden per file:// scheitert in den meisten Browsern, daher einen lokalen Server verwenden.