@tspro/web-music-score 3.2.0 → 4.0.1

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +38 -10
  2. package/README.md +189 -331
  3. package/dist/audio/index.d.mts +40 -1
  4. package/dist/audio/index.d.ts +40 -1
  5. package/dist/audio/index.js +1 -1
  6. package/dist/audio/index.mjs +2 -2
  7. package/dist/audio-cg/index.d.mts +3 -0
  8. package/dist/audio-cg/index.d.ts +3 -0
  9. package/dist/audio-cg/index.js +1 -1
  10. package/dist/audio-cg/index.mjs +2 -2
  11. package/dist/{chunk-LCTM7BID.mjs → chunk-YFPLOHP2.mjs} +2 -2
  12. package/dist/core/index.d.mts +12 -0
  13. package/dist/core/index.d.ts +12 -0
  14. package/dist/core/index.js +3 -2
  15. package/dist/core/index.mjs +4 -3
  16. package/dist/guitar-CaZJDA05.d.ts +35 -0
  17. package/dist/guitar-DdexKdN6.d.mts +35 -0
  18. package/dist/iife/index.global.js +11 -11
  19. package/dist/{interface-Bn5HFt_U.d.mts → music-objects-DJQ4d2OA.d.mts} +640 -136
  20. package/dist/{interface-BlNl69uT.d.ts → music-objects-Dc3kR-XF.d.ts} +640 -136
  21. package/dist/note-eA2xPPiG.d.mts +294 -0
  22. package/dist/note-eA2xPPiG.d.ts +294 -0
  23. package/dist/pieces/index.d.mts +22 -3
  24. package/dist/pieces/index.d.ts +22 -3
  25. package/dist/pieces/index.js +7 -7
  26. package/dist/pieces/index.mjs +11 -11
  27. package/dist/react-ui/index.d.mts +166 -17
  28. package/dist/react-ui/index.d.ts +166 -17
  29. package/dist/react-ui/index.js +78 -1
  30. package/dist/react-ui/index.mjs +79 -2
  31. package/dist/scale-B2Icbetz.d.ts +230 -0
  32. package/dist/scale-BbDJTbrG.d.mts +230 -0
  33. package/dist/score/index.d.mts +359 -39
  34. package/dist/score/index.d.ts +359 -39
  35. package/dist/score/index.js +1252 -594
  36. package/dist/score/index.mjs +1255 -599
  37. package/dist/tempo-CtUhvJbr.d.mts +369 -0
  38. package/dist/tempo-Dt8aHpol.d.ts +369 -0
  39. package/dist/theory/index.d.mts +29 -13
  40. package/dist/theory/index.d.ts +29 -13
  41. package/dist/theory/index.js +583 -96
  42. package/dist/theory/index.mjs +580 -94
  43. package/package.json +2 -2
  44. package/dist/guitar-C2Cp71NZ.d.ts +0 -17
  45. package/dist/guitar-DggbM2UL.d.mts +0 -17
  46. package/dist/note-BFa43I86.d.mts +0 -85
  47. package/dist/note-BFa43I86.d.ts +0 -85
  48. package/dist/scale-DRR-t4Kr.d.mts +0 -74
  49. package/dist/scale-ebJm37q1.d.ts +0 -74
  50. package/dist/tempo-B4h5Ktob.d.mts +0 -104
  51. package/dist/tempo-DgqDEsn0.d.ts +0 -104
@@ -1,4 +1,4 @@
1
- /* WebMusicScore v3.2.0 | (c) 2023 PahkaSoft | MIT License | Includes: Tone.js (MIT License) */
1
+ /* WebMusicScore v4.0.1 | (c) 2023 PahkaSoft | MIT License | Includes: Tone.js (MIT License) */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -34,11 +34,10 @@ __export(theory_exports, {
34
34
  Handedness: () => Handedness,
35
35
  Interval: () => Interval,
36
36
  KeySignature: () => KeySignature,
37
- MaxNoteLength: () => MaxNoteLength,
38
- MinNoteLength: () => MinNoteLength,
39
37
  Mode: () => Mode,
40
38
  Note: () => Note,
41
39
  NoteLength: () => NoteLength,
40
+ NoteLengthProps: () => NoteLengthProps,
42
41
  PitchNotation: () => PitchNotation,
43
42
  PitchNotationList: () => PitchNotationList,
44
43
  RhythmProps: () => RhythmProps,
@@ -48,6 +47,7 @@ __export(theory_exports, {
48
47
  SymbolSet: () => SymbolSet,
49
48
  TimeSignature: () => TimeSignature,
50
49
  TuningNameList: () => TuningNameList,
50
+ Tuplet: () => Tuplet,
51
51
  alterTempoSpeed: () => alterTempoSpeed,
52
52
  getDefaultKeySignature: () => getDefaultKeySignature,
53
53
  getDefaultScale: () => getDefaultScale,
@@ -65,7 +65,8 @@ __export(theory_exports, {
65
65
  validateNoteLength: () => validateNoteLength,
66
66
  validatePitchNotation: () => validatePitchNotation,
67
67
  validateScaleType: () => validateScaleType,
68
- validateTuningName: () => validateTuningName
68
+ validateTuningName: () => validateTuningName,
69
+ validateTupletRatio: () => validateTupletRatio
69
70
  });
70
71
  module.exports = __toCommonJS(theory_exports);
71
72
 
@@ -91,10 +92,10 @@ var PitchNotation = /* @__PURE__ */ ((PitchNotation2) => {
91
92
  var PitchNotationList = import_ts_utils_lib.Utils.Enum.getEnumValues(PitchNotation);
92
93
  var DefaultPitchNotation = 0 /* Scientific */;
93
94
  function validatePitchNotation(pn) {
94
- if (!import_ts_utils_lib.Utils.Is.isEnumValue(pn, PitchNotation)) {
95
- throw new import_core.MusicError(import_core.MusicErrorType.InvalidArg, `Invalid pitchNotation: ${pn}`);
96
- } else {
95
+ if (import_ts_utils_lib.Utils.Is.isEnumValue(pn, PitchNotation)) {
97
96
  return pn;
97
+ } else {
98
+ throw new import_core.MusicError(import_core.MusicErrorType.InvalidArg, `Invalid pitchNotation: ${pn}`);
98
99
  }
99
100
  }
100
101
  function getPitchNotationName(pn) {
@@ -109,10 +110,10 @@ var GuitarNoteLabel = /* @__PURE__ */ ((GuitarNoteLabel2) => {
109
110
  var DefaultGuitarNoteLabel = "Default" /* Default */;
110
111
  var GuitarNoteLabelList = import_ts_utils_lib.Utils.Enum.getEnumValues(GuitarNoteLabel);
111
112
  function validateGuitarNoteLabel(label) {
112
- if (!import_ts_utils_lib.Utils.Is.isEnumValue(label, GuitarNoteLabel)) {
113
- throw new import_core.MusicError(import_core.MusicErrorType.Timesignature, `Invalid guitarNoteLabel: ${label}`);
114
- } else {
113
+ if (import_ts_utils_lib.Utils.Is.isEnumValue(label, GuitarNoteLabel)) {
115
114
  return label;
115
+ } else {
116
+ throw new import_core.MusicError(import_core.MusicErrorType.Timesignature, `Invalid guitarNoteLabel: ${label}`);
116
117
  }
117
118
  }
118
119
 
@@ -131,8 +132,11 @@ var DiatonicToChromaticMap = [0, 2, 4, 5, 7, 9, 11];
131
132
  var NoteNameRegex = /^([A-G])((?:bb|𝄫|♭|b|#|♯|x|𝄪)?)?(-?\d+)?$/;
132
133
  var _Note = class _Note {
133
134
  constructor(arg, accidental, octave) {
135
+ /** Diatonic class */
134
136
  __publicField(this, "diatonicClass");
137
+ /** Accidental. */
135
138
  __publicField(this, "accidental");
139
+ /** Octave. */
136
140
  __publicField(this, "octave");
137
141
  if (typeof arg === "number" && typeof accidental === "number" && octave === void 0) {
138
142
  _Note.validateDiatonicId(arg);
@@ -151,21 +155,32 @@ var _Note = class _Note {
151
155
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid args: ${arg}, ${accidental}, ${octave}`);
152
156
  }
153
157
  }
158
+ /** Diatonic id getter. */
154
159
  get diatonicId() {
155
160
  return _Note.getDiatonicIdInOctave(this.diatonicClass, this.octave);
156
161
  }
162
+ /** Chromatic id getter. */
157
163
  get chromaticId() {
158
164
  return _Note.getChromaticIdInOctave(DiatonicToChromaticMap[this.diatonicClass] + this.accidental, this.octave);
159
165
  }
166
+ /** Midi number getter (implemented same as chromatic id). */
160
167
  get midiNumber() {
161
168
  return this.chromaticId;
162
169
  }
170
+ /** Chromatic class getter. */
163
171
  get chromaticClass() {
164
172
  return _Note.getChromaticClass(DiatonicToChromaticMap[this.diatonicClass] + this.accidental);
165
173
  }
174
+ /** Note letter getter. */
166
175
  get noteLetter() {
167
176
  return NoteLetters[this.diatonicClass];
168
177
  }
178
+ /**
179
+ * Format note to string presentation.
180
+ * @param pitchNotation - Pitchy notation.
181
+ * @param symbolSet - Symbol set.
182
+ * @returns - String presentation of note.
183
+ */
169
184
  format(pitchNotation, symbolSet) {
170
185
  let { noteLetter, octave } = this;
171
186
  let accidentalSymbol = _Note.getAccidentalSymbol(this.accidental, symbolSet);
@@ -179,11 +194,21 @@ var _Note = class _Note {
179
194
  return noteLetter + accidentalSymbol + octave;
180
195
  }
181
196
  }
197
+ /**
198
+ * Format note to string presentation without octave number.
199
+ * @param symbolSet - Symbol set.
200
+ * @returns - String presentation of note without octave number.
201
+ */
182
202
  formatOmitOctave(symbolSet) {
183
203
  let noteLetter = NoteLetters[this.diatonicClass];
184
204
  let accidental = _Note.getAccidentalSymbol(this.accidental, symbolSet);
185
205
  return noteLetter + accidental;
186
206
  }
207
+ /**
208
+ * Get note.
209
+ * @param noteName - Note name (e.g. "C4").
210
+ * @returns - Note.
211
+ */
187
212
  static getNote(noteName) {
188
213
  let note = this.noteByNameCache.get(noteName);
189
214
  if (note === void 0) {
@@ -199,6 +224,11 @@ var _Note = class _Note {
199
224
  }
200
225
  return note;
201
226
  }
227
+ /**
228
+ * Get chromatic note. There are number of alternatives, this function uses simple logic to choose one.
229
+ * @param chromaticId - Chromatic id.
230
+ * @returns - Note.
231
+ */
202
232
  static getChromaticNote(chromaticId) {
203
233
  let note = this.chromaticNoteCache.get(chromaticId);
204
234
  if (note === void 0) {
@@ -225,21 +255,54 @@ var _Note = class _Note {
225
255
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid arg: ${arg}`);
226
256
  }
227
257
  }
258
+ /**
259
+ * Get octave from diatonic id.
260
+ * @param diatonicId - Diatonic id.
261
+ * @returns - Octave.
262
+ */
228
263
  static getOctaveFromDiatonicId(diatonicId) {
229
264
  return Math.floor((diatonicId - C0_diatonicId) / 7);
230
265
  }
266
+ /**
267
+ * Get diatonic id in given octave (transposes diatonic id to given octave).
268
+ * @param diatonicId - Original diatonic id.
269
+ * @param octave - Octave.
270
+ * @returns - Transposed diatonic id.
271
+ */
231
272
  static getDiatonicIdInOctave(diatonicId, octave) {
232
273
  return _Note.getDiatonicClass(diatonicId) + octave * 7 + C0_diatonicId;
233
274
  }
275
+ /**
276
+ * Get chromatic class from chromatic id.
277
+ * @param chromaticId - Chromatic id.
278
+ * @returns - Chromatic class.
279
+ */
234
280
  static getChromaticClass(chromaticId) {
235
281
  return mod(chromaticId, 12);
236
282
  }
283
+ /**
284
+ * Get octave from chromatic id.
285
+ * @param chromaticId - Chromatic id.
286
+ * @returns - Octave.
287
+ */
237
288
  static getOctaveFromChromaticId(chromaticId) {
238
289
  return Math.floor((chromaticId - C0_chromaticId) / 12);
239
290
  }
291
+ /**
292
+ * Get chromatic id in given octave (transposes chromatic id to given octave).
293
+ * @param chromaticId - Original chromatic id.
294
+ * @param octave - Octave.
295
+ * @returns - Transpose chromatic id.
296
+ */
240
297
  static getChromaticIdInOctave(chromaticId, octave) {
241
298
  return _Note.getChromaticClass(chromaticId) + octave * 12 + C0_chromaticId;
242
299
  }
300
+ /**
301
+ * Test if given two notes are equal.
302
+ * @param a - Note a.
303
+ * @param b - Note b.
304
+ * @returns - True/false.
305
+ */
243
306
  static equals(a, b) {
244
307
  if (a == null && b == null) {
245
308
  return true;
@@ -249,6 +312,12 @@ var _Note = class _Note {
249
312
  return a === b || a.diatonicId === b.diatonicId && a.accidental === b.accidental;
250
313
  }
251
314
  }
315
+ /**
316
+ * Replace accidental symbols in given string to givn symbol set (ascii/unicode).
317
+ * @param str - String to replace.
318
+ * @param symbolSet - Symbol set.
319
+ * @returns - String with updated accidental symbols.
320
+ */
252
321
  static replaceAccidentalSymbols(str, symbolSet) {
253
322
  if (symbolSet === 1 /* Unicode */) {
254
323
  return str.replace("bb", "\u{1D12B}").replace("b", "\u266D").replace("#", "\u266F").replace("x", "\u{1D12A}");
@@ -256,9 +325,19 @@ var _Note = class _Note {
256
325
  return str.replace("\u{1D12B}", "bb").replace("\u266D", "b").replace("\u266F", "#").replace("\u{1D12A}", "x");
257
326
  }
258
327
  }
328
+ /**
329
+ * Test if given string is valid note name.
330
+ * @param noteName - Note name to validate.
331
+ * @returns - True/false.
332
+ */
259
333
  static isValidNoteName(noteName) {
260
334
  return NoteNameRegex.test(noteName);
261
335
  }
336
+ /**
337
+ * Parse note name string to note props.
338
+ * @param noteName - Note name to parse.
339
+ * @returns - Parsed note props or undefined if parsing error.
340
+ */
262
341
  static parseNote(noteName) {
263
342
  var _a;
264
343
  let m = NoteNameRegex.exec(noteName);
@@ -272,6 +351,12 @@ var _Note = class _Note {
272
351
  let octave = octaveStr ? _Note.validateOctave(+octaveStr) : void 0;
273
352
  return { noteLetter, accidental, octave };
274
353
  }
354
+ /**
355
+ * Get scientific note name from given note name.
356
+ * @param noteName - Note name.
357
+ * @param symbolSet - Symbol set (ascii/unicode) for scientific note name.
358
+ * @returns - Scientific note name.
359
+ */
275
360
  static getScientificNoteName(noteName, symbolSet) {
276
361
  let p = _Note.parseNote(noteName);
277
362
  if (!p) {
@@ -280,9 +365,20 @@ var _Note = class _Note {
280
365
  let { noteLetter, accidental, octave } = p;
281
366
  return noteLetter + _Note.getAccidentalSymbol(accidental, symbolSet) + (octave != null ? octave : "");
282
367
  }
368
+ /**
369
+ * Get symbol of given accidental in given symbol set (ascii/unicide).
370
+ * @param accidental - Accidental.
371
+ * @param symbolsSet - Symbol set.
372
+ * @returns - Accidental symbol or undefined (invalid accidental).
373
+ */
283
374
  static getAccidentalSymbol(accidental, symbolsSet) {
284
375
  return symbolsSet === 1 /* Unicode */ ? AccidentalUnicodeSymbolMap.get(accidental) : AccidentalAsciiSymbolMap.get(accidental);
285
376
  }
377
+ /**
378
+ * Get accidental value from given accidental symbol.
379
+ * @param accidentalSymbol - Accidental symbol (e.g. "#").
380
+ * @returns - Accidental vlaue.
381
+ */
286
382
  static getAccidental(accidentalSymbol) {
287
383
  let accidental = AccidentalMap.get(accidentalSymbol);
288
384
  if (accidental === void 0) {
@@ -290,15 +386,32 @@ var _Note = class _Note {
290
386
  }
291
387
  return accidental;
292
388
  }
389
+ /**
390
+ * Get note letter from given diatonic id.
391
+ * @param diatonicId - Diatonic id.
392
+ * @returns - Note letter.
393
+ */
293
394
  static getNoteLetter(diatonicId) {
294
395
  return NoteLetters[_Note.getDiatonicClass(diatonicId)];
295
396
  }
397
+ /**
398
+ * Find next lowest possible diatonic id that is above given bottom level.
399
+ * @param diatonicId - Diatonic id to begin with.
400
+ * @param bottomDiatonicId - Bottom diatonic id.
401
+ * @param addOctaveIfEqual - If true then add one octave if diatonic id would equal to bottom diatonic id.
402
+ * @returns - Diatonic id.
403
+ */
296
404
  static findNextDiatonicIdAbove(diatonicId, bottomDiatonicId, addOctaveIfEqual) {
297
405
  let diatonicClass = _Note.getDiatonicClass(diatonicId);
298
406
  let bottomDiatonicClass = _Note.getDiatonicClass(bottomDiatonicId);
299
407
  let addOctave = addOctaveIfEqual ? diatonicClass <= bottomDiatonicClass ? 1 : 0 : diatonicClass < bottomDiatonicClass ? 1 : 0;
300
408
  return _Note.getDiatonicIdInOctave(diatonicClass, _Note.getOctaveFromDiatonicId(bottomDiatonicId) + addOctave);
301
409
  }
410
+ /**
411
+ * Validate if given argument is diatonic id.
412
+ * @param diatonicId - Diatonic id to validate.
413
+ * @returns - Valid diatonic id or throws.
414
+ */
302
415
  static validateDiatonicId(diatonicId) {
303
416
  if (import_ts_utils_lib2.Utils.Is.isInteger(diatonicId)) {
304
417
  return diatonicId;
@@ -306,13 +419,23 @@ var _Note = class _Note {
306
419
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid diatonicId: ${diatonicId}`);
307
420
  }
308
421
  }
422
+ /**
423
+ * Validate if given argument is diatonic class.
424
+ * @param diatonicClass - Diatonic class to validate.
425
+ * @returns - Valid diatonic class or throws.
426
+ */
309
427
  static validateDiatonicClass(diatonicClass) {
310
- if (import_ts_utils_lib2.Utils.Is.isInteger(diatonicClass) && diatonicClass >= 0 && diatonicClass < 7) {
428
+ if (import_ts_utils_lib2.Utils.Is.isIntegerBetween(diatonicClass, 0, 6)) {
311
429
  return diatonicClass;
312
430
  } else {
313
431
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid diatonicClass: ${diatonicClass}`);
314
432
  }
315
433
  }
434
+ /**
435
+ * Validate if given argument is chromatic id.
436
+ * @param chromaticId - Chromatic id to validate.
437
+ * @returns - Valid chromatic id, or throws.
438
+ */
316
439
  static validateChromaticId(chromaticId) {
317
440
  if (import_ts_utils_lib2.Utils.Is.isInteger(chromaticId)) {
318
441
  return chromaticId;
@@ -320,20 +443,35 @@ var _Note = class _Note {
320
443
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid chromaticId: ${chromaticId}`);
321
444
  }
322
445
  }
446
+ /**
447
+ * Validate if given argument is chromatic class.
448
+ * @param chromaticClass - Chromatic class to validate.
449
+ * @returns - Valid chromatic class, or throws.
450
+ */
323
451
  static validatechromaticClass(chromaticClass) {
324
- if (import_ts_utils_lib2.Utils.Is.isInteger(chromaticClass) && chromaticClass >= 0 && chromaticClass < 12) {
452
+ if (import_ts_utils_lib2.Utils.Is.isIntegerBetween(chromaticClass, 0, 11)) {
325
453
  return chromaticClass;
326
454
  } else {
327
455
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid chromaticClass: ${chromaticClass}`);
328
456
  }
329
457
  }
330
- static validateNoteLetter(note) {
331
- if (NoteLetters.some((n) => n === note)) {
332
- return note;
458
+ /**
459
+ * Validate if given argument if note letter.
460
+ * @param noteLetter - Note letter to validate.
461
+ * @returns - Valid note letter or throws.
462
+ */
463
+ static validateNoteLetter(noteLetter) {
464
+ if (NoteLetters.some((n) => n === noteLetter)) {
465
+ return noteLetter;
333
466
  } else {
334
- throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid note: ${note}`);
467
+ throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid note: ${noteLetter}`);
335
468
  }
336
469
  }
470
+ /**
471
+ * Validate if given argument is octave.
472
+ * @param octave - Octave to validate.
473
+ * @returns - Valid octave or throws.
474
+ */
337
475
  static validateOctave(octave) {
338
476
  if (import_ts_utils_lib2.Utils.Is.isInteger(octave)) {
339
477
  return octave;
@@ -341,8 +479,13 @@ var _Note = class _Note {
341
479
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid octave: ${octave}`);
342
480
  }
343
481
  }
482
+ /**
483
+ * Validate if given argument is valid accidental.
484
+ * @param acc - Accidental to validate.
485
+ * @returns - Valid accidental or thorws.
486
+ */
344
487
  static validateAccidental(acc) {
345
- if (import_ts_utils_lib2.Utils.Is.isInteger(acc) && acc >= -2 && acc <= 2) {
488
+ if (import_ts_utils_lib2.Utils.Is.isIntegerBetween(acc, -2, 2)) {
346
489
  return acc;
347
490
  } else {
348
491
  throw new import_core2.MusicError(import_core2.MusicErrorType.Note, `Invalid accidental: ${acc}`);
@@ -370,6 +513,12 @@ var _Note = class _Note {
370
513
  });
371
514
  return uniqueSet;
372
515
  }
516
+ /**
517
+ * Function to compare two notes using diatonic id and accidental properties of notes.
518
+ * @param a - Note a.
519
+ * @param b - Note b.
520
+ * @returns - -1, 0 or 1.
521
+ */
373
522
  static compareFunc(a, b) {
374
523
  if (a.diatonicId < b.diatonicId) {
375
524
  return -1;
@@ -491,6 +640,10 @@ var _KeySignature = class _KeySignature {
491
640
  }
492
641
  this.orderedAccidentedNotes = flats.length > 0 ? flats : sharps;
493
642
  }
643
+ /**
644
+ * Get accidental type sharps, flats, or natural (without accidentals).
645
+ * @returns - Accidental type.
646
+ */
494
647
  getAccidentalType() {
495
648
  if (this.orderedAccidentedNotes.length === 0) {
496
649
  return 0 /* Natural */;
@@ -500,23 +653,40 @@ var _KeySignature = class _KeySignature {
500
653
  return 2 /* Sharps */;
501
654
  }
502
655
  }
656
+ /**
657
+ * Get natural scale notes of this key signature, natural scale has 7 steps (e.g. Major scale: W, W, H, W, W, W, H).
658
+ * @returns - Array of notes.
659
+ */
503
660
  getNaturalScaleNotes() {
504
661
  return this.naturalScaleNotes;
505
662
  }
663
+ /**
664
+ * Get accidental for given diatonic id.
665
+ * @param diatonicId - Diatonic id.
666
+ * @returns - Accidental -2, -1, 0, 1 or 2.
667
+ */
506
668
  getAccidental(diatonicId) {
507
669
  var _a;
508
670
  return (_a = this.accidentalByDiatonicClass[Note.getDiatonicClass(diatonicId)]) != null ? _a : 0;
509
671
  }
672
+ /**
673
+ * Get number of accidentals this key signature has.
674
+ * @returns - Number of accidentals.
675
+ */
510
676
  getNumAccidentals() {
511
677
  return this.orderedAccidentedNotes.length;
512
678
  }
679
+ /**
680
+ * Get accidental notes in correct order.
681
+ * @returns - Array of accidental notes.
682
+ */
513
683
  getOrderedAccidentalNotes() {
514
684
  return this.orderedAccidentedNotes;
515
685
  }
516
686
  /**
517
- *
518
- * @param degree - number 1..7 or string e.g "b5" or "#4"
519
- * @returns
687
+ * Get note of key signature by degree value.
688
+ * @param degree - Degree number in range [1, 7] or string e.g "b5" or "#4".
689
+ * @returns - Note.
520
690
  */
521
691
  getNoteByDegree(degree) {
522
692
  let { deg, acc } = parseDegree(degree);
@@ -527,6 +697,12 @@ var _KeySignature = class _KeySignature {
527
697
  return new Note(note.diatonicId, note.accidental + acc);
528
698
  }
529
699
  }
700
+ /**
701
+ * Test equality of given key signatures.
702
+ * @param a - Key signature a.
703
+ * @param b - Key signature b.
704
+ * @returns - True/false.
705
+ */
530
706
  static equals(a, b) {
531
707
  if (a == null && b == null) {
532
708
  return true;
@@ -664,9 +840,13 @@ var Interval = class _Interval {
664
840
  constructor(note1, note2) {
665
841
  this.note1 = note1;
666
842
  this.note2 = note2;
843
+ /** Interval direction. */
667
844
  __publicField(this, "direction");
845
+ /** Number of semitones. */
668
846
  __publicField(this, "semitones");
847
+ /** Interval quantity. */
669
848
  __publicField(this, "quantity");
849
+ /** Interval quality. */
670
850
  __publicField(this, "quality");
671
851
  if (note2.diatonicId >= note1.diatonicId) {
672
852
  this.direction = note2.diatonicId === note1.diatonicId ? "Unison" : "Ascending";
@@ -684,6 +864,12 @@ var Interval = class _Interval {
684
864
  throw new InvalidIntervalException("Unknown interval quality");
685
865
  }
686
866
  }
867
+ /**
868
+ * Get interval between given two notes.
869
+ * @param note1 - First note.
870
+ * @param note2 - Second note.
871
+ * @returns - Interval if valid, or undefined.
872
+ */
687
873
  static get(note1, note2) {
688
874
  try {
689
875
  return new _Interval(note1, note2);
@@ -695,12 +881,20 @@ var Interval = class _Interval {
695
881
  }
696
882
  }
697
883
  }
884
+ /**
885
+ * Get string presentation of interval (e.g. "Descending Major 2").
886
+ * @returns - Interval string.
887
+ */
698
888
  toString() {
699
889
  let direction = this.direction === "Unison" ? "" : this.direction + " ";
700
890
  let quality = this.quality + " ";
701
891
  let quantity = this.direction === "Unison" ? "Unison" : formatQuantity(this.quantity);
702
892
  return direction + quality + quantity;
703
893
  }
894
+ /**
895
+ * Get abbrevated string presentation of interval (e.g. "↓M2").
896
+ * @returns - Interval abbrevated string.
897
+ */
704
898
  toAbbrString() {
705
899
  var _a;
706
900
  let direction = this.direction === "Descending" ? "\u2193" : "";
@@ -795,12 +989,20 @@ function getMode(scaleType) {
795
989
  }
796
990
  }
797
991
  var Scale = class extends KeySignature {
992
+ /**
993
+ * Create nev scale object instance.
994
+ * @param tonic - Tonic (e.g. "C" in "C Major").
995
+ * @param scaleType - Scale typo ("e.g. "Major" in "C Major").
996
+ */
798
997
  constructor(tonic, scaleType) {
799
998
  super(tonic, getMode(scaleType));
800
999
  this.tonic = tonic;
801
1000
  this.scaleType = scaleType;
1001
+ /** Degrees of scale notes. */
802
1002
  __publicField(this, "scaleDegrees");
1003
+ /** Scale notes. */
803
1004
  __publicField(this, "scaleNotes");
1005
+ /** Degrees (or undefined) of chromatic classes. */
804
1006
  __publicField(this, "chromaticClassDegree");
805
1007
  __publicField(this, "preferredChromaticNoteCache", /* @__PURE__ */ new Map());
806
1008
  switch (scaleType) {
@@ -832,6 +1034,12 @@ var Scale = class extends KeySignature {
832
1034
  return id >= 0 ? this.scaleDegrees[id] : void 0;
833
1035
  });
834
1036
  }
1037
+ /**
1038
+ * Compare if two scales equals.
1039
+ * @param a - Scale a.
1040
+ * @param b - Scale b.
1041
+ * @returns - Boolean equality of given scales.
1042
+ */
835
1043
  static equals(a, b) {
836
1044
  if (a == null && b == null) {
837
1045
  return true;
@@ -841,6 +1049,11 @@ var Scale = class extends KeySignature {
841
1049
  return a === b || a.getScaleName() === b.getScaleName();
842
1050
  }
843
1051
  }
1052
+ /**
1053
+ * Get scale name.
1054
+ * @param symbolSet - Symbol set to format scale name in ascii or unicode.
1055
+ * @returns - Scale name string.
1056
+ */
844
1057
  getScaleName(symbolSet) {
845
1058
  switch (symbolSet) {
846
1059
  case 1 /* Unicode */:
@@ -849,6 +1062,12 @@ var Scale = class extends KeySignature {
849
1062
  return this.tonic + " " + this.scaleType;
850
1063
  }
851
1064
  }
1065
+ /**
1066
+ * Get scale notes.
1067
+ * @param bottomNote - Computed scale notes begin no lower than this note.
1068
+ * @param numOctaves - How many octaves?
1069
+ * @returns - Array of scale notes.
1070
+ */
852
1071
  getScaleNotes(bottomNote, numOctaves) {
853
1072
  if (!import_ts_utils_lib5.Utils.Is.isIntegerGte(numOctaves, 1)) {
854
1073
  throw new import_core5.MusicError(import_core5.MusicErrorType.Scale, `Invalid numOctaves: ${numOctaves}`);
@@ -864,9 +1083,17 @@ var Scale = class extends KeySignature {
864
1083
  return new Note(diatonicId, note.accidental);
865
1084
  });
866
1085
  }
1086
+ /**
1087
+ * Get scale overview (e.g. "C - D - E - F - G - A - B" for "C Major" scale).
1088
+ * @returns - Scale overview string.
1089
+ */
867
1090
  getScaleOverview() {
868
1091
  return this.getScaleNotes("C4", 1).map((note) => note.formatOmitOctave(1 /* Unicode */)).join(" - ");
869
1092
  }
1093
+ /**
1094
+ * Get scale steps, array of 1 (half step) and 2 (whole step), (e.g. [2, 2, 1, 2, 2, 2, 1] for Major scale).
1095
+ * @returns - Array of scale steps.
1096
+ */
870
1097
  getScaleSteps() {
871
1098
  let chromaticIds = this.getScaleNotes("C4", 1).map((note) => note.chromaticId);
872
1099
  let steps = [];
@@ -875,15 +1102,34 @@ var Scale = class extends KeySignature {
875
1102
  }
876
1103
  return steps;
877
1104
  }
1105
+ /**
1106
+ * Get scale steps string presentation, array of "H" (half step) and "W" (whole step), (e.g. ["W", "W", "H", "W", "W", "W", "H"] for Major scale).
1107
+ * @returns - Array of scale steps string presentation.
1108
+ */
878
1109
  getScaleStringSteps() {
879
1110
  return this.getScaleSteps().map((step) => step === 1 ? "H" : step === 2 ? "W" : step.toString() + "H");
880
1111
  }
1112
+ /**
1113
+ * Test if given note is scale note.
1114
+ * @param note - Note to test.
1115
+ * @returns - True/false.
1116
+ */
881
1117
  isScaleNote(note) {
882
1118
  return this.chromaticClassDegree[note.chromaticClass] !== void 0;
883
1119
  }
1120
+ /**
1121
+ * Test if given note is scale root note.
1122
+ * @param note - Note to test.
1123
+ * @returns - True/false.
1124
+ */
884
1125
  isScaleRootNote(note) {
885
1126
  return String(this.chromaticClassDegree[note.chromaticClass]) === "1";
886
1127
  }
1128
+ /**
1129
+ * Get interval value between scale root note and given note.
1130
+ * @param note - Note.
1131
+ * @returns - Interval.
1132
+ */
887
1133
  getIntervalFromRootNote(note) {
888
1134
  let rootNote = this.getScaleNotes("C0", 1)[0];
889
1135
  while (note.chromaticId >= rootNote.chromaticId + 12) {
@@ -899,6 +1145,11 @@ var Scale = class extends KeySignature {
899
1145
  return interval;
900
1146
  }
901
1147
  }
1148
+ /**
1149
+ * Get preferred chromatic note from given chromatic id.
1150
+ * @param chromaticId - Chromatic id.
1151
+ * @returns - Note.
1152
+ */
902
1153
  getPreferredChromaticNote(chromaticId) {
903
1154
  Note.validateChromaticId(chromaticId);
904
1155
  let note = this.preferredChromaticNoteCache.get(chromaticId);
@@ -941,6 +1192,10 @@ var Scale = class extends KeySignature {
941
1192
  }
942
1193
  };
943
1194
  var ScaleFactory = class {
1195
+ /**
1196
+ * Create new scale factory object instance.
1197
+ * @param type - Scale type.
1198
+ */
944
1199
  constructor(type) {
945
1200
  this.type = type;
946
1201
  __publicField(this, "tonicList", []);
@@ -986,15 +1241,32 @@ var ScaleFactory = class {
986
1241
  ...flatScales.sort(SortByAccidentalCountFunc).map((scale) => scale.tonic)
987
1242
  ];
988
1243
  }
1244
+ /**
1245
+ * Get list of tonics (e.g. "C", "C#", ... for Major scale type).
1246
+ * @returns - Array of tonics.
1247
+ */
989
1248
  getTonicList() {
990
1249
  return this.tonicList;
991
1250
  }
1251
+ /**
1252
+ * Get default tonic.
1253
+ * @returns - Default tonic.
1254
+ */
992
1255
  getDefaultTonic() {
993
1256
  return this.tonicList[0];
994
1257
  }
1258
+ /**
1259
+ * Get scale type.
1260
+ * @returns - SCale type.
1261
+ */
995
1262
  getType() {
996
1263
  return this.type;
997
1264
  }
1265
+ /**
1266
+ * Get scale by given tonic.
1267
+ * @param tonic - Tonic (e.g. "C").
1268
+ * @returns - Scale.
1269
+ */
998
1270
  getScale(tonic) {
999
1271
  let scale = this.scaleMap.get(tonic);
1000
1272
  if (!scale) {
@@ -1003,6 +1275,11 @@ var ScaleFactory = class {
1003
1275
  return scale;
1004
1276
  }
1005
1277
  }
1278
+ /**
1279
+ * Test if this scale factory has scale for given tonic value.
1280
+ * @param tonic - Tonic.
1281
+ * @returns - True/false.
1282
+ */
1006
1283
  hasScale(tonic) {
1007
1284
  return this.scaleMap.get(tonic) !== void 0;
1008
1285
  }
@@ -1048,10 +1325,19 @@ function validateScaleType(scaleType) {
1048
1325
  if (import_ts_utils_lib5.Utils.Is.isEnumValue(scaleType, ScaleType)) {
1049
1326
  return scaleType;
1050
1327
  } else {
1051
- throw new import_core5.MusicError(import_core5.MusicErrorType.Scale, `Invalid scaleType: ${scaleType}`);
1328
+ throw new import_core5.MusicError(import_core5.MusicErrorType.Scale, `Invalid scaleType: "${scaleType}"`);
1052
1329
  }
1053
1330
  }
1054
- function getScale(tonic, scaleType) {
1331
+ function getScale(arg0, arg1) {
1332
+ let tonic;
1333
+ let scaleType;
1334
+ if (arg1 !== void 0) {
1335
+ tonic = arg0;
1336
+ scaleType = validateScaleType(arg1);
1337
+ } else {
1338
+ tonic = arg0.split(" ")[0];
1339
+ scaleType = validateScaleType(arg0.substring(tonic.length + 1));
1340
+ }
1055
1341
  return getScaleFactory(scaleType).getScale(tonic);
1056
1342
  }
1057
1343
  var DefaultScale = getScale("C", "Major" /* Major */);
@@ -1158,9 +1444,13 @@ var Chord = class _Chord {
1158
1444
  constructor(chordInfo, chordNotes, rootNote, bassNote) {
1159
1445
  this.chordInfo = chordInfo;
1160
1446
  this.rootNote = rootNote;
1447
+ /** Chord name. */
1161
1448
  __publicField(this, "name");
1449
+ /** Notes of this chord. */
1162
1450
  __publicField(this, "notes");
1451
+ /** Notes that are omitted in this chord (partial chord). */
1163
1452
  __publicField(this, "omitNotes");
1453
+ /** Bass note if not chord root note (e.g. "B" in "C/B"). */
1164
1454
  __publicField(this, "slashBassNote");
1165
1455
  this.name = chordInfo.name;
1166
1456
  let notesLeft = chordNotes.slice();
@@ -1199,6 +1489,11 @@ var Chord = class _Chord {
1199
1489
  throw new InvalidChordException("Power chord no bass note allowed!");
1200
1490
  }
1201
1491
  }
1492
+ /**
1493
+ * Get all chords that can be made up with given notes.
1494
+ * @param notes - Array of notes.
1495
+ * @returns - Array of chords.
1496
+ */
1202
1497
  static getChords(notes) {
1203
1498
  let chords = [];
1204
1499
  let chordNotes = Note.sort(notes);
@@ -1238,7 +1533,8 @@ var Chord = class _Chord {
1238
1533
  return true;
1239
1534
  }
1240
1535
  /**
1241
- * @returns Chord name e.g. "C/B"
1536
+ * Get chord string presentation.
1537
+ * @returns Chord string presentation (e.g. "C/B").
1242
1538
  */
1243
1539
  toString() {
1244
1540
  let symbolSet = 1 /* Unicode */;
@@ -1247,7 +1543,8 @@ var Chord = class _Chord {
1247
1543
  return rootNoteStr + this.name + slashBassStr;
1248
1544
  }
1249
1545
  /**
1250
- * @returns Degree notation string, e.g. "E - 1(C) - 3(E) - 5(G)"
1546
+ * Get degree notation string (e.g. "E - 1(C) - 3(E) - 5(G)").
1547
+ * @returns Degree notation string.
1251
1548
  */
1252
1549
  getDegreeNotationString() {
1253
1550
  let symbolSet = 1 /* Unicode */;
@@ -1258,7 +1555,8 @@ var Chord = class _Chord {
1258
1555
  return bassNoteStr + degreeNoteStr;
1259
1556
  }
1260
1557
  /**
1261
- * @returns Omitted degrees string e.g. "Omits 5(G), 9(D)"
1558
+ * Get string presentation of omitted degrees (e.g. "Omits 5(G), 9(D)").
1559
+ * @returns - String presentation of omitted degrees.
1262
1560
  */
1263
1561
  getOmittedDegreesString() {
1264
1562
  let omittedStrList = this.omitNotes.map((omit, i) => {
@@ -1267,15 +1565,17 @@ var Chord = class _Chord {
1267
1565
  return omittedStrList.length > 0 ? "Omits " + omittedStrList.join(", ") : "";
1268
1566
  }
1269
1567
  /**
1270
- * @param i - Degree index
1271
- * @returns Degree string for given degree index, e.g. "3"
1568
+ * Degree index for given degree index (e.g. "3").
1569
+ * @param i - Chord degree index (e.g. 0 is first note degree of chord).
1570
+ * @returns Degree string.
1272
1571
  */
1273
1572
  getDegreeStr(i) {
1274
1573
  return Note.replaceAccidentalSymbols("" + this.chordInfo.degrees[i], 1 /* Unicode */);
1275
1574
  }
1276
1575
  /**
1277
- * @param i - Degree index
1278
- * @returns Note string for given degree index, e.g. "E"
1576
+ * Get note name for given degree index (e.g. "E").
1577
+ * @param i - Chord degree index (e.g. 0 is first note degree of chord).
1578
+ * @returns - Note name string.
1279
1579
  */
1280
1580
  getNoteStr(i) {
1281
1581
  return this.notes[i].formatOmitOctave(1 /* Unicode */);
@@ -1542,10 +1842,10 @@ var Handedness = /* @__PURE__ */ ((Handedness2) => {
1542
1842
  })(Handedness || {});
1543
1843
  var DefaultHandedness = 0 /* RightHanded */;
1544
1844
  function validateHandedness(h) {
1545
- if (!import_ts_utils_lib7.Utils.Is.isEnumValue(h, Handedness)) {
1546
- throw new import_core7.MusicError(import_core7.MusicErrorType.InvalidArg, `Invalid handedness: ${h}`);
1547
- } else {
1845
+ if (import_ts_utils_lib7.Utils.Is.isEnumValue(h, Handedness)) {
1548
1846
  return h;
1847
+ } else {
1848
+ throw new import_core7.MusicError(import_core7.MusicErrorType.InvalidArg, `Invalid handedness: ${h}`);
1549
1849
  }
1550
1850
  }
1551
1851
  var TuningNameList = tunings_default.list.map((data) => data.name);
@@ -1580,92 +1880,268 @@ var import_ts_utils_lib9 = require("@tspro/ts-utils-lib");
1580
1880
  // src/theory/rhythm.ts
1581
1881
  var import_ts_utils_lib8 = require("@tspro/ts-utils-lib");
1582
1882
  var import_core8 = require("@tspro/web-music-score/core");
1883
+ var cmp = (a, b) => a === b ? 0 : a < b ? -1 : 1;
1884
+ var MaxTupletRatioValue = 12;
1885
+ var TicksMultiplier = 12 * 11 * 9 * 7 * 5;
1583
1886
  var NoteLength = /* @__PURE__ */ ((NoteLength3) => {
1584
- NoteLength3[NoteLength3["Whole"] = 192] = "Whole";
1585
- NoteLength3[NoteLength3["Half"] = 96] = "Half";
1586
- NoteLength3[NoteLength3["Quarter"] = 48] = "Quarter";
1587
- NoteLength3[NoteLength3["Eighth"] = 24] = "Eighth";
1588
- NoteLength3[NoteLength3["Sixteenth"] = 12] = "Sixteenth";
1589
- NoteLength3[NoteLength3["ThirtySecond"] = 6] = "ThirtySecond";
1590
- NoteLength3[NoteLength3["SixtyFourth"] = 3] = "SixtyFourth";
1887
+ NoteLength3["Whole"] = "1n";
1888
+ NoteLength3["WholeTriplet"] = "1t";
1889
+ NoteLength3["WholeDot"] = "1.";
1890
+ NoteLength3["Whole2Dots"] = "1..";
1891
+ NoteLength3["Whole12Dots"] = "1..";
1892
+ NoteLength3["Whole3Dots"] = "1...";
1893
+ NoteLength3["Whole4Dots"] = "1....";
1894
+ NoteLength3["Whole5Dots"] = "1.....";
1895
+ NoteLength3["Whole6Dots"] = "1......";
1896
+ NoteLength3["Half"] = "2n";
1897
+ NoteLength3["HalfTriplet"] = "2t";
1898
+ NoteLength3["HalfDot"] = "2.";
1899
+ NoteLength3["Half2Dots"] = "2..";
1900
+ NoteLength3["Half3Dots"] = "2...";
1901
+ NoteLength3["Half4Dots"] = "2....";
1902
+ NoteLength3["Half5Dots"] = "2.....";
1903
+ NoteLength3["Quarter"] = "4n";
1904
+ NoteLength3["QuarterTriplet"] = "4t";
1905
+ NoteLength3["QuarterDot"] = "4.";
1906
+ NoteLength3["Quarter2Dots"] = "4..";
1907
+ NoteLength3["Quarter3Dots"] = "4...";
1908
+ NoteLength3["Quarter4Dots"] = "4....";
1909
+ NoteLength3["Eighth"] = "8n";
1910
+ NoteLength3["EighthTriplet"] = "8t";
1911
+ NoteLength3["EighthDot"] = "8.";
1912
+ NoteLength3["Eighth2Dots"] = "8..";
1913
+ NoteLength3["Eighth3Dots"] = "8...";
1914
+ NoteLength3["Sixteenth"] = "16n";
1915
+ NoteLength3["SixteenthTriplet"] = "16t";
1916
+ NoteLength3["SixteenthDot"] = "16.";
1917
+ NoteLength3["Sixteenth2Dots"] = "16..";
1918
+ NoteLength3["ThirtySecond"] = "32n";
1919
+ NoteLength3["ThirtySecondTriplet"] = "32t";
1920
+ NoteLength3["ThirtySecondDot"] = "32.";
1921
+ NoteLength3["SixtyFourth"] = "64n";
1922
+ NoteLength3["SixtyFourthTriplet"] = "64t";
1591
1923
  return NoteLength3;
1592
1924
  })(NoteLength || {});
1593
- var MaxNoteLength = 192 /* Whole */;
1594
- var MinNoteLength = 3 /* SixtyFourth */;
1595
- var FlagCountMap = /* @__PURE__ */ new Map([
1596
- [192 /* Whole */, 0],
1597
- [96 /* Half */, 0],
1598
- [48 /* Quarter */, 0],
1599
- [24 /* Eighth */, 1],
1600
- [12 /* Sixteenth */, 2],
1601
- [6 /* ThirtySecond */, 3],
1602
- [3 /* SixtyFourth */, 4]
1603
- ]);
1604
- var NoteSymbolMap = /* @__PURE__ */ new Map([
1605
- [192 /* Whole */, "\u{1D15D}"],
1606
- [96 /* Half */, "\u{1D15E}"],
1607
- [48 /* Quarter */, "\u{1D15F}"],
1608
- [24 /* Eighth */, "\u{1D160}"],
1609
- [12 /* Sixteenth */, "\u{1D161}"],
1610
- [6 /* ThirtySecond */, "\u{1D162}"],
1611
- [3 /* SixtyFourth */, "\u{1D163}"]
1612
- ]);
1613
1925
  function validateNoteLength(noteLength) {
1614
- if (!import_ts_utils_lib8.Utils.Is.isEnumValue(noteLength, NoteLength)) {
1926
+ if (import_ts_utils_lib8.Utils.Is.isEnumValue(noteLength, NoteLength)) {
1927
+ return noteLength;
1928
+ } else {
1615
1929
  throw new import_core8.MusicError(import_core8.MusicErrorType.InvalidArg, `Invalid noteLength: ${noteLength}`);
1930
+ }
1931
+ }
1932
+ var _NoteLengthProps = class _NoteLengthProps {
1933
+ constructor(noteLength) {
1934
+ /** Note length. */
1935
+ __publicField(this, "noteLength");
1936
+ /** Note size (whole=1, half=2, quarter=4, ...). */
1937
+ __publicField(this, "noteSize");
1938
+ /** Number of ticks (not altered by isTriplet). */
1939
+ __publicField(this, "ticks");
1940
+ /** Flag count. */
1941
+ __publicField(this, "flagCount");
1942
+ /** Dot count. */
1943
+ __publicField(this, "dotCount");
1944
+ /** Max dot count. */
1945
+ __publicField(this, "maxDotCount");
1946
+ /** Is triplet? */
1947
+ __publicField(this, "isTriplet");
1948
+ /** Has note stem. */
1949
+ __publicField(this, "hasStem");
1950
+ /** Is note head solid (black)? */
1951
+ __publicField(this, "isSolid");
1952
+ this.noteLength = validateNoteLength(noteLength);
1953
+ this.noteSize = parseInt(noteLength);
1954
+ this.isTriplet = noteLength.endsWith("t");
1955
+ this.maxDotCount = this.isTriplet ? 0 : Math.floor(Math.log2(_NoteLengthProps.ShortestNoteSize / this.noteSize));
1956
+ this.dotCount = import_ts_utils_lib8.Utils.Str.charCount(noteLength, ".");
1957
+ this.flagCount = this.noteSize > 4 ? Math.floor(Math.log2(this.noteSize / 4)) : 0;
1958
+ this.ticks = TicksMultiplier * _NoteLengthProps.ShortestNoteSize / this.noteSize;
1959
+ this.hasStem = this.noteSize > 1;
1960
+ this.isSolid = this.noteSize > 2;
1961
+ if (this.dotCount > this.maxDotCount) {
1962
+ throw new import_core8.MusicError(import_core8.MusicErrorType.Note, `dotCount ${this.dotCount} > maxDotCount ${this.maxDotCount}, for noteLength "${this.noteLength}".`);
1963
+ } else if (this.isTriplet && this.dotCount > 0) {
1964
+ throw new import_core8.MusicError(import_core8.MusicErrorType.Note, `noteLength "${this.noteLength}" is both triplet and dotted!`);
1965
+ }
1966
+ }
1967
+ /**
1968
+ * Get note length props.
1969
+ * @param noteLength - Note length.
1970
+ * @returns - Note length props.
1971
+ */
1972
+ static get(noteLength) {
1973
+ let p = this.cache.get(noteLength);
1974
+ if (!p) {
1975
+ this.cache.set(noteLength, p = new _NoteLengthProps(noteLength));
1976
+ }
1977
+ return p;
1978
+ }
1979
+ /**
1980
+ * Create note length props.
1981
+ * @param noteLength - Note length or note size.
1982
+ * @param dotCount - Dot count.
1983
+ * @returns - Note length props.
1984
+ */
1985
+ static create(noteLength, dotCount = 0) {
1986
+ let noteSize = typeof noteLength === "number" ? noteLength : this.get(noteLength).noteSize;
1987
+ return this.get(noteSize + (import_ts_utils_lib8.Utils.Is.isIntegerGte(dotCount, 1) ? ".".repeat(dotCount) : "n"));
1988
+ }
1989
+ /**
1990
+ * Compare note lengths/sizes. Whole (1) > half (2) > quarter (4), etc.
1991
+ * Ignores possible triplet property of note length.
1992
+ * @param a - NoteLengthProps, NoteLength/Str or noteSize
1993
+ * @param b - NoteLengthProps, NoteLength/Str or noteSize
1994
+ * @returns - -1: a < b, 0: a === b, +1: a > b (note length/size comparisons)
1995
+ */
1996
+ static cmp(a, b) {
1997
+ let aNoteSize = a instanceof _NoteLengthProps ? a.noteSize : typeof a === "number" ? a : _NoteLengthProps.get(a).noteSize;
1998
+ let bNoteSize = b instanceof _NoteLengthProps ? b.noteSize : typeof b === "number" ? b : _NoteLengthProps.get(b).noteSize;
1999
+ return cmp(bNoteSize, aNoteSize);
2000
+ }
2001
+ /**
2002
+ * Compare note lengths/sizes for equality.
2003
+ * Ignores possible triplet property of note length.
2004
+ * @param a - NoteLengthProps, NoteLength/Str or noteSize
2005
+ * @param b - NoteLengthProps, NoteLength/Str or noteSize
2006
+ * @returns - true: a === b, false: a !== b (note length/size comparisons)
2007
+ */
2008
+ static equals(a, b) {
2009
+ let aNoteSize = a instanceof _NoteLengthProps ? a.noteSize : typeof a === "number" ? a : _NoteLengthProps.get(a).noteSize;
2010
+ let bNoteSize = b instanceof _NoteLengthProps ? b.noteSize : typeof b === "number" ? b : _NoteLengthProps.get(b).noteSize;
2011
+ return aNoteSize === bNoteSize;
2012
+ }
2013
+ };
2014
+ /** Longest note size (e.g. 1 = whole note). */
2015
+ __publicField(_NoteLengthProps, "LongestNoteSize", Math.min(...import_ts_utils_lib8.Utils.Enum.getEnumValues(NoteLength).map((noteLength) => parseInt(noteLength))));
2016
+ /** Shortest note size (e.g. 64 = sixtyfourth note). */
2017
+ __publicField(_NoteLengthProps, "ShortestNoteSize", Math.max(...import_ts_utils_lib8.Utils.Enum.getEnumValues(NoteLength).map((noteLength) => parseInt(noteLength))));
2018
+ __publicField(_NoteLengthProps, "cache", /* @__PURE__ */ new Map());
2019
+ var NoteLengthProps = _NoteLengthProps;
2020
+ function validateTupletRatio(tupletRatio) {
2021
+ if (import_ts_utils_lib8.Utils.Is.isObject(tupletRatio) && import_ts_utils_lib8.Utils.Is.isIntegerBetween(tupletRatio.parts, 2, MaxTupletRatioValue) && import_ts_utils_lib8.Utils.Is.isIntegerBetween(tupletRatio.inTimeOf, 2, MaxTupletRatioValue)) {
2022
+ return tupletRatio;
1616
2023
  } else {
1617
- return noteLength;
2024
+ throw new import_core8.MusicError(import_core8.MusicErrorType.Note, `Invalid tupletRatio ${JSON.stringify(tupletRatio)}`);
1618
2025
  }
1619
2026
  }
1620
- var RhythmProps = class _RhythmProps {
1621
- constructor(noteLength, dotted, triplet) {
2027
+ var Tuplet = {
2028
+ /** Duplet: 2 in the time of 3 */
2029
+ Duplet: { parts: 2, inTimeOf: 3 },
2030
+ /** Triplet: 3 in the time of 2 */
2031
+ Triplet: { parts: 3, inTimeOf: 2 },
2032
+ /** Quadruplet: 4 in the time of 3 */
2033
+ Quadruplet: { parts: 4, inTimeOf: 3 }
2034
+ };
2035
+ var _RhythmProps = class _RhythmProps {
2036
+ constructor(noteLength, dotCount, tupletRatio) {
2037
+ /** Note length. */
1622
2038
  __publicField(this, "noteLength");
1623
- __publicField(this, "dotted");
1624
- __publicField(this, "triplet");
2039
+ /** Note size (whole=1, half=2, quarter=4, ...). */
2040
+ __publicField(this, "noteSize");
2041
+ /** Dot count. */
2042
+ __publicField(this, "dotCount");
2043
+ /** Tuplet ratio. */
2044
+ __publicField(this, "tupletRatio");
2045
+ /** Number of ticks. */
1625
2046
  __publicField(this, "ticks");
2047
+ /** Flag count. */
1626
2048
  __publicField(this, "flagCount");
1627
- var _a;
2049
+ /** Has note stem. */
2050
+ __publicField(this, "hasStem");
2051
+ /** Is note head solid (black)? */
2052
+ __publicField(this, "isSolidNoteHead");
1628
2053
  this.noteLength = validateNoteLength(noteLength);
1629
- this.dotted = dotted === true;
1630
- this.triplet = triplet === true;
1631
- this.ticks = this.noteLength;
1632
- this.flagCount = (_a = FlagCountMap.get(this.noteLength)) != null ? _a : 0;
1633
- if (this.dotted && this.triplet) {
1634
- throw new import_core8.MusicError(import_core8.MusicErrorType.Note, "Note cannot be both dotted and triplet!");
1635
- } else if (this.dotted && this.noteLength === MinNoteLength) {
1636
- throw new import_core8.MusicError(import_core8.MusicErrorType.Note, "Shortest note cannot be dotted!");
2054
+ let p = NoteLengthProps.get(noteLength);
2055
+ this.noteSize = p.noteSize;
2056
+ this.ticks = p.ticks;
2057
+ this.flagCount = p.flagCount;
2058
+ this.dotCount = dotCount != null ? dotCount : p.dotCount;
2059
+ this.hasStem = p.hasStem;
2060
+ this.isSolidNoteHead = p.isSolid;
2061
+ if (import_ts_utils_lib8.Utils.Is.isObject(tupletRatio)) {
2062
+ this.tupletRatio = validateTupletRatio(tupletRatio);
2063
+ } else if (p.isTriplet) {
2064
+ this.tupletRatio = Tuplet.Triplet;
2065
+ } else {
2066
+ this.tupletRatio = void 0;
2067
+ }
2068
+ if (this.dotCount > 0 && this.tupletRatio !== void 0) {
2069
+ throw new import_core8.MusicError(import_core8.MusicErrorType.Note, `Note cannot be both dotted and tuplet!`);
2070
+ } else if (this.dotCount > p.maxDotCount) {
2071
+ throw new import_core8.MusicError(import_core8.MusicErrorType.Note, `Too big dot count ${this.dotCount} for note length ${this.noteLength}.`);
1637
2072
  }
1638
- if (this.dotted) {
1639
- this.ticks += this.noteLength / 2;
2073
+ for (let add = this.ticks / 2, i = 1; i <= this.dotCount; i++, add /= 2) {
2074
+ this.ticks += add;
1640
2075
  }
1641
- if (this.triplet) {
1642
- this.ticks = this.ticks * 2 / 3;
2076
+ if (this.tupletRatio) {
2077
+ this.ticks *= this.tupletRatio.inTimeOf / this.tupletRatio.parts;
1643
2078
  }
1644
2079
  }
1645
- static createFromNoteSize(noteSize) {
1646
- return new _RhythmProps(MaxNoteLength / noteSize);
2080
+ /**
2081
+ * Get string presentation of rhythm props.
2082
+ * @returns - String presentation.
2083
+ */
2084
+ toString() {
2085
+ let sym = _RhythmProps.NoteSymbolMap.get(this.noteSize);
2086
+ let dots = ".".repeat(this.dotCount);
2087
+ return sym ? sym + dots : "" + this.noteSize + (dots.length > 0 ? dots : "n");
1647
2088
  }
1648
- canDot() {
1649
- return !this.dotted && this.noteLength !== MinNoteLength;
2089
+ /**
2090
+ * Get rhythm props with given arguments.
2091
+ * @param noteLength - Note length.
2092
+ * @param dotCount - Dot count.
2093
+ * @param tupletRatio - Tuplet ratio.
2094
+ * @returns - Rhythm props.
2095
+ */
2096
+ static get(noteLength, dotCount, tupletRatio) {
2097
+ if (dotCount !== void 0 || tupletRatio !== void 0) {
2098
+ return new _RhythmProps(noteLength, dotCount, tupletRatio);
2099
+ } else {
2100
+ let rhythmProps = this.cache.get(noteLength);
2101
+ if (!rhythmProps) {
2102
+ this.cache.set(noteLength, rhythmProps = new _RhythmProps(noteLength));
2103
+ }
2104
+ return rhythmProps;
2105
+ }
1650
2106
  }
1651
- hasStem() {
1652
- return this.noteLength < 192 /* Whole */;
2107
+ /**
2108
+ * Compare duration of rhythm props.
2109
+ * @param a - RhythmProps
2110
+ * @param b - RhythmProps
2111
+ * @returns - -1: a < b, 0: a === b, +1: a > b (duration comparisons)
2112
+ */
2113
+ static cmp(a, b) {
2114
+ return cmp(a.ticks, b.ticks);
1653
2115
  }
1654
- toString() {
1655
- return NoteSymbolMap.get(this.noteLength) + (this.dotted ? "." : "");
2116
+ /**
2117
+ * Compare duration equality of rhythm props.
2118
+ * @param a - RhythmProps
2119
+ * @param b - RhythmProps
2120
+ * @returns - true: a === b, false: a !== b (duration comparisons)
2121
+ */
2122
+ static equals(a, b) {
2123
+ return a.ticks === b.ticks;
1656
2124
  }
1657
2125
  };
2126
+ __publicField(_RhythmProps, "NoteSymbolMap", /* @__PURE__ */ new Map([[1, "\u{1D15D}"], [2, "\u{1D15E}"], [4, "\u{1D15F}"], [8, "\u{1D160}"], [16, "\u{1D161}"], [32, "\u{1D162}"], [64, "\u{1D163}"], [128, "\u{1D164}"]]));
2127
+ __publicField(_RhythmProps, "cache", /* @__PURE__ */ new Map());
2128
+ var RhythmProps = _RhythmProps;
1658
2129
 
1659
2130
  // src/theory/time-signature.ts
1660
2131
  var import_core9 = require("@tspro/web-music-score/core");
1661
2132
  var TimeSignature = class {
1662
2133
  constructor(...args) {
2134
+ /** Number of beats in measure, upper value (e.g. "3" in "3/4"). */
1663
2135
  __publicField(this, "beatCount");
2136
+ /** Beat size of time signature, lower value (e.g. "4" in "3/4"). */
1664
2137
  __publicField(this, "beatSize");
1665
- /** Lengths in ticks */
2138
+ /** Beat length. */
1666
2139
  __publicField(this, "beatLength");
2140
+ /** Number of ticks in measure. */
1667
2141
  __publicField(this, "measureTicks");
2142
+ /** Number of beam groups in measure. */
1668
2143
  __publicField(this, "beamGroupCount");
2144
+ /** Length of one beam group. */
1669
2145
  __publicField(this, "beamGroupLength");
1670
2146
  if (args.length === 1 && typeof args[0] === "string") {
1671
2147
  let parts = args[0].split("/");
@@ -1682,9 +2158,9 @@ var TimeSignature = class {
1682
2158
  } else if (!import_ts_utils_lib9.Utils.Is.isIntegerGte(this.beatSize, 1)) {
1683
2159
  throw new import_core9.MusicError(import_core9.MusicErrorType.Timesignature, `Invalid beatSize: ${this.beatSize}`);
1684
2160
  }
1685
- let beatLengthValue = RhythmProps.createFromNoteSize(this.beatSize);
1686
- this.beatLength = beatLengthValue.noteLength;
1687
- this.measureTicks = this.beatCount * beatLengthValue.ticks;
2161
+ let props = NoteLengthProps.create(this.beatSize);
2162
+ this.beatLength = props.noteLength;
2163
+ this.measureTicks = this.beatCount * props.ticks;
1688
2164
  if (this.is(2, 4) || this.is(3, 4) || this.is(4, 4)) {
1689
2165
  this.beamGroupCount = this.beatCount;
1690
2166
  } else if (this.is(6, 8) || this.is(9, 8)) {
@@ -1698,9 +2174,19 @@ var TimeSignature = class {
1698
2174
  throw new import_core9.MusicError(import_core9.MusicErrorType.Timesignature, `Invalid beamGroupLength: ${this.beamGroupLength}`);
1699
2175
  }
1700
2176
  }
2177
+ /**
2178
+ * Test whether this time signature has given beat count and size.
2179
+ * @param beatCount - Beat count.
2180
+ * @param beatSize - Beat size.
2181
+ * @returns - Boolean whether this time signature match given beat count and size.
2182
+ */
1701
2183
  is(beatCount, beatSize) {
1702
2184
  return this.beatCount === beatCount && this.beatSize === beatSize;
1703
2185
  }
2186
+ /**
2187
+ * Get string representation of this time signature (e.g. "3/4").
2188
+ * @returns - String representation.
2189
+ */
1704
2190
  toString() {
1705
2191
  return this.beatCount + "/" + this.beatSize;
1706
2192
  }
@@ -1717,17 +2203,17 @@ function getDefaultTimeSignature() {
1717
2203
  var defaultTempo;
1718
2204
  function getDefaultTempo() {
1719
2205
  if (!defaultTempo) {
1720
- defaultTempo = { beatsPerMinute: 120, options: { beatLength: 48 /* Quarter */, dotted: false } };
2206
+ defaultTempo = { beatsPerMinute: 120, options: { beatLength: "4n" /* Quarter */, dotCount: 0 } };
1721
2207
  }
1722
2208
  return defaultTempo;
1723
2209
  }
1724
2210
  function getTempoString(tempo) {
1725
- return new RhythmProps(tempo.options.beatLength, tempo.options.dotted).toString() + "=" + tempo.beatsPerMinute;
2211
+ return RhythmProps.get(tempo.options.beatLength, tempo.options.dotCount).toString() + "=" + tempo.beatsPerMinute;
1726
2212
  }
1727
2213
  function alterTempoSpeed(tempo, speed) {
1728
2214
  return {
1729
2215
  beatsPerMinute: tempo.beatsPerMinute * speed,
1730
- options: { beatLength: tempo.options.beatLength, dotted: tempo.options.dotted }
2216
+ options: { beatLength: tempo.options.beatLength, dotCount: tempo.options.dotCount }
1731
2217
  };
1732
2218
  }
1733
2219
 
@@ -1747,11 +2233,10 @@ var import_core10 = require("@tspro/web-music-score/core");
1747
2233
  Handedness,
1748
2234
  Interval,
1749
2235
  KeySignature,
1750
- MaxNoteLength,
1751
- MinNoteLength,
1752
2236
  Mode,
1753
2237
  Note,
1754
2238
  NoteLength,
2239
+ NoteLengthProps,
1755
2240
  PitchNotation,
1756
2241
  PitchNotationList,
1757
2242
  RhythmProps,
@@ -1761,6 +2246,7 @@ var import_core10 = require("@tspro/web-music-score/core");
1761
2246
  SymbolSet,
1762
2247
  TimeSignature,
1763
2248
  TuningNameList,
2249
+ Tuplet,
1764
2250
  alterTempoSpeed,
1765
2251
  getDefaultKeySignature,
1766
2252
  getDefaultScale,
@@ -1778,6 +2264,7 @@ var import_core10 = require("@tspro/web-music-score/core");
1778
2264
  validateNoteLength,
1779
2265
  validatePitchNotation,
1780
2266
  validateScaleType,
1781
- validateTuningName
2267
+ validateTuningName,
2268
+ validateTupletRatio
1782
2269
  });
1783
2270
  //# sourceMappingURL=index.js.map