Signatur
Beschreibung
Die schreibgeschützte Eigenschaft port des Interfaces AudioWorkletGlobalScope gibt ein MessagePort-Objekt zurück. Es ist das Gegenstück zu AudioWorklet.port im Hauptthread (audioContext.audioWorklet.port). Beide Enden bilden zusammen einen Kanal, über den Hauptthread und Audio-Rendering-Thread Nachrichten austauschen können.
Wichtig ist der Unterschied zu AudioWorkletProcessor.port: Den Port eines Prozessors gibt es einmal pro AudioWorkletNode, und er verbindet genau diesen Knoten mit seinem Prozessor. Der Port des globalen Gültigkeitsbereichs gibt es dagegen nur einmal pro BaseAudioContext, und alle Prozessoren in diesem Gültigkeitsbereich teilen ihn sich. Er eignet sich deshalb für Daten, die alle Prozessoren gemeinsam nutzen: kompilierte WebAssembly-Module, Lookup-Tabellen, Impulsantworten, globale Einstellungen oder SharedArrayBuffer-Speicher.
Im Modulcode des Worklets greift man direkt über den globalen Namen port auf die Eigenschaft zu, denn globalThis ist dort der AudioWorkletGlobalScope. Nachrichten verschickt man mit port.postMessage(), und man empfängt sie über port.onmessage oder port.addEventListener("message", …). Bei addEventListener muss man zusätzlich port.start() aufrufen.
Rückgabewert
MessagePort auf der Worklet-Seite des Kanals. Er ist mit AudioWorklet.port im Hauptthread verbunden, und innerhalb des Gültigkeitsbereichs wird immer dasselbe Objekt zurückgegeben.Beispiele
Hauptthread: globale Konfiguration an das Worklet senden
// main.js
const ctx = new AudioContext();
await ctx.audioWorklet.addModule("shared-processor.js");
// Antworten aus dem globalen Gültigkeitsbereich empfangen
ctx.audioWorklet.port.onmessage = (event) => {
console.log("Worklet meldet:", event.data);
};
// Eine Einstellung, die für alle Prozessoren gilt
ctx.audioWorklet.port.postMessage({ type: "setGain", value: 0.5 });
const nodeA = new AudioWorkletNode(ctx, "shared-gain");
const nodeB = new AudioWorkletNode(ctx, "shared-gain");
nodeA.connect(ctx.destination);
nodeB.connect(ctx.destination);
Worklet: gemeinsamen Zustand über den globalen port pflegen
// shared-processor.js (läuft im AudioWorkletGlobalScope)
const shared = { gain: 1 };
port.onmessage = (event) => {
const { type, value } = event.data;
if (type === "setGain") {
shared.gain = value;
port.postMessage({ type: "ack", gain: shared.gain });
}
};
class SharedGainProcessor extends AudioWorkletProcessor {
process(inputs, outputs) {
const input = inputs[0];
const output = outputs[0];
for (let ch = 0; ch < output.length; ch++) {
const inCh = input[ch];
const outCh = output[ch];
if (!inCh) continue;
for (let i = 0; i < outCh.length; i++) {
outCh[i] = inCh[i] * shared.gain; // alle Instanzen lesen denselben Wert
}
}
return true;
}
}
registerProcessor("shared-gain", SharedGainProcessor);
WebAssembly-Modul einmalig für alle Prozessoren bereitstellen
// main.js
const ctx = new AudioContext();
await ctx.audioWorklet.addModule("wasm-processor.js");
const bytes = await (await fetch("dsp.wasm")).arrayBuffer();
const module = await WebAssembly.compile(bytes);
// WebAssembly.Module ist strukturiert klonbar
ctx.audioWorklet.port.postMessage({ type: "wasm", module });
// wasm-processor.js
// let wasmModule = null;
// port.onmessage = ({ data }) => {
// if (data.type === "wasm") wasmModule = data.module;
// };
// Prozessoren instanziieren später mit
// new WebAssembly.Instance(wasmModule, imports)
// Wichtig · Fallstricke
- Browser-Unterstützung:
AudioWorkletGlobalScope.portundAudioWorklet.portsind erst spät in die Spezifikation aufgenommen worden und noch nicht überall verfügbar. Prüfe das vorher, z. B. mit"port" in ctx.audioWorklet. Als Ausweichlösung kann man Daten überAudioWorkletProcessor.portoderprocessorOptionspro Knoten übergeben. - Nicht verwechseln mit
AudioWorkletProcessor.port(this.portim Prozessor). Den gibt es einmal pro Knoten, den globalenportdagegen nur einmal pro Audio-Kontext. - Echtzeit-Thread: Aufwendige Arbeit in
onmessage, z. B. große Kopien oder das Kompilieren von WebAssembly, kann das Audio-Rendering stören und Aussetzer verursachen. Große Puffer überträgt man besser als Transferable (postMessage(data, [buffer])). Für häufige Parameteränderungen eignen sichAudioParamoderSharedArrayBufferbesser. - Nachrichten kommen asynchron an. Ein
process()-Aufruf kann also noch mit dem alten Zustand laufen, bis die Nachricht verarbeitet ist. - Wer
addEventListener("message", …)stattonmessagenutzt, mussport.start()aufrufen, sonst kommen keine Nachrichten an. SharedArrayBufferfunktioniert nur in einem isolierten Kontext (crossOriginIsolated, also mit COOP/COEP-Headern).