FIX: lỗi: CC7 (channel volume) chỉ được set 100/0 (mute/unmute) — volume track KHÔNG áp qua channel → track MIDI dùng chung channel: CC7 của track set cuối thắng → volume của track kia vô tác dụng
This commit is contained in:
+69
-11
@@ -14119,6 +14119,15 @@ const App = () => {
|
||||
const node = activeTrackNodesRef.current[t.id];
|
||||
const audibleGain = computeTrackAudibleGain(effective, t);
|
||||
if (node) setTrackNodeGain(node, audibleGain);
|
||||
// Section sub-nodes (key: <parent>_sub_<trackId>) — mute/solo của
|
||||
// SECTION-TAB phải áp gain cho node play section — CHỈ trong section
|
||||
// (activeTracksRef = tracks section), KHÔNG đụng track MAIN.
|
||||
Object.keys(activeTrackNodesRef.current).forEach(function(k) {
|
||||
if (k.endsWith('_sub_' + t.id)) {
|
||||
const sn = activeTrackNodesRef.current[k];
|
||||
if (sn && sn.gainNode) setTrackNodeGain(sn, audibleGain);
|
||||
}
|
||||
});
|
||||
if (node && node.sfEntry) {
|
||||
try { node.sfEntry.gain.setTargetAtTime(audibleGain, getAudioContext().currentTime, 0.02); } catch (e) {}
|
||||
}
|
||||
@@ -14126,11 +14135,21 @@ const App = () => {
|
||||
// the per-track gain cannot silence them. Every MIDI track owns a
|
||||
// dedicated channel (ensureTrackMidiChannel) → mute/solo via CC7 (channel
|
||||
// volume) applies realtime, even to notes already sounding.
|
||||
// CC7 = VOLUME của track (không chỉ 100/0) — volume slider MIDI track tác
|
||||
// dụng qua channel volume (yêu cầu channel RIÊNG cho từng track).
|
||||
if ((t.midiItems && t.midiItems.length > 0) || t.type === 'MIDI') {
|
||||
try {
|
||||
if (window.SonicSF && typeof window.SonicSF.controllerChange === 'function') {
|
||||
const ch = ensureTrackMidiChannel(t, effective);
|
||||
window.SonicSF.controllerChange(ch, 7, audible ? 100 : 0);
|
||||
var cc7 = 100;
|
||||
if (audible) {
|
||||
var volDb = (typeof t.volumeDb === 'number' && isFinite(t.volumeDb)) ? t.volumeDb : 0;
|
||||
var volLin = volDb <= -50 ? 0 : Math.pow(10, volDb / 20);
|
||||
cc7 = Math.max(1, Math.min(127, Math.round(volLin * 100)));
|
||||
} else {
|
||||
cc7 = 0;
|
||||
}
|
||||
window.SonicSF.controllerChange(ch, 7, cc7);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -14696,12 +14715,21 @@ const App = () => {
|
||||
} catch (e) {}
|
||||
}
|
||||
// MIDI tracks: mirror the gain decision onto the channel CC7 volume so
|
||||
// FluidSynth-rendered notes respect mute/solo too.
|
||||
// FluidSynth-rendered notes respect mute/solo too. CC7 = VOLUME track
|
||||
// (không chỉ 100/0) — volume slider MIDI tác dụng qua channel volume.
|
||||
if ((t.midiItems && t.midiItems.length > 0) || t.type === 'MIDI') {
|
||||
try {
|
||||
if (window.SonicSF && typeof window.SonicSF.controllerChange === 'function') {
|
||||
const ch = ensureTrackMidiChannel(t, list);
|
||||
window.SonicSF.controllerChange(ch, 7, audible ? 100 : 0);
|
||||
var cc7 = 100;
|
||||
if (audible) {
|
||||
var volDb = (typeof t.volumeDb === 'number' && isFinite(t.volumeDb)) ? t.volumeDb : 0;
|
||||
var volLin = volDb <= -50 ? 0 : Math.pow(10, volDb / 20);
|
||||
cc7 = Math.max(1, Math.min(127, Math.round(volLin * 100)));
|
||||
} else {
|
||||
cc7 = 0;
|
||||
}
|
||||
window.SonicSF.controllerChange(ch, 7, cc7);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -15914,7 +15942,14 @@ const App = () => {
|
||||
}
|
||||
if (ctrl && !alt && e.key === 's') {
|
||||
e.preventDefault();
|
||||
handleSaveProjectRef.current();
|
||||
const curTab = activeTabRef.current;
|
||||
if (curTab && curTab.startsWith('session_')) {
|
||||
// SECTION-TAB: Ctrl+S = Lưu section (như nút Lưu section)
|
||||
handleSaveSectionTabRef.current(curTab);
|
||||
showToast('Đã lưu Section', 'success');
|
||||
} else {
|
||||
handleSaveProjectRef.current();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ctrl && alt && e.key === 's' || ctrl && e.shiftKey && e.key === 's') {
|
||||
@@ -20921,7 +20956,7 @@ const App = () => {
|
||||
const wasPlaying = isPlaying;
|
||||
const playingSubTab = subTabs.find(s => s.isPlaying && s.type === 'PIANO_ROLL');
|
||||
stopAllPlayback();
|
||||
setTracks(prev => prev.map(t =>
|
||||
updateActiveTracks(prev => prev.map(t =>
|
||||
t.id === trackId ? { ...t, solo: !t.solo, muted: false } : t
|
||||
));
|
||||
setTimeout(() => {
|
||||
@@ -20950,7 +20985,7 @@ const App = () => {
|
||||
};
|
||||
const toggleTrackMute = trackId => {
|
||||
const beforeSnap = captureTrackSnapshot(trackId);
|
||||
setTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
updateActiveTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
...t,
|
||||
muted: !t.muted
|
||||
} : t));
|
||||
@@ -20968,7 +21003,21 @@ const App = () => {
|
||||
setTimeout(() => lucide.createIcons(), 50);
|
||||
};
|
||||
const updateTrackProp = (trackId, props) => {
|
||||
setTracks(prev => prev.map(t => t.id === trackId ? { ...t, ...props } : t));
|
||||
updateActiveTracks(prev => prev.map(t => t.id === trackId ? { ...t, ...props } : t));
|
||||
// Mixer fader dùng updateTrackProp({volumeDb}) — áp gain realtime cho node
|
||||
// thường + sub-node SECTION (key <parent>_sub_<trackId>)
|
||||
if (props && props.volumeDb !== undefined) {
|
||||
const volLinear = props.volumeDb <= -50 ? 0 : Math.pow(10, props.volumeDb / 20);
|
||||
const ctx = getAudioContext();
|
||||
const nodes = activeTrackNodesRef.current[trackId];
|
||||
if (nodes) nodes.gainNode.gain.setValueAtTime(volLinear, ctx.currentTime);
|
||||
Object.keys(activeTrackNodesRef.current).forEach(function(k) {
|
||||
if (k.endsWith('_sub_' + trackId)) {
|
||||
const sn = activeTrackNodesRef.current[k];
|
||||
if (sn && sn.gainNode) sn.gainNode.gain.setValueAtTime(volLinear, ctx.currentTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
setTimeout(() => lucide.createIcons(), 50);
|
||||
};
|
||||
const toggleTrackDrum = trackId => {
|
||||
@@ -21002,7 +21051,7 @@ const App = () => {
|
||||
};
|
||||
const updateTrackVolumeDb = (trackId, val) => {
|
||||
const beforeSnap = captureTrackSnapshot(trackId);
|
||||
setTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
updateActiveTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
...t,
|
||||
volumeDb: val
|
||||
} : t));
|
||||
@@ -21018,15 +21067,24 @@ const App = () => {
|
||||
return next;
|
||||
});
|
||||
// Real-time update during playback
|
||||
const volLinear = val <= -50 ? 0 : Math.pow(10, val / 20);
|
||||
const ctx = getAudioContext();
|
||||
const nodes = activeTrackNodesRef.current[trackId];
|
||||
if (nodes) {
|
||||
const volLinear = val <= -50 ? 0 : Math.pow(10, val / 20);
|
||||
nodes.gainNode.gain.setValueAtTime(volLinear, getAudioContext().currentTime);
|
||||
nodes.gainNode.gain.setValueAtTime(volLinear, ctx.currentTime);
|
||||
}
|
||||
// Section sub-nodes (key: <parent>_sub_<trackId>) — volume slider của
|
||||
// SECTION-TAB phải đổi âm section ngay lập tức
|
||||
Object.keys(activeTrackNodesRef.current).forEach(function(k) {
|
||||
if (k.endsWith('_sub_' + trackId)) {
|
||||
const sn = activeTrackNodesRef.current[k];
|
||||
if (sn && sn.gainNode) sn.gainNode.gain.setValueAtTime(volLinear, ctx.currentTime);
|
||||
}
|
||||
});
|
||||
};
|
||||
const updateTrackPan = (trackId, val) => {
|
||||
const beforeSnap = captureTrackSnapshot(trackId);
|
||||
setTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
updateActiveTracks(prev => prev.map(t => t.id === trackId ? {
|
||||
...t,
|
||||
pan: val
|
||||
} : t));
|
||||
|
||||
Reference in New Issue
Block a user