Start · Sprachen · JavaScript · Referenz · BaseAudioContext.audioWorklet

BaseAudioContext.audioWorklet

Eigenschaft

Schreibgeschützte Eigenschaft, die das <code>AudioWorklet</code> des Audio-Kontexts liefert, über das eigene <code>AudioWorkletProcessor</code>-Klassen per <code>addModule()</code> geladen werden.

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

Signatur

BaseAudioContext.audioWorklet

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

Typ
AudioWorklet
Beschreibung
Das zum Kontext gehörende 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);
});
true

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);
1 44100

// Wichtig · Fallstricke

  • Nur im sicheren Kontext: audioWorklet gibt es nur über HTTPS oder auf localhost. Über http:// ist die Eigenschaft undefined.
  • Reihenfolge beachten: new AudioWorkletNode() wirft einen InvalidStateError, wenn das Promise von addModule() 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 AudioWorkletGlobalScope gibt es kein DOM, kein fetch und kein setTimeout. process() sollte keinen Speicher anlegen (keine neuen Arrays oder Objekte pro Aufruf), sonst kann die Speicherbereinigung Knackser verursachen.
  • Autoplay-Regeln: Ein AudioContext startet oft im Zustand suspended. Erzeuge ihn deshalb in einem Nutzer-Ereignis oder rufe dort ctx.resume() auf.
  • Gibt process() den Wert false zurü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 ScriptProcessorNode ist veraltet.