IMPROVE: bật tắt ARM, MIC trên track strip, double click lên nút balance, volume

This commit is contained in:
2026-07-30 17:04:13 +07:00
parent a9be38b76f
commit 1de69b8f4e
2 changed files with 52 additions and 12 deletions
+47 -7
View File
@@ -939,7 +939,7 @@ const MasterStripConsole = ({ masterVolume, setMasterVolume, showMasteringModal,
handleFaderChange(Math.max(-60, Math.min(12, masterVolume + delta)));
}
},
React.createElement("div", { className: "w-0.5 flex-1 bg-slate-700 absolute" }),
React.createElement("div", { className: "w-0.5 h-full bg-slate-700 absolute left-1/2 -translate-x-1/2 top-0" }),
React.createElement("input", {
id: "masterFader",
type: "range",
@@ -1035,8 +1035,9 @@ const TrackStripConsole = ({ track, index, onUpdateTrack, trackVuRefs }) => {
var trackColor = track.color || '#06b6d4';
var isMuted = track.muted;
var isSoloed = track.solo;
var isArmed = track.armed;
var isArmed = track.isArmed;
var trackName = track.name || 'Track ' + (index + 1);
var isMicActive = track.inputSource?.deviceType === 'MICROPHONE';
const [pan, setPan] = React.useState(0.0);
const [panLabel, setPanLabel] = React.useState('center');
@@ -1088,7 +1089,19 @@ const TrackStripConsole = ({ track, index, onUpdateTrack, trackVuRefs }) => {
React.createElement("div", {
className: "w-6 h-6 rounded-full bg-slate-800 border-2 border-slate-400 relative flex items-center justify-center cursor-pointer shadow-md",
title: "Kéo chuột lên/xuống để chỉnh Pan",
onPointerDown: handlePanPointerDown
onPointerDown: handlePanPointerDown,
onDoubleClick: function() { setPan(0); setPanLabel('center'); if (panPointerRef.current) panPointerRef.current.style.transform = 'rotate(0deg)'; },
onWheel: function(e) {
e.preventDefault();
var step = e.deltaY > 0 ? -0.05 : 0.05;
var newPan = Math.max(-1, Math.min(1, pan + step));
newPan = Math.round(newPan * 100) / 100;
setPan(newPan);
if (panPointerRef.current) panPointerRef.current.style.transform = 'rotate(' + (newPan * 120) + 'deg)';
if (newPan === 0) setPanLabel('center');
else if (newPan < 0) setPanLabel('L' + Math.abs(Math.round(newPan * 100)));
else setPanLabel('R' + Math.round(newPan * 100));
}
},
React.createElement("div", {
ref: panPointerRef,
@@ -1123,6 +1136,15 @@ const TrackStripConsole = ({ track, index, onUpdateTrack, trackVuRefs }) => {
onChange: function(e) {
var val = parseFloat(e.target.value);
if (onUpdateTrack) onUpdateTrack(track.id, { volumeDb: val });
},
onWheel: function(e) {
e.preventDefault();
var step = e.deltaY > 0 ? -0.5 : 0.5;
var newVol = Math.max(-60, Math.min(12, vol + step));
if (onUpdateTrack) onUpdateTrack(track.id, { volumeDb: newVol });
},
onDoubleClick: function() {
if (onUpdateTrack) onUpdateTrack(track.id, { volumeDb: 0 });
}
})
),
@@ -1177,9 +1199,21 @@ const TrackStripConsole = ({ track, index, onUpdateTrack, trackVuRefs }) => {
React.createElement("div", {
className: "h-[28px] shrink-0 flex items-center justify-between mx-1.5 px-1.5 bg-black/40 rounded border border-slate-800/60"
},
React.createElement("i", { className: "fa-solid fa-volume-high text-[9px] text-slate-400", title: "Input Monitoring" }),
React.createElement("button", {
onClick: function(e) { e.stopPropagation(); if (onUpdateTrack) onUpdateTrack(track.id, { armed: !track.armed }); },
onClick: function(e) {
e.stopPropagation();
if (!onUpdateTrack) return;
if (track.inputSource?.deviceType === 'MICROPHONE') {
onUpdateTrack(track.id, { inputSource: { deviceType: 'NONE', deviceId: '' } });
} else {
onUpdateTrack(track.id, { inputSource: { deviceType: 'MICROPHONE', deviceId: 'default' } });
}
},
className: "w-4 h-4 rounded flex items-center justify-center text-[7px] font-bold transition-all" + (isMicActive ? " bg-sky-600 text-white border border-sky-400 shadow-sm" : " bg-slate-800 text-slate-500 border border-slate-700"),
title: isMicActive ? "Mic Input ON" : "Mic Input OFF"
}, "MIC"),
React.createElement("button", {
onClick: function(e) { e.stopPropagation(); if (onUpdateTrack) onUpdateTrack(track.id, { isArmed: !track.isArmed }); },
className: "w-5 h-5 rounded-full flex items-center justify-center transition-all shadow-inner" + (isArmed ? " btn-arm-active border-red-400" : " bg-red-950 border-2 border-red-800 text-red-500"),
title: "Arm for Recording"
}, React.createElement("i", { className: "fa-solid fa-circle text-[8px]" }))
@@ -5682,6 +5716,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
setSelectedNoteIds([]);
showToast(`Đã xóa ${selectedNoteIds.length} nốt!`, 'info');
}
} else if (e.key === 'F7') {
e.preventDefault();
e.stopPropagation();
var toggleMixer = window.__toggleMixerRef;
if (toggleMixer) toggleMixer();
}
};
window.addEventListener('keydown', handler);
@@ -8965,6 +9004,7 @@ const App = () => {
const [showMixer, setShowMixer] = useState(false);
const setShowMixerRef = useRef(setShowMixer);
setShowMixerRef.current = setShowMixer;
window.__toggleMixerRef = function() { setShowMixer(function(p) { return !p; }); };
const [mixerHeight, setMixerHeight] = useState(function() {
var saved = localStorage.getItem('studio_mixer_height');
return saved ? parseInt(saved) : 200;
@@ -17435,7 +17475,7 @@ const App = () => {
if (!canvas) return;
let audioPeak = 0;
if (node && node.analyserNode && isPlaying) {
if (node && node.analyserNode) {
const analyser = node.analyserNode;
const data = new Uint8Array(128);
analyser.getByteTimeDomainData(data);
@@ -17445,7 +17485,7 @@ const App = () => {
}
}
let midiPeak = midiVuActivityRef.current[trackId] || 0;
let midiPeak = isPlaying ? (midiVuActivityRef.current[trackId] || 0) : 0;
if (midiPeak > 0) {
midiVuActivityRef.current[trackId] = midiPeak * 0.90;
if (midiVuActivityRef.current[trackId] < 0.01) {
File diff suppressed because one or more lines are too long