FIX: sửa volume main out

This commit is contained in:
2026-07-30 10:05:11 +07:00
parent e64070a83e
commit 2c55db372a
3 changed files with 87 additions and 34 deletions
+76 -24
View File
@@ -58,6 +58,7 @@ function initMasterBus(ctx) {
masterBus.compressor.connect(masterBus.output); masterBus.compressor.connect(masterBus.output);
masterBus.output.connect(masterBus.analyser); masterBus.output.connect(masterBus.analyser);
masterBus.analyser.connect(ctx.destination); masterBus.analyser.connect(ctx.destination);
window.masterBus = masterBus;
return masterBus; return masterBus;
} }
@@ -116,6 +117,9 @@ function getAudioContext() {
if (audioCtx.state === 'suspended') { if (audioCtx.state === 'suspended') {
audioCtx.resume(); audioCtx.resume();
} }
if (!masterBus) {
initMasterBus(audioCtx);
}
if (window.SonicSF && window.SonicSF.init) { if (window.SonicSF && window.SonicSF.init) {
window.SonicSF.init(audioCtx); window.SonicSF.init(audioCtx);
} }
@@ -456,7 +460,7 @@ const MixerStrip = ({ track, index, onUpdateTrack }) => {
var vuColor = pct >= 80 ? '#ef4444' : pct >= 50 ? '#eab308' : '#22c55e'; var vuColor = pct >= 80 ? '#ef4444' : pct >= 50 ? '#eab308' : '#22c55e';
var trackColor = track.color || '#06b6d4'; var trackColor = track.color || '#06b6d4';
return React.createElement("div", { return React.createElement("div", {
className: "flex flex-col items-stretch w-14 shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm" className: "flex flex-col items-stretch w-[84px] shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm"
}, },
React.createElement("div", { React.createElement("div", {
className: "flex items-center justify-between px-1 py-0.5 bg-[#222] border-b border-black/60 shrink-0" className: "flex items-center justify-between px-1 py-0.5 bg-[#222] border-b border-black/60 shrink-0"
@@ -476,17 +480,39 @@ const MixerStrip = ({ track, index, onUpdateTrack }) => {
}, "S")), }, "S")),
React.createElement("div", { React.createElement("div", {
className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0" className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0"
}, React.createElement("input", { }, React.createElement("div", {
type: "range", className: "w-[30px] rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60 flex flex-col items-center cursor-pointer",
min: "-60", onMouseDown: function(e) {
max: "12", e.preventDefault();
step: "0.5", var rect = e.currentTarget.getBoundingClientRect();
value: vol, var tid = track.id;
onChange: e => { if (onUpdateTrack) onUpdateTrack(track.id, { volumeDb: parseFloat(e.target.value) }); }, function onMove(ev) {
className: "w-3 bg-[#161616] rounded appearance-none cursor-pointer accent-orange-500 border border-black/60", var pct = 1 - Math.max(0, Math.min(1, (ev.clientY - rect.top) / rect.height));
style: { writingMode: 'vertical-lr', direction: 'rtl' } var val = Math.round((pct * 72 - 60) * 2) / 2;
}), React.createElement("div", { if (onUpdateTrack) onUpdateTrack(tid, { volumeDb: val });
className: "w-2.5 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60" }
function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); }
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
onMove(e);
}
},
/* 0dB reference line */
React.createElement("div", {
className: "absolute w-full h-px bg-amber-400/60 z-10 pointer-events-none",
style: { bottom: '83.333%' }
}),
/* Background gradient */
React.createElement("div", {
className: "absolute inset-0",
style: { background: 'linear-gradient(to top, #22c55e, #eab308, #ef4444)' }
}),
/* Level overlay - dark at TOP, gradient visible at bottom */
React.createElement("div", {
className: "absolute top-0 w-full transition-all duration-75 bg-[#0d0d0d]",
style: { height: (100 - pct) + '%' }
})), React.createElement("div", {
className: "w-[15px] rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60"
}, React.createElement("div", { }, React.createElement("div", {
className: "absolute bottom-0 w-full transition-all duration-75", className: "absolute bottom-0 w-full transition-all duration-75",
style: { height: pct + '%', backgroundColor: vuColor } style: { height: pct + '%', backgroundColor: vuColor }
@@ -11394,6 +11420,8 @@ const App = () => {
gainNode.gain.setValueAtTime(volLinear, context.currentTime); gainNode.gain.setValueAtTime(volLinear, context.currentTime);
const pannerNode = context.createStereoPanner(); const pannerNode = context.createStereoPanner();
pannerNode.pan.setValueAtTime((track.pan ?? 0) / 100, context.currentTime); pannerNode.pan.setValueAtTime((track.pan ?? 0) / 100, context.currentTime);
// Ensure master bus is initialized for MAIN OUT routing
if (!masterBus) initMasterBus(context);
// Route through master bus if available, else direct to destination // Route through master bus if available, else direct to destination
const dest = masterBus ? masterBus.input : context.destination; const dest = masterBus ? masterBus.input : context.destination;
pannerNode.connect(dest); pannerNode.connect(dest);
@@ -18950,28 +18978,52 @@ const App = () => {
})))), /*#__PURE__*/React.createElement("div", { })))), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex overflow-x-auto p-1.5 gap-1.5 items-stretch" className: "flex-1 flex overflow-x-auto p-1.5 gap-1.5 items-stretch"
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: "flex flex-col items-stretch w-[72px] shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm" className: "flex flex-col items-stretch w-[108px] shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm"
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: "text-[9px] font-bold text-zinc-300 uppercase tracking-wider w-full text-center py-0.5 bg-[#222] border-b border-black/60 shrink-0" className: "text-[9px] font-bold text-zinc-300 uppercase tracking-wider w-full text-center py-0.5 bg-[#222] border-b border-black/60 shrink-0"
}, "MASTER"), /*#__PURE__*/React.createElement("div", { }, "MASTER"), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0" className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0"
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: "w-6 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60 flex flex-col items-center" className: "w-9 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60 flex flex-col items-center cursor-pointer",
}, /*#__PURE__*/React.createElement("input", { onMouseDown: function(e) {
type: "range", e.preventDefault();
min: "-60", max: "12", step: "0.5", var rect = e.currentTarget.getBoundingClientRect();
value: masterVolume, function onMove(ev) {
onChange: e => { const v = parseFloat(e.target.value); setMasterVolume(v); const linear = v <= -50 ? 0 : Math.pow(10, v / 20); if (masterBus && masterBus.output) masterBus.output.gain.setValueAtTime(linear, getAudioContext().currentTime); }, var pct = 1 - Math.max(0, Math.min(1, (ev.clientY - rect.top) / rect.height));
className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10", var val = Math.round((pct * 72 - 60) * 2) / 2;
style: { writingMode: 'vertical-lr', direction: 'rtl' } setMasterVolume(val);
}), /*#__PURE__*/React.createElement("div", { var linear = val <= -50 ? 0 : Math.pow(10, val / 20);
className: "flex-1 w-full flex flex-col-reverse items-center pb-0.5", getAudioContext();
if (masterBus && masterBus.output) masterBus.output.gain.setValueAtTime(linear, audioCtx.currentTime);
}
function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); }
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
onMove(e);
}
},
/* 0dB reference line */
React.createElement("div", {
className: "absolute w-full h-px bg-amber-400/70 z-10 pointer-events-none",
style: { bottom: '83.333%' }
}),
/* Tick marks */
[-48,-36,-24,-12,0,6].map(function(db) {
var pct = (db + 60) / 72 * 100;
return React.createElement("div", {
key: db,
className: "absolute w-1.5 h-px bg-slate-600/50 z-10 pointer-events-none",
style: { bottom: pct + '%', left: db === 0 ? '0' : 'auto', right: db === 0 ? 'auto' : '0', width: db === 0 ? '100%' : '4px' }
});
}),
React.createElement("div", {
className: "flex-1 w-full flex flex-col items-center pb-0.5",
style: { background: 'linear-gradient(to top, #38bdf8, #f59e0b, #ef4444)' } style: { background: 'linear-gradient(to top, #38bdf8, #f59e0b, #ef4444)' }
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: "w-full bg-[#0d0d0d] transition-all duration-75", className: "w-full bg-[#0d0d0d] transition-all duration-75",
style: { height: `${(1 - Math.min(1, Math.max(0, (masterVolume + 60) / 72))) * 100}%` } style: { height: `${(1 - Math.min(1, Math.max(0, (masterVolume + 60) / 72))) * 100}%` }
}))), /*#__PURE__*/React.createElement("div", { }))), /*#__PURE__*/React.createElement("div", {
className: "w-3 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60" className: "w-[18px] rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60"
}, /*#__PURE__*/React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
className: "absolute bottom-0 w-full transition-all duration-75", className: "absolute bottom-0 w-full transition-all duration-75",
style: { height: `${Math.min(100, masterVU * 100)}%`, background: masterVU > 0.85 ? '#ef4444' : masterVU > 0.7 ? '#f59e0b' : '#38bdf8' } style: { height: `${Math.min(100, masterVU * 100)}%`, background: masterVU > 0.85 ? '#ef4444' : masterVU > 0.7 ? '#f59e0b' : '#38bdf8' }
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -30,7 +30,7 @@
if (!_gainNode) { if (!_gainNode) {
_gainNode = ctx.createGain(); _gainNode = ctx.createGain();
_gainNode.gain.value = 0.3; _gainNode.gain.value = 0.3;
_gainNode.connect(ctx.destination); _gainNode.connect(window.masterBus ? window.masterBus.input : ctx.destination);
} }
return ctx; return ctx;
} }
@@ -119,8 +119,8 @@
if (!_useScriptNode) { if (!_useScriptNode) {
try { try {
_workletNode = new AudioWorkletNode(_audioCtx, 'fluidsynth-bridge'); _workletNode = new AudioWorkletNode(_audioCtx, 'fluidsynth-bridge');
_workletNode.connect(_audioCtx.destination); _workletNode.connect(_gainNode);
console.log("[SonicSF] AudioWorklet node connected"); console.log("[SonicSF] AudioWorklet node connected via gain");
_startRenderLoop(); _startRenderLoop();
} catch (e) { } catch (e) {
console.warn("[SonicSF] AudioWorkletNode failed:", e); console.warn("[SonicSF] AudioWorkletNode failed:", e);
@@ -146,9 +146,9 @@
} }
} catch (er) {} } catch (er) {}
}; };
spn.connect(_audioCtx.destination); spn.connect(_gainNode);
_workletNode = spn; _workletNode = spn;
console.log("[SonicSF] ScriptProcessorNode fallback active (buf:", spBufSz, ")"); console.log("[SonicSF] ScriptProcessorNode connected via gain (buf:", spBufSz, ")");
} }
_initialized = true; _initialized = true;