Signatur
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 desAudioContext(in Sekunden, gleiche Einheit wiecurrentTime), die das Ausgabegerät gerade abspielt.performanceTime: Der Zeitpunkt (in Millisekunden, gleiche Einheit wieperformance.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
{ 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);
});
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 });
// Wichtig · Fallstricke
- Einheiten beachten:
contextTimeist in Sekunden,performanceTimein Millisekunden. Beim Umrechnen durch 1000 teilen bzw. mit 1000 multiplizieren. - Suspendierter Kontext: Solange der
AudioContextwegen der Autoplay-Richtlinie im Zustandsuspendedist, liefert die Methode0-Werte. Erst nachctx.resume()(ausgelöst durch eine Nutzerinteraktion) sind die Werte sinnvoll. - Nicht zum Planen verwenden: Um Klänge zu starten, weiterhin
ctx.currentTimeals Basis fürstart()/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
AudioContextverfügbar, nicht aufOfflineAudioContext. Unterstützt in Chrome/Edge 57+, Firefox 70+ und Safari 14.1+. Für ältere Browser pertypeof ctx.getOutputTimestamp === 'function'prüfen und aufcurrentTimezurückfallen.