114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { KGCommand } from '../KGCommand';
|
|
import { KGCore } from '../../KGCore';
|
|
import { KGMidiRegion } from '../../region/KGMidiRegion';
|
|
import { KGMidiNote } from '../../midi/KGMidiNote';
|
|
import { generateUniqueId } from '../../../util/miscUtil';
|
|
import type { RawMidiNote } from '../../../util/midiUtil';
|
|
|
|
/**
|
|
* Command to insert a MIDI clip (from K.G.One generation) into a MIDI track.
|
|
* All note data is stored so that redo recreates the full region with notes.
|
|
*/
|
|
export class ImportMidiClipCommand extends KGCommand {
|
|
private trackId: string;
|
|
private trackIndex: number;
|
|
private regionId: string;
|
|
private regionName: string;
|
|
private startBeat: number;
|
|
private lengthInBeats: number;
|
|
private rawNotes: RawMidiNote[];
|
|
private createdRegion: KGMidiRegion | null = null;
|
|
|
|
constructor(
|
|
trackId: string,
|
|
trackIndex: number,
|
|
startBeat: number,
|
|
lengthInBeats: number,
|
|
rawNotes: RawMidiNote[],
|
|
regionName?: string,
|
|
regionId?: string
|
|
) {
|
|
super();
|
|
this.trackId = trackId;
|
|
this.trackIndex = trackIndex;
|
|
this.startBeat = startBeat;
|
|
this.lengthInBeats = lengthInBeats;
|
|
this.rawNotes = rawNotes;
|
|
this.regionId = regionId || generateUniqueId('KGMidiRegion');
|
|
this.regionName = regionName || 'KGOne Clip';
|
|
}
|
|
|
|
execute(): void {
|
|
const core = KGCore.instance();
|
|
const currentProject = core.getCurrentProject();
|
|
const track = currentProject.getTracks().find(t => t.getId().toString() === this.trackId);
|
|
if (!track) {
|
|
throw new Error(`Track ${this.trackId} not found`);
|
|
}
|
|
|
|
// Create the region
|
|
this.createdRegion = new KGMidiRegion(
|
|
this.regionId,
|
|
this.trackId,
|
|
this.trackIndex,
|
|
this.regionName,
|
|
this.startBeat,
|
|
this.lengthInBeats
|
|
);
|
|
|
|
// Populate notes
|
|
for (const raw of this.rawNotes) {
|
|
const note = new KGMidiNote(
|
|
generateUniqueId('KGMidiNote'),
|
|
raw.startBeat,
|
|
raw.endBeat,
|
|
raw.pitch,
|
|
raw.velocity
|
|
);
|
|
this.createdRegion.addNote(note);
|
|
}
|
|
|
|
track.addRegion(this.createdRegion);
|
|
console.log(`Imported MIDI clip "${this.regionName}" (${this.rawNotes.length} notes) at beat ${this.startBeat}`);
|
|
}
|
|
|
|
undo(): void {
|
|
const core = KGCore.instance();
|
|
const currentProject = core.getCurrentProject();
|
|
const track = currentProject.getTracks().find(t => t.getId().toString() === this.trackId);
|
|
if (track) {
|
|
track.removeRegion(this.regionId);
|
|
}
|
|
console.log(`Undid MIDI clip import "${this.regionName}"`);
|
|
}
|
|
|
|
getDescription(): string {
|
|
return `Import MIDI clip "${this.regionName}"`;
|
|
}
|
|
|
|
getCreatedRegion(): KGMidiRegion | null {
|
|
return this.createdRegion;
|
|
}
|
|
|
|
/**
|
|
* Factory from bar-based coordinates (common UI pattern).
|
|
*/
|
|
static fromBarCoordinates(
|
|
trackId: string,
|
|
trackIndex: number,
|
|
barNumber: number,
|
|
lengthInBars: number,
|
|
beatsPerBar: number,
|
|
rawNotes: RawMidiNote[],
|
|
regionName?: string,
|
|
regionId?: string
|
|
): ImportMidiClipCommand {
|
|
const startBeat = (barNumber - 1) * beatsPerBar;
|
|
const lengthInBeats = lengthInBars * beatsPerBar;
|
|
return new ImportMidiClipCommand(
|
|
trackId, trackIndex, startBeat, lengthInBeats,
|
|
rawNotes, regionName, regionId
|
|
);
|
|
}
|
|
}
|