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;