Signatur
Beschreibung
Das Interface AudioWorklet gehört zur Web Audio API und erbt von Worklet. Du erzeugst es nicht selbst. Jeder BaseAudioContext hat eine eigene Instanz, die du über die Eigenschaft audioContext.audioWorklet erreichst. Mit der geerbten Methode addModule(moduleURL) lädst du ein JavaScript-Modul in den AudioWorkletGlobalScope. Dieser Bereich läuft im selben Thread wie das Audio-Rendering.
Im geladenen Modul leitest du eine Klasse von AudioWorkletProcessor ab und meldest sie mit registerProcessor(name, klasse) an. Die Methode process(inputs, outputs, parameters) wird für jeden Render-Block aufgerufen, standardmäßig mit 128 Sample-Frames. Im Haupt-Thread erzeugst du danach einen AudioWorkletNode mit demselben Namen und bindest ihn wie jeden anderen Knoten in den Audiographen ein.
AudioWorklet ist der Nachfolger des veralteten ScriptProcessorNode. Weil die Verarbeitung nicht im Haupt-Thread läuft, stören Layout, Garbage Collection im UI oder aufwendiges JavaScript die Audioausgabe nicht. Typische Einsätze sind eigene Effekte (Verzerrung, Filter, Bitcrusher), Synthesizer, Pegelmesser, Analyse in Echtzeit sowie DSP-Code in WebAssembly.
Haupt-Thread und Prozessor tauschen Daten über AudioParams (festgelegt über static get parameterDescriptors()) oder über den MessagePort in node.port bzw. this.port aus.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $moduleURL Pflicht | string | URL | Parameter der geerbten Methode addModule(): die URL des Moduls mit den Prozessor-Klassen. Sie wird relativ zum Dokument aufgelöst. |
|
| $options | { credentials?: 'omit' | 'same-origin' | 'include' } | { credentials: 'same-origin' } | Optionen für addModule(). Sie legen fest, ob beim Laden des Moduls Zugangsdaten (z. B. Cookies) mitgeschickt werden. |
Beispiele
Prozessor-Modul: weißes Rauschen mit Lautstärke-Parameter
// datei: noise-processor.js (läuft im AudioWorkletGlobalScope)
class NoiseProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{ name: 'gain', defaultValue: 0.2, minValue: 0, maxValue: 1 }];
}
process(inputs, outputs, parameters) {
const output = outputs[0];
const gain = parameters.gain;
for (const channel of output) {
for (let i = 0; i < channel.length; i++) {
// Der Parameter kann ein Wert pro Block oder pro Sample sein
const g = gain.length > 1 ? gain[i] : gain[0];
channel[i] = (Math.random() * 2 - 1) * g;
}
}
return true; // Prozessor aktiv halten
}
}
registerProcessor('noise-processor', NoiseProcessor);
Modul laden und AudioWorkletNode verwenden
// Haupt-Thread, z. B. in einem Klick-Handler (Autoplay-Richtlinie)
const startButton = document.querySelector('#start');
startButton.addEventListener('click', async () => {
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('noise-processor.js');
const noise = new AudioWorkletNode(ctx, 'noise-processor');
noise.connect(ctx.destination);
// Lautstärke über den AudioParam sanft ändern
const gain = noise.parameters.get('gain');
gain.setValueAtTime(0.2, ctx.currentTime);
gain.linearRampToValueAtTime(0, ctx.currentTime + 2);
console.log('Rauschen läuft, Samplerate:', ctx.sampleRate);
});
Pegelmesser mit MessagePort-Kommunikation
// Prozessor-Code als Blob-URL, damit keine eigene Datei nötig ist
const code = `
class MeterProcessor extends AudioWorkletProcessor {
constructor() { super(); this.frames = 0; }
process(inputs) {
const input = inputs[0][0];
if (input) {
let sum = 0;
for (const s of input) sum += s * s;
this.frames += input.length;
if (this.frames >= sampleRate / 10) { // etwa 10-mal pro Sekunde
this.port.postMessage(Math.sqrt(sum / input.length));
this.frames = 0;
}
}
return true;
}
}
registerProcessor('meter', MeterProcessor);`;
const url = URL.createObjectURL(new Blob([code], { type: 'application/javascript' }));
const startMeter = async () => {
const ctx = new AudioContext();
await ctx.audioWorklet.addModule(url);
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mic = ctx.createMediaStreamSource(stream);
const meter = new AudioWorkletNode(ctx, 'meter');
meter.port.onmessage = ({ data }) => console.log('RMS:', data.toFixed(3));
mic.connect(meter);
};
startMeter();
// Wichtig · Fallstricke
- Sicherer Kontext: AudioWorklet steht nur über HTTPS bzw.
localhostzur Verfügung. Auf unsicheren Seiten istaudioContext.audioWorkletundefined. - Reihenfolge: Du darfst
new AudioWorkletNode(ctx, name)erst aufrufen, nachdem das Promise vonaddModule()erfüllt ist. Sonst wird einInvalidStateErrorgeworfen. - Echtzeit-Code: In
process()keine Speicherzuweisungen (neue Arrays/Objekte), keinpostMessagebei jedem Block und keine blockierenden Operationen. Das löst Garbage Collection aus und führt zu Knacksern. Puffer legst du besser einmal im Konstruktor an. - Eingeschränkter Scope: Im
AudioWorkletGlobalScopegibt es kein DOM, keinfetch, keinsetTimeoutund keinXMLHttpRequest. Verfügbar sind unter anderemsampleRate,currentTime,currentFrameundregisterProcessor. - Rückgabewert von
process(): Mittruebleibt der Prozessor aktiv. Beifalsedarf der Browser ihn beenden, sobald keine Eingänge mehr verbunden sind. - Parameter: Das Array eines
AudioParams hat Länge 1 (konstant im Block) oder 128 (automatisiert). Beide Fälle musst du behandeln. - Autoplay: Einen
AudioContextlegst du am besten in einem Nutzer-Ereignis an oder setzt ihn dort mitresume()fort. - Kompatibilität: Wird von allen modernen Browsern unterstützt.
ScriptProcessorNodeist veraltet und sollte nur als Rückfallebene für sehr alte Browser dienen. FürSharedArrayBufferzwischen den Threads ist Cross-Origin-Isolation nötig (COOP/COEP-Header).