UNPKG

@xiaolaa2/ableton-copilot-mcp

Version:
296 lines (295 loc) 11.7 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { tool } from '../mcp/decorators/tool.js'; import { z } from 'zod'; import { NOTE, ClipSettableProp, ClipGettableProp, NOTE_EXTENED } from '../types/zod-types.js'; import { batchModifyClipProp, getClipProps, NoteToNoteExtended } from '../utils/obj-utils.js'; import { Result } from '../utils/common.js'; import { getClipById } from '../utils/obj-utils.js'; import { createNoteSnapshot, getNotes, removeNotesExtended, replaceClipNotesExtended } from '../utils/clip-utils.js'; import { ableton } from '../ableton.js'; const NOTE_FIELD_ABBREVIATIONS = { note_id: 'id', pitch: 'p', start_time: 't', duration: 'd', velocity: 'v', mute: 'm', probability: 'pr', release_velocity: 'rv', velocity_deviation: 'vd' }; function abbreviateNoteFields(note) { const result = {}; for (const [key, value] of Object.entries(note)) { const abbrevKey = NOTE_FIELD_ABBREVIATIONS[key] || key; result[abbrevKey] = value; } return result; } class ClipTools { async getClipInfoById({ clip_id, properties }) { const clip = getClipById(clip_id); return await getClipProps(clip, properties); } async getClipNotes({ clip_id, from_pitch, from_time, time_span, pitch_span }) { const clip = getClipById(clip_id); const notes = await getNotes(clip, from_pitch, pitch_span, from_time, time_span); const abbreviatedNotes = notes.map(abbreviateNoteFields); return Result.data(abbreviatedNotes); } async removeClipNotes({ clip_id, from_pitch, pitch_span, from_time, time_span, historyId }) { const clip = getClipById(clip_id); await createNoteSnapshot(clip, historyId); await removeNotesExtended(clip, from_pitch, pitch_span, from_time, time_span); return Result.ok(); } async removeClipNotesById({ clip_id, note_ids, historyId }) { const clip = getClipById(clip_id); await createNoteSnapshot(clip, historyId); await clip.removeNotesById(note_ids); return Result.ok(); } async addClipNotes({ notes, clip_id, historyId }) { const clip = getClipById(clip_id); await createNoteSnapshot(clip, historyId); await clip.setNotes(notes); return Result.ok(); } async modifyClipNotes({ notes, clip_id, historyId }) { const clip = getClipById(clip_id); await createNoteSnapshot(clip, historyId); await clip.applyNoteModifications(notes); return Result.ok(); } async replaceClipNotes({ notes, clip_id, historyId, notes_extended }) { const clip = getClipById(clip_id); await createNoteSnapshot(clip, historyId); const liveVersion = await ableton.application.get('major_version', true); if (liveVersion >= 11) { let noteToSet = []; // Live 11+ supports extended notes and setNotesExtended if (notes_extended && notes_extended.length > 0) { noteToSet = notes_extended; } else { // Convert basic notes to extended format for Live 11+ noteToSet = notes.map(note => NoteToNoteExtended(note)); } await replaceClipNotesExtended(clip, noteToSet); } else { if (notes_extended && notes_extended.length > 0) { throw new Error('Live 10 and below does not support extended notes'); } // Live 10 and below: use legacy API await clip.selectAllNotes(); await clip.replaceSelectedNotes(notes); } return Result.ok(); } async setClipProperty({ clips }) { await batchModifyClipProp(clips); return Result.ok(); } async cropClip({ clip_id }) { const clip = getClipById(clip_id); await clip.crop(); return Result.ok(); } async duplicateLoop({ clip_id }) { const clip = getClipById(clip_id); await clip.duplicateLoop(); return Result.ok(); } async duplicateRegion({ clip_id, region_start, region_end, destination_time, pitch, transposition_amount }) { const clip = getClipById(clip_id); await clip.duplicateRegion(region_start, region_end, destination_time, pitch, transposition_amount); return Result.ok(); } } __decorate([ tool({ name: 'get_clip_properties', description: 'Get clip properties by clip id. To get specific properties, set the corresponding property name to true in the properties parameter.', paramsSchema: { clip_id: z.string(), properties: ClipGettableProp, } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "getClipInfoById", null); __decorate([ tool({ name: 'get_clip_notes', description: `Get clip notes by clip id. Returns NoteExtended array for Live 11+ and Note array for Live 10 and below. Field abbreviations in returned data: - id: note_id - p: pitch (MIDI note 0-127, 60=C3) - t: start_time (beats) - d: duration (beats) - v: velocity (0-127) - m: mute (boolean) - pr: probability (0-1, Live 11+) - rv: release_velocity (Live 11+) - vd: velocity_deviation (Live 11+)`, paramsSchema: { clip_id: z.string(), from_pitch: z.number().min(0).max(127), from_time: z.number(), time_span: z.number(), pitch_span: z.number(), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "getClipNotes", null); __decorate([ tool({ name: 'remove_clip_notes', description: 'Remove clip notes by clip id', enableSnapshot: true, paramsSchema: { clip_id: z.string(), from_pitch: z.number().min(0).max(127), pitch_span: z.number().describe('The number of semitones to remove. Must be a value greater than 0.'), from_time: z.number(), time_span: z.number().describe('The number of beats to remove. Must be a value greater than 0.'), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "removeClipNotes", null); __decorate([ tool({ name: 'remove_notes_by_ids', description: 'Remove notes by clip id and note ids', enableSnapshot: true, paramsSchema: { clip_id: z.string(), note_ids: z.array(z.number()).describe('note ids, get from get_clip_notes'), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "removeClipNotesById", null); __decorate([ tool({ name: 'add_notes_to_clip', description: 'Add notes to clip by clip id', enableSnapshot: true, paramsSchema: { notes: z.array(NOTE).describe('[array] the notes to add.'), clip_id: z.string() } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "addClipNotes", null); __decorate([ tool({ name: 'modify_clip_notes', description: 'Modify clip notes by clip id', enableSnapshot: true, paramsSchema: { notes: z.array(NOTE_EXTENED).describe('[array] the notes to modify.'), clip_id: z.string() } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "modifyClipNotes", null); __decorate([ tool({ name: 'replace_clip_notes', description: 'Replace all notes in the clip with new notes', enableSnapshot: true, paramsSchema: { clip_id: z.string(), notes: z.array(NOTE).optional().describe('[array] The new notes to replace existing notes with'), notes_extended: z.array(NOTE_EXTENED).optional().describe('[array] Extended note data (Live 11+ only)') } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "replaceClipNotes", null); __decorate([ tool({ name: 'set_clips_property', description: 'batch set clip property', paramsSchema: { clips: z.array(z.object({ clip_id: z.string(), property: ClipSettableProp, })) } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "setClipProperty", null); __decorate([ tool({ name: 'crop_clip', description: `Crops the clip. The region that is cropped depends on whether the clip is looped or not. If looped, the region outside of the loop is removed. If not looped, the region outside the start and end markers is removed.`, paramsSchema: { clip_id: z.string(), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "cropClip", null); __decorate([ tool({ name: 'duplicate_clip_loop', description: `Makes the loop twice as long and duplicates notes and envelopes. Duplicates the clip start/end range if the clip is not looped.`, paramsSchema: { clip_id: z.string(), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "duplicateLoop", null); __decorate([ tool({ name: 'duplicate_clip_region', description: `Duplicates the notes in the specified region to the destination_time. Only notes of the specified pitch are duplicated if pitch is not -1. If the transposition_amount is not 0, the notes in the region will be transposed by the transposition_amount of semitones. Raises an error on audio clips..`, paramsSchema: { clip_id: z.string(), region_start: z.number(), region_end: z.number(), destination_time: z.number(), pitch: z.number(), transposition_amount: z.number(), } }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], ClipTools.prototype, "duplicateRegion", null); export default ClipTools; //# sourceMappingURL=clip-tools.js.map