feat: added global chord region to MIDI region conversion feature
This commit is contained in:
@@ -29,6 +29,7 @@ export { PasteRegionsCommand } from './region/PasteRegionsCommand';
|
||||
export { UpdateRegionCommand, type RegionUpdateProperties } from './region/UpdateRegionCommand';
|
||||
export { ImportAudioCommand } from './region/ImportAudioCommand';
|
||||
export { ImportMidiClipCommand } from './region/ImportMidiClipCommand';
|
||||
export { ImportChordRegionsCommand } from './region/ImportChordRegionsCommand';
|
||||
export { ImportStemsCommand } from './region/ImportStemsCommand';
|
||||
export type { StemImportEntry } from './region/ImportStemsCommand';
|
||||
export { SplitRegionCommand } from './region/SplitRegionCommand';
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { ImportChordRegionsCommand } from './ImportChordRegionsCommand';
|
||||
import { createMockMidiTrack } from '../../../test/utils/mock-data';
|
||||
|
||||
const track = createMockMidiTrack({ id: 1, regions: [] });
|
||||
track.setTrackIndex(0);
|
||||
|
||||
vi.mock('../../KGCore', () => ({
|
||||
KGCore: {
|
||||
instance: () => ({
|
||||
getCurrentProject: () => ({
|
||||
getTracks: () => [track],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('ImportChordRegionsCommand', () => {
|
||||
beforeEach(() => {
|
||||
track.setRegions([]);
|
||||
});
|
||||
|
||||
it('creates one MIDI region with imported notes and supports undo', () => {
|
||||
const command = new ImportChordRegionsCommand(
|
||||
'1',
|
||||
0,
|
||||
8,
|
||||
6,
|
||||
[
|
||||
{ startBeat: 0, endBeat: 4, pitch: 48, velocity: 127 },
|
||||
{ startBeat: 0, endBeat: 4, pitch: 60, velocity: 127 },
|
||||
{ startBeat: 4, endBeat: 6, pitch: 41, velocity: 127 },
|
||||
{ startBeat: 4, endBeat: 6, pitch: 53, velocity: 127 },
|
||||
],
|
||||
'Chord Progression',
|
||||
'imported-region',
|
||||
);
|
||||
|
||||
command.execute();
|
||||
|
||||
expect(track.getRegions()).toHaveLength(1);
|
||||
const region = command.getCreatedRegion();
|
||||
expect(region?.getId()).toBe('imported-region');
|
||||
expect(region?.getStartFromBeat()).toBe(8);
|
||||
expect(region?.getLength()).toBe(6);
|
||||
expect(region?.getNotes().map(note => ({
|
||||
startBeat: note.getStartBeat(),
|
||||
endBeat: note.getEndBeat(),
|
||||
pitch: note.getPitch(),
|
||||
velocity: note.getVelocity(),
|
||||
}))).toEqual([
|
||||
{ startBeat: 0, endBeat: 4, pitch: 48, velocity: 127 },
|
||||
{ startBeat: 0, endBeat: 4, pitch: 60, velocity: 127 },
|
||||
{ startBeat: 4, endBeat: 6, pitch: 41, velocity: 127 },
|
||||
{ startBeat: 4, endBeat: 6, pitch: 53, velocity: 127 },
|
||||
]);
|
||||
|
||||
command.undo();
|
||||
expect(track.getRegions()).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
import { KGCommand } from '../KGCommand';
|
||||
import { KGCore } from '../../KGCore';
|
||||
import { KGMidiRegion } from '../../region/KGMidiRegion';
|
||||
import { KGMidiNote } from '../../midi/KGMidiNote';
|
||||
import { generateUniqueId } from '../../../util/miscUtil';
|
||||
import {
|
||||
CHORD_REGION_IMPORT_REGION_NAME,
|
||||
type ImportedChordMidiNoteData,
|
||||
} from '../../../util/chordRegionImportUtil';
|
||||
|
||||
export class ImportChordRegionsCommand extends KGCommand {
|
||||
private trackId: string;
|
||||
private trackIndex: number;
|
||||
private regionId: string;
|
||||
private regionName: string;
|
||||
private startBeat: number;
|
||||
private lengthInBeats: number;
|
||||
private notes: ImportedChordMidiNoteData[];
|
||||
private createdRegion: KGMidiRegion | null = null;
|
||||
|
||||
constructor(
|
||||
trackId: string,
|
||||
trackIndex: number,
|
||||
startBeat: number,
|
||||
lengthInBeats: number,
|
||||
notes: ImportedChordMidiNoteData[],
|
||||
regionName: string = CHORD_REGION_IMPORT_REGION_NAME,
|
||||
regionId?: string,
|
||||
) {
|
||||
super();
|
||||
this.trackId = trackId;
|
||||
this.trackIndex = trackIndex;
|
||||
this.startBeat = startBeat;
|
||||
this.lengthInBeats = lengthInBeats;
|
||||
this.notes = notes;
|
||||
this.regionId = regionId ?? generateUniqueId('KGMidiRegion');
|
||||
this.regionName = regionName;
|
||||
}
|
||||
|
||||
execute(): void {
|
||||
const track = KGCore.instance().getCurrentProject().getTracks().find(
|
||||
candidate => candidate.getId().toString() === this.trackId,
|
||||
);
|
||||
if (!track) {
|
||||
throw new Error(`Track ${this.trackId} not found`);
|
||||
}
|
||||
|
||||
this.createdRegion = new KGMidiRegion(
|
||||
this.regionId,
|
||||
this.trackId,
|
||||
this.trackIndex,
|
||||
this.regionName,
|
||||
this.startBeat,
|
||||
this.lengthInBeats,
|
||||
);
|
||||
|
||||
this.notes.forEach(noteData => {
|
||||
this.createdRegion?.addNote(new KGMidiNote(
|
||||
generateUniqueId('KGMidiNote'),
|
||||
noteData.startBeat,
|
||||
noteData.endBeat,
|
||||
noteData.pitch,
|
||||
noteData.velocity,
|
||||
));
|
||||
});
|
||||
|
||||
track.addRegion(this.createdRegion);
|
||||
}
|
||||
|
||||
undo(): void {
|
||||
const track = KGCore.instance().getCurrentProject().getTracks().find(
|
||||
candidate => candidate.getId().toString() === this.trackId,
|
||||
);
|
||||
if (!track) {
|
||||
throw new Error(`Track ${this.trackId} not found during undo`);
|
||||
}
|
||||
|
||||
track.removeRegion(this.regionId);
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return `Import chord progression "${this.regionName}"`;
|
||||
}
|
||||
|
||||
getCreatedRegion(): KGMidiRegion | null {
|
||||
return this.createdRegion;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user