musicxml-io 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/accessors/index.d.mts +82 -0
- package/dist/accessors/index.d.ts +82 -0
- package/dist/accessors/index.js +239 -0
- package/dist/accessors/index.js.map +1 -0
- package/dist/accessors/index.mjs +198 -0
- package/dist/accessors/index.mjs.map +1 -0
- package/dist/index.d.mts +285 -0
- package/dist/index.d.ts +285 -0
- package/dist/index.js +6033 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5932 -0
- package/dist/index.mjs.map +1 -0
- package/dist/operations/index.d.mts +92 -0
- package/dist/operations/index.d.ts +92 -0
- package/dist/operations/index.js +352 -0
- package/dist/operations/index.js.map +1 -0
- package/dist/operations/index.mjs +315 -0
- package/dist/operations/index.mjs.map +1 -0
- package/dist/query/index.d.mts +103 -0
- package/dist/query/index.d.ts +103 -0
- package/dist/query/index.js +272 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/index.mjs +232 -0
- package/dist/query/index.mjs.map +1 -0
- package/dist/types-DC_TnJu_.d.mts +797 -0
- package/dist/types-DC_TnJu_.d.ts +797 -0
- package/package.json +80 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// src/utils/index.ts
|
|
2
|
+
var STEPS = ["C", "D", "E", "F", "G", "A", "B"];
|
|
3
|
+
var STEP_SEMITONES = {
|
|
4
|
+
"C": 0,
|
|
5
|
+
"D": 2,
|
|
6
|
+
"E": 4,
|
|
7
|
+
"F": 5,
|
|
8
|
+
"G": 7,
|
|
9
|
+
"A": 9,
|
|
10
|
+
"B": 11
|
|
11
|
+
};
|
|
12
|
+
function createPositionState() {
|
|
13
|
+
return { position: 0, lastNonChordPosition: 0 };
|
|
14
|
+
}
|
|
15
|
+
function updatePositionForEntry(state, entry) {
|
|
16
|
+
const pos = state.position;
|
|
17
|
+
switch (entry.type) {
|
|
18
|
+
case "note": {
|
|
19
|
+
const note = entry;
|
|
20
|
+
if (!note.chord) {
|
|
21
|
+
state.lastNonChordPosition = state.position;
|
|
22
|
+
state.position += note.duration;
|
|
23
|
+
}
|
|
24
|
+
return note.chord ? state.lastNonChordPosition : pos;
|
|
25
|
+
}
|
|
26
|
+
case "backup":
|
|
27
|
+
state.position -= entry.duration;
|
|
28
|
+
state.lastNonChordPosition = state.position;
|
|
29
|
+
return pos;
|
|
30
|
+
case "forward":
|
|
31
|
+
state.position += entry.duration;
|
|
32
|
+
state.lastNonChordPosition = state.position;
|
|
33
|
+
return pos;
|
|
34
|
+
default:
|
|
35
|
+
return pos;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getMeasureEndPosition(measure) {
|
|
39
|
+
const state = createPositionState();
|
|
40
|
+
for (const entry of measure.entries) updatePositionForEntry(state, entry);
|
|
41
|
+
return state.position;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/operations/index.ts
|
|
45
|
+
function cloneScore(score) {
|
|
46
|
+
return JSON.parse(JSON.stringify(score));
|
|
47
|
+
}
|
|
48
|
+
function transposePitch(pitch, semitones) {
|
|
49
|
+
const currentSemitone = STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0) + pitch.octave * 12;
|
|
50
|
+
const targetSemitone = currentSemitone + semitones;
|
|
51
|
+
const targetOctave = Math.floor(targetSemitone / 12);
|
|
52
|
+
const targetPitchClass = (targetSemitone % 12 + 12) % 12;
|
|
53
|
+
let bestStep = "C";
|
|
54
|
+
let bestAlter = 99;
|
|
55
|
+
for (const step of STEPS) {
|
|
56
|
+
const stepSemitone = STEP_SEMITONES[step];
|
|
57
|
+
let diff = targetPitchClass - stepSemitone;
|
|
58
|
+
if (diff > 6) diff -= 12;
|
|
59
|
+
if (diff < -6) diff += 12;
|
|
60
|
+
if (diff >= -2 && diff <= 2) {
|
|
61
|
+
if (Math.abs(diff) < Math.abs(bestAlter)) {
|
|
62
|
+
bestStep = step;
|
|
63
|
+
bestAlter = diff;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
step: bestStep,
|
|
69
|
+
octave: targetOctave,
|
|
70
|
+
alter: bestAlter !== 0 ? bestAlter : void 0
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function transpose(score, semitones) {
|
|
74
|
+
if (semitones === 0) return score;
|
|
75
|
+
const result = cloneScore(score);
|
|
76
|
+
for (const part of result.parts) {
|
|
77
|
+
for (const measure of part.measures) {
|
|
78
|
+
for (const entry of measure.entries) {
|
|
79
|
+
if (entry.type === "note" && entry.pitch) {
|
|
80
|
+
entry.pitch = transposePitch(entry.pitch, semitones);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function addNote(score, options) {
|
|
88
|
+
const result = cloneScore(score);
|
|
89
|
+
const part = result.parts[options.partIndex];
|
|
90
|
+
if (!part) return result;
|
|
91
|
+
const measure = part.measures[options.measureIndex];
|
|
92
|
+
if (!measure) return result;
|
|
93
|
+
const newNote = {
|
|
94
|
+
type: "note",
|
|
95
|
+
voice: options.voice,
|
|
96
|
+
staff: options.staff,
|
|
97
|
+
...options.note
|
|
98
|
+
};
|
|
99
|
+
const currentPosition = getMeasureEndPosition(measure);
|
|
100
|
+
const positionDiff = options.position - currentPosition;
|
|
101
|
+
if (positionDiff < 0) {
|
|
102
|
+
measure.entries.push({
|
|
103
|
+
type: "backup",
|
|
104
|
+
duration: -positionDiff
|
|
105
|
+
});
|
|
106
|
+
} else if (positionDiff > 0) {
|
|
107
|
+
measure.entries.push({
|
|
108
|
+
type: "forward",
|
|
109
|
+
duration: positionDiff,
|
|
110
|
+
voice: options.voice,
|
|
111
|
+
staff: options.staff
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
measure.entries.push(newNote);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
function deleteNote(score, options) {
|
|
118
|
+
const result = cloneScore(score);
|
|
119
|
+
const part = result.parts[options.partIndex];
|
|
120
|
+
if (!part) return result;
|
|
121
|
+
const measure = part.measures[options.measureIndex];
|
|
122
|
+
if (!measure) return result;
|
|
123
|
+
let noteCount = 0;
|
|
124
|
+
let entryIndex = -1;
|
|
125
|
+
for (let i = 0; i < measure.entries.length; i++) {
|
|
126
|
+
if (measure.entries[i].type === "note") {
|
|
127
|
+
if (noteCount === options.noteIndex) {
|
|
128
|
+
entryIndex = i;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
noteCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (entryIndex !== -1) {
|
|
135
|
+
measure.entries.splice(entryIndex, 1);
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
function changeKey(score, key, options) {
|
|
140
|
+
const result = cloneScore(score);
|
|
141
|
+
const targetMeasure = String(options.fromMeasure);
|
|
142
|
+
for (const part of result.parts) {
|
|
143
|
+
for (const measure of part.measures) {
|
|
144
|
+
if (measure.number === targetMeasure) {
|
|
145
|
+
if (!measure.attributes) {
|
|
146
|
+
measure.attributes = {};
|
|
147
|
+
}
|
|
148
|
+
measure.attributes.key = key;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function changeTime(score, time, options) {
|
|
155
|
+
const result = cloneScore(score);
|
|
156
|
+
const targetMeasure = String(options.fromMeasure);
|
|
157
|
+
for (const part of result.parts) {
|
|
158
|
+
for (const measure of part.measures) {
|
|
159
|
+
if (measure.number === targetMeasure) {
|
|
160
|
+
if (!measure.attributes) {
|
|
161
|
+
measure.attributes = {};
|
|
162
|
+
}
|
|
163
|
+
measure.attributes.time = time;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
function insertMeasure(score, options) {
|
|
170
|
+
const result = cloneScore(score);
|
|
171
|
+
const targetMeasure = String(options.afterMeasure);
|
|
172
|
+
for (const part of result.parts) {
|
|
173
|
+
const insertIndex = part.measures.findIndex((m) => m.number === targetMeasure);
|
|
174
|
+
if (insertIndex === -1) continue;
|
|
175
|
+
const numericPart = parseInt(targetMeasure, 10);
|
|
176
|
+
const newMeasureNumber = String(isNaN(numericPart) ? insertIndex + 2 : numericPart + 1);
|
|
177
|
+
const newMeasure = {
|
|
178
|
+
number: newMeasureNumber,
|
|
179
|
+
entries: []
|
|
180
|
+
};
|
|
181
|
+
if (options.copyAttributes) {
|
|
182
|
+
const sourceMeasure = part.measures[insertIndex];
|
|
183
|
+
if (sourceMeasure.attributes) {
|
|
184
|
+
newMeasure.attributes = { ...sourceMeasure.attributes };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
part.measures.splice(insertIndex + 1, 0, newMeasure);
|
|
188
|
+
for (let i = insertIndex + 2; i < part.measures.length; i++) {
|
|
189
|
+
const currentNum = parseInt(part.measures[i].number, 10);
|
|
190
|
+
if (!isNaN(currentNum)) {
|
|
191
|
+
part.measures[i].number = String(currentNum + 1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
function deleteMeasure(score, measureNumber) {
|
|
198
|
+
const result = cloneScore(score);
|
|
199
|
+
const targetMeasure = String(measureNumber);
|
|
200
|
+
for (const part of result.parts) {
|
|
201
|
+
const deleteIndex = part.measures.findIndex((m) => m.number === targetMeasure);
|
|
202
|
+
if (deleteIndex === -1) continue;
|
|
203
|
+
part.measures.splice(deleteIndex, 1);
|
|
204
|
+
for (let i = deleteIndex; i < part.measures.length; i++) {
|
|
205
|
+
const currentNum = parseInt(part.measures[i].number, 10);
|
|
206
|
+
if (!isNaN(currentNum)) {
|
|
207
|
+
part.measures[i].number = String(currentNum - 1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
function setDivisions(score, options) {
|
|
214
|
+
const result = cloneScore(score);
|
|
215
|
+
const part = result.parts[options.partIndex];
|
|
216
|
+
if (!part) return result;
|
|
217
|
+
const measure = part.measures[options.measureIndex];
|
|
218
|
+
if (!measure) return result;
|
|
219
|
+
if (!measure.attributes) {
|
|
220
|
+
measure.attributes = {};
|
|
221
|
+
}
|
|
222
|
+
measure.attributes.divisions = options.divisions;
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
function addChordNote(score, options) {
|
|
226
|
+
const result = cloneScore(score);
|
|
227
|
+
const part = result.parts[options.partIndex];
|
|
228
|
+
if (!part) return result;
|
|
229
|
+
const measure = part.measures[options.measureIndex];
|
|
230
|
+
if (!measure) return result;
|
|
231
|
+
let noteCount = 0;
|
|
232
|
+
let entryIndex = -1;
|
|
233
|
+
let targetNote = null;
|
|
234
|
+
for (let i = 0; i < measure.entries.length; i++) {
|
|
235
|
+
const entry = measure.entries[i];
|
|
236
|
+
if (entry.type === "note") {
|
|
237
|
+
if (noteCount === options.afterNoteIndex) {
|
|
238
|
+
entryIndex = i;
|
|
239
|
+
targetNote = entry;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
noteCount++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (entryIndex !== -1 && targetNote) {
|
|
246
|
+
const chordNote = {
|
|
247
|
+
type: "note",
|
|
248
|
+
pitch: options.pitch,
|
|
249
|
+
duration: targetNote.duration,
|
|
250
|
+
voice: targetNote.voice,
|
|
251
|
+
staff: targetNote.staff,
|
|
252
|
+
chord: true,
|
|
253
|
+
noteType: targetNote.noteType,
|
|
254
|
+
dots: targetNote.dots
|
|
255
|
+
};
|
|
256
|
+
measure.entries.splice(entryIndex + 1, 0, chordNote);
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
function modifyNotePitch(score, options) {
|
|
261
|
+
const result = cloneScore(score);
|
|
262
|
+
const part = result.parts[options.partIndex];
|
|
263
|
+
if (!part) return result;
|
|
264
|
+
const measure = part.measures[options.measureIndex];
|
|
265
|
+
if (!measure) return result;
|
|
266
|
+
let noteCount = 0;
|
|
267
|
+
for (const entry of measure.entries) {
|
|
268
|
+
if (entry.type === "note") {
|
|
269
|
+
if (noteCount === options.noteIndex) {
|
|
270
|
+
entry.pitch = options.pitch;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
noteCount++;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
function modifyNoteDuration(score, options) {
|
|
279
|
+
const result = cloneScore(score);
|
|
280
|
+
const part = result.parts[options.partIndex];
|
|
281
|
+
if (!part) return result;
|
|
282
|
+
const measure = part.measures[options.measureIndex];
|
|
283
|
+
if (!measure) return result;
|
|
284
|
+
let noteCount = 0;
|
|
285
|
+
for (const entry of measure.entries) {
|
|
286
|
+
if (entry.type === "note") {
|
|
287
|
+
if (noteCount === options.noteIndex) {
|
|
288
|
+
entry.duration = options.duration;
|
|
289
|
+
if (options.noteType !== void 0) {
|
|
290
|
+
entry.noteType = options.noteType;
|
|
291
|
+
}
|
|
292
|
+
if (options.dots !== void 0) {
|
|
293
|
+
entry.dots = options.dots;
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
noteCount++;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
export {
|
|
303
|
+
addChordNote,
|
|
304
|
+
addNote,
|
|
305
|
+
changeKey,
|
|
306
|
+
changeTime,
|
|
307
|
+
deleteMeasure,
|
|
308
|
+
deleteNote,
|
|
309
|
+
insertMeasure,
|
|
310
|
+
modifyNoteDuration,
|
|
311
|
+
modifyNotePitch,
|
|
312
|
+
setDivisions,
|
|
313
|
+
transpose
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts","../../src/operations/index.ts"],"sourcesContent":["import type { Pitch, Measure, MeasureEntry, NoteEntry } from '../types';\n\n// Pitch constants\nexport const STEPS: Pitch['step'][] = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];\nexport const STEP_SEMITONES: Record<Pitch['step'], number> = {\n 'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11,\n};\n\n/** Convert pitch to semitone value (MIDI-like) */\nexport function pitchToSemitone(pitch: Pitch): number {\n return pitch.octave * 12 + STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0);\n}\n\n// Position tracking for measure iteration\nexport interface PositionState {\n position: number;\n lastNonChordPosition: number;\n}\n\nexport function createPositionState(): PositionState {\n return { position: 0, lastNonChordPosition: 0 };\n}\n\n/** Update position state for entry, returns position before update */\nexport function updatePositionForEntry(state: PositionState, entry: MeasureEntry): number {\n const pos = state.position;\n switch (entry.type) {\n case 'note': {\n const note = entry as NoteEntry;\n if (!note.chord) {\n state.lastNonChordPosition = state.position;\n state.position += note.duration;\n }\n return note.chord ? state.lastNonChordPosition : pos;\n }\n case 'backup':\n state.position -= entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n case 'forward':\n state.position += entry.duration;\n state.lastNonChordPosition = state.position;\n return pos;\n default:\n return pos;\n }\n}\n\n/** Get absolute position of a note within a measure */\nexport function getAbsolutePositionForNote(note: NoteEntry, measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) {\n if (entry === note) return entry.chord ? state.lastNonChordPosition : state.position;\n updatePositionForEntry(state, entry);\n }\n return state.position;\n}\n\n/** Get position at end of measure */\nexport function getMeasureEndPosition(measure: Measure): number {\n const state = createPositionState();\n for (const entry of measure.entries) updatePositionForEntry(state, entry);\n return state.position;\n}\n","import type {\n Score,\n Measure,\n NoteEntry,\n Pitch,\n KeySignature,\n TimeSignature,\n} from '../types';\nimport { STEPS, STEP_SEMITONES, getMeasureEndPosition } from '../utils';\n\n/**\n * Deep clone a score\n */\nfunction cloneScore(score: Score): Score {\n return JSON.parse(JSON.stringify(score));\n}\n\n/**\n * Transpose a pitch by a number of semitones\n */\nfunction transposePitch(pitch: Pitch, semitones: number): Pitch {\n const currentSemitone = STEP_SEMITONES[pitch.step] + (pitch.alter ?? 0) + pitch.octave * 12;\n const targetSemitone = currentSemitone + semitones;\n\n const targetOctave = Math.floor(targetSemitone / 12);\n const targetPitchClass = ((targetSemitone % 12) + 12) % 12;\n\n // Find the closest natural step with smallest alteration\n let bestStep: Pitch['step'] = 'C';\n let bestAlter = 99; // Start with large value so any real alter is smaller\n\n for (const step of STEPS) {\n const stepSemitone = STEP_SEMITONES[step];\n // Calculate the alteration needed to reach target from this step\n let diff = targetPitchClass - stepSemitone;\n\n // Normalize to range -6 to +6 for smallest alteration\n if (diff > 6) diff -= 12;\n if (diff < -6) diff += 12;\n\n // Only consider alterations within -2 to +2 (double flat to double sharp)\n if (diff >= -2 && diff <= 2) {\n if (Math.abs(diff) < Math.abs(bestAlter)) {\n bestStep = step;\n bestAlter = diff;\n }\n }\n }\n\n return {\n step: bestStep,\n octave: targetOctave,\n alter: bestAlter !== 0 ? bestAlter : undefined,\n };\n}\n\n/**\n * Transpose all notes in a score by a number of semitones\n */\nexport function transpose(score: Score, semitones: number): Score {\n if (semitones === 0) return score;\n\n const result = cloneScore(score);\n\n for (const part of result.parts) {\n for (const measure of part.measures) {\n for (const entry of measure.entries) {\n if (entry.type === 'note' && entry.pitch) {\n entry.pitch = transposePitch(entry.pitch, semitones);\n }\n }\n }\n }\n\n return result;\n}\n\n/**\n * Options for adding a note\n */\nexport interface AddNoteOptions {\n partIndex: number;\n measureIndex: number;\n staff?: number;\n voice: number;\n position: number;\n note: Omit<NoteEntry, 'type' | 'voice' | 'staff'>;\n}\n\n/**\n * Add a note to a measure\n * Automatically handles backup/forward insertion\n */\nexport function addNote(score: Score, options: AddNoteOptions): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n const newNote: NoteEntry = {\n type: 'note',\n voice: options.voice,\n staff: options.staff,\n ...options.note,\n };\n\n // Find the current position at the end of the measure using shared utility\n const currentPosition = getMeasureEndPosition(measure);\n\n // Calculate backup/forward needed\n const positionDiff = options.position - currentPosition;\n\n if (positionDiff < 0) {\n // Need to backup\n measure.entries.push({\n type: 'backup',\n duration: -positionDiff,\n });\n } else if (positionDiff > 0) {\n // Need to forward\n measure.entries.push({\n type: 'forward',\n duration: positionDiff,\n voice: options.voice,\n staff: options.staff,\n });\n }\n\n measure.entries.push(newNote);\n\n return result;\n}\n\n/**\n * Delete a note from a measure\n */\nexport function deleteNote(score: Score, options: {\n partIndex: number;\n measureIndex: number;\n noteIndex: number;\n}): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n // Find the note by counting only notes\n let noteCount = 0;\n let entryIndex = -1;\n\n for (let i = 0; i < measure.entries.length; i++) {\n if (measure.entries[i].type === 'note') {\n if (noteCount === options.noteIndex) {\n entryIndex = i;\n break;\n }\n noteCount++;\n }\n }\n\n if (entryIndex !== -1) {\n measure.entries.splice(entryIndex, 1);\n }\n\n return result;\n}\n\n/**\n * Change key signature from a specific measure\n */\nexport function changeKey(\n score: Score,\n key: KeySignature,\n options: { fromMeasure: string | number }\n): Score {\n const result = cloneScore(score);\n const targetMeasure = String(options.fromMeasure);\n\n for (const part of result.parts) {\n for (const measure of part.measures) {\n if (measure.number === targetMeasure) {\n if (!measure.attributes) {\n measure.attributes = {};\n }\n measure.attributes.key = key;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Change time signature from a specific measure\n */\nexport function changeTime(\n score: Score,\n time: TimeSignature,\n options: { fromMeasure: string | number }\n): Score {\n const result = cloneScore(score);\n const targetMeasure = String(options.fromMeasure);\n\n for (const part of result.parts) {\n for (const measure of part.measures) {\n if (measure.number === targetMeasure) {\n if (!measure.attributes) {\n measure.attributes = {};\n }\n measure.attributes.time = time;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Insert a new measure after a specific measure number\n */\nexport function insertMeasure(\n score: Score,\n options: {\n afterMeasure: string | number;\n copyAttributes?: boolean;\n }\n): Score {\n const result = cloneScore(score);\n const targetMeasure = String(options.afterMeasure);\n\n for (const part of result.parts) {\n const insertIndex = part.measures.findIndex((m) => m.number === targetMeasure);\n if (insertIndex === -1) continue;\n\n // Parse target measure number and increment for new measure\n const numericPart = parseInt(targetMeasure, 10);\n const newMeasureNumber = String(isNaN(numericPart) ? insertIndex + 2 : numericPart + 1);\n\n // Create new empty measure\n const newMeasure: Measure = {\n number: newMeasureNumber,\n entries: [],\n };\n\n // Copy attributes if requested\n if (options.copyAttributes) {\n const sourceMeasure = part.measures[insertIndex];\n if (sourceMeasure.attributes) {\n newMeasure.attributes = { ...sourceMeasure.attributes };\n }\n }\n\n // Insert the new measure\n part.measures.splice(insertIndex + 1, 0, newMeasure);\n\n // Update measure numbers for subsequent measures\n for (let i = insertIndex + 2; i < part.measures.length; i++) {\n const currentNum = parseInt(part.measures[i].number, 10);\n if (!isNaN(currentNum)) {\n part.measures[i].number = String(currentNum + 1);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Delete a measure\n */\nexport function deleteMeasure(score: Score, measureNumber: string | number): Score {\n const result = cloneScore(score);\n const targetMeasure = String(measureNumber);\n\n for (const part of result.parts) {\n const deleteIndex = part.measures.findIndex((m) => m.number === targetMeasure);\n if (deleteIndex === -1) continue;\n\n // Remove the measure\n part.measures.splice(deleteIndex, 1);\n\n // Update measure numbers for subsequent measures\n for (let i = deleteIndex; i < part.measures.length; i++) {\n const currentNum = parseInt(part.measures[i].number, 10);\n if (!isNaN(currentNum)) {\n part.measures[i].number = String(currentNum - 1);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Set divisions for a measure\n */\nexport function setDivisions(\n score: Score,\n options: {\n partIndex: number;\n measureIndex: number;\n divisions: number;\n }\n): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n if (!measure.attributes) {\n measure.attributes = {};\n }\n measure.attributes.divisions = options.divisions;\n\n return result;\n}\n\n/**\n * Add a chord note (note that sounds simultaneously with the previous note)\n */\nexport function addChordNote(\n score: Score,\n options: {\n partIndex: number;\n measureIndex: number;\n afterNoteIndex: number;\n pitch: Pitch;\n }\n): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n // Find the note by counting only notes\n let noteCount = 0;\n let entryIndex = -1;\n let targetNote: NoteEntry | null = null;\n\n for (let i = 0; i < measure.entries.length; i++) {\n const entry = measure.entries[i];\n if (entry.type === 'note') {\n if (noteCount === options.afterNoteIndex) {\n entryIndex = i;\n targetNote = entry;\n break;\n }\n noteCount++;\n }\n }\n\n if (entryIndex !== -1 && targetNote) {\n const chordNote: NoteEntry = {\n type: 'note',\n pitch: options.pitch,\n duration: targetNote.duration,\n voice: targetNote.voice,\n staff: targetNote.staff,\n chord: true,\n noteType: targetNote.noteType,\n dots: targetNote.dots,\n };\n\n measure.entries.splice(entryIndex + 1, 0, chordNote);\n }\n\n return result;\n}\n\n/**\n * Modify a note's pitch\n */\nexport function modifyNotePitch(\n score: Score,\n options: {\n partIndex: number;\n measureIndex: number;\n noteIndex: number;\n pitch: Pitch;\n }\n): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n // Find the note by counting only notes\n let noteCount = 0;\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n if (noteCount === options.noteIndex) {\n entry.pitch = options.pitch;\n break;\n }\n noteCount++;\n }\n }\n\n return result;\n}\n\n/**\n * Modify a note's duration\n */\nexport function modifyNoteDuration(\n score: Score,\n options: {\n partIndex: number;\n measureIndex: number;\n noteIndex: number;\n duration: number;\n noteType?: NoteEntry['noteType'];\n dots?: number;\n }\n): Score {\n const result = cloneScore(score);\n const part = result.parts[options.partIndex];\n if (!part) return result;\n\n const measure = part.measures[options.measureIndex];\n if (!measure) return result;\n\n // Find the note by counting only notes\n let noteCount = 0;\n\n for (const entry of measure.entries) {\n if (entry.type === 'note') {\n if (noteCount === options.noteIndex) {\n entry.duration = options.duration;\n if (options.noteType !== undefined) {\n entry.noteType = options.noteType;\n }\n if (options.dots !== undefined) {\n entry.dots = options.dots;\n }\n break;\n }\n noteCount++;\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { S as Score, M as Measure, f as MeasureAttributes, a as Pitch, N as NoteEntry, P as Part } from '../types-DC_TnJu_.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get a specific measure from the score
|
|
5
|
+
*/
|
|
6
|
+
declare function getMeasure(score: Score, options: {
|
|
7
|
+
part: number;
|
|
8
|
+
measure: string | number;
|
|
9
|
+
}): Measure | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Get measure by index
|
|
12
|
+
*/
|
|
13
|
+
declare function getMeasureByIndex(score: Score, options: {
|
|
14
|
+
part: number;
|
|
15
|
+
measureIndex: number;
|
|
16
|
+
}): Measure | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Get the total number of measures in a score
|
|
19
|
+
*/
|
|
20
|
+
declare function getMeasureCount(score: Score): number;
|
|
21
|
+
/**
|
|
22
|
+
* Get the divisions value at a specific measure
|
|
23
|
+
* Searches backwards from the specified measure to find the most recent divisions
|
|
24
|
+
*/
|
|
25
|
+
declare function getDivisions(score: Score, options: {
|
|
26
|
+
part: number;
|
|
27
|
+
measure: string | number;
|
|
28
|
+
}): number;
|
|
29
|
+
/**
|
|
30
|
+
* Get the current attributes at a specific measure
|
|
31
|
+
* Merges all attribute changes from measure 1 to the specified measure
|
|
32
|
+
*/
|
|
33
|
+
declare function getAttributesAtMeasure(score: Score, options: {
|
|
34
|
+
part: number;
|
|
35
|
+
measure: string | number;
|
|
36
|
+
}): MeasureAttributes;
|
|
37
|
+
/**
|
|
38
|
+
* Pitch range filter
|
|
39
|
+
*/
|
|
40
|
+
interface PitchRange {
|
|
41
|
+
min?: Pitch;
|
|
42
|
+
max?: Pitch;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Find notes filter
|
|
46
|
+
*/
|
|
47
|
+
interface FindNotesFilter {
|
|
48
|
+
pitchRange?: PitchRange;
|
|
49
|
+
voice?: number;
|
|
50
|
+
staff?: number;
|
|
51
|
+
noteType?: string;
|
|
52
|
+
hasTie?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Find notes matching specific criteria
|
|
56
|
+
*/
|
|
57
|
+
declare function findNotes(score: Score, filter: FindNotesFilter): NoteEntry[];
|
|
58
|
+
/**
|
|
59
|
+
* Get the total duration of the score in divisions
|
|
60
|
+
*/
|
|
61
|
+
declare function getDuration(score: Score): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get part by ID
|
|
64
|
+
*/
|
|
65
|
+
declare function getPartById(score: Score, id: string): Part | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Get part index by ID
|
|
68
|
+
*/
|
|
69
|
+
declare function getPartIndex(score: Score, id: string): number;
|
|
70
|
+
/**
|
|
71
|
+
* Check if the score has multiple staves (e.g., piano grand staff)
|
|
72
|
+
*/
|
|
73
|
+
declare function hasMultipleStaves(score: Score, partIndex?: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Get the number of staves for a part
|
|
76
|
+
*/
|
|
77
|
+
declare function getStaveCount(score: Score, partIndex?: number): number;
|
|
78
|
+
/**
|
|
79
|
+
* Round-trip metrics for measuring preservation fidelity
|
|
80
|
+
*/
|
|
81
|
+
interface RoundtripMetrics {
|
|
82
|
+
notesOriginal: number;
|
|
83
|
+
notesPreserved: number;
|
|
84
|
+
measuresOriginal: number;
|
|
85
|
+
measuresPreserved: number;
|
|
86
|
+
partsOriginal: number;
|
|
87
|
+
partsPreserved: number;
|
|
88
|
+
preservationRate: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Compare two scores and calculate round-trip preservation metrics
|
|
92
|
+
*/
|
|
93
|
+
declare function measureRoundtrip(original: Score, exported: Score): RoundtripMetrics;
|
|
94
|
+
/**
|
|
95
|
+
* Count total notes in a score
|
|
96
|
+
*/
|
|
97
|
+
declare function countNotes(score: Score): number;
|
|
98
|
+
/**
|
|
99
|
+
* Compare two scores for structural equality
|
|
100
|
+
*/
|
|
101
|
+
declare function scoresEqual(a: Score, b: Score): boolean;
|
|
102
|
+
|
|
103
|
+
export { type FindNotesFilter, type PitchRange, type RoundtripMetrics, countNotes, findNotes, getAttributesAtMeasure, getDivisions, getDuration, getMeasure, getMeasureByIndex, getMeasureCount, getPartById, getPartIndex, getStaveCount, hasMultipleStaves, measureRoundtrip, scoresEqual };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { S as Score, M as Measure, f as MeasureAttributes, a as Pitch, N as NoteEntry, P as Part } from '../types-DC_TnJu_.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get a specific measure from the score
|
|
5
|
+
*/
|
|
6
|
+
declare function getMeasure(score: Score, options: {
|
|
7
|
+
part: number;
|
|
8
|
+
measure: string | number;
|
|
9
|
+
}): Measure | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Get measure by index
|
|
12
|
+
*/
|
|
13
|
+
declare function getMeasureByIndex(score: Score, options: {
|
|
14
|
+
part: number;
|
|
15
|
+
measureIndex: number;
|
|
16
|
+
}): Measure | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Get the total number of measures in a score
|
|
19
|
+
*/
|
|
20
|
+
declare function getMeasureCount(score: Score): number;
|
|
21
|
+
/**
|
|
22
|
+
* Get the divisions value at a specific measure
|
|
23
|
+
* Searches backwards from the specified measure to find the most recent divisions
|
|
24
|
+
*/
|
|
25
|
+
declare function getDivisions(score: Score, options: {
|
|
26
|
+
part: number;
|
|
27
|
+
measure: string | number;
|
|
28
|
+
}): number;
|
|
29
|
+
/**
|
|
30
|
+
* Get the current attributes at a specific measure
|
|
31
|
+
* Merges all attribute changes from measure 1 to the specified measure
|
|
32
|
+
*/
|
|
33
|
+
declare function getAttributesAtMeasure(score: Score, options: {
|
|
34
|
+
part: number;
|
|
35
|
+
measure: string | number;
|
|
36
|
+
}): MeasureAttributes;
|
|
37
|
+
/**
|
|
38
|
+
* Pitch range filter
|
|
39
|
+
*/
|
|
40
|
+
interface PitchRange {
|
|
41
|
+
min?: Pitch;
|
|
42
|
+
max?: Pitch;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Find notes filter
|
|
46
|
+
*/
|
|
47
|
+
interface FindNotesFilter {
|
|
48
|
+
pitchRange?: PitchRange;
|
|
49
|
+
voice?: number;
|
|
50
|
+
staff?: number;
|
|
51
|
+
noteType?: string;
|
|
52
|
+
hasTie?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Find notes matching specific criteria
|
|
56
|
+
*/
|
|
57
|
+
declare function findNotes(score: Score, filter: FindNotesFilter): NoteEntry[];
|
|
58
|
+
/**
|
|
59
|
+
* Get the total duration of the score in divisions
|
|
60
|
+
*/
|
|
61
|
+
declare function getDuration(score: Score): number;
|
|
62
|
+
/**
|
|
63
|
+
* Get part by ID
|
|
64
|
+
*/
|
|
65
|
+
declare function getPartById(score: Score, id: string): Part | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Get part index by ID
|
|
68
|
+
*/
|
|
69
|
+
declare function getPartIndex(score: Score, id: string): number;
|
|
70
|
+
/**
|
|
71
|
+
* Check if the score has multiple staves (e.g., piano grand staff)
|
|
72
|
+
*/
|
|
73
|
+
declare function hasMultipleStaves(score: Score, partIndex?: number): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Get the number of staves for a part
|
|
76
|
+
*/
|
|
77
|
+
declare function getStaveCount(score: Score, partIndex?: number): number;
|
|
78
|
+
/**
|
|
79
|
+
* Round-trip metrics for measuring preservation fidelity
|
|
80
|
+
*/
|
|
81
|
+
interface RoundtripMetrics {
|
|
82
|
+
notesOriginal: number;
|
|
83
|
+
notesPreserved: number;
|
|
84
|
+
measuresOriginal: number;
|
|
85
|
+
measuresPreserved: number;
|
|
86
|
+
partsOriginal: number;
|
|
87
|
+
partsPreserved: number;
|
|
88
|
+
preservationRate: number;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Compare two scores and calculate round-trip preservation metrics
|
|
92
|
+
*/
|
|
93
|
+
declare function measureRoundtrip(original: Score, exported: Score): RoundtripMetrics;
|
|
94
|
+
/**
|
|
95
|
+
* Count total notes in a score
|
|
96
|
+
*/
|
|
97
|
+
declare function countNotes(score: Score): number;
|
|
98
|
+
/**
|
|
99
|
+
* Compare two scores for structural equality
|
|
100
|
+
*/
|
|
101
|
+
declare function scoresEqual(a: Score, b: Score): boolean;
|
|
102
|
+
|
|
103
|
+
export { type FindNotesFilter, type PitchRange, type RoundtripMetrics, countNotes, findNotes, getAttributesAtMeasure, getDivisions, getDuration, getMeasure, getMeasureByIndex, getMeasureCount, getPartById, getPartIndex, getStaveCount, hasMultipleStaves, measureRoundtrip, scoresEqual };
|