fix: rename start_beat to start and end_beat to end in 3 tools.
This commit is contained in:
@@ -17,7 +17,7 @@ export class AddNotesTool extends BaseTool {
|
||||
readonly parameters: Record<string, ToolParameter> = {
|
||||
notes: {
|
||||
type: 'array',
|
||||
description: 'List of notes to add. To create a chord, give multiple notes the same start_beat. To create a melody, use sequential start_beat values.',
|
||||
description: 'List of notes to add. To create a chord, give multiple notes the same start beat. To create a melody, use sequential start values.',
|
||||
required: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
@@ -28,9 +28,9 @@ export class AddNotesTool extends BaseTool {
|
||||
description: 'Pitch in scientific notation: note name, optional accidental (# or b), and octave number. Examples: "C4" (middle C), "F#3" (F-sharp 3rd octave), "Bb2" (B-flat 2nd octave).',
|
||||
required: true
|
||||
},
|
||||
start_beat: {
|
||||
start: {
|
||||
type: 'number',
|
||||
description: 'Absolute beat position on the project timeline where the note starts. This is NOT relative to the region — beat 6 means beat 6 in the project regardless of where the region begins. Fractional values are supported (e.g., 0.5 = half a beat after beat 0).',
|
||||
description: 'Start beat — the absolute beat position on the project timeline where the note begins. This is NOT relative to the region — beat 6 means beat 6 in the project regardless of where the region begins. Fractional values are supported (e.g., 0.5 = half a beat after beat 0).',
|
||||
required: true
|
||||
},
|
||||
length: {
|
||||
@@ -60,7 +60,7 @@ export class AddNotesTool extends BaseTool {
|
||||
|
||||
const notes = params.notes as Array<{
|
||||
pitch: string;
|
||||
start_beat: number;
|
||||
start: number;
|
||||
length: number;
|
||||
velocity?: number;
|
||||
}>;
|
||||
@@ -79,7 +79,7 @@ export class AddNotesTool extends BaseTool {
|
||||
|
||||
// Validate and convert notes to creation data
|
||||
const noteCreationData: NoteCreationData[] = [];
|
||||
const createdNotes: Array<{ pitch: string; start_beat: number; length: number }> = [];
|
||||
const createdNotes: Array<{ pitch: string; start: number; length: number }> = [];
|
||||
|
||||
for (const note of notes) {
|
||||
try {
|
||||
@@ -92,8 +92,8 @@ export class AddNotesTool extends BaseTool {
|
||||
}
|
||||
|
||||
// Validate beat positions
|
||||
if (note.start_beat < 0) {
|
||||
return this.createErrorResult(`Invalid start_beat ${note.start_beat}. Must be >= 0.`);
|
||||
if (note.start < 0) {
|
||||
return this.createErrorResult(`Invalid start ${note.start}. Must be >= 0.`);
|
||||
}
|
||||
|
||||
if (note.length <= 0) {
|
||||
@@ -102,7 +102,7 @@ export class AddNotesTool extends BaseTool {
|
||||
|
||||
// Adjust note position relative to region's start beat
|
||||
const regionStartBeat = targetRegion.getStartFromBeat();
|
||||
const adjustedStartBeat = note.start_beat - regionStartBeat;
|
||||
const adjustedStartBeat = note.start - regionStartBeat;
|
||||
const adjustedEndBeat = adjustedStartBeat + note.length;
|
||||
|
||||
// Create note creation data
|
||||
@@ -116,7 +116,7 @@ export class AddNotesTool extends BaseTool {
|
||||
|
||||
createdNotes.push({
|
||||
pitch: note.pitch,
|
||||
start_beat: note.start_beat,
|
||||
start: note.start,
|
||||
length: note.length
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ export class AddNotesTool extends BaseTool {
|
||||
// Create success message
|
||||
const noteCount = createdNotes.length;
|
||||
const noteList = createdNotes
|
||||
.map(note => `${note.pitch} (beat ${note.start_beat}, length ${note.length})`)
|
||||
.map(note => `${note.pitch} (beat ${note.start}, length ${note.length})`)
|
||||
.join(', ');
|
||||
|
||||
return this.createSuccessResult(
|
||||
|
||||
@@ -20,9 +20,9 @@ export class ReadMusicTool extends BaseTool {
|
||||
description: 'Which track to read. Pass a specific track ID, or "all" to read every track. If omitted, reads the first available track.',
|
||||
required: false
|
||||
},
|
||||
start_beat: {
|
||||
start: {
|
||||
type: 'number',
|
||||
description: 'Absolute beat position to start reading from. The actual output will be rounded down to the nearest bar boundary. Defaults to 0.',
|
||||
description: 'Start beat — the absolute beat position to start reading from. The actual output will be rounded down to the nearest bar boundary. Defaults to 0.',
|
||||
required: false
|
||||
},
|
||||
length: {
|
||||
@@ -39,11 +39,11 @@ export class ReadMusicTool extends BaseTool {
|
||||
try {
|
||||
const instrument = track.getInstrument();
|
||||
const instrumentInfo = FLUIDR3_INSTRUMENT_MAP[instrument];
|
||||
|
||||
|
||||
if (instrumentInfo && instrumentInfo.group === 'PERCUSSION_KIT') {
|
||||
return instrumentInfo.displayName;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error getting percussion display name:', error);
|
||||
@@ -55,23 +55,23 @@ export class ReadMusicTool extends BaseTool {
|
||||
try {
|
||||
// Validate parameters
|
||||
this.validateParameters(params);
|
||||
|
||||
|
||||
const trackId = params.track_id as string | undefined;
|
||||
const startBeat = (params.start_beat as number) || 0;
|
||||
const startBeat = (params.start as number) || 0;
|
||||
const length = params.length as number | undefined;
|
||||
|
||||
|
||||
const project = this.getCurrentProject();
|
||||
const tracks = project.getTracks();
|
||||
|
||||
|
||||
if (tracks.length === 0) {
|
||||
return this.createErrorResult('No tracks found in the project');
|
||||
}
|
||||
|
||||
// Validate start_beat
|
||||
|
||||
// Validate start
|
||||
if (startBeat < 0) {
|
||||
return this.createErrorResult(`Invalid start_beat ${startBeat}. Must be >= 0.`);
|
||||
return this.createErrorResult(`Invalid start ${startBeat}. Must be >= 0.`);
|
||||
}
|
||||
|
||||
|
||||
// Validate length
|
||||
if (length !== undefined && length <= 0) {
|
||||
return this.createErrorResult(`Invalid length ${length}. Must be > 0.`);
|
||||
@@ -80,7 +80,7 @@ export class ReadMusicTool extends BaseTool {
|
||||
// Get project settings for bar rounding
|
||||
const timeSignature = project.getTimeSignature();
|
||||
const beatsPerBar = timeSignature.numerator;
|
||||
|
||||
|
||||
// Round startBeat to floor bar beats and calculate endBeat
|
||||
const roundedStartBeat = Math.floor(startBeat / beatsPerBar) * beatsPerBar;
|
||||
const rawEndBeat = length !== undefined ? startBeat + length : undefined;
|
||||
@@ -94,27 +94,27 @@ export class ReadMusicTool extends BaseTool {
|
||||
abcOutput = this.generateAllTracksABC(midiTracks, roundedStartBeat, roundedEndBeat);
|
||||
} else {
|
||||
// Read specific track or first available track
|
||||
const targetTrack = trackId
|
||||
const targetTrack = trackId
|
||||
? tracks.find(t => t.getId().toString() === trackId)
|
||||
: tracks[0];
|
||||
|
||||
|
||||
if (!targetTrack) {
|
||||
return this.createErrorResult(
|
||||
trackId
|
||||
trackId
|
||||
? `Track with ID "${trackId}" not found`
|
||||
: 'No tracks available'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!(targetTrack instanceof KGMidiTrack)) {
|
||||
return this.createErrorResult(`Track "${targetTrack.getName()}" is not a MIDI track`);
|
||||
}
|
||||
|
||||
|
||||
abcOutput = this.generateSingleTrackABC(targetTrack, roundedStartBeat, roundedEndBeat);
|
||||
}
|
||||
|
||||
|
||||
return this.createSuccessResult(abcOutput);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return this.createErrorResult(`Failed to read music: ${error}`);
|
||||
}
|
||||
@@ -127,16 +127,16 @@ export class ReadMusicTool extends BaseTool {
|
||||
private findTracksToSkip(tracks: KGMidiTrack[]): KGMidiTrack[] {
|
||||
try {
|
||||
const tracksToSkip: KGMidiTrack[] = [];
|
||||
|
||||
|
||||
for (const track of tracks) {
|
||||
const regions = track.getRegions();
|
||||
|
||||
|
||||
// Skip tracks with no regions
|
||||
if (regions.length === 0) {
|
||||
tracksToSkip.push(track);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Check if all regions in this track are empty (have no notes)
|
||||
const hasAnyNotes = regions.some(region => {
|
||||
if (region.getCurrentType() === 'KGMidiRegion') {
|
||||
@@ -144,42 +144,42 @@ export class ReadMusicTool extends BaseTool {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
// Skip tracks where no regions have notes
|
||||
if (!hasAnyNotes) {
|
||||
tracksToSkip.push(track);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return tracksToSkip;
|
||||
} catch (error) {
|
||||
console.error('Error finding tracks to skip:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generate ABC notation for all tracks
|
||||
*/
|
||||
private generateAllTracksABC(tracks: KGMidiTrack[], startBeat: number, endBeat?: number): string {
|
||||
const midiTracks = tracks.filter(track => track instanceof KGMidiTrack);
|
||||
|
||||
|
||||
if (midiTracks.length === 0) {
|
||||
return 'No MIDI tracks found in the project.';
|
||||
}
|
||||
|
||||
|
||||
// Find tracks to skip (tracks with no content)
|
||||
const tracksToSkip = this.findTracksToSkip(midiTracks);
|
||||
|
||||
|
||||
// Get project settings for proper notation
|
||||
const project = this.getCurrentProject();
|
||||
const timeSignature = project.getTimeSignature();
|
||||
const keySignature = project.getKeySignature();
|
||||
const abcKeySignature = KEY_SIGNATURE_MAP[keySignature]?.abcNotationKeySignature || 'C';
|
||||
|
||||
|
||||
let output = `All Tracks (beats ${startBeat}-${endBeat || 'end'}):\n\n`;
|
||||
|
||||
|
||||
midiTracks.forEach((track, index) => {
|
||||
// Skip tracks that have no musical content
|
||||
if (tracksToSkip.includes(track)) {
|
||||
@@ -187,10 +187,10 @@ export class ReadMusicTool extends BaseTool {
|
||||
}
|
||||
const trackNumber = index + 1;
|
||||
const trackName = track.getName() || `Track ${trackNumber}`;
|
||||
|
||||
|
||||
// Check if this track uses a percussion instrument
|
||||
const percussionDisplayName = this.getPercussionDisplayName(track);
|
||||
|
||||
|
||||
let displayTrackName: string;
|
||||
if (percussionDisplayName) {
|
||||
// Use percussion instrument display name for all percussion tracks
|
||||
@@ -202,12 +202,12 @@ export class ReadMusicTool extends BaseTool {
|
||||
// Use original track name for other non-percussion tracks
|
||||
displayTrackName = trackName;
|
||||
}
|
||||
|
||||
|
||||
output += `Track ${trackNumber} - ${displayTrackName}:\n`;
|
||||
|
||||
|
||||
// Get all regions from the track and convert each one
|
||||
const regions = track.getRegions().filter(region => region instanceof KGMidiRegion) as KGMidiRegion[];
|
||||
|
||||
|
||||
if (regions.length === 0) {
|
||||
output += 'X:' + trackNumber + '\n';
|
||||
output += `M:${timeSignature.numerator}/${timeSignature.denominator}\n`;
|
||||
@@ -219,11 +219,11 @@ export class ReadMusicTool extends BaseTool {
|
||||
regions.forEach((region) => {
|
||||
const regionStart = region.getStartFromBeat();
|
||||
const regionEnd = regionStart + region.getLength();
|
||||
|
||||
|
||||
// Check if region overlaps with requested range
|
||||
if (regionStart < (endBeat || Infinity) && regionEnd > startBeat) {
|
||||
const abcNotation = convertRegionToABCNotation(region, startBeat, endBeat);
|
||||
|
||||
|
||||
// Update the X: line to include track number
|
||||
const lines = abcNotation.split('\n');
|
||||
lines[0] = `X:${trackNumber}`;
|
||||
@@ -231,7 +231,7 @@ export class ReadMusicTool extends BaseTool {
|
||||
hasContent = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (!hasContent) {
|
||||
output += 'X:' + trackNumber + '\n';
|
||||
output += `M:${timeSignature.numerator}/${timeSignature.denominator}\n`;
|
||||
@@ -240,7 +240,7 @@ export class ReadMusicTool extends BaseTool {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return output.trim();
|
||||
}
|
||||
|
||||
@@ -251,20 +251,20 @@ export class ReadMusicTool extends BaseTool {
|
||||
if (!(track instanceof KGMidiTrack)) {
|
||||
return `Track is not a MIDI track.`;
|
||||
}
|
||||
|
||||
|
||||
// Get project settings for proper notation
|
||||
const project = this.getCurrentProject();
|
||||
const timeSignature = project.getTimeSignature();
|
||||
const keySignature = project.getKeySignature();
|
||||
const abcKeySignature = KEY_SIGNATURE_MAP[keySignature]?.abcNotationKeySignature || 'C';
|
||||
|
||||
|
||||
const trackName = track.getName() || 'Unnamed Track';
|
||||
|
||||
|
||||
let output = `Track "${trackName}" (beats ${startBeat}-${endBeat || 'end'}):\n`;
|
||||
|
||||
|
||||
// Get all regions from the track and convert each one
|
||||
const regions = track.getRegions().filter(region => region instanceof KGMidiRegion) as KGMidiRegion[];
|
||||
|
||||
|
||||
if (regions.length === 0) {
|
||||
output += 'X:1\n';
|
||||
output += `T:${trackName}\n`;
|
||||
@@ -278,11 +278,11 @@ export class ReadMusicTool extends BaseTool {
|
||||
regions.forEach((region) => {
|
||||
const regionStart = region.getStartFromBeat();
|
||||
const regionEnd = regionStart + region.getLength();
|
||||
|
||||
|
||||
// Check if region overlaps with requested range
|
||||
if (regionStart < (endBeat || Infinity) && regionEnd > startBeat) {
|
||||
const abcNotation = convertRegionToABCNotation(region, startBeat, endBeat);
|
||||
|
||||
|
||||
// Update the title to include track name
|
||||
const lines = abcNotation.split('\n');
|
||||
lines[1] = `T:${trackName}`;
|
||||
@@ -290,7 +290,7 @@ export class ReadMusicTool extends BaseTool {
|
||||
hasContent = true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (!hasContent) {
|
||||
output += 'X:1\n';
|
||||
output += `T:${trackName}\n`;
|
||||
@@ -300,7 +300,7 @@ export class ReadMusicTool extends BaseTool {
|
||||
output += 'z4 | // No content in specified range';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ export class RemoveNotesTool extends BaseTool {
|
||||
readonly description = 'Remove all MIDI notes whose start position falls within the specified beat range. Use this to clear a section before rewriting it, or to delete unwanted notes. Beat positions are absolute on the project timeline.';
|
||||
|
||||
readonly parameters: Record<string, ToolParameter> = {
|
||||
start_beat: {
|
||||
start: {
|
||||
type: 'number',
|
||||
description: 'Absolute beat position where the removal range begins (inclusive). A note starting at exactly this beat will be removed.',
|
||||
description: 'Start beat — the absolute beat position where the removal range begins (inclusive). A note starting at exactly this beat will be removed.',
|
||||
required: true
|
||||
},
|
||||
end_beat: {
|
||||
end: {
|
||||
type: 'number',
|
||||
description: 'Absolute beat position where the removal range ends (exclusive). A note starting at exactly this beat will NOT be removed. Must be greater than start_beat.',
|
||||
description: 'End beat — the absolute beat position where the removal range ends (exclusive). A note starting at exactly this beat will NOT be removed. Must be greater than start.',
|
||||
required: true
|
||||
},
|
||||
region_id: {
|
||||
@@ -36,17 +36,17 @@ export class RemoveNotesTool extends BaseTool {
|
||||
// Validate parameters
|
||||
this.validateParameters(params);
|
||||
|
||||
const startBeat = params.start_beat as number;
|
||||
const endBeat = params.end_beat as number;
|
||||
const startBeat = params.start as number;
|
||||
const endBeat = params.end as number;
|
||||
const regionId = params.region_id as string | undefined;
|
||||
|
||||
// Validate beat range
|
||||
if (startBeat < 0) {
|
||||
return this.createErrorResult(`Invalid start_beat ${startBeat}. Must be >= 0.`);
|
||||
return this.createErrorResult(`Invalid start ${startBeat}. Must be >= 0.`);
|
||||
}
|
||||
|
||||
|
||||
if (endBeat <= startBeat) {
|
||||
return this.createErrorResult(`Invalid beat range: end_beat (${endBeat}) must be greater than start_beat (${startBeat}).`);
|
||||
return this.createErrorResult(`Invalid beat range: end (${endBeat}) must be greater than start (${startBeat}).`);
|
||||
}
|
||||
|
||||
// Find the target region
|
||||
|
||||
Reference in New Issue
Block a user