@scorelabs/core 1.0.7 → 1.0.9
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/dist/importers/MusicXMLParser.d.ts +2 -2
- package/dist/importers/MusicXMLParser.js +75 -17
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/models/Instrument.d.ts +8 -19
- package/dist/models/Instrument.js +77 -23
- package/dist/models/Measure.d.ts +2 -0
- package/dist/models/Measure.js +6 -0
- package/dist/models/NoteSet.d.ts +40 -38
- package/dist/models/NoteSet.js +5 -2
- package/dist/models/Pitch.js +4 -0
- package/dist/models/PreMeasure.d.ts +24 -0
- package/dist/models/PreMeasure.js +30 -0
- package/dist/models/Score.d.ts +16 -1
- package/dist/models/Score.js +190 -5
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/types.d.ts +2 -213
- package/dist/models/types.js +2 -213
- package/dist/types/Accidental.d.ts +7 -0
- package/dist/types/Accidental.js +8 -0
- package/dist/types/Arpeggio.d.ts +5 -0
- package/dist/types/Arpeggio.js +6 -0
- package/dist/types/Articulation.d.ts +10 -0
- package/dist/types/Articulation.js +11 -0
- package/dist/types/BarlineStyle.d.ts +9 -0
- package/dist/types/BarlineStyle.js +10 -0
- package/dist/types/Bowing.d.ts +4 -0
- package/dist/types/Bowing.js +5 -0
- package/dist/types/Clef.d.ts +10 -0
- package/dist/types/Clef.js +11 -0
- package/dist/types/Duration.d.ts +20 -0
- package/dist/types/Duration.js +60 -0
- package/dist/types/Dynamic.d.ts +12 -0
- package/dist/types/Dynamic.js +13 -0
- package/dist/types/Fretboard.d.ts +19 -0
- package/dist/types/Fretboard.js +1 -0
- package/dist/types/Genre.d.ts +27 -0
- package/dist/types/Genre.js +28 -0
- package/dist/types/Glissando.d.ts +8 -0
- package/dist/types/Glissando.js +5 -0
- package/dist/types/Hairpin.d.ts +10 -0
- package/dist/types/Hairpin.js +11 -0
- package/dist/types/InstrumentPreset.d.ts +11 -0
- package/dist/types/InstrumentPreset.js +12 -0
- package/dist/types/InstrumentType.d.ts +8 -0
- package/dist/types/InstrumentType.js +9 -0
- package/dist/types/KeySignature.d.ts +3 -0
- package/dist/types/KeySignature.js +1 -0
- package/dist/types/Lyric.d.ts +11 -0
- package/dist/types/Lyric.js +7 -0
- package/dist/types/NoteheadShape.d.ts +8 -0
- package/dist/types/NoteheadShape.js +9 -0
- package/dist/types/Ornament.d.ts +8 -0
- package/dist/types/Ornament.js +9 -0
- package/dist/types/Ottava.d.ts +10 -0
- package/dist/types/Ottava.js +7 -0
- package/dist/types/Pedal.d.ts +4 -0
- package/dist/types/Pedal.js +1 -0
- package/dist/types/Repeat.d.ts +8 -0
- package/dist/types/Repeat.js +1 -0
- package/dist/types/Slur.d.ts +4 -0
- package/dist/types/Slur.js +1 -0
- package/dist/types/StemDirection.d.ts +4 -0
- package/dist/types/StemDirection.js +5 -0
- package/dist/types/Tempo.d.ts +8 -0
- package/dist/types/Tempo.js +1 -0
- package/dist/types/TimeSignature.d.ts +6 -0
- package/dist/types/TimeSignature.js +3 -0
- package/dist/types/Tuplet.d.ts +5 -0
- package/dist/types/Tuplet.js +1 -0
- package/dist/types/index.d.ts +26 -0
- package/dist/types/index.js +26 -0
- package/package.json +1 -1
|
@@ -12,9 +12,9 @@ export declare class MusicXMLParser {
|
|
|
12
12
|
private currentSymbol;
|
|
13
13
|
private instrumentPitchMap;
|
|
14
14
|
private _domParser;
|
|
15
|
-
constructor(domParserInstance?: DOMParser |
|
|
15
|
+
constructor(domParserInstance?: DOMParser | unknown);
|
|
16
16
|
private parseFromString;
|
|
17
|
-
static getXMLFromBinary(data: ArrayBuffer, domParser?: DOMParser |
|
|
17
|
+
static getXMLFromBinary(data: ArrayBuffer, domParser?: DOMParser | unknown): Promise<string>;
|
|
18
18
|
parseBinary(data: ArrayBuffer): Promise<ScoreJSON>;
|
|
19
19
|
parse(xmlString: string): ScoreJSON;
|
|
20
20
|
private parseSubtitle;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import JSZip from 'jszip';
|
|
2
|
-
import { getInstrumentByProgram } from '../models/Instrument';
|
|
3
|
-
import { Accidental, Arpeggio, Articulation, BarlineStyle, Bowing, Clef, Duration, GlissandoType, HairpinType, NoteheadShape, Ornament, OttavaType, StemDirection, } from '../models/types';
|
|
2
|
+
import { getInstrumentByProgram, guessInstrumentByName } from '../models/Instrument';
|
|
3
|
+
import { Accidental, Arpeggio, Articulation, BarlineStyle, Bowing, Clef, DURATION_VALUES, Duration, GlissandoType, HairpinType, NoteheadShape, Ornament, OttavaType, StemDirection, decomposeDuration, } from '../models/types';
|
|
4
4
|
/**
|
|
5
5
|
* MusicXML Parser
|
|
6
6
|
*
|
|
@@ -151,19 +151,28 @@ export class MusicXMLParser {
|
|
|
151
151
|
for (const scorePart of Array.from(scoreParts)) {
|
|
152
152
|
const id = scorePart.getAttribute('id');
|
|
153
153
|
const name = this.getText(scorePart, 'part-name') || 'Part';
|
|
154
|
-
let instrument
|
|
154
|
+
let instrument;
|
|
155
155
|
// Find first midi-instrument to determine main instrument
|
|
156
156
|
const firstMidiInst = scorePart.querySelector('midi-instrument');
|
|
157
157
|
if (firstMidiInst) {
|
|
158
158
|
const programStr = this.getText(firstMidiInst, 'midi-program');
|
|
159
159
|
if (programStr) {
|
|
160
160
|
const program = parseInt(programStr);
|
|
161
|
-
// MusicXML uses 1-128, MIDI uses 0-127. Adjust if needed.
|
|
162
|
-
// Often it's accurate to 1-based, so checking bounds.
|
|
163
|
-
// Assuming input is 1-based as per standard
|
|
164
161
|
instrument = getInstrumentByProgram(Math.max(0, program - 1));
|
|
165
162
|
}
|
|
166
163
|
}
|
|
164
|
+
// Fallback: guess by part name
|
|
165
|
+
if (!instrument || instrument.sound === 'piano') {
|
|
166
|
+
const guessed = guessInstrumentByName(name);
|
|
167
|
+
// Only override if the guess is more specific than default piano
|
|
168
|
+
if (guessed.sound !== 'piano') {
|
|
169
|
+
instrument = guessed;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Final default
|
|
173
|
+
if (!instrument) {
|
|
174
|
+
instrument = getInstrumentByProgram(0);
|
|
175
|
+
}
|
|
167
176
|
if (id) {
|
|
168
177
|
partInfo.set(id, { name: name.trim(), instrument });
|
|
169
178
|
}
|
|
@@ -242,7 +251,8 @@ export class MusicXMLParser {
|
|
|
242
251
|
const sign = this.getText(clef, 'sign');
|
|
243
252
|
if (sign && number <= numStaves) {
|
|
244
253
|
const line = this.getNumber(clef, 'line') || 0;
|
|
245
|
-
|
|
254
|
+
const octaveChange = this.getNumber(clef, 'clef-octave-change') || 0;
|
|
255
|
+
staffClefs[number - 1] = this.mapClef(sign, line, octaveChange);
|
|
246
256
|
}
|
|
247
257
|
});
|
|
248
258
|
const staffDetails = attributes.querySelectorAll('staff-details');
|
|
@@ -258,7 +268,10 @@ export class MusicXMLParser {
|
|
|
258
268
|
let systemText = undefined;
|
|
259
269
|
let contextDynamic = undefined;
|
|
260
270
|
const { repeats, volta, barlineStyle } = this.parseBarlines(measureElement);
|
|
261
|
-
|
|
271
|
+
// Map of staffIndex -> Map of voiceId -> NoteSetJSON[]
|
|
272
|
+
const staffVoices = new Map();
|
|
273
|
+
for (let i = 0; i < numStaves; i++)
|
|
274
|
+
staffVoices.set(i, new Map());
|
|
262
275
|
const children = Array.from(measureElement.children);
|
|
263
276
|
let pendingChord = undefined;
|
|
264
277
|
let currentNoteSetMap = new Map();
|
|
@@ -375,6 +388,11 @@ export class MusicXMLParser {
|
|
|
375
388
|
note.pedal = currentPedal;
|
|
376
389
|
currentPedal = undefined;
|
|
377
390
|
}
|
|
391
|
+
const voiceId = this.getText(child, 'voice') || '1';
|
|
392
|
+
const voiceMap = staffVoices.get(staffIdx);
|
|
393
|
+
if (!voiceMap.has(voiceId))
|
|
394
|
+
voiceMap.set(voiceId, []);
|
|
395
|
+
const voiceNoteSets = voiceMap.get(voiceId);
|
|
378
396
|
if (isChord) {
|
|
379
397
|
const existing = currentNoteSetMap.get(staffIdx);
|
|
380
398
|
if (existing)
|
|
@@ -385,26 +403,42 @@ export class MusicXMLParser {
|
|
|
385
403
|
else {
|
|
386
404
|
const existing = currentNoteSetMap.get(staffIdx);
|
|
387
405
|
if (existing)
|
|
388
|
-
|
|
406
|
+
voiceNoteSets.push({ notes: existing });
|
|
389
407
|
currentNoteSetMap.set(staffIdx, [note]);
|
|
390
408
|
}
|
|
391
409
|
}
|
|
392
410
|
}
|
|
393
411
|
else if (child.nodeName === 'backup' || child.nodeName === 'forward') {
|
|
394
412
|
for (const [sIdx, notes] of currentNoteSetMap.entries()) {
|
|
395
|
-
if (notes.length > 0)
|
|
396
|
-
|
|
413
|
+
if (notes.length > 0) {
|
|
414
|
+
const voiceId = this.getText(child.previousElementSibling, 'voice') || '1';
|
|
415
|
+
const voiceMap = staffVoices.get(sIdx);
|
|
416
|
+
if (!voiceMap.has(voiceId))
|
|
417
|
+
voiceMap.set(voiceId, []);
|
|
418
|
+
voiceMap.get(voiceId).push({ notes });
|
|
419
|
+
}
|
|
397
420
|
}
|
|
398
421
|
currentNoteSetMap.clear();
|
|
399
422
|
}
|
|
400
423
|
});
|
|
401
424
|
for (const [sIdx, notes] of currentNoteSetMap.entries()) {
|
|
402
|
-
if (notes.length > 0)
|
|
403
|
-
|
|
425
|
+
if (notes.length > 0) {
|
|
426
|
+
// Last note in measure
|
|
427
|
+
const voiceId = '1'; // Defaulting to 1 for final push if not tracked
|
|
428
|
+
const voiceMap = staffVoices.get(sIdx);
|
|
429
|
+
if (!voiceMap.has(voiceId))
|
|
430
|
+
voiceMap.set(voiceId, []);
|
|
431
|
+
voiceMap.get(voiceId).push({ notes });
|
|
432
|
+
}
|
|
404
433
|
}
|
|
405
434
|
for (let i = 0; i < numStaves; i++) {
|
|
435
|
+
const vMap = staffVoices.get(i);
|
|
436
|
+
let voicesIndices = Array.from(vMap.keys()).sort();
|
|
437
|
+
if (voicesIndices.length === 0)
|
|
438
|
+
voicesIndices = ['1'];
|
|
439
|
+
const voices = voicesIndices.map(id => vMap.get(id) || []);
|
|
406
440
|
const measure = {
|
|
407
|
-
voices:
|
|
441
|
+
voices: voices,
|
|
408
442
|
timeSignature: measureTimeSignature,
|
|
409
443
|
keySignature: measureKeySignature,
|
|
410
444
|
isPickup: isPickup || undefined,
|
|
@@ -415,6 +449,30 @@ export class MusicXMLParser {
|
|
|
415
449
|
systemText: i === 0 ? systemText : undefined,
|
|
416
450
|
barlineStyle,
|
|
417
451
|
};
|
|
452
|
+
// Padding with rests if not a pickup
|
|
453
|
+
if (!isPickup) {
|
|
454
|
+
const targetDur = (this.currentBeats * 4) / this.currentBeatType;
|
|
455
|
+
measure.voices = measure.voices.map((v) => {
|
|
456
|
+
const currentDur = v.reduce((sum, ns) => {
|
|
457
|
+
const base = DURATION_VALUES[ns.notes[0].duration];
|
|
458
|
+
return sum + (ns.notes[0].isDotted ? base * 1.5 : base);
|
|
459
|
+
}, 0);
|
|
460
|
+
if (currentDur < targetDur - 0.001) {
|
|
461
|
+
const gap = targetDur - currentDur;
|
|
462
|
+
const rests = decomposeDuration(gap).map((d) => ({
|
|
463
|
+
notes: [
|
|
464
|
+
{
|
|
465
|
+
duration: d.duration,
|
|
466
|
+
isRest: true,
|
|
467
|
+
isDotted: d.isDotted,
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
}));
|
|
471
|
+
return [...v, ...rests];
|
|
472
|
+
}
|
|
473
|
+
return v;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
418
476
|
staves[i].measures.push(measure);
|
|
419
477
|
staves[i].clef = staffClefs[i];
|
|
420
478
|
}
|
|
@@ -891,12 +949,12 @@ export class MusicXMLParser {
|
|
|
891
949
|
};
|
|
892
950
|
return map[type] ?? Duration.Quarter;
|
|
893
951
|
}
|
|
894
|
-
mapClef(sign, line = 0) {
|
|
952
|
+
mapClef(sign, line = 0, octaveChange = 0) {
|
|
895
953
|
switch (sign.toUpperCase()) {
|
|
896
954
|
case 'G':
|
|
897
|
-
return Clef.Treble;
|
|
955
|
+
return octaveChange === -1 ? Clef.Treble8vaBassa : Clef.Treble;
|
|
898
956
|
case 'F':
|
|
899
|
-
return Clef.Bass;
|
|
957
|
+
return octaveChange === -1 ? Clef.Bass8vaBassa : Clef.Bass;
|
|
900
958
|
case 'C':
|
|
901
959
|
return line === 4 ? Clef.Tenor : Clef.Alto;
|
|
902
960
|
case 'PERCUSSION':
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './models';
|
|
2
|
-
export * from './importers';
|
|
1
|
+
export * from './models/index.js';
|
|
2
|
+
export * from './importers/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './models';
|
|
2
|
-
export * from './importers';
|
|
1
|
+
export * from './models/index.js';
|
|
2
|
+
export * from './importers/index.js';
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Brass = "brass",
|
|
4
|
-
Woodwind = "woodwind",
|
|
5
|
-
Percussion = "percussion",
|
|
6
|
-
Keyboard = "keyboard",
|
|
7
|
-
Synth = "synth"
|
|
8
|
-
}
|
|
9
|
-
export declare enum InstrumentPreset {
|
|
10
|
-
Piano = "piano",
|
|
11
|
-
Violin = "violin",
|
|
12
|
-
Cello = "cello",
|
|
13
|
-
Guitar = "guitar",
|
|
14
|
-
ElectricGuitar = "electric-guitar",
|
|
15
|
-
Bass = "bass",
|
|
16
|
-
Flute = "flute",
|
|
17
|
-
Trumpet = "trumpet",
|
|
18
|
-
Drums = "drums"
|
|
19
|
-
}
|
|
1
|
+
import { InstrumentPreset } from '../types/InstrumentPreset.js';
|
|
2
|
+
import { InstrumentType } from '../types/InstrumentType.js';
|
|
20
3
|
export interface Instrument {
|
|
21
4
|
name: string;
|
|
22
5
|
midiProgram: number;
|
|
6
|
+
sound: InstrumentPreset;
|
|
23
7
|
type?: InstrumentType;
|
|
24
8
|
transposition?: number;
|
|
25
9
|
}
|
|
26
10
|
export declare const PRESET_INSTRUMENTS: Record<string, Instrument>;
|
|
11
|
+
export declare const TRANSLATED_INSTRUMENT_NAMES: Record<string, Record<InstrumentPreset, string>>;
|
|
27
12
|
export declare function getInstrumentByProgram(program: number): Instrument;
|
|
13
|
+
/**
|
|
14
|
+
* Guesses the instrument based on a string (like part name).
|
|
15
|
+
*/
|
|
16
|
+
export declare function guessInstrumentByName(name: string): Instrument;
|
|
@@ -1,67 +1,121 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
InstrumentType["String"] = "string";
|
|
4
|
-
InstrumentType["Brass"] = "brass";
|
|
5
|
-
InstrumentType["Woodwind"] = "woodwind";
|
|
6
|
-
InstrumentType["Percussion"] = "percussion";
|
|
7
|
-
InstrumentType["Keyboard"] = "keyboard";
|
|
8
|
-
InstrumentType["Synth"] = "synth";
|
|
9
|
-
})(InstrumentType || (InstrumentType = {}));
|
|
10
|
-
export var InstrumentPreset;
|
|
11
|
-
(function (InstrumentPreset) {
|
|
12
|
-
InstrumentPreset["Piano"] = "piano";
|
|
13
|
-
InstrumentPreset["Violin"] = "violin";
|
|
14
|
-
InstrumentPreset["Cello"] = "cello";
|
|
15
|
-
InstrumentPreset["Guitar"] = "guitar";
|
|
16
|
-
InstrumentPreset["ElectricGuitar"] = "electric-guitar";
|
|
17
|
-
InstrumentPreset["Bass"] = "bass";
|
|
18
|
-
InstrumentPreset["Flute"] = "flute";
|
|
19
|
-
InstrumentPreset["Trumpet"] = "trumpet";
|
|
20
|
-
InstrumentPreset["Drums"] = "drums";
|
|
21
|
-
})(InstrumentPreset || (InstrumentPreset = {}));
|
|
1
|
+
import { InstrumentPreset } from '../types/InstrumentPreset.js';
|
|
2
|
+
import { InstrumentType } from '../types/InstrumentType.js';
|
|
22
3
|
export const PRESET_INSTRUMENTS = {
|
|
23
4
|
[InstrumentPreset.Piano]: {
|
|
24
5
|
name: 'Acoustic Grand Piano',
|
|
25
6
|
midiProgram: 0,
|
|
7
|
+
sound: InstrumentPreset.Piano,
|
|
26
8
|
type: InstrumentType.Keyboard,
|
|
27
9
|
},
|
|
28
|
-
[InstrumentPreset.Violin]: {
|
|
29
|
-
|
|
10
|
+
[InstrumentPreset.Violin]: {
|
|
11
|
+
name: 'Violin',
|
|
12
|
+
midiProgram: 40,
|
|
13
|
+
sound: InstrumentPreset.Violin,
|
|
14
|
+
type: InstrumentType.String,
|
|
15
|
+
},
|
|
16
|
+
[InstrumentPreset.Cello]: {
|
|
17
|
+
name: 'Cello',
|
|
18
|
+
midiProgram: 42,
|
|
19
|
+
sound: InstrumentPreset.Cello,
|
|
20
|
+
type: InstrumentType.String,
|
|
21
|
+
},
|
|
30
22
|
[InstrumentPreset.Guitar]: {
|
|
31
23
|
name: 'Acoustic Guitar (nylon)',
|
|
32
24
|
midiProgram: 24,
|
|
25
|
+
sound: InstrumentPreset.Guitar,
|
|
33
26
|
type: InstrumentType.String,
|
|
34
27
|
},
|
|
35
28
|
[InstrumentPreset.ElectricGuitar]: {
|
|
36
29
|
name: 'Electric Guitar (clean)',
|
|
37
30
|
midiProgram: 27,
|
|
31
|
+
sound: InstrumentPreset.ElectricGuitar,
|
|
38
32
|
type: InstrumentType.String,
|
|
39
33
|
},
|
|
40
34
|
[InstrumentPreset.Bass]: {
|
|
41
35
|
name: 'Acoustic Bass',
|
|
42
36
|
midiProgram: 32,
|
|
37
|
+
sound: InstrumentPreset.Bass,
|
|
43
38
|
type: InstrumentType.String,
|
|
44
39
|
},
|
|
45
40
|
[InstrumentPreset.Flute]: {
|
|
46
41
|
name: 'Flute',
|
|
47
42
|
midiProgram: 73,
|
|
43
|
+
sound: InstrumentPreset.Flute,
|
|
48
44
|
type: InstrumentType.Woodwind,
|
|
49
45
|
transposition: 0,
|
|
50
46
|
},
|
|
51
47
|
[InstrumentPreset.Trumpet]: {
|
|
52
48
|
name: 'Trumpet',
|
|
53
49
|
midiProgram: 56,
|
|
50
|
+
sound: InstrumentPreset.Trumpet,
|
|
54
51
|
type: InstrumentType.Brass,
|
|
55
52
|
transposition: -2,
|
|
56
53
|
},
|
|
57
54
|
[InstrumentPreset.Drums]: {
|
|
58
55
|
name: 'Drum Kit',
|
|
59
56
|
midiProgram: 118,
|
|
57
|
+
sound: InstrumentPreset.Drums,
|
|
60
58
|
type: InstrumentType.Percussion,
|
|
61
59
|
transposition: 0,
|
|
62
60
|
},
|
|
63
61
|
};
|
|
62
|
+
export const TRANSLATED_INSTRUMENT_NAMES = {
|
|
63
|
+
en: {
|
|
64
|
+
[InstrumentPreset.Piano]: 'Acoustic Grand Piano',
|
|
65
|
+
[InstrumentPreset.Violin]: 'Violin',
|
|
66
|
+
[InstrumentPreset.Cello]: 'Cello',
|
|
67
|
+
[InstrumentPreset.Guitar]: 'Acoustic Guitar',
|
|
68
|
+
[InstrumentPreset.ElectricGuitar]: 'Electric Guitar',
|
|
69
|
+
[InstrumentPreset.Bass]: 'Acoustic Bass',
|
|
70
|
+
[InstrumentPreset.Flute]: 'Flute',
|
|
71
|
+
[InstrumentPreset.Trumpet]: 'Trumpet',
|
|
72
|
+
[InstrumentPreset.Drums]: 'Drum Kit',
|
|
73
|
+
},
|
|
74
|
+
ca: {
|
|
75
|
+
[InstrumentPreset.Piano]: 'Piano',
|
|
76
|
+
[InstrumentPreset.Violin]: 'Violí',
|
|
77
|
+
[InstrumentPreset.Cello]: 'Violoncel',
|
|
78
|
+
[InstrumentPreset.Guitar]: 'Guitarra Acústica',
|
|
79
|
+
[InstrumentPreset.ElectricGuitar]: 'Guitarra Elèctrica',
|
|
80
|
+
[InstrumentPreset.Bass]: 'Baix',
|
|
81
|
+
[InstrumentPreset.Flute]: 'Flauta',
|
|
82
|
+
[InstrumentPreset.Trumpet]: 'Trompeta',
|
|
83
|
+
[InstrumentPreset.Drums]: 'Bateria',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
64
86
|
export function getInstrumentByProgram(program) {
|
|
65
87
|
const match = Object.values(PRESET_INSTRUMENTS).find((i) => i.midiProgram === program);
|
|
66
88
|
return match || PRESET_INSTRUMENTS[InstrumentPreset.Piano];
|
|
67
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Guesses the instrument based on a string (like part name).
|
|
92
|
+
*/
|
|
93
|
+
export function guessInstrumentByName(name) {
|
|
94
|
+
const n = name.toLowerCase();
|
|
95
|
+
if (n.includes('piano'))
|
|
96
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Piano];
|
|
97
|
+
if (n.includes('violin'))
|
|
98
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Violin];
|
|
99
|
+
if (n.includes('cello') || n.includes('violoncel'))
|
|
100
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Cello];
|
|
101
|
+
if (n.includes('electric guitar'))
|
|
102
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.ElectricGuitar];
|
|
103
|
+
if (n.includes('guitar'))
|
|
104
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Guitar];
|
|
105
|
+
if (n.includes('bass') || n.includes('baixo') || n.includes('baix'))
|
|
106
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Bass];
|
|
107
|
+
if (n.includes('flute') || n.includes('flauta'))
|
|
108
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Flute];
|
|
109
|
+
if (n.includes('trumpet') || n.includes('trompeta'))
|
|
110
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Trumpet];
|
|
111
|
+
if (n.includes('drum') || n.includes('perc') || n.includes('bateria'))
|
|
112
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Drums];
|
|
113
|
+
// Try partial matches for strings, brass, etc.
|
|
114
|
+
if (n.includes('string'))
|
|
115
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Violin];
|
|
116
|
+
if (n.includes('brass'))
|
|
117
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Trumpet];
|
|
118
|
+
if (n.includes('wind'))
|
|
119
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Flute];
|
|
120
|
+
return PRESET_INSTRUMENTS[InstrumentPreset.Piano];
|
|
121
|
+
}
|
package/dist/models/Measure.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export declare class Measure {
|
|
|
20
20
|
readonly barlineStyle: BarlineStyle;
|
|
21
21
|
constructor(voices: NoteSet[][], timeSignature?: TimeSignature | undefined, keySignature?: KeySignature | undefined, systemBreak?: boolean, pageBreak?: boolean, repeats?: Repeat[], volta?: Volta | undefined, isPickup?: boolean, clef?: Clef | undefined, tempo?: Tempo | undefined, rehearsalMark?: string | undefined, systemText?: string | undefined, barlineStyle?: BarlineStyle);
|
|
22
22
|
get notes(): NoteSet[];
|
|
23
|
+
get hasStartRepeat(): boolean;
|
|
24
|
+
get hasEndRepeat(): boolean;
|
|
23
25
|
changeNoteDuration(noteIndex: number, newDuration: Duration, isDotted?: boolean, voiceIndex?: number): Measure;
|
|
24
26
|
getTotalDuration(voiceIndex?: number): number;
|
|
25
27
|
static fromJSON(data: MeasureJSON): Measure;
|
package/dist/models/Measure.js
CHANGED
|
@@ -39,6 +39,12 @@ export class Measure {
|
|
|
39
39
|
get notes() {
|
|
40
40
|
return this.voices[0] || [];
|
|
41
41
|
}
|
|
42
|
+
get hasStartRepeat() {
|
|
43
|
+
return this.repeats.some((r) => r.type === "start");
|
|
44
|
+
}
|
|
45
|
+
get hasEndRepeat() {
|
|
46
|
+
return this.repeats.some((r) => r.type === "end");
|
|
47
|
+
}
|
|
42
48
|
changeNoteDuration(noteIndex, newDuration, isDotted = false, voiceIndex = 0) {
|
|
43
49
|
const voice = this.voices[voiceIndex];
|
|
44
50
|
if (!voice || noteIndex < 0 || noteIndex >= voice.length)
|
package/dist/models/NoteSet.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Note, NoteJSON } from './Note';
|
|
2
|
-
import {
|
|
2
|
+
import { Pitch } from './Pitch';
|
|
3
|
+
import { Accidental, Arpeggio, Articulation, Bowing, Duration, Dynamic, FretboardDiagram, Glissando, Hairpin, Lyric, NoteheadShape, Ornament, Ottava, Pedal, Slur, StemDirection, Tuplet } from './types';
|
|
3
4
|
export declare class NoteSet {
|
|
4
5
|
readonly notes: Note[];
|
|
5
6
|
constructor(notes: Note[]);
|
|
@@ -7,16 +8,16 @@ export declare class NoteSet {
|
|
|
7
8
|
get isDotted(): boolean;
|
|
8
9
|
get isRest(): boolean;
|
|
9
10
|
get beamGroup(): number | undefined;
|
|
10
|
-
get tuplet():
|
|
11
|
+
get tuplet(): Tuplet | undefined;
|
|
11
12
|
getDurationValue(): number;
|
|
12
13
|
isBeamable(): boolean;
|
|
13
14
|
withDuration(duration: Duration, isDotted?: boolean): NoteSet;
|
|
14
15
|
withBeamGroup(group?: number): NoteSet;
|
|
15
16
|
withNotes(notes: Note[]): NoteSet;
|
|
16
|
-
withLyrics(lyrics: (string |
|
|
17
|
-
withTuplet(tuplet?:
|
|
18
|
-
withPitch(pitch:
|
|
19
|
-
withAccidental(accidental
|
|
17
|
+
withLyrics(lyrics: (string | Lyric)[]): NoteSet;
|
|
18
|
+
withTuplet(tuplet?: Tuplet): NoteSet;
|
|
19
|
+
withPitch(pitch: Pitch): NoteSet;
|
|
20
|
+
withAccidental(accidental?: Accidental): NoteSet;
|
|
20
21
|
withRest(isRest: boolean): NoteSet;
|
|
21
22
|
withGrace(isGrace: boolean): NoteSet;
|
|
22
23
|
toggleEnharmonic(): NoteSet;
|
|
@@ -26,58 +27,59 @@ export declare class NoteSet {
|
|
|
26
27
|
withChord(chord?: string): NoteSet;
|
|
27
28
|
withTab(fret: number, string: number): NoteSet;
|
|
28
29
|
withTie(tie: boolean): NoteSet;
|
|
29
|
-
withSlur(slur?:
|
|
30
|
-
withArticulation(articulation?:
|
|
31
|
-
withOrnament(ornament?:
|
|
32
|
-
withDynamic(dynamic?:
|
|
33
|
-
withHairpin(hairpin?:
|
|
34
|
-
withGlissando(glissando?:
|
|
35
|
-
withArpeggio(arpeggio?:
|
|
36
|
-
withOttava(ottava?:
|
|
37
|
-
withPedal(pedal?:
|
|
30
|
+
withSlur(slur?: Slur): NoteSet;
|
|
31
|
+
withArticulation(articulation?: Articulation): NoteSet;
|
|
32
|
+
withOrnament(ornament?: Ornament): NoteSet;
|
|
33
|
+
withDynamic(dynamic?: Dynamic): NoteSet;
|
|
34
|
+
withHairpin(hairpin?: Hairpin): NoteSet;
|
|
35
|
+
withGlissando(glissando?: Glissando): NoteSet;
|
|
36
|
+
withArpeggio(arpeggio?: Arpeggio): NoteSet;
|
|
37
|
+
withOttava(ottava?: Ottava): NoteSet;
|
|
38
|
+
withPedal(pedal?: Pedal): NoteSet;
|
|
38
39
|
withColor(color?: string): NoteSet;
|
|
39
|
-
withNotehead(notehead
|
|
40
|
-
withBowing(bowing?:
|
|
40
|
+
withNotehead(notehead?: NoteheadShape): NoteSet;
|
|
41
|
+
withBowing(bowing?: Bowing): NoteSet;
|
|
41
42
|
withFingering(fingering?: number): NoteSet;
|
|
42
43
|
withHammerOn(type?: 'start' | 'stop'): NoteSet;
|
|
43
44
|
withPullOff(type?: 'start' | 'stop'): NoteSet;
|
|
44
45
|
withPalmMute(type?: 'start' | 'stop'): NoteSet;
|
|
45
46
|
withStringCircled(isStringCircled?: boolean): NoteSet;
|
|
46
|
-
withStemDirection(dir?:
|
|
47
|
-
withLyric(lyric?:
|
|
47
|
+
withStemDirection(dir?: StemDirection): NoteSet;
|
|
48
|
+
withLyric(lyric?: string): NoteSet;
|
|
48
49
|
withStaffText(text?: string): NoteSet;
|
|
49
|
-
withFretboardDiagram(diagram?:
|
|
50
|
-
get pitch():
|
|
51
|
-
get accidental():
|
|
50
|
+
withFretboardDiagram(diagram?: FretboardDiagram): NoteSet;
|
|
51
|
+
get pitch(): Pitch | undefined;
|
|
52
|
+
get accidental(): Accidental | undefined;
|
|
52
53
|
get tie(): boolean | undefined;
|
|
53
|
-
get slur():
|
|
54
|
-
get
|
|
55
|
-
get
|
|
56
|
-
get
|
|
57
|
-
get
|
|
58
|
-
get
|
|
59
|
-
get
|
|
60
|
-
get
|
|
61
|
-
get
|
|
62
|
-
get
|
|
54
|
+
get slur(): Slur | undefined;
|
|
55
|
+
get articulations(): Articulation[];
|
|
56
|
+
get articulation(): Articulation | undefined;
|
|
57
|
+
get ornament(): Ornament | undefined;
|
|
58
|
+
get dynamic(): Dynamic | undefined;
|
|
59
|
+
get hairpin(): Hairpin | undefined;
|
|
60
|
+
get glissando(): Glissando | undefined;
|
|
61
|
+
get arpeggio(): Arpeggio | undefined;
|
|
62
|
+
get ottava(): Ottava | undefined;
|
|
63
|
+
get pedal(): Pedal | undefined;
|
|
64
|
+
get notehead(): NoteheadShape | undefined;
|
|
63
65
|
get color(): string | undefined;
|
|
64
66
|
get fret(): number | undefined;
|
|
65
67
|
get string(): number | undefined;
|
|
66
|
-
get bowing():
|
|
68
|
+
get bowing(): Bowing | undefined;
|
|
67
69
|
get fingering(): number | undefined;
|
|
68
|
-
get allLyrics():
|
|
69
|
-
get lyrics(): (string |
|
|
70
|
+
get allLyrics(): Lyric[];
|
|
71
|
+
get lyrics(): (string | Lyric)[] | undefined;
|
|
70
72
|
get lyric(): string | undefined;
|
|
71
73
|
get chord(): string | undefined;
|
|
72
|
-
get fretboardDiagram():
|
|
74
|
+
get fretboardDiagram(): FretboardDiagram | undefined;
|
|
73
75
|
get staffText(): string | undefined;
|
|
74
76
|
get hammerOn(): "start" | "stop" | undefined;
|
|
75
77
|
get pullOff(): "start" | "stop" | undefined;
|
|
76
78
|
get palmMute(): "start" | "stop" | undefined;
|
|
77
79
|
get isStringCircled(): boolean | undefined;
|
|
78
|
-
get stemDirection():
|
|
80
|
+
get stemDirection(): StemDirection | undefined;
|
|
79
81
|
toJSON(): NoteSetJSON;
|
|
80
|
-
static fromJSON(data:
|
|
82
|
+
static fromJSON(data: Partial<NoteSetJSON> | NoteJSON | NoteJSON[]): NoteSet;
|
|
81
83
|
}
|
|
82
84
|
export interface NoteSetJSON {
|
|
83
85
|
notes: NoteJSON[];
|
package/dist/models/NoteSet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Note } from './Note';
|
|
2
|
-
import { Duration } from './types';
|
|
2
|
+
import { Duration, } from './types';
|
|
3
3
|
export class NoteSet {
|
|
4
4
|
notes;
|
|
5
5
|
constructor(notes) {
|
|
@@ -178,6 +178,9 @@ export class NoteSet {
|
|
|
178
178
|
get slur() {
|
|
179
179
|
return this.notes[0].slur;
|
|
180
180
|
}
|
|
181
|
+
get articulations() {
|
|
182
|
+
return this.notes[0].articulations;
|
|
183
|
+
}
|
|
181
184
|
get articulation() {
|
|
182
185
|
return this.notes[0].articulation;
|
|
183
186
|
}
|
|
@@ -264,7 +267,7 @@ export class NoteSet {
|
|
|
264
267
|
return new NoteSet([new Note(Duration.Quarter, undefined, true)]);
|
|
265
268
|
}
|
|
266
269
|
let notesArray;
|
|
267
|
-
if (
|
|
270
|
+
if ('notes' in data && Array.isArray(data.notes)) {
|
|
268
271
|
notesArray = data.notes;
|
|
269
272
|
}
|
|
270
273
|
else if (Array.isArray(data)) {
|
package/dist/models/Pitch.js
CHANGED
|
@@ -33,7 +33,9 @@ export class Pitch {
|
|
|
33
33
|
getStaffPosition(clef) {
|
|
34
34
|
const middleLineAbsStep = {
|
|
35
35
|
[Clef.Treble]: 4 * 7 + 6, // B4
|
|
36
|
+
[Clef.Treble8vaBassa]: 3 * 7 + 6, // B3
|
|
36
37
|
[Clef.Bass]: 3 * 7 + 1, // D3
|
|
38
|
+
[Clef.Bass8vaBassa]: 2 * 7 + 1, // D2
|
|
37
39
|
[Clef.Alto]: 4 * 7 + 0, // C4
|
|
38
40
|
[Clef.Tenor]: 3 * 7 + 5, // A3
|
|
39
41
|
[Clef.Percussion]: 4 * 7 + 6, // B4
|
|
@@ -100,7 +102,9 @@ export class Pitch {
|
|
|
100
102
|
static fromStaffPosition(clef, staffPosition) {
|
|
101
103
|
const middleLineAbsStep = {
|
|
102
104
|
[Clef.Treble]: 4 * 7 + 6, // B4
|
|
105
|
+
[Clef.Treble8vaBassa]: 3 * 7 + 6, // B3
|
|
103
106
|
[Clef.Bass]: 3 * 7 + 1, // D3
|
|
107
|
+
[Clef.Bass8vaBassa]: 2 * 7 + 1, // D2
|
|
104
108
|
[Clef.Alto]: 4 * 7 + 0, // C4
|
|
105
109
|
[Clef.Tenor]: 3 * 7 + 5, // A3
|
|
106
110
|
[Clef.Percussion]: 4 * 7 + 0,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Clef, KeySignature, TimeSignature } from './types';
|
|
2
|
+
export interface PreMeasureJSON {
|
|
3
|
+
clef?: string;
|
|
4
|
+
keySignature?: KeySignature;
|
|
5
|
+
timeSignature?: TimeSignature;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Represents the musical context before the first measure of a system.
|
|
9
|
+
* This includes the clef, key signature, and time signature.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PreMeasure {
|
|
12
|
+
readonly clef?: Clef | undefined;
|
|
13
|
+
readonly keySignature?: KeySignature | undefined;
|
|
14
|
+
readonly timeSignature?: TimeSignature | undefined;
|
|
15
|
+
constructor(clef?: Clef | undefined, keySignature?: KeySignature | undefined, timeSignature?: TimeSignature | undefined);
|
|
16
|
+
/**
|
|
17
|
+
* Creates a PreMeasure from JSON data.
|
|
18
|
+
*/
|
|
19
|
+
static fromJSON(data: Partial<PreMeasureJSON>): PreMeasure;
|
|
20
|
+
/**
|
|
21
|
+
* Converts the PreMeasure to a JSON-serializable object.
|
|
22
|
+
*/
|
|
23
|
+
toJSON(): PreMeasureJSON;
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents the musical context before the first measure of a system.
|
|
3
|
+
* This includes the clef, key signature, and time signature.
|
|
4
|
+
*/
|
|
5
|
+
export class PreMeasure {
|
|
6
|
+
clef;
|
|
7
|
+
keySignature;
|
|
8
|
+
timeSignature;
|
|
9
|
+
constructor(clef, keySignature, timeSignature) {
|
|
10
|
+
this.clef = clef;
|
|
11
|
+
this.keySignature = keySignature;
|
|
12
|
+
this.timeSignature = timeSignature;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a PreMeasure from JSON data.
|
|
16
|
+
*/
|
|
17
|
+
static fromJSON(data) {
|
|
18
|
+
return new PreMeasure(data.clef, data.keySignature, data.timeSignature);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Converts the PreMeasure to a JSON-serializable object.
|
|
22
|
+
*/
|
|
23
|
+
toJSON() {
|
|
24
|
+
return {
|
|
25
|
+
clef: this.clef,
|
|
26
|
+
keySignature: this.keySignature,
|
|
27
|
+
timeSignature: this.timeSignature,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/models/Score.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { Measure } from './Measure';
|
|
|
2
2
|
import { NoteSet } from './NoteSet';
|
|
3
3
|
import { Part, PartJSON } from './Part';
|
|
4
4
|
import { Staff } from './Staff';
|
|
5
|
-
import { Duration, Genre, KeySignature, TimeSignature } from './types';
|
|
5
|
+
import { Clef, Duration, Genre, KeySignature, TimeSignature } from './types';
|
|
6
6
|
/**
|
|
7
7
|
* Represents a complete musical score.
|
|
8
8
|
*/
|
|
@@ -38,6 +38,21 @@ export declare class Score {
|
|
|
38
38
|
static fromJSON(data: ScoreJSON): Score;
|
|
39
39
|
transpose(semitones: number): Score;
|
|
40
40
|
replaceNote(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, newNote: NoteSet, voiceIndex?: number): Score;
|
|
41
|
+
withKeySignature(keySig: KeySignature): Score;
|
|
42
|
+
withTimeSignature(timeSig: TimeSignature): Score;
|
|
43
|
+
updateMeasureSignatures(measureIndex: number, signatures: {
|
|
44
|
+
keySignature?: KeySignature;
|
|
45
|
+
timeSignature?: TimeSignature;
|
|
46
|
+
clef?: Clef;
|
|
47
|
+
}, autoBeam?: boolean): Score;
|
|
48
|
+
/**
|
|
49
|
+
* Reflows the notes starting from a specific measure index.
|
|
50
|
+
* This is useful when time signatures change and notes need to be redistributed.
|
|
51
|
+
*/
|
|
52
|
+
reflow(fromMeasureIndex: number, autoBeam?: boolean): Score;
|
|
53
|
+
private isTied;
|
|
54
|
+
private areSamePitch;
|
|
55
|
+
private reflowStaff;
|
|
41
56
|
replaceMeasure(partIndex: number, staffIndex: number, measureIndex: number, newMeasure: Measure, autoBeam?: boolean): Score;
|
|
42
57
|
deleteNote(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, voiceIndex?: number): Score;
|
|
43
58
|
changeNoteDuration(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, newDuration: Duration, isDotted?: boolean, voiceIndex?: number): Score;
|