From 18661961a1849b1d0f64fc4677ca1ab6333742f8 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Sat, 21 Mar 2026 17:52:38 +0100 Subject: [PATCH] fix: reduce master clock to 240 Hz + eliminate note-off timeouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Master clock 960→240 Hz: reduces CPU/GC pressure by 4x while still providing 12x headroom for 300 BPM sixteenths - Remove Tone.getContext().setTimeout() for note-off scheduling — these accumulated over time causing periodic hiccups - Note-off now happens at step boundary: previous gate turned off at the start of each new step (cleaner, zero accumulation) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/SequencerWidget.jsx | 15 ++++++++------- src/engine/audioEngine.js | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/SequencerWidget.jsx b/src/components/SequencerWidget.jsx index 39a2c77..e725737 100644 --- a/src/components/SequencerWidget.jsx +++ b/src/components/SequencerWidget.jsx @@ -71,18 +71,23 @@ export default function SequencerWidget({ moduleId }) { } let lastStepIdx = -1; - let noteOffTimeout = null; + let lastGateOn = false; subscribeTick(`seq-${moduleId}`, (time, elapsed) => { const currentBpm = bpmRef.current; const currentNumSteps = numStepsRef.current; const sixteenthRate = (currentBpm * 4) / 60; - const stepDuration = 1 / sixteenthRate; const stepIdx = Math.floor(elapsed * sixteenthRate) % currentNumSteps; if (stepIdx === lastStepIdx) return; lastStepIdx = stepIdx; + // Turn off previous note at step boundary (no setTimeout needed) + if (lastGateOn) { + setSequencerSignals(moduleId, 0, false); + lastGateOn = false; + } + const s = stepsRef.current[stepIdx]; if (!s) return; @@ -90,11 +95,7 @@ export default function SequencerWidget({ moduleId }) { if (s.gate) { setSequencerSignals(moduleId, midiToFreq(s.midi), true); - Tone.getContext().setTimeout(() => { - setSequencerSignals(moduleId, midiToFreq(s.midi), false); - }, stepDuration * 0.8); - } else { - setSequencerSignals(moduleId, midiToFreq(s.midi), false); + lastGateOn = true; } }); diff --git a/src/engine/audioEngine.js b/src/engine/audioEngine.js index fcd950f..d962962 100644 --- a/src/engine/audioEngine.js +++ b/src/engine/audioEngine.js @@ -15,7 +15,7 @@ const keyboardState = { frequency: 440, gate: false }; // ==================== Global Master Clock ==================== // Single high-resolution clock (960 ticks/sec ≈ 1ms precision). // All sequencers/piano rolls derive their timing from this. -const MASTER_TICK_RATE = 960; // Hz +const MASTER_TICK_RATE = 240; // Hz — enough for 300 BPM sixteenths (20 Hz) with 12x headroom let _masterClock = null; let _masterTime = 0; // audio-context seconds at clock start const _tickListeners = new Map(); // id → callback(audioTime, elapsed)