feat: drag-drop MIDI file creates new tracks
This commit is contained in:
+52
-1
@@ -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
Reference in New Issue
Block a user