fix: main session có thể play midi item
This commit is contained in:
+37
-10
@@ -4307,7 +4307,7 @@ const AIPresetModal = ({ isOpen, onClose }) => {
|
|||||||
}, "Đóng"))));
|
}, "Đó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 [activeRollTool, setActiveRollTool] = React.useState('select');
|
||||||
const [snapVal, setSnapVal] = React.useState('1/16');
|
const [snapVal, setSnapVal] = React.useState('1/16');
|
||||||
const [ccMode, setCcMode] = React.useState('velocity');
|
const [ccMode, setCcMode] = React.useState('velocity');
|
||||||
@@ -4320,11 +4320,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
const rulerScrollRef = React.useRef(null);
|
const rulerScrollRef = React.useRef(null);
|
||||||
|
|
||||||
const NoteHeight = 18;
|
const NoteHeight = 18;
|
||||||
const PITCH_START = 36; // C2
|
const PITCH_START = 0; // C0 (render all 128 keys)
|
||||||
const KeybedPixelHeight = (127 - PITCH_START + 1) * NoteHeight;
|
const KeybedPixelHeight = (128 - PITCH_START) * NoteHeight;
|
||||||
const pixelsPerBeat = rollZoom;
|
const pixelsPerBeat = rollZoom;
|
||||||
const timeSigNum = 4;
|
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 drawWidth = totalBeats * pixelsPerBeat;
|
||||||
|
|
||||||
const [notes, setNotes] = React.useState(st.notes || []);
|
const [notes, setNotes] = React.useState(st.notes || []);
|
||||||
@@ -4523,7 +4523,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
ctx.lineWidth = 1.5;
|
ctx.lineWidth = 1.5;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(phX, 0);
|
ctx.moveTo(phX, 0);
|
||||||
ctx.lineTo(phX, (127 - PITCH_START) * NoteHeight);
|
ctx.lineTo(phX, (128 - PITCH_START) * NoteHeight);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4575,7 +4575,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (gridScrollRef.current) {
|
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", {
|
/*#__PURE__*/React.createElement("div", {
|
||||||
key: pitch,
|
key: pitch,
|
||||||
style: { height: `${NoteHeight}px` },
|
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)
|
}, showLabel && label)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4886,7 +4892,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
left: `${x}px`,
|
left: `${x}px`,
|
||||||
top: '4px'
|
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}`)
|
}, `Bar ${bar}`)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4933,6 +4944,18 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
|
|||||||
}, mode)))), /*#__PURE__*/React.createElement("div", {
|
}, mode)))), /*#__PURE__*/React.createElement("div", {
|
||||||
className: "flex items-center gap-1"
|
className: "flex items-center gap-1"
|
||||||
}, /*#__PURE__*/React.createElement("button", {
|
}, /*#__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),
|
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"
|
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", {
|
}, /*#__PURE__*/React.createElement("i", {
|
||||||
@@ -8246,6 +8269,8 @@ const App = () => {
|
|||||||
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);
|
||||||
pannerNode.connect(context.destination);
|
pannerNode.connect(context.destination);
|
||||||
|
// Connect gain → panner for all tracks (audio clips AND MIDI items)
|
||||||
|
gainNode.connect(pannerNode);
|
||||||
activeTrackNodesRef.current[track.id] = {
|
activeTrackNodesRef.current[track.id] = {
|
||||||
gainNode,
|
gainNode,
|
||||||
pannerNode
|
pannerNode
|
||||||
@@ -8263,7 +8288,6 @@ const App = () => {
|
|||||||
source.buffer = clip.buffer;
|
source.buffer = clip.buffer;
|
||||||
source.playbackRate.value = clip.speed || 1.0;
|
source.playbackRate.value = clip.speed || 1.0;
|
||||||
source.connect(gainNode);
|
source.connect(gainNode);
|
||||||
gainNode.connect(pannerNode);
|
|
||||||
const clipStart = clip.startTime || 0;
|
const clipStart = clip.startTime || 0;
|
||||||
const clipDuration = clip.buffer.duration / (clip.speed || 1.0);
|
const clipDuration = clip.buffer.duration / (clip.speed || 1.0);
|
||||||
const clipEnd = clipStart + clipDuration;
|
const clipEnd = clipStart + clipDuration;
|
||||||
@@ -13904,7 +13928,10 @@ const App = () => {
|
|||||||
onClose: () => closeSubTab(st.id),
|
onClose: () => closeSubTab(st.id),
|
||||||
onUpdateNotes: handleUpdateMidiNotes,
|
onUpdateNotes: handleUpdateMidiNotes,
|
||||||
onSaveNotes: handleSaveMidiNotes,
|
onSaveNotes: handleSaveMidiNotes,
|
||||||
setSubTabs: setSubTabs
|
setSubTabs: setSubTabs,
|
||||||
|
onPlayPause: handlePlayPause,
|
||||||
|
onStop: stopAllPlayback,
|
||||||
|
isPlaying: isPlaying
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const subTrack = tracks.find(t => t.id === st.trackId);
|
const subTrack = tracks.find(t => t.id === st.trackId);
|
||||||
|
|||||||
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/lucide@latest"></script>
|
||||||
<script src="https://unpkg.com/react@18.3.1/umd/react.production.min.js"></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="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/api.js?v=202607232050"></script>
|
||||||
<script src="/static/js/services/audioEngine.js?v=202607232030"></script>
|
<script src="/static/js/services/audioEngine.js?v=202607232050"></script>
|
||||||
<script src="/static/js/services/storage.js?v=202607232030"></script>
|
<script src="/static/js/services/storage.js?v=202607232050"></script>
|
||||||
<script src="/static/js/services/soundfontPlayer.js?v=202607232030"></script>
|
<script src="/static/js/services/soundfontPlayer.js?v=202607232050"></script>
|
||||||
<script src="/static/js/services/aiGateway.js?v=202607232030"></script>
|
<script src="/static/js/services/aiGateway.js?v=202607232050"></script>
|
||||||
<script src="/static/js/services/dawCommandDispatcher.js?v=202607232030"></script>
|
<script src="/static/js/services/dawCommandDispatcher.js?v=202607232050"></script>
|
||||||
<script src="/static/js/app.precompiled.js?v=202607232030" defer></script>
|
<script src="/static/js/app.precompiled.js?v=202607232050" defer></script>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--right-sidebar-width: 320px;
|
--right-sidebar-width: 320px;
|
||||||
|
|||||||
Reference in New Issue
Block a user