Start · Sprachen · JavaScript · Referenz · AudioWorkletNode.port

AudioWorkletNode.port

Eigenschaft

Die schreibgeschützte Eigenschaft <code>port</code> gibt den <code>MessagePort</code> zurück, über den die <code>AudioWorkletNode</code> mit ihrem <code>AudioWorkletProcessor</code> kommuniziert.

seit JavaScript Baseline: alle modernen Browser (seit 2021, Safari Kategorie: webapi

Signatur

AudioWorkletNode.port

Beschreibung

Eine AudioWorkletNode lebt im Haupt-Thread, der zugehörige AudioWorkletProcessor dagegen im Audio-Rendering-Thread (AudioWorkletGlobalScope). Beide Objekte sind über einen MessageChannel verbunden: Die Node erhält das eine Ende als node.port, der Prozessor das andere Ende als this.port.

Über diesen Port lassen sich beliebige strukturiert klonbare Daten in beide Richtungen senden, z. B. Konfigurationen, Samples, Pegelmessungen oder Statusmeldungen. Mit postMessage() sendet man, empfangen wird über onmessage oder addEventListener("message", …).

Für Werte, die sich kontinuierlich und sample-genau ändern sollen (Lautstärke, Frequenz), sind AudioParams über parameterDescriptors meist besser geeignet. Der Port ist das Mittel der Wahl für unregelmäßige, strukturierte oder größere Datenmengen und für Rückmeldungen vom Prozessor an die Oberfläche.

Rückgabewert

Typ
MessagePort
Beschreibung
Der MessagePort, der mit dem port des zugehörigen AudioWorkletProcessor verbunden ist. Die Eigenschaft ist schreibgeschützt und liefert immer dasselbe Objekt.

Beispiele

Nachrichten in beide Richtungen senden

// processor.js (läuft im AudioWorkletGlobalScope)
class PingProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.port.onmessage = (event) => {
      if (event.data === 'ping') this.port.postMessage('pong');
    };
  }
  process() {
    return true;
  }
}
registerProcessor('ping-processor', PingProcessor);

// main.js (Haupt-Thread)
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('processor.js');
const node = new AudioWorkletNode(ctx, 'ping-processor');

node.port.onmessage = (event) => console.log('Antwort:', event.data);
node.port.postMessage('ping');
Antwort: pong

Pegelmessung vom Prozessor an die UI melden

// meter-processor.js
class MeterProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.frames = 0;
  }
  process(inputs) {
    const channel = inputs[0][0];
    if (channel) {
      let sum = 0;
      for (const s of channel) sum += s * s;
      // nur ca. alle 20 Blöcke senden, um den Port nicht zu fluten
      if (++this.frames % 20 === 0) {
        this.port.postMessage({ rms: Math.sqrt(sum / channel.length) });
      }
    }
    return true;
  }
}
registerProcessor('meter-processor', MeterProcessor);

// main.js
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('meter-processor.js');
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const source = ctx.createMediaStreamSource(stream);
const meter = new AudioWorkletNode(ctx, 'meter-processor');
source.connect(meter);

meter.port.addEventListener('message', ({ data }) => {
  document.querySelector('#level').value = data.rms;
});
meter.port.start(); // bei addEventListener nötig!

Große Puffer per Transfer ohne Kopie übergeben

const ctx = new AudioContext();
await ctx.audioWorklet.addModule('sampler-processor.js');
const sampler = new AudioWorkletNode(ctx, 'sampler-processor');

const response = await fetch('kick.wav');
const audioBuffer = await ctx.decodeAudioData(await response.arrayBuffer());
const samples = audioBuffer.getChannelData(0).slice(); // eigene Kopie

// ArrayBuffer wird übertragen statt kopiert
sampler.port.postMessage({ type: 'load', samples }, [samples.buffer]);
console.log(samples.length); // danach im Haupt-Thread unbrauchbar

// Wichtig · Fallstricke

  • start() nicht vergessen: Wer addEventListener("message", …) statt onmessage verwendet, muss port.start() aufrufen, sonst kommen keine Nachrichten an.
  • Keine Echtzeit-Garantie: Nachrichten werden asynchron zugestellt und sind nicht sample-genau. Für präzise Automation AudioParams nutzen.
  • Performance: Im process()-Callback nicht bei jedem Block (128 Frames) senden. Das Klonen verursacht Speicherbelegung und Garbage Collection im Audio-Thread und damit Knackser. Nachrichten drosseln oder für Hochfrequenz-Daten einen SharedArrayBuffer verwenden (setzt Cross-Origin-Isolation per COOP/COEP voraus).
  • Große Puffer über die Transfer-Liste von postMessage() übergeben, damit nicht kopiert wird. Der Puffer ist danach beim Absender unbrauchbar (Länge 0).
  • Funktionen, DOM-Knoten und andere nicht klonbare Werte lösen einen DataCloneError aus.
  • AudioWorklet steht nur in sicheren Kontexten (HTTPS oder localhost) zur Verfügung.