Start · Sprachen · JavaScript · Referenz · AudioContext.getOutputTimestamp()

AudioContext.getOutputTimestamp()

Methode

Gibt ein <code>AudioTimestamp</code>-Objekt mit <code>contextTime</code> und <code>performanceTime</code> zurück, um Audio-Ausgabezeit und <code>performance.now()</code> abzugleichen.

seit JavaScript Baseline 2024: Chrome 57+, Firefox 70+, Safari 14. Kategorie: webapi

Signatur

AudioContext.prototype.getOutputTimestamp()

Beschreibung

Die Methode getOutputTimestamp() liefert eine Momentaufnahme davon, welcher Zeitpunkt der Audio-Zeitachse gerade tatsächlich aus dem Lautsprecher kommt. Das zurückgegebene AudioTimestamp-Objekt hat zwei Eigenschaften:

  • contextTime: Die Position in der Zeitachse des AudioContext (in Sekunden, gleiche Einheit wie currentTime), die das Ausgabegerät gerade abspielt.
  • performanceTime: Der Zeitpunkt (in Millisekunden, gleiche Einheit wie performance.now()), an dem genau dieses Sample ausgegeben wurde bzw. wird.

Der Unterschied zu AudioContext.currentTime ist entscheidend: currentTime gibt an, wie weit die Audio-Engine verarbeitet hat. Wegen Puffern und Gerätelatenz hört man diesen Zeitpunkt aber erst später. getOutputTimestamp() berücksichtigt diese Verzögerung und eignet sich deshalb, um Grafik und Ton exakt zu synchronisieren, z. B. bei Visualisierungen, Rhythmusspielen, Karaoke-Anzeigen oder Metronomen.

Typisch rechnet man mit beiden Werten einen beliebigen performance.now()-Zeitpunkt (etwa den Zeitstempel eines requestAnimationFrame-Callbacks) in Audio-Kontextzeit um. Da sich die beiden Uhren leicht auseinander bewegen können, sollte man den Zeitstempel regelmäßig (z. B. pro Frame) neu abfragen, statt ihn einmalig zu speichern.

Rückgabewert

Typ
AudioTimestamp
Beschreibung
Ein neues Objekt { contextTime: number, performanceTime: number }. contextTime in Sekunden (Kontext-Zeitachse), performanceTime in Millisekunden (Zeitbasis von performance.now()). Ist der Kontext noch nicht gestartet (Zustand suspended), sind beide Werte in der Regel 0.

Beispiele

Zeitstempel auslesen und mit currentTime vergleichen

const ctx = new AudioContext();

document.querySelector('#start').addEventListener('click', async () => {
  await ctx.resume(); // Autoplay-Richtlinie: erst nach Nutzerinteraktion

  const osc = new OscillatorNode(ctx, { frequency: 440 });
  osc.connect(ctx.destination);
  osc.start();
  osc.stop(ctx.currentTime + 1);

  setTimeout(() => {
    const { contextTime, performanceTime } = ctx.getOutputTimestamp();
    console.log('Verarbeitet bis:', ctx.currentTime.toFixed(3), 's');
    console.log('Hörbar gerade:', contextTime.toFixed(3), 's');
    console.log('Ausgabe bei performance.now() =', performanceTime.toFixed(1), 'ms');
  }, 500);
});
Verarbeitet bis: 0.512 s Hörbar gerade: 0.491 s Ausgabe bei performance.now() = 3120.4 ms

Frame-genaue Synchronisation einer Visualisierung

const ctx = new AudioContext();
const marker = document.querySelector('#marker');

// Rechnet einen performance.now()-Zeitpunkt in Kontextzeit um
const toContextTime = (perfNow) => {
  const ts = ctx.getOutputTimestamp();
  return ts.contextTime + (perfNow - ts.performanceTime) / 1000;
};

const BPM = 120;
const beatLength = 60 / BPM;

const draw = (frameTime) => {
  const audioTime = toContextTime(frameTime);
  const phase = (audioTime % beatLength) / beatLength;
  // Marker blinkt exakt zum hörbaren Schlag
  marker.style.opacity = phase < 0.1 ? '1' : '0.2';
  requestAnimationFrame(draw);
};

document.addEventListener('click', async () => {
  await ctx.resume();
  requestAnimationFrame(draw);
}, { once: true });

Ausgabelatenz grob schätzen

const ctx = new AudioContext();

const estimateLatency = () => {
  const { contextTime } = ctx.getOutputTimestamp();
  const diffMs = (ctx.currentTime - contextTime) * 1000;
  return {
    viaTimestamp: diffMs.toFixed(1),
    outputLatency: ((ctx.outputLatency ?? 0) * 1000).toFixed(1),
    baseLatency: (ctx.baseLatency * 1000).toFixed(1)
  };
};

document.addEventListener('click', async () => {
  await ctx.resume();
  setTimeout(() => console.table(estimateLatency()), 1000);
}, { once: true });
┌──────────────┬────────┐ │ viaTimestamp │ '21.3' │ │ outputLatency│ '10.7' │ │ baseLatency │ '5.8' │ └──────────────┴────────┘

// Wichtig · Fallstricke

  • Einheiten beachten: contextTime ist in Sekunden, performanceTime in Millisekunden. Beim Umrechnen durch 1000 teilen bzw. mit 1000 multiplizieren.
  • Suspendierter Kontext: Solange der AudioContext wegen der Autoplay-Richtlinie im Zustand suspended ist, liefert die Methode 0-Werte. Erst nach ctx.resume() (ausgelöst durch eine Nutzerinteraktion) sind die Werte sinnvoll.
  • Nicht zum Planen verwenden: Um Klänge zu starten, weiterhin ctx.currentTime als Basis für start()/setValueAtTime() nehmen. getOutputTimestamp() dient der Anzeige und Synchronisation mit der Bildausgabe.
  • Uhrendrift: Audio-Hardwareuhr und Systemuhr laufen nicht exakt gleich schnell. Deshalb pro Frame neu abfragen, statt einen einmal gemessenen Versatz dauerhaft zu verwenden.
  • Genauigkeit: Wegen Schutzmaßnahmen gegen Fingerprinting ist die Auflösung von performance.now() in Browsern reduziert; die Werte können daher leicht gerundet sein.
  • Kompatibilität: Nur auf AudioContext verfügbar, nicht auf OfflineAudioContext. Unterstützt in Chrome/Edge 57+, Firefox 70+ und Safari 14.1+. Für ältere Browser per typeof ctx.getOutputTimestamp === 'function' prüfen und auf currentTime zurückfallen.