feat: added adding pitch bend feature and delete selected events feature to the list event panel

This commit is contained in:
Xiaohan-Tian
2026-05-06 17:36:54 -07:00
parent 6ed1dddebd
commit fc1a044e98
8 changed files with 382 additions and 40 deletions
@@ -0,0 +1,153 @@
import { KGCommand } from '../KGCommand';
import { KGCore } from '../../KGCore';
import { KGMidiNote } from '../../midi/KGMidiNote';
import { KGMidiPitchBend } from '../../midi/KGMidiPitchBend';
import { KGMidiRegion } from '../../region/KGMidiRegion';
interface DeletedNoteData {
note: KGMidiNote;
regionId: string;
originalIndex: number;
}
interface DeletedPitchBendData {
pitchBend: KGMidiPitchBend;
regionId: string;
originalIndex: number;
}
export class DeleteMidiEventsCommand extends KGCommand {
private noteIds: string[];
private pitchBendIds: string[];
private deletedNoteData: DeletedNoteData[] = [];
private deletedPitchBendData: DeletedPitchBendData[] = [];
constructor(noteIds: string[] = [], pitchBendIds: string[] = []) {
super();
this.noteIds = noteIds;
this.pitchBendIds = pitchBendIds;
}
execute(): void {
const core = KGCore.instance();
const tracks = core.getCurrentProject().getTracks();
this.deletedNoteData = [];
this.deletedPitchBendData = [];
for (const noteId of this.noteIds) {
for (const track of tracks) {
for (const region of track.getRegions()) {
if (!(region instanceof KGMidiRegion)) continue;
const noteIndex = region.getNotes().findIndex(note => note.getId() === noteId);
if (noteIndex === -1) continue;
this.deletedNoteData.push({
note: region.getNotes()[noteIndex],
regionId: region.getId(),
originalIndex: noteIndex,
});
break;
}
}
}
for (const pitchBendId of this.pitchBendIds) {
for (const track of tracks) {
for (const region of track.getRegions()) {
if (!(region instanceof KGMidiRegion)) continue;
const pitchBendIndex = region.getPitchBends().findIndex(pitchBend => pitchBend.getId() === pitchBendId);
if (pitchBendIndex === -1) continue;
this.deletedPitchBendData.push({
pitchBend: region.getPitchBends()[pitchBendIndex],
regionId: region.getId(),
originalIndex: pitchBendIndex,
});
break;
}
}
}
if (this.deletedNoteData.length === 0 && this.deletedPitchBendData.length === 0) {
throw new Error('No MIDI events found to delete');
}
this.deletedNoteData.sort((a, b) => b.originalIndex - a.originalIndex);
this.deletedPitchBendData.sort((a, b) => b.originalIndex - a.originalIndex);
for (const data of this.deletedNoteData) {
const region = this.resolveRegion(tracks, data.regionId);
region.removeNote(data.note.getId());
const selectedNote = core.getSelectedItems().find(item => item instanceof KGMidiNote && item.getId() === data.note.getId());
if (selectedNote) {
core.removeSelectedItem(selectedNote);
}
}
for (const data of this.deletedPitchBendData) {
const region = this.resolveRegion(tracks, data.regionId);
region.removePitchBend(data.pitchBend.getId());
const selectedPitchBend = core.getSelectedItems().find(
item => item instanceof KGMidiPitchBend && item.getId() === data.pitchBend.getId()
);
if (selectedPitchBend) {
core.removeSelectedItem(selectedPitchBend);
}
}
}
undo(): void {
const tracks = KGCore.instance().getCurrentProject().getTracks();
for (const data of [...this.deletedNoteData].sort((a, b) => a.originalIndex - b.originalIndex)) {
const region = this.resolveRegion(tracks, data.regionId);
const notes = region.getNotes();
if (data.originalIndex >= 0 && data.originalIndex <= notes.length) {
notes.splice(data.originalIndex, 0, data.note);
region.setNotes(notes);
} else {
region.addNote(data.note);
}
}
for (const data of [...this.deletedPitchBendData].sort((a, b) => a.originalIndex - b.originalIndex)) {
const region = this.resolveRegion(tracks, data.regionId);
const pitchBends = region.getPitchBends();
if (data.originalIndex >= 0 && data.originalIndex <= pitchBends.length) {
pitchBends.splice(data.originalIndex, 0, data.pitchBend);
region.setPitchBends(pitchBends);
} else {
region.addPitchBend(data.pitchBend);
}
}
}
getDescription(): string {
const noteCount = this.noteIds.length;
const pitchBendCount = this.pitchBendIds.length;
if (noteCount > 0 && pitchBendCount > 0) {
return `Delete ${noteCount} note${noteCount === 1 ? '' : 's'} and ${pitchBendCount} pitch bend${pitchBendCount === 1 ? '' : 's'}`;
}
if (pitchBendCount > 0) {
return pitchBendCount === 1 ? 'Delete pitch bend' : `Delete ${pitchBendCount} pitch bends`;
}
return noteCount === 1 ? 'Delete note' : `Delete ${noteCount} notes`;
}
private resolveRegion(tracks: Array<{ getRegions(): unknown[] }>, regionId: string): KGMidiRegion {
for (const track of tracks) {
const region = track.getRegions().find(candidate => candidate instanceof KGMidiRegion && candidate.getId() === regionId);
if (region instanceof KGMidiRegion) {
return region;
}
}
throw new Error(`MIDI region with ID ${regionId} not found`);
}
}