Start · Sprachen · JavaScript · Referenz · AudioWorkletProcessor.port

AudioWorkletProcessor.port

Eigenschaft

Schreibgeschützte Eigenschaft, die den <code>MessagePort</code> für die bidirektionale Kommunikation zwischen dem Prozessor und seinem <code>AudioWorkletNode</code> zurückgibt.

seit JavaScript Baseline: alle modernen Browser (seit 2021) Kategorie: webapi

Signatur

AudioWorkletProcessor.port

Beschreibung

Ein AudioWorkletProcessor läuft im AudioWorkletGlobalScope, also auf dem Audio-Rendering-Thread und getrennt vom Haupt-Thread. Die Eigenschaft port liefert einen MessagePort, der mit AudioWorkletNode.port auf der Haupt-Thread-Seite verbunden ist. Über dieses Kanalpaar tauschen beide Seiten mit postMessage() und dem message-Event beliebige strukturierbar klonbare Daten aus.

Typische Anwendungsfälle sind das Senden von Konfigurationen oder Befehlen an den Prozessor (z. B. „Reset“, neue Filterkoeffizienten, Samples zum Abspielen) und das Zurückmelden von Messwerten wie Pegel, erkannten Tonhöhen oder Statusinformationen an die Benutzeroberfläche.

Für Werte, die sich pro Sample oder pro Block kontinuierlich und zeitgenau ändern sollen, ist AudioParam (über static get parameterDescriptors()) meist die bessere Wahl. Der Port eignet sich für seltene, ereignisartige Nachrichten. Größere Binärdaten sollten als Transferable (z. B. ArrayBuffer) übertragen werden, um Kopien zu vermeiden.

Rückgabewert

Typ
MessagePort
Beschreibung
Der mit dem zugehörigen AudioWorkletNode.port verbundene MessagePort. Die Eigenschaft ist schreibgeschützt; der Port ist bereits gestartet, wenn onmessage gesetzt wird.

Beispiele

Befehle vom Haupt-Thread empfangen

// gain-processor.js (AudioWorkletGlobalScope)
class GainProcessor extends AudioWorkletProcessor {
  constructor() {
    super();
    this.gain = 1;
    // Nachrichten vom AudioWorkletNode entgegennehmen
    this.port.onmessage = (event) => {
      const { type, value } = event.data;
      if (type === 'setGain') this.gain = value;
    };
  }

  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] * this.gain;
    }
    return true;
  }
}

registerProcessor('gain-processor', GainProcessor);

// main.js (Haupt-Thread)
const ctx = new AudioContext();
await ctx.audioWorklet.addModule('gain-processor.js');
const node = new AudioWorkletNode(ctx, 'gain-processor');
node.port.postMessage({ type: 'setGain', value: 0.5 });

Pegelmesser: Werte an die Oberfläche zurückmelden

// 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;
      const rms = Math.sqrt(sum / channel.length);
      // nur ca. alle 20 Blöcke senden, um den Port nicht zu fluten
      if (++this.frames % 20 === 0) this.port.postMessage({ rms });
    }
    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.onmessage = ({ data }) => {
  console.log(`Pegel: ${data.rms.toFixed(3)}`);
};
Pegel: 0.042 Pegel: 0.057 ...

Audiodaten per Transferable übertragen

// main.js – Sample-Daten ohne Kopie an den Prozessor schicken
const samples = new Float32Array(48000).map((_, i) => Math.sin(i / 10));
node.port.postMessage({ type: 'load', buffer: samples.buffer }, [samples.buffer]);
console.log(samples.byteLength); // Puffer wurde übertragen

// im Prozessor-Konstruktor
this.port.onmessage = ({ data }) => {
  if (data.type === 'load') this.sample = new Float32Array(data.buffer);
};

// Wichtig · Fallstricke

  • Echtzeit-Thread: process() läuft unter harten Zeitvorgaben (ca. 2,67 ms pro 128-Frame-Block bei 48 kHz). Häufiges postMessage() erzeugt Objekte und Garbage-Collection-Druck – Nachrichten drosseln oder für kontinuierlichen Datenaustausch SharedArrayBuffer mit Ringpuffer verwenden (erfordert Cross-Origin-Isolation via COOP/COEP-Header).
  • Mit onmessage wird der Port automatisch gestartet; bei addEventListener('message', …) muss port.start() explizit aufgerufen werden.
  • Nachrichten kommen asynchron an, also nicht sample-genau. Für zeitgenaue Parameteränderungen AudioParam nutzen.
  • Für einmalige Startwerte kann alternativ processorOptions im AudioWorkletNode-Konstruktor verwendet werden – die Werte stehen dann bereits im Prozessor-Konstruktor zur Verfügung.
  • AudioWorklet steht nur in sicheren Kontexten (HTTPS oder localhost) zur Verfügung. Unterstützt in Chrome 66+, Firefox 76+ und Safari 14.1+.