fix: main session có thể play midi item
This commit is contained in:
+46
-19
@@ -4307,7 +4307,7 @@ const AIPresetModal = ({ isOpen, onClose }) => {
|
||||
}, "Đóng"))));
|
||||
};
|
||||
|
||||
const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNotes, onSaveNotes, setSubTabs }) => {
|
||||
const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNotes, onSaveNotes, setSubTabs, onPlayPause, onStop, isPlaying }) => {
|
||||
const [activeRollTool, setActiveRollTool] = React.useState('select');
|
||||
const [snapVal, setSnapVal] = React.useState('1/16');
|
||||
const [ccMode, setCcMode] = React.useState('velocity');
|
||||
@@ -4320,11 +4320,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
const rulerScrollRef = React.useRef(null);
|
||||
|
||||
const NoteHeight = 18;
|
||||
const PITCH_START = 36; // C2
|
||||
const KeybedPixelHeight = (127 - PITCH_START + 1) * NoteHeight;
|
||||
const PITCH_START = 0; // C0 (render all 128 keys)
|
||||
const KeybedPixelHeight = (128 - PITCH_START) * NoteHeight;
|
||||
const pixelsPerBeat = rollZoom;
|
||||
const timeSigNum = 4;
|
||||
const totalBeats = (st.duration || 4) * timeSigNum;
|
||||
const totalBeats = Math.max((st.duration || 4) * timeSigNum, 64); // at least 64 beats (16 bars) for scrolling
|
||||
const drawWidth = totalBeats * pixelsPerBeat;
|
||||
|
||||
const [notes, setNotes] = React.useState(st.notes || []);
|
||||
@@ -4523,7 +4523,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(phX, 0);
|
||||
ctx.lineTo(phX, (127 - PITCH_START) * NoteHeight);
|
||||
ctx.lineTo(phX, (128 - PITCH_START) * NoteHeight);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
@@ -4575,7 +4575,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
|
||||
React.useEffect(() => {
|
||||
if (gridScrollRef.current) {
|
||||
gridScrollRef.current.scrollTop = (127 - 48) * NoteHeight - 180;
|
||||
gridScrollRef.current.scrollTop = (127 - 48) * NoteHeight;
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -4852,7 +4852,13 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
/*#__PURE__*/React.createElement("div", {
|
||||
key: pitch,
|
||||
style: { height: `${NoteHeight}px` },
|
||||
className: `w-[60px] shrink-0 border-b border-zinc-800 text-[9px] font-bold flex items-center justify-end pr-2 transition ${isBlack ? 'bg-zinc-950 text-slate-500 border-zinc-900' : 'bg-white text-zinc-800 border-r border-zinc-400'}`
|
||||
className: `w-[60px] shrink-0 border-b border-zinc-800 text-[9px] font-bold flex items-center justify-end pr-2 transition cursor-pointer ${isBlack ? 'bg-zinc-950 text-slate-500 border-zinc-900 hover:bg-zinc-800' : 'bg-white text-zinc-800 border-r border-zinc-400 hover:bg-amber-100'}`,
|
||||
onMouseDown: (e) => {
|
||||
e.stopPropagation();
|
||||
if (window.SonicSF) {
|
||||
window.SonicSF.playNote(pitch, 100, 500, undefined, st.instrumentProgram, null);
|
||||
}
|
||||
}
|
||||
}, showLabel && label)
|
||||
);
|
||||
}
|
||||
@@ -4886,7 +4892,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
left: `${x}px`,
|
||||
top: '4px'
|
||||
},
|
||||
className: "pl-1 border-l border-zinc-700 h-full select-none"
|
||||
className: "pl-1 border-l border-zinc-700 h-full select-none cursor-pointer hover:bg-zinc-800/30",
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
const barTime = bar * 4 * beatSec;
|
||||
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: barTime } : s));
|
||||
}
|
||||
}, `Bar ${bar}`)
|
||||
);
|
||||
}
|
||||
@@ -4933,6 +4944,18 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
||||
}, mode)))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex items-center gap-1"
|
||||
}, /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => onPlayPause && onPlayPause(),
|
||||
className: `px-2.5 py-1 rounded text-xs flex items-center gap-1 transition font-bold ${st.isPlaying ? 'bg-amber-600 text-white' : 'bg-zinc-700 hover:bg-zinc-600 text-zinc-200'}`
|
||||
}, /*#__PURE__*/React.createElement("i", {
|
||||
"data-lucide": st.isPlaying ? "pause" : "play",
|
||||
className: "w-3 h-3"
|
||||
}), st.isPlaying ? "Pause" : "Play"), /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => onStop && onStop(),
|
||||
className: "px-2.5 py-1 bg-zinc-700 hover:bg-zinc-600 text-zinc-300 rounded text-xs flex items-center gap-1 transition"
|
||||
}, /*#__PURE__*/React.createElement("i", {
|
||||
"data-lucide": "square",
|
||||
className: "w-3 h-3"
|
||||
}), "Stop"), /*#__PURE__*/React.createElement("button", {
|
||||
onClick: () => onSaveNotes(st.id, st.trackId, st.target_id, notes),
|
||||
className: "px-2.5 py-1 bg-emerald-600 hover:bg-emerald-500 text-white rounded text-xs flex items-center gap-1 transition font-semibold"
|
||||
}, /*#__PURE__*/React.createElement("i", {
|
||||
@@ -8246,6 +8269,8 @@ const App = () => {
|
||||
const pannerNode = context.createStereoPanner();
|
||||
pannerNode.pan.setValueAtTime((track.pan ?? 0) / 100, context.currentTime);
|
||||
pannerNode.connect(context.destination);
|
||||
// Connect gain → panner for all tracks (audio clips AND MIDI items)
|
||||
gainNode.connect(pannerNode);
|
||||
activeTrackNodesRef.current[track.id] = {
|
||||
gainNode,
|
||||
pannerNode
|
||||
@@ -8263,7 +8288,6 @@ const App = () => {
|
||||
source.buffer = clip.buffer;
|
||||
source.playbackRate.value = clip.speed || 1.0;
|
||||
source.connect(gainNode);
|
||||
gainNode.connect(pannerNode);
|
||||
const clipStart = clip.startTime || 0;
|
||||
const clipDuration = clip.buffer.duration / (clip.speed || 1.0);
|
||||
const clipEnd = clipStart + clipDuration;
|
||||
@@ -13896,16 +13920,19 @@ const App = () => {
|
||||
const st = subTabs.find(s => s.id === activeTab);
|
||||
if (!st) return null;
|
||||
if (st.type === 'PIANO_ROLL') {
|
||||
return /*#__PURE__*/React.createElement(PianoRollTabEditor, {
|
||||
st: st,
|
||||
zoom: zoom,
|
||||
bpm: bpm,
|
||||
viewportWidth: viewportWidth,
|
||||
onClose: () => closeSubTab(st.id),
|
||||
onUpdateNotes: handleUpdateMidiNotes,
|
||||
onSaveNotes: handleSaveMidiNotes,
|
||||
setSubTabs: setSubTabs
|
||||
});
|
||||
return /*#__PURE__*/React.createElement(PianoRollTabEditor, {
|
||||
st: st,
|
||||
zoom: zoom,
|
||||
bpm: bpm,
|
||||
viewportWidth: viewportWidth,
|
||||
onClose: () => closeSubTab(st.id),
|
||||
onUpdateNotes: handleUpdateMidiNotes,
|
||||
onSaveNotes: handleSaveMidiNotes,
|
||||
setSubTabs: setSubTabs,
|
||||
onPlayPause: handlePlayPause,
|
||||
onStop: stopAllPlayback,
|
||||
isPlaying: isPlaying
|
||||
});
|
||||
}
|
||||
const subTrack = tracks.find(t => t.id === st.trackId);
|
||||
const vTrack = subTrack ? {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -10,13 +10,13 @@
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js"></script>
|
||||
<script src="/static/js/services/api.js?v=202607232030"></script>
|
||||
<script src="/static/js/services/audioEngine.js?v=202607232030"></script>
|
||||
<script src="/static/js/services/storage.js?v=202607232030"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202607232030"></script>
|
||||
<script src="/static/js/services/aiGateway.js?v=202607232030"></script>
|
||||
<script src="/static/js/services/dawCommandDispatcher.js?v=202607232030"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202607232030" defer></script>
|
||||
<script src="/static/js/services/api.js?v=202607232050"></script>
|
||||
<script src="/static/js/services/audioEngine.js?v=202607232050"></script>
|
||||
<script src="/static/js/services/storage.js?v=202607232050"></script>
|
||||
<script src="/static/js/services/soundfontPlayer.js?v=202607232050"></script>
|
||||
<script src="/static/js/services/aiGateway.js?v=202607232050"></script>
|
||||
<script src="/static/js/services/dawCommandDispatcher.js?v=202607232050"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202607232050" defer></script>
|
||||
<style>
|
||||
:root {
|
||||
--right-sidebar-width: 320px;
|
||||
|
||||
Reference in New Issue
Block a user