@scorelabs/core 1.0.10 → 1.0.11
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/constants.d.ts +4 -0
- package/dist/constants.js +10 -0
- package/dist/importers/MusicXMLParser.d.ts +2 -1
- package/dist/importers/MusicXMLParser.js +57 -15
- package/dist/importers/index.d.ts +1 -1
- package/dist/importers/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/Measure.d.ts +7 -4
- package/dist/models/Measure.js +100 -14
- package/dist/models/Note.d.ts +7 -8
- package/dist/models/Note.js +48 -46
- package/dist/models/NoteSet.d.ts +5 -4
- package/dist/models/NoteSet.js +7 -4
- package/dist/models/Part.d.ts +5 -5
- package/dist/models/Part.js +2 -2
- package/dist/models/Pitch.d.ts +1 -1
- package/dist/models/Pitch.js +1 -1
- package/dist/models/PreMeasure.d.ts +1 -1
- package/dist/models/Score.d.ts +10 -6
- package/dist/models/Score.js +42 -27
- package/dist/models/Staff.d.ts +4 -4
- package/dist/models/Staff.js +2 -2
- package/dist/models/index.d.ts +10 -10
- package/dist/models/index.js +10 -10
- package/dist/types/Duration.d.ts +4 -0
- package/dist/types/Duration.js +13 -0
- package/dist/types/Tempo.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const getEnv = (key, defaultValue) => {
|
|
2
|
+
if (typeof process !== 'undefined' && process.env && process.env[key]) {
|
|
3
|
+
return process.env[key];
|
|
4
|
+
}
|
|
5
|
+
return defaultValue;
|
|
6
|
+
};
|
|
7
|
+
export const DEFAULT_WEBSITE_URL = getEnv('WEBSITE_URL', 'http://localhost:5173');
|
|
8
|
+
export const DEFAULT_VIEWER_URL = getEnv('VIEWER_URL', 'http://localhost:5174');
|
|
9
|
+
export const DEFAULT_API_URL = getEnv('API_URL', 'http://localhost:3000');
|
|
10
|
+
export const DEFAULT_DATABASE_URL = getEnv('TURSO_DATABASE_URL', 'file:local.db');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScoreJSON } from '../models/Score';
|
|
1
|
+
import { ScoreJSON } from '../models/Score.js';
|
|
2
2
|
/**
|
|
3
3
|
* MusicXML Parser
|
|
4
4
|
*
|
|
@@ -25,6 +25,7 @@ export declare class MusicXMLParser {
|
|
|
25
25
|
private parseTitle;
|
|
26
26
|
private parseCopyright;
|
|
27
27
|
private parseComposer;
|
|
28
|
+
private parseLyricist;
|
|
28
29
|
private parsePartList;
|
|
29
30
|
private parseParts;
|
|
30
31
|
private parsePart;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import JSZip from 'jszip';
|
|
2
|
-
import { getInstrumentByProgram, guessInstrumentByName } from '../models/Instrument';
|
|
3
|
-
import { Accidental, Arpeggio, Articulation, BarlineStyle, Bowing, Clef,
|
|
2
|
+
import { getInstrumentByProgram, guessInstrumentByName } from '../models/Instrument.js';
|
|
3
|
+
import { Accidental, Arpeggio, Articulation, BarlineStyle, Bowing, Clef, Duration, GlissandoType, HairpinType, NoteheadShape, Ornament, OttavaType, StemDirection, calculateDurationValue, decomposeDuration, } from '../models/types.js';
|
|
4
|
+
// TODO: Write tests for MusicXMLParser. It is important to test it thoroughly.
|
|
4
5
|
/**
|
|
5
6
|
* MusicXML Parser
|
|
6
7
|
*
|
|
@@ -81,7 +82,7 @@ export class MusicXMLParser {
|
|
|
81
82
|
if (!_parser)
|
|
82
83
|
throw new Error('No DOMParser available');
|
|
83
84
|
const containerDoc = _parser.parseFromString(containerXml, 'application/xml');
|
|
84
|
-
const rootfile =
|
|
85
|
+
const rootfile = new MusicXMLParser(_parser).querySelector(containerDoc, 'rootfile');
|
|
85
86
|
const fullPath = rootfile?.getAttribute('full-path');
|
|
86
87
|
if (!fullPath)
|
|
87
88
|
throw new Error('Invalid MXL: Could not find main score file path');
|
|
@@ -120,6 +121,7 @@ export class MusicXMLParser {
|
|
|
120
121
|
const title = this.parseTitle(doc);
|
|
121
122
|
const subtitle = this.parseSubtitle(doc);
|
|
122
123
|
const composer = this.parseComposer(doc);
|
|
124
|
+
const lyricist = this.parseLyricist(doc);
|
|
123
125
|
const copyright = this.parseCopyright(doc);
|
|
124
126
|
const partInfo = this.parsePartList(doc);
|
|
125
127
|
const parts = this.parseParts(doc, partInfo);
|
|
@@ -127,6 +129,7 @@ export class MusicXMLParser {
|
|
|
127
129
|
let globalTempoDuration = Duration.Quarter;
|
|
128
130
|
let globalTempoIsDotted = false;
|
|
129
131
|
let globalTempoText = '';
|
|
132
|
+
let globalTempoDotCount = 0;
|
|
130
133
|
if (parts.length > 0 && parts[0].staves.length > 0 && parts[0].staves[0].measures.length > 0) {
|
|
131
134
|
const firstMeasure = parts[0].staves[0].measures[0];
|
|
132
135
|
if (firstMeasure.tempo) {
|
|
@@ -134,6 +137,7 @@ export class MusicXMLParser {
|
|
|
134
137
|
globalTempoDuration = firstMeasure.tempo.duration;
|
|
135
138
|
globalTempoIsDotted = firstMeasure.tempo.isDotted;
|
|
136
139
|
globalTempoText = firstMeasure.tempo.text || '';
|
|
140
|
+
globalTempoDotCount = firstMeasure.tempo.dotCount || 0;
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
143
|
const score = {
|
|
@@ -151,7 +155,9 @@ export class MusicXMLParser {
|
|
|
151
155
|
tempoDuration: globalTempoDuration,
|
|
152
156
|
tempoIsDotted: globalTempoIsDotted,
|
|
153
157
|
tempoText: globalTempoText,
|
|
158
|
+
tempoDotCount: globalTempoDotCount,
|
|
154
159
|
copyright,
|
|
160
|
+
lyricist,
|
|
155
161
|
};
|
|
156
162
|
return this.postProcess(score);
|
|
157
163
|
}
|
|
@@ -210,7 +216,38 @@ export class MusicXMLParser {
|
|
|
210
216
|
if (creator.getAttribute('type') === 'composer')
|
|
211
217
|
return creator.textContent ?? 'Unknown';
|
|
212
218
|
}
|
|
213
|
-
|
|
219
|
+
// If no type="composer", but there is only one creator, assume it's the composer
|
|
220
|
+
if (creators.length === 1)
|
|
221
|
+
return (creators[0].textContent ?? 'Unknown');
|
|
222
|
+
// Check credits for composer type
|
|
223
|
+
const credits = this.querySelectorAll(doc, 'credit');
|
|
224
|
+
for (const credit of Array.from(credits)) {
|
|
225
|
+
const type = this.querySelector(credit, 'credit-type');
|
|
226
|
+
if (type?.textContent?.toLowerCase() === 'composer') {
|
|
227
|
+
const words = this.querySelector(credit, 'credit-words');
|
|
228
|
+
if (words?.textContent)
|
|
229
|
+
return words.textContent;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return creators.length > 0 ? creators[0].textContent ?? 'Unknown' : 'Unknown';
|
|
233
|
+
}
|
|
234
|
+
parseLyricist(doc) {
|
|
235
|
+
const creators = this.querySelectorAll(doc, 'identification creator');
|
|
236
|
+
for (const creator of Array.from(creators)) {
|
|
237
|
+
if (creator.getAttribute('type') === 'lyricist')
|
|
238
|
+
return creator.textContent ?? '';
|
|
239
|
+
}
|
|
240
|
+
// Check credits for lyricist type
|
|
241
|
+
const credits = this.querySelectorAll(doc, 'credit');
|
|
242
|
+
for (const credit of Array.from(credits)) {
|
|
243
|
+
const type = this.querySelector(credit, 'credit-type');
|
|
244
|
+
if (type?.textContent?.toLowerCase() === 'lyricist') {
|
|
245
|
+
const words = this.querySelector(credit, 'credit-words');
|
|
246
|
+
if (words?.textContent)
|
|
247
|
+
return words.textContent;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return '';
|
|
214
251
|
}
|
|
215
252
|
parsePartList(doc) {
|
|
216
253
|
const partInfo = new Map();
|
|
@@ -347,7 +384,7 @@ export class MusicXMLParser {
|
|
|
347
384
|
staffVoices.set(i, new Map());
|
|
348
385
|
const children = this.getChildren(measureElement);
|
|
349
386
|
let pendingChord = undefined;
|
|
350
|
-
|
|
387
|
+
const currentNoteSetMap = new Map();
|
|
351
388
|
const beamGroupIdMap = new Map();
|
|
352
389
|
let globalBeamId = 1;
|
|
353
390
|
let currentHairpin = undefined;
|
|
@@ -364,8 +401,9 @@ export class MusicXMLParser {
|
|
|
364
401
|
if (metronome) {
|
|
365
402
|
const beatUnit = this.getText(metronome, 'beat-unit') || 'quarter';
|
|
366
403
|
const bpm = parseInt(this.getText(metronome, 'per-minute') || '120');
|
|
367
|
-
const
|
|
368
|
-
|
|
404
|
+
const dotCount = this.querySelectorAll(metronome, 'beat-unit-dot').length;
|
|
405
|
+
const isDotted = dotCount > 0;
|
|
406
|
+
measureTempo = { bpm, duration: this.mapDuration(beatUnit), isDotted, dotCount };
|
|
369
407
|
}
|
|
370
408
|
const rehearsal = this.querySelector(child, 'rehearsal');
|
|
371
409
|
if (rehearsal)
|
|
@@ -514,12 +552,13 @@ export class MusicXMLParser {
|
|
|
514
552
|
const v1 = vMap0.get('1') || vMap0.get(Array.from(vMap0.keys())[0]);
|
|
515
553
|
if (v1) {
|
|
516
554
|
// Check duration of first voice in first staff
|
|
517
|
-
const expectedDivisions = this.currentBeats * (divisions * 4 / this.currentBeatType);
|
|
555
|
+
const expectedDivisions = this.currentBeats * ((divisions * 4) / this.currentBeatType);
|
|
518
556
|
let totalMeasureDivs = 0;
|
|
519
557
|
const notes = this.querySelectorAll(measureElement, 'note');
|
|
520
558
|
// Find first voice in first staff
|
|
521
559
|
for (const n of notes) {
|
|
522
|
-
if ((this.getText(n, 'staff') || '1') === '1' &&
|
|
560
|
+
if ((this.getText(n, 'staff') || '1') === '1' &&
|
|
561
|
+
(this.getText(n, 'voice') || '1') === (Array.from(vMap0.keys())[0] || '1')) {
|
|
523
562
|
if (this.querySelector(n, 'chord'))
|
|
524
563
|
continue;
|
|
525
564
|
totalMeasureDivs += this.getNumber(n, 'duration') || 0;
|
|
@@ -535,7 +574,7 @@ export class MusicXMLParser {
|
|
|
535
574
|
let voicesIndices = Array.from(vMap.keys()).sort();
|
|
536
575
|
if (voicesIndices.length === 0)
|
|
537
576
|
voicesIndices = ['1'];
|
|
538
|
-
const voices = voicesIndices.map(id => vMap.get(id) || []);
|
|
577
|
+
const voices = voicesIndices.map((id) => vMap.get(id) || []);
|
|
539
578
|
const measure = {
|
|
540
579
|
voices: voices,
|
|
541
580
|
timeSignature: measureTimeSignature,
|
|
@@ -553,8 +592,7 @@ export class MusicXMLParser {
|
|
|
553
592
|
const targetDur = (this.currentBeats * 4) / this.currentBeatType;
|
|
554
593
|
measure.voices = measure.voices.map((v) => {
|
|
555
594
|
const currentDur = v.reduce((sum, ns) => {
|
|
556
|
-
|
|
557
|
-
return sum + (ns.notes[0].isDotted ? base * 1.5 : base);
|
|
595
|
+
return sum + calculateDurationValue(ns.notes[0].duration, ns.notes[0].dotCount || (ns.notes[0].isDotted ? 1 : 0));
|
|
558
596
|
}, 0);
|
|
559
597
|
if (currentDur < targetDur - 0.001) {
|
|
560
598
|
const gap = targetDur - currentDur;
|
|
@@ -583,9 +621,10 @@ export class MusicXMLParser {
|
|
|
583
621
|
const isGrace = this.querySelector(noteElement, 'grace') !== null;
|
|
584
622
|
const type = this.getText(noteElement, 'type');
|
|
585
623
|
const duration = type ? this.mapDuration(type) : Duration.Quarter;
|
|
586
|
-
const
|
|
624
|
+
const dotCount = this.querySelectorAll(noteElement, 'dot').length;
|
|
625
|
+
const isDotted = dotCount > 0;
|
|
587
626
|
if (isRest)
|
|
588
|
-
return { duration, isRest: true, isDotted };
|
|
627
|
+
return { duration, isRest: true, isDotted, dotCount };
|
|
589
628
|
let step = 'C', octave = 4, alter = 0;
|
|
590
629
|
const unpitched = this.querySelector(noteElement, 'unpitched');
|
|
591
630
|
if (unpitched) {
|
|
@@ -751,7 +790,9 @@ export class MusicXMLParser {
|
|
|
751
790
|
pullOff = pullOffEl.getAttribute('type') || 'start';
|
|
752
791
|
}
|
|
753
792
|
let glissando = undefined;
|
|
754
|
-
const glissEl = notations
|
|
793
|
+
const glissEl = notations
|
|
794
|
+
? this.querySelector(notations, 'glissando') || this.querySelector(notations, 'slide')
|
|
795
|
+
: null;
|
|
755
796
|
if (glissEl) {
|
|
756
797
|
const type = glissEl.getAttribute('type');
|
|
757
798
|
if (type === 'start' || type === 'stop') {
|
|
@@ -782,6 +823,7 @@ export class MusicXMLParser {
|
|
|
782
823
|
duration,
|
|
783
824
|
pitch: { midiNumber, step: this.stepToNumber(step), alter, octave },
|
|
784
825
|
isDotted,
|
|
826
|
+
dotCount,
|
|
785
827
|
accidental,
|
|
786
828
|
slur,
|
|
787
829
|
tie,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './MusicXMLParser';
|
|
1
|
+
export * from './MusicXMLParser.js';
|
package/dist/importers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './MusicXMLParser';
|
|
1
|
+
export * from './MusicXMLParser.js';
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/models/Measure.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Note, NoteJSON } from './Note';
|
|
2
|
-
import { NoteSet, NoteSetJSON } from './NoteSet';
|
|
3
|
-
import { BarlineStyle, Clef, Duration, KeySignature, Repeat, Tempo, TimeSignature, Volta } from './types';
|
|
1
|
+
import { Note, NoteJSON } from './Note.js';
|
|
2
|
+
import { NoteSet, NoteSetJSON } from './NoteSet.js';
|
|
3
|
+
import { BarlineStyle, Clef, Duration, KeySignature, Repeat, Tempo, TimeSignature, Volta } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a single measure containing note sets, potentially in multiple voices.
|
|
6
6
|
*/
|
|
@@ -20,9 +20,12 @@ 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
|
+
getNoteOffset(voiceIndex: number, noteIndex: number): number;
|
|
24
|
+
moveNoteToVoice(fromVoiceIndex: number, noteIndex: number, toVoiceIndex: number): Measure;
|
|
25
|
+
splitNote(voiceIndex: number, noteIndex: number, splitOffset: number): Measure;
|
|
23
26
|
get hasStartRepeat(): boolean;
|
|
24
27
|
get hasEndRepeat(): boolean;
|
|
25
|
-
changeNoteDuration(noteIndex: number, newDuration: Duration,
|
|
28
|
+
changeNoteDuration(noteIndex: number, newDuration: Duration, dotCountOrIsDotted?: number | boolean, voiceIndex?: number): Measure;
|
|
26
29
|
getTotalDuration(voiceIndex?: number): number;
|
|
27
30
|
static fromJSON(data: MeasureJSON): Measure;
|
|
28
31
|
transpose(semitones: number): Measure;
|
package/dist/models/Measure.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Note } from './Note';
|
|
2
|
-
import { NoteSet } from './NoteSet';
|
|
3
|
-
import { BarlineStyle,
|
|
1
|
+
import { Note } from './Note.js';
|
|
2
|
+
import { NoteSet } from './NoteSet.js';
|
|
3
|
+
import { BarlineStyle, Duration, calculateDurationValue, decomposeDuration, } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a single measure containing note sets, potentially in multiple voices.
|
|
6
6
|
*/
|
|
@@ -39,26 +39,110 @@ export class Measure {
|
|
|
39
39
|
get notes() {
|
|
40
40
|
return this.voices[0] || [];
|
|
41
41
|
}
|
|
42
|
+
getNoteOffset(voiceIndex, noteIndex) {
|
|
43
|
+
const voice = this.voices[voiceIndex];
|
|
44
|
+
if (!voice)
|
|
45
|
+
return 0;
|
|
46
|
+
let offset = 0;
|
|
47
|
+
for (let i = 0; i < noteIndex && i < voice.length; i++) {
|
|
48
|
+
offset += voice[i].getDurationValue();
|
|
49
|
+
}
|
|
50
|
+
return offset;
|
|
51
|
+
}
|
|
52
|
+
moveNoteToVoice(fromVoiceIndex, noteIndex, toVoiceIndex) {
|
|
53
|
+
if (fromVoiceIndex === toVoiceIndex)
|
|
54
|
+
return this;
|
|
55
|
+
const fromVoice = this.voices[fromVoiceIndex];
|
|
56
|
+
if (!fromVoice || noteIndex < 0 || noteIndex >= fromVoice.length)
|
|
57
|
+
return this;
|
|
58
|
+
const noteSetToMove = fromVoice[noteIndex];
|
|
59
|
+
if (noteSetToMove.isRest)
|
|
60
|
+
return this;
|
|
61
|
+
const onset = this.getNoteOffset(fromVoiceIndex, noteIndex);
|
|
62
|
+
// 1. Replace with rest in fromVoice first to get the source updated
|
|
63
|
+
let updatedMeasure = this.deleteNote(noteIndex, fromVoiceIndex);
|
|
64
|
+
// 2. Ensure toVoice exists and has rests up to total duration
|
|
65
|
+
const targetDur = this.timeSignature
|
|
66
|
+
? (this.timeSignature.beats * 4) / this.timeSignature.beatType
|
|
67
|
+
: this.getTotalDuration(fromVoiceIndex);
|
|
68
|
+
updatedMeasure = updatedMeasure.fillVoiceWithRests(toVoiceIndex, targetDur);
|
|
69
|
+
// 3. Find the note/rest at 'onset' in toVoice and split if necessary
|
|
70
|
+
let toVoice = updatedMeasure.voices[toVoiceIndex];
|
|
71
|
+
let currentOffset = 0;
|
|
72
|
+
let targetNoteIndex = -1;
|
|
73
|
+
for (let i = 0; i < toVoice.length; i++) {
|
|
74
|
+
const dur = toVoice[i].getDurationValue();
|
|
75
|
+
if (Math.abs(currentOffset - onset) < 0.001) {
|
|
76
|
+
targetNoteIndex = i;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
if (onset > currentOffset + 0.001 && onset < currentOffset + dur - 0.001) {
|
|
80
|
+
// Need to split
|
|
81
|
+
updatedMeasure = updatedMeasure.splitNote(toVoiceIndex, i, onset - currentOffset);
|
|
82
|
+
toVoice = updatedMeasure.voices[toVoiceIndex]; // Update reference
|
|
83
|
+
// After split, the target should be at i + (number of segments added - 1)
|
|
84
|
+
// Let's just re-scan to be safe
|
|
85
|
+
currentOffset = 0;
|
|
86
|
+
for (let j = 0; j < toVoice.length; j++) {
|
|
87
|
+
if (Math.abs(currentOffset - onset) < 0.001) {
|
|
88
|
+
targetNoteIndex = j;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
currentOffset += toVoice[j].getDurationValue();
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
currentOffset += dur;
|
|
96
|
+
}
|
|
97
|
+
if (targetNoteIndex !== -1) {
|
|
98
|
+
// We found the correct position in the prepared target voice
|
|
99
|
+
updatedMeasure = updatedMeasure.replaceNoteSet(targetNoteIndex, noteSetToMove, toVoiceIndex);
|
|
100
|
+
}
|
|
101
|
+
return updatedMeasure;
|
|
102
|
+
}
|
|
103
|
+
splitNote(voiceIndex, noteIndex, splitOffset) {
|
|
104
|
+
const voice = this.voices[voiceIndex];
|
|
105
|
+
if (!voice || noteIndex < 0 || noteIndex >= voice.length)
|
|
106
|
+
return this;
|
|
107
|
+
const target = voice[noteIndex];
|
|
108
|
+
const duration = target.getDurationValue();
|
|
109
|
+
if (splitOffset <= 0 || splitOffset >= duration - 0.001)
|
|
110
|
+
return this;
|
|
111
|
+
const leftParts = decomposeDuration(splitOffset);
|
|
112
|
+
const rightParts = decomposeDuration(duration - splitOffset);
|
|
113
|
+
const leftNotes = leftParts.map((p) => target.withDuration(p.duration, p.isDotted).withTie(target.isRest ? false : true));
|
|
114
|
+
const rightNotes = rightParts.map((p, idx) => {
|
|
115
|
+
const isLast = idx === rightParts.length - 1;
|
|
116
|
+
return target
|
|
117
|
+
.withDuration(p.duration, p.isDotted)
|
|
118
|
+
.withTie(isLast ? !!target.notes[0].tie : !target.isRest);
|
|
119
|
+
});
|
|
120
|
+
const newVoice = [...voice];
|
|
121
|
+
newVoice.splice(noteIndex, 1, ...leftNotes, ...rightNotes);
|
|
122
|
+
const newVoices = [...this.voices];
|
|
123
|
+
newVoices[voiceIndex] = newVoice;
|
|
124
|
+
return this.withVoices(newVoices);
|
|
125
|
+
}
|
|
42
126
|
get hasStartRepeat() {
|
|
43
|
-
return this.repeats.some((r) => r.type ===
|
|
127
|
+
return this.repeats.some((r) => r.type === 'start');
|
|
44
128
|
}
|
|
45
129
|
get hasEndRepeat() {
|
|
46
|
-
return this.repeats.some((r) => r.type ===
|
|
130
|
+
return this.repeats.some((r) => r.type === 'end');
|
|
47
131
|
}
|
|
48
|
-
changeNoteDuration(noteIndex, newDuration,
|
|
132
|
+
changeNoteDuration(noteIndex, newDuration, dotCountOrIsDotted = 0, voiceIndex = 0) {
|
|
49
133
|
const voice = this.voices[voiceIndex];
|
|
50
134
|
if (!voice || noteIndex < 0 || noteIndex >= voice.length)
|
|
51
135
|
return this;
|
|
52
136
|
const targetNoteSet = voice[noteIndex];
|
|
53
137
|
const oldVal = targetNoteSet.getDurationValue();
|
|
54
|
-
const
|
|
55
|
-
const newVal =
|
|
138
|
+
const dotCount = typeof dotCountOrIsDotted === 'number' ? dotCountOrIsDotted : (dotCountOrIsDotted ? 1 : 0);
|
|
139
|
+
const newVal = calculateDurationValue(newDuration, dotCount);
|
|
56
140
|
if (Math.abs(newVal - oldVal) < 0.001)
|
|
57
141
|
return this;
|
|
58
142
|
const updatedVoice = [...voice];
|
|
59
143
|
if (newVal < oldVal) {
|
|
60
144
|
const gap = oldVal - newVal;
|
|
61
|
-
updatedVoice[noteIndex] = targetNoteSet.withDuration(newDuration,
|
|
145
|
+
updatedVoice[noteIndex] = targetNoteSet.withDuration(newDuration, dotCountOrIsDotted);
|
|
62
146
|
const rests = decomposeDuration(gap).map((d) => new NoteSet([new Note(d.duration, undefined, true, d.isDotted)]));
|
|
63
147
|
updatedVoice.splice(noteIndex + 1, 0, ...rests);
|
|
64
148
|
}
|
|
@@ -70,7 +154,7 @@ export class Measure {
|
|
|
70
154
|
}
|
|
71
155
|
if (available < delta - 0.001)
|
|
72
156
|
return this;
|
|
73
|
-
updatedVoice[noteIndex] = targetNoteSet.withDuration(newDuration,
|
|
157
|
+
updatedVoice[noteIndex] = targetNoteSet.withDuration(newDuration, dotCountOrIsDotted);
|
|
74
158
|
let consumed = 0;
|
|
75
159
|
let removeCount = 0;
|
|
76
160
|
const replacements = [];
|
|
@@ -164,7 +248,7 @@ export class Measure {
|
|
|
164
248
|
for (let i = index + 1; i < updatedVoice.length; i++) {
|
|
165
249
|
available += updatedVoice[i].getDurationValue();
|
|
166
250
|
}
|
|
167
|
-
// If there's enough space to consume, do it.
|
|
251
|
+
// If there's enough space to consume, do it.
|
|
168
252
|
// If not, we still replace it but it will overflow (which simplifyRests might fix or layout will handle)
|
|
169
253
|
updatedVoice[index] = newNoteSet;
|
|
170
254
|
let consumed = 0;
|
|
@@ -235,7 +319,7 @@ export class Measure {
|
|
|
235
319
|
if (ts.beats === 4 || ts.beats === 2) {
|
|
236
320
|
// In 4/4 or 2/4, eighth notes are often beamed in groups of 2 beats (length 2.0)
|
|
237
321
|
// unless there are sixteenth notes which force 1-beat groupings.
|
|
238
|
-
beamBeatLength =
|
|
322
|
+
beamBeatLength = lastGroupHasSixteenths || isSixteenth ? 1.0 : 2.0;
|
|
239
323
|
}
|
|
240
324
|
}
|
|
241
325
|
else if (ts.beatType === 8) {
|
|
@@ -368,9 +452,11 @@ export class Measure {
|
|
|
368
452
|
if (!voice || voice.length === 0)
|
|
369
453
|
return this;
|
|
370
454
|
const totalDur = this.getTotalDuration(voiceIndex);
|
|
371
|
-
const expectedDur = this.timeSignature
|
|
455
|
+
const expectedDur = this.timeSignature
|
|
456
|
+
? (this.timeSignature.beats * 4) / this.timeSignature.beatType
|
|
457
|
+
: 4.0;
|
|
372
458
|
// Special Case: If the whole measure is rests, just return a single whole rest (or appropriate duration)
|
|
373
|
-
const allRests = voice.every(ns => ns.isRest);
|
|
459
|
+
const allRests = voice.every((ns) => ns.isRest);
|
|
374
460
|
if (allRests && Math.abs(totalDur - expectedDur) < 0.001) {
|
|
375
461
|
const newVoice = [new NoteSet([new Note(Duration.Whole, undefined, true)])];
|
|
376
462
|
const newVoices = [...this.voices];
|
package/dist/models/Note.d.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { Pitch } from './Pitch';
|
|
2
|
-
import { Accidental, Arpeggio, Articulation, Bowing, Duration, Dynamic, FretboardDiagram, Glissando, Hairpin, Lyric, NoteheadShape, Ornament, Ottava, Pedal, Slur, StemDirection, Tuplet } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Represents a single note or rest in a measure.
|
|
5
|
-
*/
|
|
1
|
+
import { Pitch } from './Pitch.js';
|
|
2
|
+
import { Accidental, Arpeggio, Articulation, Bowing, Duration, Dynamic, FretboardDiagram, Glissando, Hairpin, Lyric, NoteheadShape, Ornament, Ottava, Pedal, Slur, StemDirection, Tuplet } from './types.js';
|
|
6
3
|
export declare class Note {
|
|
7
4
|
readonly duration: Duration;
|
|
8
5
|
readonly pitch?: Pitch | undefined;
|
|
9
6
|
readonly isRest: boolean;
|
|
10
|
-
readonly isDotted: boolean;
|
|
11
7
|
readonly accidental?: Accidental | undefined;
|
|
12
8
|
readonly beamGroup?: number | undefined;
|
|
13
9
|
readonly articulations: Articulation[];
|
|
@@ -39,8 +35,10 @@ export declare class Note {
|
|
|
39
35
|
readonly hammerOn?: "start" | "stop" | undefined;
|
|
40
36
|
readonly pullOff?: "start" | "stop" | undefined;
|
|
41
37
|
constructor(duration: Duration, pitch?: Pitch | undefined, // undefined for rests
|
|
42
|
-
isRest?: boolean,
|
|
38
|
+
isRest?: boolean, dotCountOrIsDotted?: number | boolean, accidental?: Accidental | undefined, beamGroup?: number | undefined, // Group ID for beamed notes
|
|
43
39
|
articulations?: Articulation[], dynamic?: Dynamic | undefined, tie?: boolean | undefined, slur?: Slur | undefined, tuplet?: Tuplet | undefined, hairpin?: Hairpin | undefined, isGrace?: boolean, lyric?: Lyric | undefined, chord?: string | undefined, glissando?: Glissando | undefined, arpeggio?: Arpeggio | undefined, ottava?: Ottava | undefined, pedal?: Pedal | undefined, ornament?: Ornament | undefined, fret?: number | undefined, string?: number | undefined, fretboardDiagram?: FretboardDiagram | undefined, lyrics?: Lyric[] | undefined, staffText?: string | undefined, color?: string | undefined, notehead?: NoteheadShape | undefined, bowing?: Bowing | undefined, fingering?: number | undefined, stemDirection?: StemDirection | undefined, isStringCircled?: boolean | undefined, palmMute?: "start" | "stop" | undefined, hammerOn?: "start" | "stop" | undefined, pullOff?: "start" | "stop" | undefined);
|
|
40
|
+
readonly dotCount: number;
|
|
41
|
+
get isDotted(): boolean;
|
|
44
42
|
/**
|
|
45
43
|
* Get all lyrics as standardized objects
|
|
46
44
|
*/
|
|
@@ -74,7 +72,7 @@ export declare class Note {
|
|
|
74
72
|
withLyrics(lyrics: Lyric[]): Note;
|
|
75
73
|
withHairpin(hairpin?: Hairpin): Note;
|
|
76
74
|
withAccidental(accidental?: Accidental): Note;
|
|
77
|
-
withDuration(duration: Duration,
|
|
75
|
+
withDuration(duration: Duration, dotCountOrIsDotted?: number | boolean): Note;
|
|
78
76
|
withRest(isRest: boolean): Note;
|
|
79
77
|
withFretboardDiagram(diagram?: FretboardDiagram): Note;
|
|
80
78
|
withBeamGroup(beamGroup?: number): Note;
|
|
@@ -107,6 +105,7 @@ export interface NoteJSON {
|
|
|
107
105
|
};
|
|
108
106
|
isRest?: boolean;
|
|
109
107
|
isDotted?: boolean;
|
|
108
|
+
dotCount?: number;
|
|
110
109
|
accidental?: string;
|
|
111
110
|
beamGroup?: number;
|
|
112
111
|
articulation?: string;
|
package/dist/models/Note.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { Pitch } from './Pitch';
|
|
2
|
-
import { Accidental,
|
|
3
|
-
|
|
4
|
-
* Represents a single note or rest in a measure.
|
|
5
|
-
*/
|
|
1
|
+
import { Pitch } from './Pitch.js';
|
|
2
|
+
import { Accidental, Duration, calculateDurationValue, } from './types.js';
|
|
3
|
+
// TODO: replace dotCountOrIsDotted with dotCount: number and use it everywhere. Do not worry about legacy stuff.
|
|
6
4
|
export class Note {
|
|
7
5
|
duration;
|
|
8
6
|
pitch;
|
|
9
7
|
isRest;
|
|
10
|
-
isDotted;
|
|
11
8
|
accidental;
|
|
12
9
|
beamGroup;
|
|
13
10
|
articulations;
|
|
@@ -39,12 +36,11 @@ export class Note {
|
|
|
39
36
|
hammerOn;
|
|
40
37
|
pullOff;
|
|
41
38
|
constructor(duration, pitch, // undefined for rests
|
|
42
|
-
isRest = false,
|
|
39
|
+
isRest = false, dotCountOrIsDotted = 0, accidental, beamGroup, // Group ID for beamed notes
|
|
43
40
|
articulations = [], dynamic, tie, slur, tuplet, hairpin, isGrace = false, lyric, chord, glissando, arpeggio, ottava, pedal, ornament, fret, string, fretboardDiagram, lyrics, staffText, color, notehead, bowing, fingering, stemDirection, isStringCircled, palmMute, hammerOn, pullOff) {
|
|
44
41
|
this.duration = duration;
|
|
45
42
|
this.pitch = pitch;
|
|
46
43
|
this.isRest = isRest;
|
|
47
|
-
this.isDotted = isDotted;
|
|
48
44
|
this.accidental = accidental;
|
|
49
45
|
this.beamGroup = beamGroup;
|
|
50
46
|
this.articulations = articulations;
|
|
@@ -75,6 +71,12 @@ export class Note {
|
|
|
75
71
|
this.palmMute = palmMute;
|
|
76
72
|
this.hammerOn = hammerOn;
|
|
77
73
|
this.pullOff = pullOff;
|
|
74
|
+
this.dotCount =
|
|
75
|
+
typeof dotCountOrIsDotted === 'number' ? dotCountOrIsDotted : dotCountOrIsDotted ? 1 : 0;
|
|
76
|
+
}
|
|
77
|
+
dotCount;
|
|
78
|
+
get isDotted() {
|
|
79
|
+
return this.dotCount > 0;
|
|
78
80
|
}
|
|
79
81
|
/**
|
|
80
82
|
* Get all lyrics as standardized objects
|
|
@@ -100,8 +102,7 @@ export class Note {
|
|
|
100
102
|
getDurationValue() {
|
|
101
103
|
if (this.isGrace)
|
|
102
104
|
return 0;
|
|
103
|
-
|
|
104
|
-
let val = this.isDotted ? base * 1.5 : base;
|
|
105
|
+
let val = calculateDurationValue(this.duration, this.dotCount);
|
|
105
106
|
if (this.tuplet) {
|
|
106
107
|
val = val * (this.tuplet.normal / this.tuplet.actual);
|
|
107
108
|
}
|
|
@@ -120,10 +121,10 @@ export class Note {
|
|
|
120
121
|
this.duration === Duration.TwoHundredFiftySixth));
|
|
121
122
|
}
|
|
122
123
|
withGrace(isGrace) {
|
|
123
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
124
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
124
125
|
}
|
|
125
126
|
withChord(chord) {
|
|
126
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
127
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
127
128
|
}
|
|
128
129
|
transpose(semitones) {
|
|
129
130
|
if (this.isRest || !this.pitch) {
|
|
@@ -133,7 +134,7 @@ export class Note {
|
|
|
133
134
|
const chromaticIdx = newPitch.midiNumber % 12;
|
|
134
135
|
const hasSharp = [1, 3, 6, 8, 10].includes(chromaticIdx);
|
|
135
136
|
const newAccidental = hasSharp ? Accidental.Sharp : undefined;
|
|
136
|
-
return new Note(this.duration, newPitch, this.isRest, this.
|
|
137
|
+
return new Note(this.duration, newPitch, this.isRest, this.dotCount, newAccidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
137
138
|
}
|
|
138
139
|
transposeOctave(octaves) {
|
|
139
140
|
if (!this.pitch || this.isRest)
|
|
@@ -166,97 +167,97 @@ export class Note {
|
|
|
166
167
|
return this.withPitch(newPitch).withAccidental(newAccidental);
|
|
167
168
|
}
|
|
168
169
|
withPitch(pitch) {
|
|
169
|
-
return new Note(this.duration, pitch, false, this.
|
|
170
|
+
return new Note(this.duration, pitch, false, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
170
171
|
}
|
|
171
172
|
withArticulation(articulation) {
|
|
172
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
173
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, articulation ? [articulation] : [], this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
173
174
|
}
|
|
174
175
|
withArticulations(articulations) {
|
|
175
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
176
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
176
177
|
}
|
|
177
178
|
withDynamic(dynamic) {
|
|
178
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
179
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
179
180
|
}
|
|
180
181
|
withTie(tie) {
|
|
181
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
182
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
182
183
|
}
|
|
183
184
|
withSlur(slur) {
|
|
184
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
185
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
185
186
|
}
|
|
186
187
|
withTuplet(tuplet) {
|
|
187
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
188
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
188
189
|
}
|
|
189
190
|
withLyric(lyric) {
|
|
190
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
191
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
191
192
|
}
|
|
192
193
|
withLyrics(lyrics) {
|
|
193
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
194
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
194
195
|
}
|
|
195
196
|
withHairpin(hairpin) {
|
|
196
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
197
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
197
198
|
}
|
|
198
199
|
withAccidental(accidental) {
|
|
199
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
200
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
200
201
|
}
|
|
201
|
-
withDuration(duration,
|
|
202
|
-
return new Note(duration, this.pitch, this.isRest,
|
|
202
|
+
withDuration(duration, dotCountOrIsDotted = 0) {
|
|
203
|
+
return new Note(duration, this.pitch, this.isRest, dotCountOrIsDotted, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
203
204
|
}
|
|
204
205
|
withRest(isRest) {
|
|
205
|
-
return new Note(this.duration, this.pitch, isRest, this.
|
|
206
|
+
return new Note(this.duration, this.pitch, isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
206
207
|
}
|
|
207
208
|
withFretboardDiagram(diagram) {
|
|
208
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
209
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, diagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
209
210
|
}
|
|
210
211
|
withBeamGroup(beamGroup) {
|
|
211
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
212
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
212
213
|
}
|
|
213
214
|
withGlissando(glissando) {
|
|
214
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
215
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
215
216
|
}
|
|
216
217
|
withArpeggio(arpeggio) {
|
|
217
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
218
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
218
219
|
}
|
|
219
220
|
withOttava(ottava) {
|
|
220
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
221
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
221
222
|
}
|
|
222
223
|
withPedal(pedal) {
|
|
223
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
224
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
224
225
|
}
|
|
225
226
|
withOrnament(ornament) {
|
|
226
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
227
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
227
228
|
}
|
|
228
229
|
withTab(fret, string) {
|
|
229
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
230
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, fret, string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
230
231
|
}
|
|
231
232
|
withStaffText(text) {
|
|
232
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
233
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, text, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
233
234
|
}
|
|
234
235
|
withColor(color) {
|
|
235
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
236
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
236
237
|
}
|
|
237
238
|
withNotehead(notehead) {
|
|
238
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
239
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
239
240
|
}
|
|
240
241
|
withBowing(bowing) {
|
|
241
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
242
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
242
243
|
}
|
|
243
244
|
withFingering(fingering) {
|
|
244
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
245
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
245
246
|
}
|
|
246
247
|
withStemDirection(stemDirection) {
|
|
247
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
248
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
248
249
|
}
|
|
249
250
|
withStringCircled(isStringCircled) {
|
|
250
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
251
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, isStringCircled, this.palmMute, this.hammerOn, this.pullOff);
|
|
251
252
|
}
|
|
252
253
|
withPalmMute(palmMute) {
|
|
253
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
254
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, palmMute, this.hammerOn, this.pullOff);
|
|
254
255
|
}
|
|
255
256
|
withHammerOn(hammerOn) {
|
|
256
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
257
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, hammerOn, this.pullOff);
|
|
257
258
|
}
|
|
258
259
|
withPullOff(pullOff) {
|
|
259
|
-
return new Note(this.duration, this.pitch, this.isRest, this.
|
|
260
|
+
return new Note(this.duration, this.pitch, this.isRest, this.dotCount, this.accidental, this.beamGroup, this.articulations, this.dynamic, this.tie, this.slur, this.tuplet, this.hairpin, this.isGrace, this.lyric, this.chord, this.glissando, this.arpeggio, this.ottava, this.pedal, this.ornament, this.fret, this.string, this.fretboardDiagram, this.lyrics, this.staffText, this.color, this.notehead, this.bowing, this.fingering, this.stemDirection, this.isStringCircled, this.palmMute, this.hammerOn, pullOff);
|
|
260
261
|
}
|
|
261
262
|
toJSON() {
|
|
262
263
|
return {
|
|
@@ -273,6 +274,7 @@ export class Note {
|
|
|
273
274
|
: undefined,
|
|
274
275
|
isRest: this.isRest || undefined,
|
|
275
276
|
isDotted: this.isDotted || undefined,
|
|
277
|
+
dotCount: this.dotCount || undefined,
|
|
276
278
|
accidental: this.accidental,
|
|
277
279
|
beamGroup: this.beamGroup,
|
|
278
280
|
articulations: this.articulations.length > 0 ? this.articulations : undefined,
|
|
@@ -319,6 +321,6 @@ export class Note {
|
|
|
319
321
|
else if (data.articulation) {
|
|
320
322
|
articulations = [data.articulation];
|
|
321
323
|
}
|
|
322
|
-
return new Note(data.duration, pitch, data.isRest ?? false, data.isDotted
|
|
324
|
+
return new Note(data.duration, pitch, data.isRest ?? false, data.dotCount !== undefined ? data.dotCount : data.isDotted ? 1 : 0, data.accidental, data.beamGroup, articulations, data.dynamic, data.tie, data.slur, data.tuplet, data.hairpin, data.isGrace ?? false, data.lyric, data.chord, data.glissando, data.arpeggio, data.ottava, data.pedal, data.ornament, data.fret, data.string, data.fretboardDiagram, data.lyrics, data.staffText, data.color, data.notehead, data.bowing, data.fingering, data.stemDirection, data.isStringCircled, data.palmMute, data.hammerOn, data.pullOff);
|
|
323
325
|
}
|
|
324
326
|
}
|
package/dist/models/NoteSet.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { Note, NoteJSON } from './Note';
|
|
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';
|
|
1
|
+
import { Note, NoteJSON } from './Note.js';
|
|
2
|
+
import { Pitch } from './Pitch.js';
|
|
3
|
+
import { Accidental, Arpeggio, Articulation, Bowing, Duration, Dynamic, FretboardDiagram, Glissando, Hairpin, Lyric, NoteheadShape, Ornament, Ottava, Pedal, Slur, StemDirection, Tuplet } from './types.js';
|
|
4
4
|
export declare class NoteSet {
|
|
5
5
|
readonly notes: Note[];
|
|
6
6
|
constructor(notes: Note[]);
|
|
7
7
|
get duration(): Duration;
|
|
8
8
|
get isDotted(): boolean;
|
|
9
|
+
get dotCount(): number;
|
|
9
10
|
get isRest(): boolean;
|
|
10
11
|
get beamGroup(): number | undefined;
|
|
11
12
|
get tuplet(): Tuplet | undefined;
|
|
12
13
|
getDurationValue(): number;
|
|
13
14
|
isBeamable(): boolean;
|
|
14
|
-
withDuration(duration: Duration,
|
|
15
|
+
withDuration(duration: Duration, dotCountOrIsDotted?: number | boolean): NoteSet;
|
|
15
16
|
withBeamGroup(group?: number): NoteSet;
|
|
16
17
|
withNotes(notes: Note[]): NoteSet;
|
|
17
18
|
withLyrics(lyrics: Lyric[]): NoteSet;
|
package/dist/models/NoteSet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Note } from './Note';
|
|
2
|
-
import { Duration, } from './types';
|
|
1
|
+
import { Note } from './Note.js';
|
|
2
|
+
import { Duration, } from './types.js';
|
|
3
3
|
export class NoteSet {
|
|
4
4
|
notes;
|
|
5
5
|
constructor(notes) {
|
|
@@ -14,6 +14,9 @@ export class NoteSet {
|
|
|
14
14
|
get isDotted() {
|
|
15
15
|
return this.notes[0].isDotted;
|
|
16
16
|
}
|
|
17
|
+
get dotCount() {
|
|
18
|
+
return this.notes[0].dotCount;
|
|
19
|
+
}
|
|
17
20
|
get isRest() {
|
|
18
21
|
return this.notes[0].isRest;
|
|
19
22
|
}
|
|
@@ -29,8 +32,8 @@ export class NoteSet {
|
|
|
29
32
|
isBeamable() {
|
|
30
33
|
return this.notes[0].isBeamable();
|
|
31
34
|
}
|
|
32
|
-
withDuration(duration,
|
|
33
|
-
return new NoteSet(this.notes.map((n) => n.withDuration(duration,
|
|
35
|
+
withDuration(duration, dotCountOrIsDotted = 0) {
|
|
36
|
+
return new NoteSet(this.notes.map((n) => n.withDuration(duration, dotCountOrIsDotted)));
|
|
34
37
|
}
|
|
35
38
|
withBeamGroup(group) {
|
|
36
39
|
return new NoteSet(this.notes.map((n) => n.withBeamGroup(group)));
|
package/dist/models/Part.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Instrument } from './Instrument';
|
|
2
|
-
import { Measure } from './Measure';
|
|
3
|
-
import { Note } from './Note';
|
|
4
|
-
import { Staff, StaffJSON } from './Staff';
|
|
5
|
-
import { Duration } from './types';
|
|
1
|
+
import { Instrument } from './Instrument.js';
|
|
2
|
+
import { Measure } from './Measure.js';
|
|
3
|
+
import { Note } from './Note.js';
|
|
4
|
+
import { Staff, StaffJSON } from './Staff.js';
|
|
5
|
+
import { Duration } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Represents a musical part (instrument/voice) which can have multiple staves.
|
|
8
8
|
*/
|
package/dist/models/Part.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PRESET_INSTRUMENTS } from './Instrument';
|
|
2
|
-
import { Staff } from './Staff';
|
|
1
|
+
import { PRESET_INSTRUMENTS } from './Instrument.js';
|
|
2
|
+
import { Staff } from './Staff.js';
|
|
3
3
|
/**
|
|
4
4
|
* Represents a musical part (instrument/voice) which can have multiple staves.
|
|
5
5
|
*/
|
package/dist/models/Pitch.d.ts
CHANGED
package/dist/models/Pitch.js
CHANGED
package/dist/models/Score.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Measure } from './Measure';
|
|
2
|
-
import { NoteSet } from './NoteSet';
|
|
3
|
-
import { Part, PartJSON } from './Part';
|
|
4
|
-
import { Staff } from './Staff';
|
|
5
|
-
import { Clef, Duration, Genre, KeySignature, TimeSignature } from './types';
|
|
1
|
+
import { Measure } from './Measure.js';
|
|
2
|
+
import { NoteSet } from './NoteSet.js';
|
|
3
|
+
import { Part, PartJSON } from './Part.js';
|
|
4
|
+
import { Staff } from './Staff.js';
|
|
5
|
+
import { Clef, Duration, Genre, KeySignature, TimeSignature } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Represents a complete musical score.
|
|
8
8
|
*/
|
|
@@ -21,11 +21,13 @@ export declare class Score {
|
|
|
21
21
|
readonly subtitle: string;
|
|
22
22
|
readonly genre: Genre;
|
|
23
23
|
readonly tempoText: string;
|
|
24
|
-
|
|
24
|
+
readonly tempoDotCount: number;
|
|
25
|
+
constructor(title: string, composer: string, timeSignature: TimeSignature, keySignature: KeySignature, parts: Part[], bpm?: number, tempoDuration?: Duration, tempoIsDotted?: boolean, copyright?: string, lyricist?: string, swing?: boolean, subtitle?: string, genre?: Genre, tempoText?: string, tempoDotCount?: number);
|
|
25
26
|
withTitle(title: string): Score;
|
|
26
27
|
withComposer(composer: string): Score;
|
|
27
28
|
withSubtitle(subtitle: string): Score;
|
|
28
29
|
withGenre(genre: Genre): Score;
|
|
30
|
+
withBpm(bpm: number): Score;
|
|
29
31
|
getMeasureCount(): number;
|
|
30
32
|
getTimeSignatureAt(measureIndex: number): TimeSignature;
|
|
31
33
|
getKeySignatureAt(measureIndex: number): KeySignature;
|
|
@@ -56,6 +58,7 @@ export declare class Score {
|
|
|
56
58
|
replaceMeasure(partIndex: number, staffIndex: number, measureIndex: number, newMeasure: Measure, autoBeam?: boolean): Score;
|
|
57
59
|
deleteNote(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, voiceIndex?: number): Score;
|
|
58
60
|
changeNoteDuration(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, newDuration: Duration, isDotted?: boolean, voiceIndex?: number): Score;
|
|
61
|
+
moveNoteToVoice(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, fromVoiceIndex: number, toVoiceIndex: number): Score;
|
|
59
62
|
pasteNotes(partIndex: number, staffIndex: number, measureIndex: number, noteIndex: number, notesToPaste: NoteSet[]): Score;
|
|
60
63
|
withTempo(bpm: number, duration: Duration, isDotted: boolean): Score;
|
|
61
64
|
withTempoText(text: string): Score;
|
|
@@ -81,6 +84,7 @@ export interface ScoreJSON {
|
|
|
81
84
|
bpm?: number;
|
|
82
85
|
tempoDuration?: Duration;
|
|
83
86
|
tempoIsDotted?: boolean;
|
|
87
|
+
tempoDotCount?: number;
|
|
84
88
|
copyright?: string;
|
|
85
89
|
lyricist?: string;
|
|
86
90
|
swing?: boolean;
|
package/dist/models/Score.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Measure } from './Measure';
|
|
2
|
-
import { Part } from './Part';
|
|
3
|
-
import { Duration, Genre, decomposeDuration } from './types';
|
|
1
|
+
import { Measure } from './Measure.js';
|
|
2
|
+
import { Part } from './Part.js';
|
|
3
|
+
import { Duration, Genre, decomposeDuration } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Represents a complete musical score.
|
|
6
6
|
*/
|
|
@@ -19,7 +19,8 @@ export class Score {
|
|
|
19
19
|
subtitle;
|
|
20
20
|
genre;
|
|
21
21
|
tempoText;
|
|
22
|
-
|
|
22
|
+
tempoDotCount;
|
|
23
|
+
constructor(title, composer, timeSignature, keySignature, parts, bpm = 120, tempoDuration = Duration.Quarter, tempoIsDotted = false, copyright = '', lyricist = '', swing = false, subtitle = '', genre = Genre.Unknown, tempoText = '', tempoDotCount = 0) {
|
|
23
24
|
this.title = title;
|
|
24
25
|
this.composer = composer;
|
|
25
26
|
this.timeSignature = timeSignature;
|
|
@@ -34,18 +35,22 @@ export class Score {
|
|
|
34
35
|
this.subtitle = subtitle;
|
|
35
36
|
this.genre = genre;
|
|
36
37
|
this.tempoText = tempoText;
|
|
38
|
+
this.tempoDotCount = tempoDotCount;
|
|
37
39
|
}
|
|
38
40
|
withTitle(title) {
|
|
39
|
-
return new Score(title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
41
|
+
return new Score(title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
40
42
|
}
|
|
41
43
|
withComposer(composer) {
|
|
42
|
-
return new Score(this.title, composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
44
|
+
return new Score(this.title, composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
43
45
|
}
|
|
44
46
|
withSubtitle(subtitle) {
|
|
45
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, subtitle, this.genre, this.tempoText);
|
|
47
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
46
48
|
}
|
|
47
49
|
withGenre(genre) {
|
|
48
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, genre, this.tempoText);
|
|
50
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, genre, this.tempoText, this.tempoDotCount);
|
|
51
|
+
}
|
|
52
|
+
withBpm(bpm) {
|
|
53
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
49
54
|
}
|
|
50
55
|
getMeasureCount() {
|
|
51
56
|
return this.parts[0]?.getMeasureCount() ?? 0;
|
|
@@ -88,7 +93,7 @@ export class Score {
|
|
|
88
93
|
}
|
|
89
94
|
static fromJSON(data) {
|
|
90
95
|
const parts = data.parts.map((p) => Part.fromJSON(p));
|
|
91
|
-
return new Score(data.title, data.composer, data.timeSignature, data.keySignature, parts, data.bpm ?? 120, data.tempoDuration ?? Duration.Quarter, data.tempoIsDotted ?? false, data.copyright ?? '', data.lyricist ?? '', data.swing ?? false, data.subtitle ?? '', data.genre || Genre.Unknown, data.tempoText ?? '');
|
|
96
|
+
return new Score(data.title, data.composer, data.timeSignature, data.keySignature, parts, data.bpm ?? 120, data.tempoDuration ?? Duration.Quarter, data.tempoIsDotted ?? false, data.copyright ?? '', data.lyricist ?? '', data.swing ?? false, data.subtitle ?? '', data.genre || Genre.Unknown, data.tempoText ?? '', data.tempoDotCount ?? (data.tempoIsDotted ? 1 : 0));
|
|
92
97
|
}
|
|
93
98
|
transpose(semitones) {
|
|
94
99
|
const semitoneToFifths = {
|
|
@@ -112,7 +117,7 @@ export class Score {
|
|
|
112
117
|
newFifths -= 12;
|
|
113
118
|
while (newFifths < -7)
|
|
114
119
|
newFifths += 12;
|
|
115
|
-
return new Score(this.title, this.composer, this.timeSignature, { fifths: newFifths }, this.parts.map((p) => p.transpose(semitones)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
120
|
+
return new Score(this.title, this.composer, this.timeSignature, { fifths: newFifths }, this.parts.map((p) => p.transpose(semitones)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
116
121
|
}
|
|
117
122
|
replaceNote(partIndex, staffIndex, measureIndex, noteIndex, newNote, voiceIndex = 0) {
|
|
118
123
|
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
@@ -122,10 +127,10 @@ export class Score {
|
|
|
122
127
|
return this.replaceMeasure(partIndex, staffIndex, measureIndex, updatedMeasure, true);
|
|
123
128
|
}
|
|
124
129
|
withKeySignature(keySig) {
|
|
125
|
-
return new Score(this.title, this.composer, this.timeSignature, keySig, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
130
|
+
return new Score(this.title, this.composer, this.timeSignature, keySig, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
126
131
|
}
|
|
127
132
|
withTimeSignature(timeSig) {
|
|
128
|
-
return new Score(this.title, this.composer, timeSig, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
133
|
+
return new Score(this.title, this.composer, timeSig, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
129
134
|
}
|
|
130
135
|
updateMeasureSignatures(measureIndex, signatures, autoBeam = true) {
|
|
131
136
|
let score = this;
|
|
@@ -170,7 +175,7 @@ export class Score {
|
|
|
170
175
|
for (let sIdx = 0; sIdx < currentScore.parts[pIdx].staves.length; sIdx++) {
|
|
171
176
|
const staff = currentScore.parts[pIdx].staves[sIdx];
|
|
172
177
|
if (staff.measures.length < maxMeasures) {
|
|
173
|
-
|
|
178
|
+
const updatedMsrs = [...staff.measures];
|
|
174
179
|
for (let i = staff.measures.length; i < maxMeasures; i++) {
|
|
175
180
|
const ts = currentScore.getTimeSignatureAt(i);
|
|
176
181
|
const targetDur = ts.beats * (4 / ts.beatType);
|
|
@@ -201,10 +206,10 @@ export class Score {
|
|
|
201
206
|
const staff = this.parts[pIdx].staves[sIdx];
|
|
202
207
|
const measures = staff.measures;
|
|
203
208
|
const maxVoices = Math.max(...measures.map((m) => m.voices.length), 1);
|
|
204
|
-
|
|
209
|
+
const updatedMeasures = [...measures];
|
|
205
210
|
for (let vIdx = 0; vIdx < maxVoices; vIdx++) {
|
|
206
211
|
// 1. Collect notes and merge tied notes into logical streams
|
|
207
|
-
|
|
212
|
+
const stream = [];
|
|
208
213
|
for (let mIdx = fromMeasureIndex; mIdx < measures.length; mIdx++) {
|
|
209
214
|
const msr = measures[mIdx];
|
|
210
215
|
const voice = msr.voices[vIdx] || [];
|
|
@@ -295,7 +300,7 @@ export class Score {
|
|
|
295
300
|
}
|
|
296
301
|
const newParts = [...this.parts];
|
|
297
302
|
newParts[partIndex] = newParts[partIndex].replaceMeasure(staffIndex, measureIndex, measureToUse);
|
|
298
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
303
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
299
304
|
}
|
|
300
305
|
deleteNote(partIndex, staffIndex, measureIndex, noteIndex, voiceIndex = 0) {
|
|
301
306
|
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
@@ -311,6 +316,15 @@ export class Score {
|
|
|
311
316
|
const updatedMeasure = measure.changeNoteDuration(noteIndex, newDuration, isDotted, voiceIndex);
|
|
312
317
|
return this.replaceMeasure(partIndex, staffIndex, measureIndex, updatedMeasure, true);
|
|
313
318
|
}
|
|
319
|
+
moveNoteToVoice(partIndex, staffIndex, measureIndex, noteIndex, fromVoiceIndex, toVoiceIndex) {
|
|
320
|
+
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
321
|
+
return this;
|
|
322
|
+
const measure = this.parts[partIndex].staves[staffIndex].measures[measureIndex];
|
|
323
|
+
if (!measure)
|
|
324
|
+
return this;
|
|
325
|
+
const updatedMeasure = measure.moveNoteToVoice(fromVoiceIndex, noteIndex, toVoiceIndex);
|
|
326
|
+
return this.replaceMeasure(partIndex, staffIndex, measureIndex, updatedMeasure, true);
|
|
327
|
+
}
|
|
314
328
|
pasteNotes(partIndex, staffIndex, measureIndex, noteIndex, notesToPaste) {
|
|
315
329
|
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
316
330
|
return this;
|
|
@@ -370,22 +384,22 @@ export class Score {
|
|
|
370
384
|
return newScore;
|
|
371
385
|
}
|
|
372
386
|
withTempo(bpm, duration, isDotted) {
|
|
373
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, bpm, duration, isDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
387
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, bpm, duration, isDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
374
388
|
}
|
|
375
389
|
withTempoText(text) {
|
|
376
390
|
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, text);
|
|
377
391
|
}
|
|
378
392
|
withSwing(swing) {
|
|
379
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, swing, this.subtitle, this.genre, this.tempoText);
|
|
393
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
380
394
|
}
|
|
381
395
|
withLyricist(lyricist) {
|
|
382
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
396
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
383
397
|
}
|
|
384
398
|
withCopyright(copyright) {
|
|
385
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
399
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts, this.bpm, this.tempoDuration, this.tempoIsDotted, copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
386
400
|
}
|
|
387
401
|
withParts(parts) {
|
|
388
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
402
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, parts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
389
403
|
}
|
|
390
404
|
toJSON() {
|
|
391
405
|
return {
|
|
@@ -403,6 +417,7 @@ export class Score {
|
|
|
403
417
|
subtitle: this.subtitle,
|
|
404
418
|
genre: this.genre,
|
|
405
419
|
tempoText: this.tempoText,
|
|
420
|
+
tempoDotCount: this.tempoDotCount,
|
|
406
421
|
};
|
|
407
422
|
}
|
|
408
423
|
replacePart(partIndex, newPart) {
|
|
@@ -410,29 +425,29 @@ export class Score {
|
|
|
410
425
|
return this;
|
|
411
426
|
const newParts = [...this.parts];
|
|
412
427
|
newParts[partIndex] = newPart;
|
|
413
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
428
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
414
429
|
}
|
|
415
430
|
addPart(newPart) {
|
|
416
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, [...this.parts, newPart], this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
431
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, [...this.parts, newPart], this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
417
432
|
}
|
|
418
433
|
removePart(partIndex) {
|
|
419
434
|
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
420
435
|
return this;
|
|
421
436
|
const newParts = this.parts.filter((_, i) => i !== partIndex);
|
|
422
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
437
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
423
438
|
}
|
|
424
439
|
replaceStaff(partIndex, staffIndex, newStaff) {
|
|
425
440
|
if (partIndex < 0 || partIndex >= this.parts.length)
|
|
426
441
|
return this;
|
|
427
442
|
const newParts = [...this.parts];
|
|
428
443
|
newParts[partIndex] = newParts[partIndex].replaceStaff(staffIndex, newStaff);
|
|
429
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
444
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, newParts, this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
430
445
|
}
|
|
431
446
|
addMeasure(index, measure) {
|
|
432
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts.map((p) => p.addMeasure(index, measure)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
447
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts.map((p) => p.addMeasure(index, measure)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
433
448
|
}
|
|
434
449
|
deleteMeasure(index) {
|
|
435
|
-
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts.map((p) => p.deleteMeasure(index)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText);
|
|
450
|
+
return new Score(this.title, this.composer, this.timeSignature, this.keySignature, this.parts.map((p) => p.deleteMeasure(index)), this.bpm, this.tempoDuration, this.tempoIsDotted, this.copyright, this.lyricist, this.swing, this.subtitle, this.genre, this.tempoText, this.tempoDotCount);
|
|
436
451
|
}
|
|
437
452
|
getPlaybackSequence() {
|
|
438
453
|
const measures = this.parts[0]?.staves[0]?.measures || [];
|
package/dist/models/Staff.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Measure, MeasureJSON } from './Measure';
|
|
2
|
-
import { Note } from './Note';
|
|
3
|
-
import { Pitch } from './Pitch';
|
|
4
|
-
import { Clef, Duration } from './types';
|
|
1
|
+
import { Measure, MeasureJSON } from './Measure.js';
|
|
2
|
+
import { Note } from './Note.js';
|
|
3
|
+
import { Pitch } from './Pitch.js';
|
|
4
|
+
import { Clef, Duration } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Represents a single staff (one set of 5 lines with a clef).
|
|
7
7
|
*/
|
package/dist/models/Staff.js
CHANGED
package/dist/models/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from './types';
|
|
2
|
-
export * from './Pitch';
|
|
3
|
-
export * from './Note';
|
|
4
|
-
export * from './NoteSet';
|
|
5
|
-
export * from './Measure';
|
|
6
|
-
export * from './Staff';
|
|
7
|
-
export * from './Part';
|
|
8
|
-
export * from './Score';
|
|
9
|
-
export * from './Instrument';
|
|
10
|
-
export * from './PreMeasure';
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './Pitch.js';
|
|
3
|
+
export * from './Note.js';
|
|
4
|
+
export * from './NoteSet.js';
|
|
5
|
+
export * from './Measure.js';
|
|
6
|
+
export * from './Staff.js';
|
|
7
|
+
export * from './Part.js';
|
|
8
|
+
export * from './Score.js';
|
|
9
|
+
export * from './Instrument.js';
|
|
10
|
+
export * from './PreMeasure.js';
|
package/dist/models/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export * from './types';
|
|
2
|
-
export * from './Pitch';
|
|
3
|
-
export * from './Note';
|
|
4
|
-
export * from './NoteSet';
|
|
5
|
-
export * from './Measure';
|
|
6
|
-
export * from './Staff';
|
|
7
|
-
export * from './Part';
|
|
8
|
-
export * from './Score';
|
|
9
|
-
export * from './Instrument';
|
|
10
|
-
export * from './PreMeasure';
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './Pitch.js';
|
|
3
|
+
export * from './Note.js';
|
|
4
|
+
export * from './NoteSet.js';
|
|
5
|
+
export * from './Measure.js';
|
|
6
|
+
export * from './Staff.js';
|
|
7
|
+
export * from './Part.js';
|
|
8
|
+
export * from './Score.js';
|
|
9
|
+
export * from './Instrument.js';
|
|
10
|
+
export * from './PreMeasure.js';
|
package/dist/types/Duration.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export declare enum Duration {
|
|
|
10
10
|
TwoHundredFiftySixth = "two-hundred-fifty-sixth"
|
|
11
11
|
}
|
|
12
12
|
export declare const DURATION_VALUES: Record<Duration, number>;
|
|
13
|
+
/**
|
|
14
|
+
* Calculates the numeric duration value (quarter = 1) for a given duration and dot count.
|
|
15
|
+
*/
|
|
16
|
+
export declare function calculateDurationValue(duration: Duration, dotCount?: number): number;
|
|
13
17
|
/**
|
|
14
18
|
* Decomposes a duration value (quarter = 1) into a list of duration/dot pairs.
|
|
15
19
|
*/
|
package/dist/types/Duration.js
CHANGED
|
@@ -22,6 +22,19 @@ export const DURATION_VALUES = {
|
|
|
22
22
|
[Duration.OneHundredTwentyEighth]: 0.03125,
|
|
23
23
|
[Duration.TwoHundredFiftySixth]: 0.015625,
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Calculates the numeric duration value (quarter = 1) for a given duration and dot count.
|
|
27
|
+
*/
|
|
28
|
+
export function calculateDurationValue(duration, dotCount = 0) {
|
|
29
|
+
const base = DURATION_VALUES[duration];
|
|
30
|
+
let multiplier = 1;
|
|
31
|
+
let dotValue = 0.5;
|
|
32
|
+
for (let i = 0; i < dotCount; i++) {
|
|
33
|
+
multiplier += dotValue;
|
|
34
|
+
dotValue /= 2;
|
|
35
|
+
}
|
|
36
|
+
return base * multiplier;
|
|
37
|
+
}
|
|
25
38
|
/**
|
|
26
39
|
* Decomposes a duration value (quarter = 1) into a list of duration/dot pairs.
|
|
27
40
|
*/
|
package/dist/types/Tempo.d.ts
CHANGED