fix: MIDI Rearrange — selected item triggers dedicated tool flow

- midiExtractor.js: extractSelectedMIDIContext with note name conversion
- aiGateway.js: REARRANGE_TOOL_SPEC + buildRearrangeMessage
- dawCommandDispatcher.js: register REARRANGE_MIDI_MELODY command
- app.jsx: handleAISend detects selected MIDI item → rearrange flow
- rearrangeMidiMelody handler places A/B track below source track
This commit is contained in:
2026-07-28 10:54:35 +07:00
parent 421535ca0e
commit 2eb327056d
6 changed files with 285 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
const SonicMidiExtractor = (function() {
const NOTE_NAMES = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
function midiPitchToNoteName(pitch) {
const note = NOTE_NAMES[pitch % 12];
const octave = Math.floor(pitch / 12) - 1;
return note + octave;
}
function extractSelectedMIDIContext(tracks, selectedItemId, bpm) {
let targetItem = null;
let targetTrack = null;
for (const track of tracks) {
const items = track.midiItems || [];
const item = items.find(i => i.id === selectedItemId);
if (item) { targetItem = item; targetTrack = track; break; }
}
if (!targetItem) {
throw new Error('Please select a MIDI Item on the Timeline before requesting a Rearrangement!');
}
const notes = targetItem.notes || [];
if (notes.length === 0) {
throw new Error('Selected MIDI item has no notes to rearrange.');
}
const compactNotes = notes.map(n => ({
pitch: n.pitch,
note_name: midiPitchToNoteName(n.pitch),
start_beat: parseFloat((n.start_beat || 0).toFixed(2)),
duration_beats: parseFloat((n.duration_beats || 1).toFixed(2)),
velocity: parseFloat((n.velocity || 0.8).toFixed(2))
}));
const totalDurationBeats = compactNotes.reduce((max, n) => Math.max(max, n.start_beat + n.duration_beats), 0);
return {
track_name: targetTrack.name,
track_id: targetTrack.id,
item_id: targetItem.id,
item_name: targetItem.name,
duration_bars: targetItem.length_bars || Math.ceil(totalDurationBeats / 4),
total_beats: Math.ceil(totalDurationBeats),
bpm: parseInt(bpm || '120'),
total_notes: compactNotes.length,
notes: compactNotes,
instrument: targetTrack.synth_engine ? {
soundfont_id: targetTrack.synth_engine.soundfont_id || '',
soundfont_bank: targetTrack.synth_engine.soundfont_bank ?? 0,
soundfont_program: targetTrack.synth_engine.soundfont_program ?? 0
} : null
};
}
return { extractSelectedMIDIContext, midiPitchToNoteName };
})();
window.SonicMidiExtractor = SonicMidiExtractor;