Signatur
Beschreibung
AudioWorkletProcessor ist die Basisklasse, von der du in einem AudioWorklet-Modul eigene Prozessoren ableitest. Der Code läuft nicht im Hauptthread, sondern im AudioWorkletGlobalScope direkt im Audio-Rendering-Thread. Dadurch verarbeitest du Samples mit niedriger Latenz und ohne Aussetzer, die durch Layout, Garbage Collection oder andere Arbeit im Hauptthread entstehen würden. Das Interface ersetzt den veralteten ScriptProcessorNode.
Ein Prozessor wird im Worklet-Modul per registerProcessor(name, Klasse) registriert. Im Hauptthread lädst du das Modul mit audioContext.audioWorklet.addModule(url) und erzeugst anschließend einen new AudioWorkletNode(context, name, options). Dabei wird automatisch eine Instanz deines Prozessors erstellt. Den Konstruktor rufst du nie selbst auf, sonst wird ein TypeError geworfen.
Die zentrale Methode ist process(inputs, outputs, parameters). Sie wird für jeden Render-Block aufgerufen, in der Regel mit 128 Sample-Frames. inputs und outputs sind Arrays von Eingängen bzw. Ausgängen. Jeder davon ist ein Array von Kanälen, und jeder Kanal ist ein Float32Array. Über den statischen Getter parameterDescriptors definierst du eigene AudioParams, die im Hauptthread über node.parameters erreichbar sind. Mit der Eigenschaft port (ein MessagePort) tauschst du Nachrichten in beide Richtungen mit dem Knoten aus.
Der Rückgabewert von process() legt die Lebensdauer fest. Mit true bleibt der Prozessor aktiv. Mit false darf ihn der Browser abbauen, sobald keine aktiven Eingänge mehr anliegen. Quellknoten wie Oszillatoren geben daher meist true zurück, reine Effekte oft false.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | AudioWorkletNodeOptions | Wird dem Konstruktor des Prozessors automatisch übergeben. Es sind die Optionen aus new AudioWorkletNode(), z. B. numberOfInputs, numberOfOutputs, outputChannelCount, parameterData und processorOptions (beliebige eigene Daten, die per Structured Clone übertragen werden). |
|
| $inputs Pflicht | Float32Array[][] | Parameter von process(): inputs[n][kanal][sample]. Ist ein Eingang nicht verbunden, hat er 0 Kanäle. |
|
| $outputs Pflicht | Float32Array[][] | Parameter von process(): Ausgabepuffer, die du beschreibst. Sie sind mit Nullen vorbelegt. |
|
| $parameters Pflicht | Object<string, Float32Array> | Parameter von process(): aktuelle Werte der eigenen AudioParams. Das Array hat die Länge 128 (a-rate mit Automation) oder 1 (Wert im ganzen Block konstant). |
Beispiele
Einfacher Gain-Prozessor mit eigenem AudioParam (gain-processor.js)
// gain-processor.js – läuft im AudioWorkletGlobalScope
class GainProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{ name: 'gain', defaultValue: 0.5, minValue: 0, maxValue: 1, automationRate: 'a-rate' }];
}
process(inputs, outputs, parameters) {
const input = inputs[0];
const output = outputs[0];
const gain = parameters.gain;
for (let ch = 0; ch < input.length; ch++) {
const inCh = input[ch];
const outCh = output[ch];
for (let i = 0; i < inCh.length; i++) {
// Länge 1 = konstanter Wert im ganzen Block
outCh[i] = inCh[i] * (gain.length > 1 ? gain[i] : gain[0]);
}
}
return true;
}
}
registerProcessor('gain-processor', GainProcessor);
Prozessor im Hauptthread laden und verwenden
const ctx = new AudioContext();
const start = async () => {
await ctx.audioWorklet.addModule('gain-processor.js');
const osc = new OscillatorNode(ctx, { frequency: 440 });
const gainNode = new AudioWorkletNode(ctx, 'gain-processor');
osc.connect(gainNode).connect(ctx.destination);
// Eigenen AudioParam automatisieren
const gain = gainNode.parameters.get('gain');
gain.setValueAtTime(0, ctx.currentTime);
gain.linearRampToValueAtTime(0.8, ctx.currentTime + 2);
osc.start();
};
// AudioContext erst nach einer Nutzeraktion starten (Autoplay-Richtlinie)
document.querySelector('#play').addEventListener('click', start, { once: true });
Pegelmesser mit Kommunikation über port
// meter-processor.js
class MeterProcessor extends AudioWorkletProcessor {
constructor(options) {
super();
this.interval = options.processorOptions?.interval ?? 20; // Blöcke
this.count = 0;
this.active = true;
this.port.onmessage = (e) => {
if (e.data === 'stop') this.active = false;
};
}
process(inputs) {
const ch = inputs[0][0];
if (ch && ++this.count >= this.interval) {
let sum = 0;
for (let i = 0; i < ch.length; i++) sum += ch[i] * ch[i];
this.port.postMessage({ rms: Math.sqrt(sum / ch.length), time: currentTime });
this.count = 0;
}
return this.active;
}
}
registerProcessor('meter-processor', MeterProcessor);
// --- Hauptthread ---
// await ctx.audioWorklet.addModule('meter-processor.js');
// const meter = new AudioWorkletNode(ctx, 'meter-processor', {
// processorOptions: { interval: 10 }
// });
// meter.port.onmessage = ({ data }) => console.log(data.rms.toFixed(3));
// source.connect(meter);
// Wichtig · Fallstricke
- Kein
newim Hauptthread: Der Prozessor wird nur durchnew AudioWorkletNode()erzeugt. Rufst du im eigenen Konstruktorsuper()nicht auf, istthis.portnicht verfügbar. - Echtzeitregeln: Erzeuge in
process()keine Objekte oder Arrays, denn die Garbage Collection kann Knackser verursachen. Vermeide außerdem blockierende Arbeit und häufigepostMessage-Aufrufe. Legst du Puffer im Konstruktor an, verwende sie wieder. - Blockgröße: Gehe nicht fest von 128 Frames aus, sondern nutze
channel.length. Künftige Versionen der Spezifikation erlauben andere Render-Quanten. - Nicht verbundene Eingänge haben 0 Kanäle,
inputs[0][0]ist dannundefined. Prüfe das immer vorher. - Einschränkungen im Worklet-Scope: Es gibt kein DOM, kein
fetchund keine Timer wiesetTimeout. Zur Verfügung stehencurrentTime,currentFrame,sampleRateundregisterProcessor. - Sicherer Kontext:
audioWorkletgibt es nur über HTTPS bzw.localhost. Das Modul unterliegt CORS und der CSP (script-src/worker-src). - Fehler: Eine Ausnahme in
process()löst am Knoten das Ereignisprocessorerroraus. Danach gibt der Knoten nur noch Stille aus. - Für große Datenmengen ohne Kopieren eignet sich ein
SharedArrayBuffer. Dafür muss die Seite cross-origin-isolated sein (COOP/COEP-Header).