Signatur
Beschreibung
Der Konstruktor AudioWorkletNode() erzeugt einen AudioNode, dessen Signalverarbeitung von einer eigenen JavaScript-Klasse übernommen wird. Diese Klasse erbt von AudioWorkletProcessor und läuft im Audio-Rendering-Thread, also getrennt vom Haupt-Thread. Dadurch ist eine latenzarme, unterbrechungsfreie Verarbeitung möglich. Der AudioWorklet ersetzt den veralteten ScriptProcessorNode.
Vor dem Aufruf muss das Modul mit dem Prozessor über audioContext.audioWorklet.addModule(url) geladen werden. Darin wird die Prozessorklasse mit registerProcessor(name, klasse) unter einem Namen registriert. Genau dieser Name wird dem Konstruktor als zweites Argument übergeben.
Über das Optionsobjekt legst du Folgendes fest:
- die Anzahl der Ein- und Ausgänge,
- die Kanalanzahl der Ausgänge,
- die Startwerte der
AudioParams, die der Prozessor überstatic get parameterDescriptors()deklariert, - beliebige serialisierbare Daten (
processorOptions), die der Konstruktor des Prozessors erhält.
Haupt-Thread und Prozessor kommunizieren über node.port, einen MessagePort.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $context Pflicht | BaseAudioContext | Der AudioContext oder OfflineAudioContext, zu dem der Knoten gehören soll. |
|
| $name Pflicht | string | Der Name, unter dem der Prozessor im Worklet-Modul mit registerProcessor() registriert wurde. |
|
| $options | AudioWorkletNodeOptions | {} | Optionales Objekt mit: numberOfInputs (Standard 1), numberOfOutputs (Standard 1), outputChannelCount (Array mit einer Kanalanzahl pro Ausgang), parameterData (Objekt mit Startwerten der AudioParams), processorOptions (beliebige, per structured clone übertragbare Daten) sowie die allgemeinen AudioNode-Optionen channelCount, channelCountMode und channelInterpretation. |
Rückgabewert
AudioWorkletNode mit den Eigenschaften port (MessagePort) und parameters (AudioParamMap). Er lässt sich wie jeder andere AudioNode verbinden.Beispiele
Prozessor registrieren (Datei gain-processor.js)
// gain-processor.js – läuft im AudioWorkletGlobalScope
class GainProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{ name: 'gain', defaultValue: 1, minValue: 0, maxValue: 2 }];
}
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 aktiv halten
}
}
registerProcessor('gain-processor', GainProcessor);
Knoten erzeugen und in den Audiograph einbinden
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('gain-processor.js');
const gainNode = new AudioWorkletNode(ctx, 'gain-processor', {
parameterData: { gain: 0.5 },
outputChannelCount: [2]
});
const osc = new OscillatorNode(ctx, { frequency: 440 });
osc.connect(gainNode).connect(ctx.destination);
osc.start();
// AudioParam zeitgesteuert automatisieren
const gain = gainNode.parameters.get('gain');
gain.linearRampToValueAtTime(0, ctx.currentTime + 2);
console.log(gain.value);
processorOptions und Kommunikation über port
// meter-processor.js
class MeterProcessor extends AudioWorkletProcessor {
constructor(options) {
super();
this.interval = options.processorOptions.interval;
this.frames = 0;
}
process(inputs) {
const data = inputs[0][0];
if (data) {
this.frames += data.length;
if (this.frames >= this.interval) {
const rms = Math.sqrt(data.reduce((s, v) => s + v * v, 0) / data.length);
this.port.postMessage({ rms });
this.frames = 0;
}
}
return true;
}
}
registerProcessor('meter-processor', MeterProcessor);
// main.js
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('meter-processor.js');
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const source = ctx.createMediaStreamSource(stream);
const meter = new AudioWorkletNode(ctx, 'meter-processor', {
numberOfOutputs: 0,
processorOptions: { interval: 4096 }
});
meter.port.onmessage = ({ data }) => console.log('Pegel:', data.rms.toFixed(3));
source.connect(meter);
// Wichtig · Fallstricke
- Reihenfolge: Wird der Konstruktor aufgerufen, bevor das Promise von
addModule()erfüllt ist, oder ist der Name nicht registriert, wirft er einenNotSupportedError(in älteren BrowsernInvalidStateError). - Sicherer Kontext:
audioWorkletgibt es nur unter HTTPS bzw.localhost. Das Modul unterliegt CORS und CSP (script-src/worker-src). - Ungültige Optionen: Sind
numberOfInputsundnumberOfOutputsbeide0, entsteht einNotSupportedError. Passt die Länge vonoutputChannelCountnicht zunumberOfOutputs, gibt es einenIndexSizeError. - Autoplay-Regeln: Ein
AudioContextstartet oft im Zustandsuspended. Rufectx.resume()nach einer Nutzerinteraktion auf. - Performance:
process()wird alle 128 Samples aufgerufen. Erzeuge darin keine Objekte, sende keine Nachrichten in hoher Frequenz und vermeide aufwendige Berechnungen, sonst entstehen Knackser (Buffer-Underruns). Die Rückgabefalseerlaubt dem Browser, den Knoten zu entsorgen. processorOptionswerden per structured clone kopiert. Funktionen und DOM-Objekte lassen sich nicht übertragen. Für große Datenmengen eignen sichSharedArrayBuffer(setzt Cross-Origin-Isolation voraus) oder übertragbare Objekte überport.- Browser: Safari unterstützt die Schnittstelle erst ab Version 14.1. Für sehr alte Browser bleibt nur der veraltete
ScriptProcessorNodeals Ersatz.