Skip to content

Commit a71d08b

Browse files
Defer cache parity renders until interaction settles
1 parent 5777d0b commit a71d08b

3 files changed

Lines changed: 51 additions & 16 deletions

File tree

web/app.js

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,8 @@ function updateInputGain() {
589589

590590
// Debounce timer for cache render
591591
let cacheRenderTimeout = null;
592-
const CACHE_RENDER_DEBOUNCE_MS = 300;
592+
let cacheRenderQueued = false;
593+
const CACHE_RENDER_DEBOUNCE_MS = 500;
593594

594595
// getCurrentSettings imported from ./ui/controls.js
595596

@@ -614,13 +615,15 @@ function scheduleRenderToCache() {
614615

615616
// Debounce the actual render
616617
cacheRenderTimeout = setTimeout(async () => {
618+
cacheRenderTimeout = null;
617619
if (!fileState.originalBuffer) return;
618620
if (fileState.isRenderingCache) {
619-
// Already rendering, schedule another after current completes
620-
scheduleRenderToCache();
621+
// Already rendering; remember to run one more pass when it completes.
622+
cacheRenderQueued = true;
621623
return;
622624
}
623625

626+
cacheRenderQueued = false;
624627
fileState.isRenderingCache = true;
625628
console.log('[Cache] Starting render, version:', thisVersion);
626629

@@ -644,13 +647,11 @@ function scheduleRenderToCache() {
644647
// Build a master-preview buffer that matches export (includes final limiter).
645648
'export',
646649
(progress, status) => {
647-
// Show progress in modal and LUFS display
650+
// Show progress in LUFS display
648651
const percent = Math.round(progress * 100);
649652
if (outputLufsDisplay && progress < 1) {
650653
outputLufsDisplay.textContent = `${percent}%`;
651654
}
652-
// Update modal progress (85-100% range for cache phase)
653-
showLoadingModal('Building cache...', 85 + percent * 0.15);
654655
}
655656
);
656657
buffer = result.audioBuffer;
@@ -688,7 +689,6 @@ function scheduleRenderToCache() {
688689
// Enable playback now that cache is ready
689690
playBtn.disabled = false;
690691
stopBtn.disabled = false;
691-
hideLoadingModal();
692692

693693
console.log('[Cache] Render complete, version:', thisVersion, 'LUFS:', lufs.toFixed(1));
694694

@@ -711,9 +711,12 @@ function scheduleRenderToCache() {
711711
// Still enable playback on error so user isn't stuck
712712
playBtn.disabled = false;
713713
stopBtn.disabled = false;
714-
hideLoadingModal();
715714
} finally {
716715
fileState.isRenderingCache = false;
716+
if (cacheRenderQueued && fileState.originalBuffer) {
717+
cacheRenderQueued = false;
718+
scheduleRenderToCache();
719+
}
717720
}
718721
}, CACHE_RENDER_DEBOUNCE_MS);
719722
}
@@ -905,8 +908,8 @@ async function loadAudioFile(file) {
905908
// Show live indicators
906909
document.body.classList.add('audio-loaded');
907910

908-
// Show "Building cache" modal - stays visible until cache render completes
909-
showLoadingModal('Building cache...', 85);
911+
// Cache render now runs in the background; loading flow is complete here.
912+
hideLoadingModal();
910913

911914
return true;
912915
} catch (error) {
@@ -1524,7 +1527,7 @@ normalizeLoudness.addEventListener('change', () => {
15241527
updateChecklist();
15251528
});
15261529

1527-
[truePeakLimit, cleanLowEnd, glueCompression, centerBass, cutMud, addAir, tapeWarmth, autoLevel, addPunch].forEach(el => {
1530+
[truePeakLimit, cleanLowEnd, glueCompression, centerBass, cutMud, autoLevel].forEach(el => {
15281531
el.addEventListener('change', () => {
15291532
updateAudioChain();
15301533
updateChecklist();
@@ -1536,6 +1539,7 @@ normalizeLoudness.addEventListener('change', () => {
15361539
[deharsh, addAir, tapeWarmth, addPunch].forEach(el => {
15371540
el.addEventListener('change', () => {
15381541
processEffects();
1542+
updateChecklist();
15391543
});
15401544
});
15411545

@@ -1554,7 +1558,11 @@ levelMatchBtn.addEventListener('change', () => {
15541558
stereoWidthSlider.addEventListener('input', () => {
15551559
stereoWidthValue.textContent = `${stereoWidthSlider.value}%`;
15561560
updateStereoWidth();
1557-
// Stereo width affects the master preview render, so rebuild cache.
1561+
// Live node update while dragging; defer cache rebuild to interaction commit.
1562+
updateAudioChain({ scheduleCache: false });
1563+
});
1564+
1565+
stereoWidthSlider.addEventListener('change', () => {
15581566
updateAudioChain();
15591567
});
15601568

@@ -1642,7 +1650,7 @@ targetLufsSlider.addEventListener('input', () => {
16421650

16431651
lufsDebounceTimeout = setTimeout(() => {
16441652
renormalizeAudio(newValue);
1645-
}, 300); // 300ms debounce
1653+
}, 500); // 500ms debounce
16461654
});
16471655

16481656
[sampleRate, bitDepth].forEach(el => {
@@ -1764,13 +1772,25 @@ window.addEventListener('beforeunload', () => {
17641772
initFaders({
17651773
onInputGainChange: (val) => {
17661774
updateInputGain();
1767-
// Input gain affects the master preview render (pre-limiter), so rebuild cache.
1775+
// Keep live chain responsive while dragging; defer cache rebuild to commit.
1776+
updateAudioChain({ scheduleCache: false });
1777+
},
1778+
onInputGainChangeEnd: () => {
1779+
updateAudioChain();
1780+
},
1781+
onCeilingChange: () => {
1782+
// Live preview updates immediately; defer cache rebuild to commit.
1783+
updateAudioChain({ scheduleCache: false });
1784+
},
1785+
onCeilingChangeEnd: () => {
17681786
updateAudioChain();
17691787
},
1770-
onCeilingChange: (val) => updateAudioChain(),
17711788
onEQChange: (eqVals) => {
17721789
updateEQ();
1773-
// EQ affects the master preview render, so rebuild cache.
1790+
// Keep live chain responsive while dragging; defer cache rebuild to commit.
1791+
updateAudioChain({ scheduleCache: false });
1792+
},
1793+
onEQChangeEnd: () => {
17741794
updateAudioChain();
17751795
}
17761796
});

web/components/Fader.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ export class Fader {
262262
}
263263
const newValue = this.options.min + norm * (this.options.max - this.options.min);
264264
this.setValue(this.quantize(newValue), true);
265+
this.options.onChangeEnd(this.value);
265266
});
266267

267268
// Wheel
@@ -275,12 +276,14 @@ export class Fader {
275276
e.shiftKey
276277
);
277278
this.setValue(this.quantize(newValue), true);
279+
this.options.onChangeEnd(this.value);
278280
}, { passive: false });
279281

280282
// Double-click reset
281283
this.thumb.addEventListener('dblclick', () => {
282284
const resetValue = this.options.unit === 'dB' ? 0 : (this.options.min + this.options.max) / 2;
283285
this.setValue(resetValue, true);
286+
this.options.onChangeEnd(this.value);
284287
});
285288
}
286289

web/ui/controls.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,11 @@ export function getExportSettings() {
112112
* Initialize all faders
113113
* @param {Object} callbacks - Callback functions for fader changes
114114
* @param {Function} callbacks.onInputGainChange - Called when input gain changes
115+
* @param {Function} callbacks.onInputGainChangeEnd - Called when input gain interaction commits
115116
* @param {Function} callbacks.onCeilingChange - Called when ceiling changes
117+
* @param {Function} callbacks.onCeilingChangeEnd - Called when ceiling interaction commits
116118
* @param {Function} callbacks.onEQChange - Called when any EQ fader changes
119+
* @param {Function} callbacks.onEQChangeEnd - Called when any EQ interaction commits
117120
*/
118121
export function initFaders(callbacks = {}) {
119122
// Destroy old faders before creating new ones (prevents memory leaks on re-init)
@@ -139,6 +142,9 @@ export function initFaders(callbacks = {}) {
139142
onChange: (val) => {
140143
inputGainValue = val;
141144
if (callbacks.onInputGainChange) callbacks.onInputGainChange(val);
145+
},
146+
onChangeEnd: (val) => {
147+
if (callbacks.onInputGainChangeEnd) callbacks.onInputGainChangeEnd(val);
142148
}
143149
});
144150

@@ -157,6 +163,9 @@ export function initFaders(callbacks = {}) {
157163
onChange: (val) => {
158164
ceilingValueDb = val;
159165
if (callbacks.onCeilingChange) callbacks.onCeilingChange(val);
166+
},
167+
onChangeEnd: (val) => {
168+
if (callbacks.onCeilingChangeEnd) callbacks.onCeilingChangeEnd(val);
160169
}
161170
});
162171

@@ -185,6 +194,9 @@ export function initFaders(callbacks = {}) {
185194
eqValues[stateKey] = val;
186195
clearActivePreset();
187196
if (callbacks.onEQChange) callbacks.onEQChange(eqValues);
197+
},
198+
onChangeEnd: () => {
199+
if (callbacks.onEQChangeEnd) callbacks.onEQChangeEnd(eqValues);
188200
}
189201
});
190202
});

0 commit comments

Comments
 (0)