feat: drag-drop MIDI file creates new tracks

This commit is contained in:
2026-07-31 09:31:10 +07:00
parent 3c4f106e14
commit dfbeb24a33
2 changed files with 54 additions and 3 deletions
+52 -1
View File
@@ -15668,6 +15668,39 @@ const App = () => {
}
};
// Drop MIDI file create new track(s)
const handleDropMidiToNewTracks = async (file) => {
if (!file) return;
showToast('Đang nạp MIDI: ' + file.name + '...', 'info');
try {
var arrayBuffer = await file.arrayBuffer();
var midiResult = parseMidiFile(arrayBuffer);
if (!midiResult || midiResult.length === 0) {
showToast('Không tìm thấy nốt nhạc trong file MIDI.', 'error');
return;
}
var colors = ['#0f766e', '#1d4ed8', '#701a75', '#a21caf', '#b45309', '#ca8a04', '#dc2626', '#0891b2'];
var now = Date.now();
var newTracks = midiResult.map(function(midiItem, idx) {
return {
id: 'midi_track_' + now + '_' + idx,
name: midiItem.name || (midiResult.length > 1 ? 'MIDI Track ' + (idx + 1) : (file.name || 'MIDI').replace(/\.midi?$/i, '')),
buffer: null, startTime: 0, volumeDb: 0, pan: 0,
muted: false, solo: false, color: colors[idx % colors.length],
markers: [], serverFileId: null, clips: [], sections: [],
midiItems: [midiItem],
isArmed: false, monitoringEnabled: true,
inputSource: { deviceType: 'NONE', deviceId: '' }
};
});
updateActiveTracks(function(prev) { return prev.concat(newTracks); });
if (newTracks.length > 0) setSelectedTrackId(newTracks[0].id);
showToast('Đã tải MIDI: ' + newTracks.length + ' track(s) từ ' + file.name, 'success');
} catch (err) {
showToast('Lỗi giải mã MIDI.', 'error');
}
};
// Synth Generators
const generateSynthToTrack = (trackId, type) => {
const sampleRate = 44100;
@@ -20327,6 +20360,17 @@ const App = () => {
}
})), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex flex-col divide-y divide-[#141414] relative bg-[#111111] min-h-full",
onDragOver: e => e.preventDefault(),
onDrop: e => {
e.preventDefault();
const f = e.dataTransfer.files && e.dataTransfer.files[0];
if (!f) return;
if (/\.mid$|\.midi$/i.test(f.name || '')) {
handleDropMidiToNewTracks(f);
} else if (selectedTrackId) {
loadFileOnTrack(selectedTrackId, f);
}
},
onMouseDown: e => {
if (e.ctrlKey && !e.shiftKey && !e.altKey && e.button === 0) {
const wrapper = timelineWrapperRef.current;
@@ -20351,7 +20395,14 @@ const App = () => {
onDragOver: e => e.preventDefault(),
onDrop: e => {
e.preventDefault();
if (e.dataTransfer.files[0]) loadFileOnTrack(track.id, e.dataTransfer.files[0]);
e.stopPropagation();
const f = e.dataTransfer.files && e.dataTransfer.files[0];
if (!f) return;
if (/\.mid$|\.midi$/i.test(f.name || '')) {
handleDropMidiToNewTracks(f);
} else {
loadFileOnTrack(track.id, f);
}
},
onMouseEnter: () => { setHoveredTrackId(track.id); hoveredTrackIdRef.current = track.id; }
}, /*#__PURE__*/React.createElement(WaveformLane, {
File diff suppressed because one or more lines are too long