Signatur
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
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);
// Wichtig · Fallstricke
- Sicherer Kontext nötig:
audioWorkletgibt es nur über HTTPS bzw.localhost. Das Modul unterliegt CORS und der CSP (script-srcbzw.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: Beifalsedarf 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 einemNotSupportedError. - Im Scope stehen kein DOM, kein
fetchund keine Timer zur Verfügung.TextDecoder/TextEncoderfehlen je nach Browser ebenfalls. Daten müssen deshalb perport.postMessage()oder alsSharedArrayBufferübergeben werden, wobei Letzteres Cross-Origin-Isolation voraussetzt. - Das Laden per
file://scheitert in den meisten Browsern, daher einen lokalen Server verwenden.