Gap 10: multiband compressor + de-esser (LR4 crossover, UI, test_62)
This commit is contained in:
+90
-2
@@ -13506,12 +13506,16 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
|
||||
maximizer: { name: 'Maximizer', sub: 'IRC IV True Peak', icon: 'gauge', color: '#34d399' },
|
||||
compressor: { name: 'Bus Compressor', sub: 'Glue & Punch', icon: 'compress', color: '#fbbf24' },
|
||||
limiter: { name: 'Brickwall Limiter', sub: 'True-Peak 20:1', icon: 'shield-half', color: '#f43f5e' },
|
||||
multiband: { name: 'Multiband Compressor', sub: '3-Band LR4', icon: 'layers', color: '#a3e635' },
|
||||
deesser: { name: 'De-Esser', sub: 'Sibilance tamer', icon: 'mic-2', color: '#fb923c' },
|
||||
exciter: { name: 'Harmonic Exciter', sub: 'Saturation & Air', icon: 'wand-2', color: '#c084fc' },
|
||||
rebalance: { name: 'Master Rebalance', sub: 'M/S Balance', icon: 'sliders-horizontal', color: '#38bdf8' },
|
||||
vst3: { name: 'VST FX', sub: 'VST3 plugin', icon: 'plug', color: '#2dd4bf' }
|
||||
};
|
||||
const chainFlag = (type) => type === 'eq' ? 'eqActive' : type === 'eqpro' ? 'eqproActive' : type === 'imager' ? 'imagerActive' : type === 'maximizer' ? 'maximizerActive' : type === 'compressor' ? 'compActive' : type === 'limiter' ? 'limActive' : type === 'exciter' ? 'excActive' : type === 'vst3' ? 'vstFxActive' : 'rebalActive';
|
||||
const chainFlag = (type) => type === 'eq' ? 'eqActive' : type === 'eqpro' ? 'eqproActive' : type === 'imager' ? 'imagerActive' : type === 'maximizer' ? 'maximizerActive' : type === 'compressor' ? 'compActive' : type === 'limiter' ? 'limActive' : type === 'exciter' ? 'excActive' : type === 'vst3' ? 'vstFxActive' : type === 'multiband' ? 'mbActive' : type === 'deesser' ? 'deessActive' : 'rebalActive';
|
||||
const chainActive = (type) => !!ozState[chainFlag(type)];
|
||||
const mb = (ozState.chain || []).find(m => m.type === 'multiband');
|
||||
const de = (ozState.chain || []).find(m => m.type === 'deesser');
|
||||
|
||||
const toggleChainModule = (modId) => {
|
||||
setOzState(prev => {
|
||||
@@ -13563,6 +13567,8 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
|
||||
const id = 'mod_' + type + '_' + Date.now();
|
||||
const entry = { id, type, name: meta.name, active: true };
|
||||
if (type === 'eqpro') entry.params = { amount: 100, bands: JSON.parse(JSON.stringify(EQPRO_DEFAULT_BANDS)) };
|
||||
if (type === 'multiband') entry.params = { lf_cross: 200, hf_cross: 4000, low_thr: -24, low_ratio: 3, low_makeup: 0, mid_thr: -24, mid_ratio: 3, mid_makeup: 0, high_thr: -24, high_ratio: 3, high_makeup: 0 };
|
||||
if (type === 'deesser') entry.params = { threshold: -30, ratio: 40, freq: 6000, makeup: 0 };
|
||||
setOzState(prev => ({
|
||||
...prev,
|
||||
chain: [...(prev.chain || []), entry],
|
||||
@@ -13996,6 +14002,86 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* VIEW: MULTIBAND COMPRESSOR */}
|
||||
<div className={`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule === 'multiband' ? '' : 'hidden'}`}>
|
||||
<div className="oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center">
|
||||
<div className="col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4">
|
||||
<span className="text-xs font-bold text-lime-400 uppercase oz-font-mono mb-3">Multiband Compressor</span>
|
||||
<div className="text-[9px] text-slate-500 oz-font-mono text-center mb-2">LR4 crossover · low/mid/high</div>
|
||||
<button onClick={() => toggleChainModule(mb?.id)}
|
||||
className={`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.mbActive ? 'bg-lime-700 border-lime-500 text-white' : 'bg-slate-800 border-slate-700 text-slate-300'}`}>
|
||||
{ozState.mbActive ? 'ON' : 'OFF'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="col-span-8 grid grid-cols-3 gap-4">
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="lf_cross" min={40} max={1000} value={mb?.params?.lf_cross ?? 200} unit="Hz" label="LF XOVER" color="#a3e635" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { lf_cross: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="hf_cross" min={1000} max={12000} value={mb?.params?.hf_cross ?? 4000} unit="Hz" label="HF XOVER" color="#a3e635" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { hf_cross: 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">Low < LF<br/>Mid giữa 2 crossover<br/>High > HF · Release 250ms</span>
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="low_thr" min={-60} max={0} value={mb?.params?.low_thr ?? -24} unit="dB" label="LOW THRESHOLD" color="#84cc16" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { low_thr: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="low_ratio" min={1} max={20} value={mb?.params?.low_ratio ?? 3} unit=":1" label="LOW RATIO" color="#84cc16" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { low_ratio: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="low_makeup" min={0} max={12} value={mb?.params?.low_makeup ?? 0} unit="dB" label="LOW MAKE-UP" color="#84cc16" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { low_makeup: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="mid_thr" min={-60} max={0} value={mb?.params?.mid_thr ?? -24} unit="dB" label="MID THRESHOLD" color="#65a30d" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { mid_thr: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="mid_ratio" min={1} max={20} value={mb?.params?.mid_ratio ?? 3} unit=":1" label="MID RATIO" color="#65a30d" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { mid_ratio: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="mid_makeup" min={0} max={12} value={mb?.params?.mid_makeup ?? 0} unit="dB" label="MID MAKE-UP" color="#65a30d" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { mid_makeup: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="high_thr" min={-60} max={0} value={mb?.params?.high_thr ?? -24} unit="dB" label="HIGH THRESHOLD" color="#4d7c0f" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { high_thr: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="high_ratio" min={1} max={20} value={mb?.params?.high_ratio ?? 3} unit=":1" label="HIGH RATIO" color="#4d7c0f" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { high_ratio: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="high_makeup" min={0} max={12} value={mb?.params?.high_makeup ?? 0} unit="dB" label="HIGH MAKE-UP" color="#4d7c0f" onChange={(p, v) => mb && updateChainEntryParams(mb.id, { high_makeup: v })} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* VIEW: DE-ESSER */}
|
||||
<div className={`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule === 'deesser' ? '' : 'hidden'}`}>
|
||||
<div className="oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center">
|
||||
<div className="col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4">
|
||||
<span className="text-xs font-bold text-orange-400 uppercase oz-font-mono mb-3">De-Esser</span>
|
||||
<div className="text-[9px] text-slate-500 oz-font-mono text-center mb-2">Detect sibilance @freq · envelope release 150ms</div>
|
||||
<button onClick={() => toggleChainModule(de?.id)}
|
||||
className={`px-3 py-1 rounded text-[10px] font-bold border transition-colors mt-2 ${ozState.deessActive ? 'bg-orange-700 border-orange-500 text-white' : 'bg-slate-800 border-slate-700 text-slate-300'}`}>
|
||||
{ozState.deessActive ? 'ON' : 'OFF'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="col-span-8 grid grid-cols-4 gap-4">
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="threshold" min={-60} max={0} value={de?.params?.threshold ?? -30} unit="dB" label="THRESHOLD" color="#fb923c" onChange={(p, v) => de && updateChainEntryParams(de.id, { threshold: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="ratio" min={0} max={100} value={de?.params?.ratio ?? 40} unit="%" label="STRENGTH" color="#fb923c" onChange={(p, v) => de && updateChainEntryParams(de.id, { ratio: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="freq" min={2000} max={12000} value={de?.params?.freq ?? 6000} unit="Hz" label="FREQ" color="#f97316" onChange={(p, v) => de && updateChainEntryParams(de.id, { freq: v })} />
|
||||
</div>
|
||||
<div className="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center justify-center h-full">
|
||||
<MasteringKnob param="makeup" min={0} max={12} value={de?.params?.makeup ?? 0} unit="dB" label="MAKE-UP" color="#f97316" onChange={(p, v) => de && updateChainEntryParams(de.id, { makeup: v })} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* VIEW: HARMONIC EXCITER */}
|
||||
<div className={`flex-1 flex flex-col p-4 gap-4 ${ozState.activeModule === 'exciter' ? '' : 'hidden'}`}>
|
||||
<div className="oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center">
|
||||
@@ -14204,7 +14290,9 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
|
||||
{ t: 'rebalance', c: 'text-sky-400', i: 'sliders-horizontal', d: 'Cân bằng Mid/Side (Vocal/Bass vs stereo width).' },
|
||||
{ t: 'eq', c: 'text-cyan-400', i: 'activity', d: 'EQ 4-band (lowshelf, 2× peaking, highshelf) + presets.' },
|
||||
{ t: 'imager', c: 'text-fuchsia-400', i: 'radio', d: 'Stereo width 4-band M/S + vectorscope/correlation.' },
|
||||
{ t: 'maximizer', c: 'text-emerald-400', i: 'gauge', d: 'Maximizer: boost, soft clip, upward comp, ceiling.' }
|
||||
{ t: 'maximizer', c: 'text-emerald-400', i: 'gauge', d: 'Maximizer: boost, soft clip, upward comp, ceiling.' },
|
||||
{ t: 'multiband', c: 'text-lime-400', i: 'layers', d: 'Compressor 3 băng tần low/mid/high với crossover LR4.' },
|
||||
{ t: 'deesser', c: 'text-orange-400', i: 'mic-2', d: 'Giảm sibilance (âm xuýt) theo tần số phát hiện.' }
|
||||
].map(m => (
|
||||
<button key={m.t} onClick={() => addModuleToChain(m.t)} className="p-3 bg-slate-950 hover:bg-slate-800 border border-slate-800 rounded-xl text-left space-y-1 transition-colors">
|
||||
<div className={`font-bold ${m.c} flex items-center gap-1.5`}><i data-lucide={m.i} className="w-3.5 h-3.5"></i> {MODULE_META[m.t].name}</div>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user