D2: LimiterFx brickwall — lookahead 1-5ms + oversample 4x + hard clip, UI toggle SOFT/BRICKWALL + LOOKAHEAD ms

This commit is contained in:
2026-08-23 07:23:52 +07:00
parent fe82b19557
commit 17e22bcf32
6 changed files with 202 additions and 32 deletions
+1 -1
View File
@@ -320,7 +320,7 @@ _BUILTIN_PARAM_MAP = {
("g3", "eqMid2Gain"), ("g4", "eqHighGain")],
"compressor": [("threshold", "compThreshold"), ("ratio", "compRatio"),
("makeup", "compMakeup")],
"limiter": [("ceiling", "limThreshold")],
"limiter": [("ceiling", "limThreshold"), ("mode", "limMode"), ("lookahead_ms", "limLookahead")],
"exciter": [("drive", "excDrive")],
"rebalance": [("mid", "rebalMid"), ("side", "rebalSide")],
"imager": [("w1", "w1"), ("w2", "w2"), ("w3", "w3"), ("w4", "w4")],
+14 -8
View File
@@ -819,7 +819,7 @@ function applyMasteringSettings(s) {
// Re-applying identical values in rapid bursts is "fast parameter automation"
// and makes Chromium flag the biquad EQ filters as unstable ("state is bad").
// Only touch the graph when a value actually changed.
const sig = [s.masterConnected, s.isBypassed, s.eqActive, s.eqLowGain, s.eqMid1Gain, s.eqMid2Gain, s.eqHighGain, s.imagerActive, s.w1, s.w2, s.w3, s.w4, s.maximizerActive, s.maxGain, s.maxSoftClip, s.maxUpward, s.ceiling, s.compActive, s.compThreshold, s.compRatio, s.compMakeup, s.limActive, s.limThreshold, s.excActive, s.excDrive, s.rebalActive, s.rebalMid, s.rebalSide].join('|');
const sig = [s.masterConnected, s.isBypassed, s.eqActive, s.eqLowGain, s.eqMid1Gain, s.eqMid2Gain, s.eqHighGain, s.imagerActive, s.w1, s.w2, s.w3, s.w4, s.maximizerActive, s.maxGain, s.maxSoftClip, s.maxUpward, s.ceiling, s.compActive, s.compThreshold, s.compRatio, s.compMakeup, s.limActive, s.limThreshold, s.limMode, s.limLookahead, s.excActive, s.excDrive, s.rebalActive, s.rebalMid, s.rebalSide].join('|');
if (sig === _lastMasteringSig) return;
_lastMasteringSig = sig;
@@ -1607,7 +1607,7 @@ function fxRtChain() {
const FX_RT_PARAM_MAP = {
eq: [['g1', 'eqLowGain'], ['g2', 'eqMid1Gain'], ['g3', 'eqMid2Gain'], ['g4', 'eqHighGain']],
compressor: [['threshold', 'compThreshold'], ['ratio', 'compRatio'], ['makeup', 'compMakeup']],
limiter: [['ceiling', 'limThreshold']],
limiter: [['ceiling', 'limThreshold'], ['mode', 'limMode'], ['lookahead_ms', 'limLookahead']],
exciter: [['drive', 'excDrive']],
rebalance: [['mid', 'rebalMid'], ['side', 'rebalSide']],
imager: [['w1', 'w1'], ['w2', 'w2'], ['w3', 'w3'], ['w4', 'w4']],
@@ -7057,7 +7057,7 @@ const ProfileModal = ({
isBypassed: false,
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
limActive: false, limThreshold: -1.0, limMode: 0, limLookahead: 2,
excActive: false, excDrive: 40,
rebalActive: false, rebalMid: 0, rebalSide: 0,
});
@@ -11473,6 +11473,8 @@ const deserializeProjectFromSchema = (schemaObj) => {
if (migrated.compMakeup === undefined) migrated.compMakeup = 0;
if (migrated.limActive === undefined) migrated.limActive = false;
if (migrated.limThreshold === undefined) migrated.limThreshold = -1.0;
if (migrated.limMode === undefined) migrated.limMode = 0;
if (migrated.limLookahead === undefined) migrated.limLookahead = 2;
if (migrated.excActive === undefined) migrated.excActive = false;
if (migrated.excDrive === undefined) migrated.excDrive = 40;
if (migrated.rebalActive === undefined) migrated.rebalActive = false;
@@ -11607,7 +11609,7 @@ const TRACK_FX_DEFAULTS = {
eq: { g1: 0, g2: 0, g3: 0, g4: 0 },
eqpro: { amount: 100, bands: EQPRO_DEFAULT_BANDS },
compressor: { threshold: -16, ratio: 3, makeup: 0 },
limiter: { ceiling: -1.0 },
limiter: { ceiling: -1.0, mode: 'soft', lookahead_ms: 2 },
exciter: { drive: 40 },
rebalance: { mid: 0, side: 0 }
};
@@ -13950,8 +13952,12 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
<MasteringKnob param="limThreshold" min={-24} max={0} value={ozState.limThreshold} unit="dB" label="CEILING" color="#f43f5e" onChange={(p, v) => setOzState(prev => ({ ...prev, [p]: v }))} />
</div>
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
<span className="text-[10px] text-slate-500 oz-font-mono text-center leading-snug px-2">True-Peak limiting<br/>Ratio 20:1 · Knee 0dB<br/>Attack 1ms · Release 50ms</span>
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center gap-2 h-full">
<button onClick={() => setOzState(prev => ({ ...prev, limMode: prev.limMode ? 0 : 1 }))}
className={`px-3 py-1 rounded text-[10px] font-bold border transition-colors ${ozState.limMode ? 'bg-rose-700 border-rose-500 text-white' : 'bg-slate-800 border-slate-700 text-slate-400'}`}>
{ozState.limMode ? 'BRICKWALL' : 'SOFT'}
</button>
<MasteringKnob param="limLookahead" min={1} max={5} value={ozState.limLookahead} unit="ms" label="LOOKAHEAD" color="#f43f5e" onChange={(p, v) => setOzState(prev => ({ ...prev, [p]: v }))} />
</div>
</div>
</div>
@@ -18577,7 +18583,7 @@ const App = () => {
isBypassed: false,
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
limActive: false, limThreshold: -1.0, limMode: 0, limLookahead: 2,
excActive: false, excDrive: 40,
rebalActive: false, rebalMid: 0, rebalSide: 0,
vstFxChain: [],
@@ -19012,7 +19018,7 @@ const App = () => {
isBypassed: false,
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
limActive: false, limThreshold: -1.0, limMode: 0, limLookahead: 2,
excActive: false, excDrive: 40,
rebalActive: false, rebalMid: 0, rebalSide: 0,
});
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -51,7 +51,7 @@
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/services/chordTheory.js?v=202608211300"></script>
<script src="/static/js/app.precompiled.js?v=202608221703" defer></script>
<script src="/static/js/app.precompiled.js?v=202608212130" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {