fix: reduce main thread pressure to prevent audio buffer underruns
The periodic audio glitches were caused by main thread starvation: ~840 events/sec during playback starved the audio buffer. Changes: - Master clock 480→120 Hz (still 6x headroom for 300 BPM sixteenths) - Connection cache: replace O(n) reduce hash with dirty flag (zero work on cache hit, flag set only when connections actually change) - Tone.js lookAhead: 100ms→50ms for tighter scheduling - ModuleNode LFO visualization RAF: 60fps→15fps (every 4th frame) - ScopeDisplay RAF: 60fps→30fps (every 2nd frame) Net effect: ~840 events/sec → ~200 events/sec during playback. Audio processing gets 4x more main thread headroom. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ const keyboardState = { frequency: 440, gate: false };
|
||||
// Single clock with integer tick counter. All sequencers/piano rolls
|
||||
// derive their step positions from this shared tick count.
|
||||
// Using integers avoids floating-point drift entirely.
|
||||
export const MASTER_TICK_RATE = 480; // Hz — must be high enough for fastest BPM
|
||||
export const MASTER_TICK_RATE = 120; // Hz — 6x headroom for 300 BPM sixteenths (20 Hz). Lower = less main thread pressure.
|
||||
let _masterClock = null;
|
||||
const _tickListeners = new Map(); // id → callback(audioTime, ticks)
|
||||
|
||||
@@ -406,21 +406,23 @@ export function updateParam(moduleId, paramName, value) {
|
||||
}
|
||||
|
||||
// Cache connection lookups for hot-path audio scheduling
|
||||
// Rebuilt lazily when connections change
|
||||
let _connCacheVersion = -1;
|
||||
// Rebuilt only when connections actually change (dirty flag, no computation on hit)
|
||||
let _connCacheDirty = true;
|
||||
const _connByModulePort = new Map(); // "moduleId-portName" → [connections]
|
||||
|
||||
export function invalidateConnectionCache() {
|
||||
_connCacheDirty = true;
|
||||
}
|
||||
|
||||
function getConnectionsFrom(moduleId, portName) {
|
||||
// Rebuild cache if connections changed
|
||||
const version = state.connections.length + state.connections.reduce((s, c) => s + c.id, 0);
|
||||
if (version !== _connCacheVersion) {
|
||||
if (_connCacheDirty) {
|
||||
_connByModulePort.clear();
|
||||
for (const conn of state.connections) {
|
||||
const key = `${conn.from.moduleId}-${conn.from.port}`;
|
||||
if (!_connByModulePort.has(key)) _connByModulePort.set(key, []);
|
||||
_connByModulePort.get(key).push(conn);
|
||||
}
|
||||
_connCacheVersion = version;
|
||||
_connCacheDirty = false;
|
||||
}
|
||||
return _connByModulePort.get(`${moduleId}-${portName}`) || [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user