fix(mastering): empty chain by default, allow removing all modules

MASTERING PANEL auto-seeded 3 modules (Dynamic EQ/Imager/Maximizer) and
removeChainModule re-added them when the chain hit zero. Chain now starts
empty everywhere (init, restore fallback, migration, graph fallback) and
can be removed down to empty; activeModule guards empty chain (null).

Cache-bust precompiled.js ?v= bump.
This commit is contained in:
2026-08-22 15:58:39 +07:00
parent 9c6e200708
commit d74ccdaa8d
2 changed files with 11 additions and 19 deletions
+10 -18
View File
@@ -1330,12 +1330,6 @@ const MASTER_MODULE_IO = {
rebalance: { input: 'rebalInput', output: 'rebalOutput' }
};
const DEFAULT_MASTER_CHAIN = [
{ id: 'mod_eq', type: 'eq', name: 'Dynamic EQ', active: true },
{ id: 'mod_imager', type: 'imager', name: 'Imager', active: true },
{ id: 'mod_maximizer', type: 'maximizer', name: 'Maximizer', active: true }
];
function chainSignature(chainArray) {
return (chainArray || []).map(m => (m.type || '') + (m.active ? '1' : '0')).join(',');
}
@@ -1950,7 +1944,7 @@ function toggleMasteringOnMaster(activate, isBypassed) {
if (!masterBus) return;
const active = !!(activate && !isBypassed);
const chain = (window.currentMasteringSettings && window.currentMasteringSettings.chain) || DEFAULT_MASTER_CHAIN;
const chain = (window.currentMasteringSettings && window.currentMasteringSettings.chain) || [];
const cSig = chainSignature(chain);
// Idempotent: don't rebuild unless active state OR chain layout changed.
if (_lastMasteringActive === active && masterBus.masteringActive === active && _lastChainSig === cSig) return;
@@ -7179,7 +7173,7 @@ const ProfileModal = ({
} else {
setMasteringSettings({
masterConnected: false,
activeModule: 'eq',
activeModule: null,
eqActive: true,
imagerActive: true,
maximizerActive: true,
@@ -7198,7 +7192,7 @@ const ProfileModal = ({
maxTransient: 25,
ceiling: -0.1,
isBypassed: false,
chain: DEFAULT_MASTER_CHAIN.map(m => ({ ...m })),
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
excActive: false, excDrive: 40,
@@ -11610,7 +11604,7 @@ const deserializeProjectFromSchema = (schemaObj) => {
};
// mastering_expand.md: extension modules + dynamic chain (default = old chain order)
if (!Array.isArray(migrated.chain)) {
migrated.chain = DEFAULT_MASTER_CHAIN.map(m => ({ ...m }));
migrated.chain = [];
}
if (migrated.compActive === undefined) migrated.compActive = false;
if (migrated.compThreshold === undefined) migrated.compThreshold = -16;
@@ -13638,8 +13632,7 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
if (removedMod && removedMod.type === 'vst3' && masterGuiOpen && masterGuiOpen.path === removedMod.path) closeMasterGui();
setOzState(prev => {
const removed = prev.chain.find(m => m.id === modId);
let chain = prev.chain.filter(m => m.id !== modId);
if (chain.length === 0) chain = DEFAULT_MASTER_CHAIN.map(m => ({ ...m })); // keep ≥1
const chain = prev.chain.filter(m => m.id !== modId);
const flags = {};
chain.forEach(m => { flags[chainFlag(m.type)] = !!m.active; });
const activeModule = prev.activeModule;
@@ -13648,7 +13641,7 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
const vi = vstFxChain.findIndex(s => s.path === removed.path);
if (vi >= 0) vstFxChain = [...vstFxChain.slice(0, vi), ...vstFxChain.slice(vi + 1)];
}
return { ...prev, chain, ...flags, vstFxChain, activeModule: chain.some(m => m.type === activeModule) ? activeModule : chain[chain.length - 1].type };
return { ...prev, chain, ...flags, vstFxChain, activeModule: chain.length ? (chain.some(m => m.type === activeModule) ? activeModule : chain[chain.length - 1].type) : null };
});
};
const reorderChain = (fromIdx, toIdx) => {
@@ -18765,8 +18758,7 @@ const App = () => {
};
const [masteringSettings, setMasteringSettings] = useState({
masterConnected: true, // ⚠️ FIX: bật ON mặc định — MIDI item/preview PHẢI được xử lí qua mastering FX ngay khi chạy app (không cần bật/tắt power)
activeModule: 'eq',
eqActive: true,
activeModule: null,
imagerActive: true,
maximizerActive: true,
eqLowGain: 1.5,
@@ -18784,7 +18776,7 @@ const App = () => {
maxTransient: 25,
ceiling: -0.1,
isBypassed: false,
chain: DEFAULT_MASTER_CHAIN.map(m => ({ ...m })),
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
excActive: false, excDrive: 40,
@@ -19192,7 +19184,7 @@ const App = () => {
} else {
setMasteringSettings({
masterConnected: false,
activeModule: 'eq',
activeModule: null,
eqActive: true,
imagerActive: true,
maximizerActive: true,
@@ -19211,7 +19203,7 @@ const App = () => {
maxTransient: 25,
ceiling: -0.1,
isBypassed: false,
chain: DEFAULT_MASTER_CHAIN.map(m => ({ ...m })),
chain: [],
compActive: false, compThreshold: -16, compRatio: 3, compMakeup: 0,
limActive: false, limThreshold: -1.0,
excActive: false, excDrive: 40,
+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=202608220330" defer></script>
<script src="/static/js/app.precompiled.js?v=202608181030" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style>
:root {