fix: derive master clock ticks from AudioContext.currentTime
The _masterTicks++ counter fell behind when Tone.Clock callbacks were delayed by GC pauses, UI interactions, or tab throttling. The counter never recovered, causing cumulative drift between sequencers. Now ticks are derived from the callback's time parameter (which comes from AudioContext.currentTime — hardware clock, always precise): ticks = Math.round((time - startTime) * MASTER_TICK_RATE) If a callback is delayed by 50ms, the time is still correct and ticks jump ahead to the right value. No accumulation, no drift, self-healing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,13 +18,8 @@ const keyboardState = { frequency: 440, gate: false };
|
||||
// Using integers avoids floating-point drift entirely.
|
||||
export const MASTER_TICK_RATE = 480; // Hz — must be high enough for fastest BPM
|
||||
let _masterClock = null;
|
||||
let _masterTicks = 0;
|
||||
const _tickListeners = new Map(); // id → callback(audioTime, ticks)
|
||||
|
||||
export function getMasterTicks() {
|
||||
return _masterTicks;
|
||||
}
|
||||
|
||||
export function subscribeTick(id, callback) {
|
||||
_tickListeners.set(id, callback);
|
||||
}
|
||||
@@ -35,11 +30,16 @@ export function unsubscribeTick(id) {
|
||||
|
||||
function startMasterClock() {
|
||||
if (_masterClock) return;
|
||||
_masterTicks = 0;
|
||||
let _startTime = 0;
|
||||
let _started = false;
|
||||
_masterClock = new Tone.Clock((time) => {
|
||||
_masterTicks++;
|
||||
if (!_started) { _startTime = time; _started = true; }
|
||||
// Derive ticks from precise AudioContext.currentTime, not a counter.
|
||||
// Counters fall behind when callbacks are delayed (GC, UI, tab throttle).
|
||||
// The time parameter is always accurate regardless of callback jitter.
|
||||
const ticks = Math.round((time - _startTime) * MASTER_TICK_RATE);
|
||||
for (const cb of _tickListeners.values()) {
|
||||
cb(time, _masterTicks);
|
||||
cb(time, ticks);
|
||||
}
|
||||
}, MASTER_TICK_RATE);
|
||||
_masterClock.start();
|
||||
|
||||
Reference in New Issue
Block a user