Signatur
Beschreibung
Die Eigenschaft audioWorklet von BaseAudioContext (und damit von AudioContext und OfflineAudioContext) gibt ein AudioWorklet-Objekt zurück. Darüber lädst du JavaScript-Module in den Audio-Rendering-Thread. In diesen Modulen registrierst du mit registerProcessor() Klassen, die von AudioWorkletProcessor erben und Audiodaten Sample für Sample bzw. Block für Block (128 Frames) verarbeiten.
Gebraucht wird das für eigene Audioeffekte, Synthesizer, Pegelmesser oder Analysen, die sich mit den eingebauten Knoten nicht umsetzen lassen. AudioWorklet löst den veralteten ScriptProcessorNode ab: Der Code läuft nicht im Hauptthread, sondern im Audio-Thread. Deshalb gibt es kaum Latenz und keine Aussetzer, wenn die Oberfläche gerade viel zu tun hat.
Der übliche Ablauf: Du rufst await context.audioWorklet.addModule(url) auf, erzeugst danach mit new AudioWorkletNode(context, 'prozessor-name') einen Knoten und hängst ihn in den Audiographen ein. Zwischen Hauptthread und Prozessor kommunizierst du über node.port (ein MessagePort) oder über AudioParams.
Rückgabewert
AudioWorklet-Objekt. Bei jedem Zugriff auf denselben Kontext ist es dieselbe Instanz. Die wichtigste Methode ist addModule(moduleURL, options), die ein Promise<void> zurückgibt.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++) {
// gain hat Länge 1 (konstant) oder 128 (automatisiert)
const g = gain.length > 1 ? gain[i] : gain[0];
channel[i] = (Math.random() * 2 - 1) * g;
}
}
return true; // Prozessor am Leben halten
}
}
registerProcessor('noise-processor', NoiseProcessor);
Modul laden und Knoten im Hauptthread verwenden
const startButton = document.querySelector('#start');
startButton.addEventListener('click', async () => {
const ctx = new AudioContext();
// Modul in den Audio-Thread laden
await ctx.audioWorklet.addModule('noise-processor.js');
const noise = new AudioWorkletNode(ctx, 'noise-processor');
noise.connect(ctx.destination);
// AudioParam wie bei eingebauten Knoten steuern
const gain = noise.parameters.get('gain');
gain.setValueAtTime(0, ctx.currentTime);
gain.linearRampToValueAtTime(0.3, ctx.currentTime + 2);
console.log(ctx.audioWorklet instanceof AudioWorklet);
});
Nachrichten über den MessagePort austauschen (Pegelmesser)
// meter-processor.js
// class MeterProcessor extends AudioWorkletProcessor {
// process([input]) {
// const ch = input[0];
// if (ch) {
// let sum = 0;
// for (const s of ch) sum += s * s;
// this.port.postMessage(Math.sqrt(sum / ch.length));
// }
// return true;
// }
// }
// registerProcessor('meter-processor', MeterProcessor);
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('meter-processor.js');
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mic = ctx.createMediaStreamSource(stream);
const meter = new AudioWorkletNode(ctx, 'meter-processor');
meter.port.onmessage = ({ data }) => {
document.querySelector('#level').value = data;
};
mic.connect(meter);
Offline-Rendering mit OfflineAudioContext
const offline = new OfflineAudioContext(1, 44100, 44100); // 1 s Mono
await offline.audioWorklet.addModule('noise-processor.js');
const node = new AudioWorkletNode(offline, 'noise-processor');
node.connect(offline.destination);
const buffer = await offline.startRendering();
console.log(buffer.duration, buffer.getChannelData(0).length);
// Wichtig · Fallstricke
- Nur im sicheren Kontext:
audioWorkletgibt es nur über HTTPS oder auflocalhost. Überhttp://ist die Eigenschaftundefined. - Reihenfolge beachten:
new AudioWorkletNode()wirft einenInvalidStateError, wenn das Promise vonaddModule()noch nicht erfüllt oder der Prozessorname nicht registriert ist. - Same-Origin/CORS: Module werden wie ES-Module geladen. Liegen sie auf einer fremden Domain, braucht es passende CORS-Header. Eine Content Security Policy (
script-src) gilt auch hier. Als Alternative lässt sich eine Blob-URL verwenden, sofern die CSP das zulässt. - Eingeschränkte Umgebung: Im
AudioWorkletGlobalScopegibt es kein DOM, keinfetchund keinsetTimeout.process()sollte keinen Speicher anlegen (keine neuen Arrays oder Objekte pro Aufruf), sonst kann die Speicherbereinigung Knackser verursachen. - Autoplay-Regeln: Ein
AudioContextstartet oft im Zustandsuspended. Erzeuge ihn deshalb in einem Nutzer-Ereignis oder rufe dortctx.resume()auf. - Gibt
process()den Wertfalsezurück, darf der Browser den Prozessor abbauen, sobald keine Eingänge mehr aktiv sind. - Kompatibilität: Chrome/Edge 66+, Firefox 76+, Safari 14.1+. Der Vorgänger
ScriptProcessorNodeist veraltet.