web-music-score 0.0.1 → 6.0.0-pre.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 (42) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/LICENSE +62 -1
  3. package/README.md +46 -2
  4. package/dist/audio/index.d.ts +50 -0
  5. package/dist/audio/index.js +1858 -0
  6. package/dist/audio/index.mjs +92 -0
  7. package/dist/audio-cg/index.d.ts +21 -0
  8. package/dist/audio-cg/index.js +17568 -0
  9. package/dist/audio-cg/index.mjs +90 -0
  10. package/dist/audio-synth/index.d.ts +15 -0
  11. package/dist/audio-synth/index.js +18497 -0
  12. package/dist/audio-synth/index.mjs +63 -0
  13. package/dist/chunk-A7C2G7OG.mjs +101 -0
  14. package/dist/chunk-GNT3ECDB.mjs +267 -0
  15. package/dist/chunk-LO4NI4AU.mjs +18381 -0
  16. package/dist/chunk-PW2SO6EZ.mjs +37 -0
  17. package/dist/chunk-VHB57TXT.mjs +11 -0
  18. package/dist/chunk-X7BMJX7E.mjs +3867 -0
  19. package/dist/core/index.d.ts +31 -0
  20. package/dist/core/index.js +74 -0
  21. package/dist/core/index.mjs +22 -0
  22. package/dist/iife/audio-cg.global.js +220 -0
  23. package/dist/iife/index.global.js +228 -0
  24. package/dist/instrument-DS-9C6_8.d.ts +44 -0
  25. package/dist/music-objects-DLmp5uL6.d.ts +2398 -0
  26. package/dist/note-RVXvpfyV.d.ts +306 -0
  27. package/dist/pieces/index.d.ts +46 -0
  28. package/dist/pieces/index.js +79 -0
  29. package/dist/pieces/index.mjs +50 -0
  30. package/dist/react-ui/index.d.ts +86 -0
  31. package/dist/react-ui/index.js +132 -0
  32. package/dist/react-ui/index.mjs +96 -0
  33. package/dist/scale-B1M10_fu.d.ts +230 -0
  34. package/dist/score/index.d.ts +466 -0
  35. package/dist/score/index.js +13964 -0
  36. package/dist/score/index.mjs +10092 -0
  37. package/dist/tempo-D-JF-8b_.d.ts +409 -0
  38. package/dist/theory/index.d.ts +78 -0
  39. package/dist/theory/index.js +4842 -0
  40. package/dist/theory/index.mjs +1986 -0
  41. package/package.json +131 -3
  42. package/workspace.code-workspace +0 -9
@@ -0,0 +1,1986 @@
1
+ /* WebMusicScore v6.0.0-pre.1 | (c) 2023-2025 Stefan Brockmann | MIT License | Includes: Tone.js (MIT License), Color Name to Code (MIT License) */
2
+ import {
3
+ NoteLength,
4
+ NoteLengthProps,
5
+ RhythmProps,
6
+ Tuplet,
7
+ isNoteLength,
8
+ isTupletRatio,
9
+ validateNoteLength,
10
+ validateTupletRatio
11
+ } from "../chunk-GNT3ECDB.mjs";
12
+ import {
13
+ IndexArray,
14
+ LRUCache,
15
+ SignedIndexArray,
16
+ UniMap,
17
+ guard_exports,
18
+ utils_exports
19
+ } from "../chunk-X7BMJX7E.mjs";
20
+ import {
21
+ __publicField
22
+ } from "../chunk-A7C2G7OG.mjs";
23
+
24
+ // src/theory/types.ts
25
+ import { MusicError, MusicErrorType } from "web-music-score/core";
26
+ var SymbolSet = /* @__PURE__ */ ((SymbolSet2) => {
27
+ SymbolSet2[SymbolSet2["Ascii"] = 0] = "Ascii";
28
+ SymbolSet2[SymbolSet2["Unicode"] = 1] = "Unicode";
29
+ return SymbolSet2;
30
+ })(SymbolSet || {});
31
+ var PitchNotation = /* @__PURE__ */ ((PitchNotation2) => {
32
+ PitchNotation2[PitchNotation2["Scientific"] = 0] = "Scientific";
33
+ PitchNotation2[PitchNotation2["Helmholtz"] = 1] = "Helmholtz";
34
+ return PitchNotation2;
35
+ })(PitchNotation || {});
36
+ var PitchNotationList = utils_exports.Enum.getEnumValues(PitchNotation);
37
+ var DefaultPitchNotation = 0 /* Scientific */;
38
+ function validatePitchNotation(pn) {
39
+ if (guard_exports.isEnumValue(pn, PitchNotation)) {
40
+ return pn;
41
+ } else {
42
+ throw new MusicError(MusicErrorType.InvalidArg, `Invalid pitchNotation: ${pn}`);
43
+ }
44
+ }
45
+ function getPitchNotationName(pn) {
46
+ return PitchNotation[validatePitchNotation(pn)];
47
+ }
48
+ var GuitarNoteLabel = /* @__PURE__ */ ((GuitarNoteLabel2) => {
49
+ GuitarNoteLabel2["Default"] = "Default";
50
+ GuitarNoteLabel2["OmitOctave"] = "Omit Octave";
51
+ GuitarNoteLabel2["Interval"] = "Interval";
52
+ return GuitarNoteLabel2;
53
+ })(GuitarNoteLabel || {});
54
+ var DefaultGuitarNoteLabel = "Default" /* Default */;
55
+ var GuitarNoteLabelList = utils_exports.Enum.getEnumValues(GuitarNoteLabel);
56
+ function validateGuitarNoteLabel(label) {
57
+ if (guard_exports.isEnumValue(label, GuitarNoteLabel)) {
58
+ return label;
59
+ } else {
60
+ throw new MusicError(MusicErrorType.Timesignature, `Invalid guitarNoteLabel: ${label}`);
61
+ }
62
+ }
63
+
64
+ // src/theory/note.ts
65
+ import { MusicError as MusicError2, MusicErrorType as MusicErrorType2 } from "web-music-score/core";
66
+ function mod(n, m) {
67
+ return (n % m + m) % m;
68
+ }
69
+ var C0_chromaticId = 12;
70
+ var C0_diatonicId = 7;
71
+ var AccidentalAsciiSymbolMap = /* @__PURE__ */ new Map([[-2, "bb"], [-1, "b"], [0, ""], [1, "#"], [2, "x"]]);
72
+ var AccidentalUnicodeSymbolMap = /* @__PURE__ */ new Map([[-2, "\u{1D12B}"], [-1, "\u266D"], [0, ""], [1, "\u266F"], [2, "\u{1D12A}"]]);
73
+ var AccidentalMap = /* @__PURE__ */ new Map([["", 0], ["bb", -2], ["b", -1], ["#", 1], ["x", 2], ["\u{1D12B}", -2], ["\u266D", -1], ["\u266F", 1], ["\u{1D12A}", 2]]);
74
+ var NoteLetters = ["C", "D", "E", "F", "G", "A", "B"];
75
+ var DiatonicToChromaticMap = [0, 2, 4, 5, 7, 9, 11];
76
+ var NoteNameRegex = /^([A-G])((?:bb|𝄫|♭|b|#|♯|x|𝄪)?)?(-?\d+)?$/;
77
+ var _Note = class _Note {
78
+ constructor(arg, accidental, octave) {
79
+ /** Diatonic class */
80
+ __publicField(this, "diatonicClass");
81
+ /** Accidental. */
82
+ __publicField(this, "accidental");
83
+ /** Octave. */
84
+ __publicField(this, "octave");
85
+ if (typeof arg === "number" && typeof accidental === "number" && octave === void 0) {
86
+ _Note.validateDiatonicId(arg);
87
+ this.diatonicClass = _Note.getDiatonicClass(arg);
88
+ this.accidental = _Note.validateAccidental(accidental);
89
+ this.octave = _Note.getOctaveFromDiatonicId(arg);
90
+ } else if (typeof arg === "number" && typeof accidental === "number" && typeof octave === "number") {
91
+ this.diatonicClass = _Note.validateDiatonicClass(arg);
92
+ this.accidental = _Note.validateAccidental(accidental);
93
+ this.octave = _Note.validateOctave(octave);
94
+ } else if (typeof arg === "string" && typeof accidental === "number" && typeof octave === "number") {
95
+ this.diatonicClass = _Note.getDiatonicClass(arg);
96
+ this.accidental = _Note.validateAccidental(accidental);
97
+ this.octave = _Note.validateOctave(octave);
98
+ } else {
99
+ throw new MusicError2(MusicErrorType2.Note, `Invalid args: ${arg}, ${accidental}, ${octave}`);
100
+ }
101
+ }
102
+ /** Diatonic id getter. */
103
+ get diatonicId() {
104
+ return _Note.getDiatonicIdInOctave(this.diatonicClass, this.octave);
105
+ }
106
+ /** Chromatic id getter. */
107
+ get chromaticId() {
108
+ return _Note.getChromaticIdInOctave(DiatonicToChromaticMap[this.diatonicClass] + this.accidental, this.octave);
109
+ }
110
+ /** Midi number getter (implemented same as chromatic id). */
111
+ get midiNumber() {
112
+ return this.chromaticId;
113
+ }
114
+ /** Chromatic class getter. */
115
+ get chromaticClass() {
116
+ return _Note.getChromaticClass(DiatonicToChromaticMap[this.diatonicClass] + this.accidental);
117
+ }
118
+ /** Note letter getter. */
119
+ get noteLetter() {
120
+ return NoteLetters[this.diatonicClass];
121
+ }
122
+ /**
123
+ * Format note to string presentation.
124
+ * @param pitchNotation - Pitchy notation.
125
+ * @param symbolSet - Symbol set.
126
+ * @returns - String presentation of note.
127
+ */
128
+ format(pitchNotation, symbolSet) {
129
+ let { noteLetter, octave } = this;
130
+ let accidentalSymbol = _Note.getAccidentalSymbol(this.accidental, symbolSet);
131
+ if (pitchNotation === 1 /* Helmholtz */) {
132
+ if (octave >= 3) {
133
+ return noteLetter.toLowerCase() + accidentalSymbol + "\u2032".repeat(octave - 3);
134
+ } else {
135
+ return noteLetter.toUpperCase() + accidentalSymbol + "\u0375".repeat(2 - octave);
136
+ }
137
+ } else {
138
+ return noteLetter + accidentalSymbol + octave;
139
+ }
140
+ }
141
+ /**
142
+ * Format note to string presentation without octave number.
143
+ * @param symbolSet - Symbol set.
144
+ * @returns - String presentation of note without octave number.
145
+ */
146
+ formatOmitOctave(symbolSet) {
147
+ let noteLetter = NoteLetters[this.diatonicClass];
148
+ let accidental = _Note.getAccidentalSymbol(this.accidental, symbolSet);
149
+ return noteLetter + accidental;
150
+ }
151
+ /**
152
+ * Get note.
153
+ * @param noteName - Note name (e.g. "C4").
154
+ * @returns - Note.
155
+ */
156
+ static getNote(noteName) {
157
+ return this.noteCache.getOrCreate(noteName, () => {
158
+ let p = _Note.parseNote(noteName);
159
+ if (!p)
160
+ throw new MusicError2(MusicErrorType2.Note, `Invalid note "${noteName}".`);
161
+ if (p.octave === void 0)
162
+ throw new MusicError2(MusicErrorType2.Note, `Invalid note "${noteName}" (missing octave).`);
163
+ return new _Note(p.noteLetter, p.accidental, p.octave);
164
+ });
165
+ }
166
+ /**
167
+ * Test if given note name valid.
168
+ * @param noteName - Note name.
169
+ * @returns - True/false.
170
+ */
171
+ static isNote(noteName) {
172
+ let p = _Note.parseNote(noteName);
173
+ return p !== void 0 && p.octave !== void 0;
174
+ }
175
+ /**
176
+ * Validate given note name.
177
+ * @param noteName - Note name.
178
+ * @returns - True or throws.
179
+ */
180
+ static validateNote(noteName) {
181
+ if (this.isNote(noteName)) return true;
182
+ throw new MusicError2(MusicErrorType2.Note, `Invalid note "${noteName}"`);
183
+ }
184
+ /**
185
+ * Get chromatic note. There are number of alternatives, this function uses simple logic to choose one.
186
+ * @param chromaticId - Chromatic id.
187
+ * @returns - Note.
188
+ */
189
+ static getChromaticNote(chromaticId) {
190
+ return this.chromaticNoteCache.getOrCreate(chromaticId, () => {
191
+ const NoteNameList = ["C/B#", "C#/Db", "D", "D#/Eb", "E/Fb", "F/E#", "F#/Gb", "G", "G#/Ab", "A", "A#/Bb", "B/Cb"];
192
+ let noteName = NoteNameList[_Note.getChromaticClass(chromaticId)].split("/")[0] + _Note.getOctaveFromChromaticId(chromaticId);
193
+ return _Note.getNote(noteName);
194
+ });
195
+ }
196
+ static getDiatonicClass(arg) {
197
+ if (typeof arg === "number") {
198
+ return mod(arg, 7);
199
+ } else if (typeof arg === "string" && arg.length > 0) {
200
+ return NoteLetters.indexOf(_Note.validateNoteLetter(arg[0]));
201
+ } else {
202
+ throw new MusicError2(MusicErrorType2.Note, `Invalid arg: ${arg}`);
203
+ }
204
+ }
205
+ /**
206
+ * Get octave from diatonic id.
207
+ * @param diatonicId - Diatonic id.
208
+ * @returns - Octave.
209
+ */
210
+ static getOctaveFromDiatonicId(diatonicId) {
211
+ return Math.floor((diatonicId - C0_diatonicId) / 7);
212
+ }
213
+ /**
214
+ * Get diatonic id in given octave (transposes diatonic id to given octave).
215
+ * @param diatonicId - Original diatonic id.
216
+ * @param octave - Octave.
217
+ * @returns - Transposed diatonic id.
218
+ */
219
+ static getDiatonicIdInOctave(diatonicId, octave) {
220
+ return _Note.getDiatonicClass(diatonicId) + octave * 7 + C0_diatonicId;
221
+ }
222
+ /**
223
+ * Get chromatic class from chromatic id.
224
+ * @param chromaticId - Chromatic id.
225
+ * @returns - Chromatic class.
226
+ */
227
+ static getChromaticClass(chromaticId) {
228
+ return mod(chromaticId, 12);
229
+ }
230
+ /**
231
+ * Get octave from chromatic id.
232
+ * @param chromaticId - Chromatic id.
233
+ * @returns - Octave.
234
+ */
235
+ static getOctaveFromChromaticId(chromaticId) {
236
+ return Math.floor((chromaticId - C0_chromaticId) / 12);
237
+ }
238
+ /**
239
+ * Get chromatic id in given octave (transposes chromatic id to given octave).
240
+ * @param chromaticId - Original chromatic id.
241
+ * @param octave - Octave.
242
+ * @returns - Transpose chromatic id.
243
+ */
244
+ static getChromaticIdInOctave(chromaticId, octave) {
245
+ return _Note.getChromaticClass(chromaticId) + octave * 12 + C0_chromaticId;
246
+ }
247
+ /**
248
+ * Test if given two notes are equal.
249
+ * @param a - Note a.
250
+ * @param b - Note b.
251
+ * @returns - True/false.
252
+ */
253
+ static equals(a, b) {
254
+ if (a == null && b == null) {
255
+ return true;
256
+ } else if (a == null || b == null) {
257
+ return false;
258
+ } else {
259
+ return a === b || a.diatonicId === b.diatonicId && a.accidental === b.accidental;
260
+ }
261
+ }
262
+ /**
263
+ * Replace accidental symbols in given string to givn symbol set (ascii/unicode).
264
+ * @param str - String to replace.
265
+ * @param symbolSet - Symbol set.
266
+ * @returns - String with updated accidental symbols.
267
+ */
268
+ static replaceAccidentalSymbols(str, symbolSet) {
269
+ if (symbolSet === 1 /* Unicode */) {
270
+ return str.replace("bb", "\u{1D12B}").replace("b", "\u266D").replace("#", "\u266F").replace("x", "\u{1D12A}");
271
+ } else {
272
+ return str.replace("\u{1D12B}", "bb").replace("\u266D", "b").replace("\u266F", "#").replace("\u{1D12A}", "x");
273
+ }
274
+ }
275
+ /**
276
+ * Test if given string is valid note name.
277
+ * @param noteName - Note name to validate.
278
+ * @returns - True/false.
279
+ */
280
+ static isValidNoteName(noteName) {
281
+ return NoteNameRegex.test(noteName);
282
+ }
283
+ /**
284
+ * Parse note name string to note props.
285
+ * @param noteName - Note name to parse.
286
+ * @returns - Parsed note props or undefined if parsing error.
287
+ */
288
+ static parseNote(noteName) {
289
+ var _a;
290
+ let m = NoteNameRegex.exec(noteName);
291
+ if (!m) {
292
+ return void 0;
293
+ }
294
+ let noteLetter = _Note.validateNoteLetter(m[1]);
295
+ let accidentalStr = m[2];
296
+ let accidental = _Note.validateAccidental((_a = AccidentalMap.get(accidentalStr)) != null ? _a : 0);
297
+ let octaveStr = m[3];
298
+ let octave = octaveStr ? _Note.validateOctave(+octaveStr) : void 0;
299
+ return { noteLetter, accidental, octave };
300
+ }
301
+ /**
302
+ * Get scientific note name from given note name.
303
+ * @param noteName - Note name.
304
+ * @param symbolSet - Symbol set (ascii/unicode) for scientific note name.
305
+ * @returns - Scientific note name.
306
+ */
307
+ static getScientificNoteName(noteName, symbolSet) {
308
+ let p = _Note.parseNote(noteName);
309
+ if (!p) {
310
+ throw new MusicError2(MusicErrorType2.Note, `Invalid noteName: ${noteName}`);
311
+ }
312
+ let { noteLetter, accidental, octave } = p;
313
+ return noteLetter + _Note.getAccidentalSymbol(accidental, symbolSet) + (octave != null ? octave : "");
314
+ }
315
+ /**
316
+ * Get symbol of given accidental in given symbol set (ascii/unicide).
317
+ * @param accidental - Accidental.
318
+ * @param symbolsSet - Symbol set.
319
+ * @returns - Accidental symbol or undefined (invalid accidental).
320
+ */
321
+ static getAccidentalSymbol(accidental, symbolsSet) {
322
+ return symbolsSet === 1 /* Unicode */ ? AccidentalUnicodeSymbolMap.get(accidental) : AccidentalAsciiSymbolMap.get(accidental);
323
+ }
324
+ /**
325
+ * Get accidental value from given accidental symbol.
326
+ * @param accidentalSymbol - Accidental symbol (e.g. "#").
327
+ * @returns - Accidental vlaue.
328
+ */
329
+ static getAccidental(accidentalSymbol) {
330
+ let accidental = AccidentalMap.get(accidentalSymbol);
331
+ if (accidental === void 0) {
332
+ throw new MusicError2(MusicErrorType2.Note, `Invalid accidental: ${accidentalSymbol}`);
333
+ }
334
+ return accidental;
335
+ }
336
+ /**
337
+ * Get note letter from given diatonic id.
338
+ * @param diatonicId - Diatonic id.
339
+ * @returns - Note letter.
340
+ */
341
+ static getNoteLetter(diatonicId) {
342
+ return NoteLetters[_Note.getDiatonicClass(diatonicId)];
343
+ }
344
+ /**
345
+ * Find next lowest possible diatonic id that is above given bottom level.
346
+ * @param diatonicId - Diatonic id to begin with.
347
+ * @param bottomDiatonicId - Bottom diatonic id.
348
+ * @param addOctaveIfEqual - If true then add one octave if diatonic id would equal to bottom diatonic id.
349
+ * @returns - Diatonic id.
350
+ */
351
+ static findNextDiatonicIdAbove(diatonicId, bottomDiatonicId, addOctaveIfEqual) {
352
+ let diatonicClass = _Note.getDiatonicClass(diatonicId);
353
+ let bottomDiatonicClass = _Note.getDiatonicClass(bottomDiatonicId);
354
+ let addOctave = addOctaveIfEqual ? diatonicClass <= bottomDiatonicClass ? 1 : 0 : diatonicClass < bottomDiatonicClass ? 1 : 0;
355
+ return _Note.getDiatonicIdInOctave(diatonicClass, _Note.getOctaveFromDiatonicId(bottomDiatonicId) + addOctave);
356
+ }
357
+ /**
358
+ * Validate if given argument is diatonic id.
359
+ * @param diatonicId - Diatonic id to validate.
360
+ * @returns - Valid diatonic id or throws.
361
+ */
362
+ static validateDiatonicId(diatonicId) {
363
+ if (guard_exports.isInteger(diatonicId)) {
364
+ return diatonicId;
365
+ } else {
366
+ throw new MusicError2(MusicErrorType2.Note, `Invalid diatonicId: ${diatonicId}`);
367
+ }
368
+ }
369
+ /**
370
+ * Validate if given argument is diatonic class.
371
+ * @param diatonicClass - Diatonic class to validate.
372
+ * @returns - Valid diatonic class or throws.
373
+ */
374
+ static validateDiatonicClass(diatonicClass) {
375
+ if (guard_exports.isIntegerBetween(diatonicClass, 0, 6)) {
376
+ return diatonicClass;
377
+ } else {
378
+ throw new MusicError2(MusicErrorType2.Note, `Invalid diatonicClass: ${diatonicClass}`);
379
+ }
380
+ }
381
+ /**
382
+ * Validate if given argument is chromatic id.
383
+ * @param chromaticId - Chromatic id to validate.
384
+ * @returns - Valid chromatic id, or throws.
385
+ */
386
+ static validateChromaticId(chromaticId) {
387
+ if (guard_exports.isInteger(chromaticId)) {
388
+ return chromaticId;
389
+ } else {
390
+ throw new MusicError2(MusicErrorType2.Note, `Invalid chromaticId: ${chromaticId}`);
391
+ }
392
+ }
393
+ /**
394
+ * Validate if given argument is chromatic class.
395
+ * @param chromaticClass - Chromatic class to validate.
396
+ * @returns - Valid chromatic class, or throws.
397
+ */
398
+ static validatechromaticClass(chromaticClass) {
399
+ if (guard_exports.isIntegerBetween(chromaticClass, 0, 11)) {
400
+ return chromaticClass;
401
+ } else {
402
+ throw new MusicError2(MusicErrorType2.Note, `Invalid chromaticClass: ${chromaticClass}`);
403
+ }
404
+ }
405
+ /**
406
+ * Validate if given argument if note letter.
407
+ * @param noteLetter - Note letter to validate.
408
+ * @returns - Valid note letter or throws.
409
+ */
410
+ static validateNoteLetter(noteLetter) {
411
+ if (NoteLetters.some((n) => n === noteLetter)) {
412
+ return noteLetter;
413
+ } else {
414
+ throw new MusicError2(MusicErrorType2.Note, `Invalid note: ${noteLetter}`);
415
+ }
416
+ }
417
+ /**
418
+ * Validate if given argument is octave.
419
+ * @param octave - Octave to validate.
420
+ * @returns - Valid octave or throws.
421
+ */
422
+ static validateOctave(octave) {
423
+ if (guard_exports.isInteger(octave)) {
424
+ return octave;
425
+ } else {
426
+ throw new MusicError2(MusicErrorType2.Note, `Invalid octave: ${octave}`);
427
+ }
428
+ }
429
+ /**
430
+ * Validate if given argument is valid accidental.
431
+ * @param acc - Accidental to validate.
432
+ * @returns - Valid accidental or thorws.
433
+ */
434
+ static validateAccidental(acc) {
435
+ if (guard_exports.isIntegerBetween(acc, -2, 2)) {
436
+ return acc;
437
+ } else {
438
+ throw new MusicError2(MusicErrorType2.Note, `Invalid accidental: ${acc}`);
439
+ }
440
+ }
441
+ /**
442
+ * Sort notes by diatonicId in ascending order.
443
+ * @param notes - Array of notes.
444
+ * @returns Sorted array of notes.
445
+ */
446
+ static sort(notes) {
447
+ return notes.slice().sort(_Note.compareFunc);
448
+ }
449
+ /**
450
+ * Remove duplicate notes.
451
+ * @param notes - Array of notes.
452
+ * @returns Sorted set of notes.
453
+ */
454
+ static removeDuplicates(notes) {
455
+ let uniqueSet = [];
456
+ notes.forEach((note) => {
457
+ if (uniqueSet.find((n) => _Note.equals(note, n)) === void 0) {
458
+ uniqueSet.push(note);
459
+ }
460
+ });
461
+ return uniqueSet;
462
+ }
463
+ /**
464
+ * Function to compare two notes using diatonic id and accidental properties of notes.
465
+ * @param a - Note a.
466
+ * @param b - Note b.
467
+ * @returns - -1, 0 or 1.
468
+ */
469
+ static compareFunc(a, b) {
470
+ if (a.diatonicId < b.diatonicId) {
471
+ return -1;
472
+ } else if (a.diatonicId > b.diatonicId) {
473
+ return 1;
474
+ } else {
475
+ if (a.accidental < b.accidental) {
476
+ return -1;
477
+ } else if (a.accidental > b.accidental) {
478
+ return 1;
479
+ } else {
480
+ return 0;
481
+ }
482
+ }
483
+ }
484
+ };
485
+ __publicField(_Note, "noteCache", new UniMap());
486
+ __publicField(_Note, "chromaticNoteCache", new UniMap());
487
+ var Note = _Note;
488
+
489
+ // src/theory/key-signature.ts
490
+ import { MusicError as MusicError3, MusicErrorType as MusicErrorType3 } from "web-music-score/core";
491
+ function getAccidental(chromaticId, diatonicId) {
492
+ let a = Note.getChromaticClass(chromaticId) - new Note(diatonicId, 0).chromaticClass;
493
+ while (a > 2) {
494
+ a -= 12;
495
+ }
496
+ while (a < -2) {
497
+ a += 12;
498
+ }
499
+ return Note.validateAccidental(a);
500
+ }
501
+ var DegreeRule = /^(bb?|b?|#?|x?)([0-9]*)$/;
502
+ function parseDegree(degree) {
503
+ var _a, _b;
504
+ let m = DegreeRule.exec("" + degree);
505
+ if (!m) {
506
+ throw new MusicError3(MusicErrorType3.KeySignature, `Invalid degree: ${degree}`);
507
+ }
508
+ let acc = (_b = Note.getAccidental((_a = m[1]) != null ? _a : "")) != null ? _b : 0;
509
+ let deg = +m[2];
510
+ if (!guard_exports.isInteger(acc) || acc < -2 || acc > 2 || !guard_exports.isInteger(deg) || deg < 1) {
511
+ throw new MusicError3(MusicErrorType3.KeySignature, `Invalid degree: ${degree}`);
512
+ } else {
513
+ return { deg, acc };
514
+ }
515
+ }
516
+ var Mode = /* @__PURE__ */ ((Mode2) => {
517
+ Mode2[Mode2["Ionian"] = 1] = "Ionian";
518
+ Mode2[Mode2["Dorian"] = 2] = "Dorian";
519
+ Mode2[Mode2["Phrygian"] = 3] = "Phrygian";
520
+ Mode2[Mode2["Lydian"] = 4] = "Lydian";
521
+ Mode2[Mode2["Mixolydian"] = 5] = "Mixolydian";
522
+ Mode2[Mode2["Aeolian"] = 6] = "Aeolian";
523
+ Mode2[Mode2["Locrian"] = 7] = "Locrian";
524
+ return Mode2;
525
+ })(Mode || {});
526
+ var AccidentalType = /* @__PURE__ */ ((AccidentalType2) => {
527
+ AccidentalType2[AccidentalType2["Natural"] = 0] = "Natural";
528
+ AccidentalType2[AccidentalType2["Flats"] = 1] = "Flats";
529
+ AccidentalType2[AccidentalType2["Sharps"] = 2] = "Sharps";
530
+ return AccidentalType2;
531
+ })(AccidentalType || {});
532
+ function getDefaultKeySignature() {
533
+ return getScale("C", "Major" /* Major */);
534
+ }
535
+ var _KeySignature = class _KeySignature {
536
+ /**
537
+ * @param tonic - Tonic/root note.
538
+ * @param mode - Mode: Ionian/Major = 1, Dorian = 2, ..., Locrian = 7
539
+ */
540
+ constructor(tonic, mode) {
541
+ this.tonic = tonic;
542
+ this.mode = mode;
543
+ __publicField(this, "naturalScaleNotes");
544
+ __publicField(this, "accidentalByDiatonicClass");
545
+ __publicField(this, "orderedAccidentedNotes");
546
+ if (!guard_exports.isEnumValue(mode, Mode)) {
547
+ throw new MusicError3(MusicErrorType3.KeySignature, `Invalid mode: ${mode}`);
548
+ }
549
+ let intervals = [2, 2, 1, 2, 2, 2, 1];
550
+ for (let i = 1; i < mode; i++) {
551
+ intervals.push(intervals.shift());
552
+ }
553
+ this.naturalScaleNotes = [];
554
+ this.accidentalByDiatonicClass = [];
555
+ let diatonicId = Note.getDiatonicClass(tonic[0]);
556
+ let chromaticId = Note.getNote(tonic + "0").chromaticId;
557
+ for (let id = 0; id < 7; diatonicId++, chromaticId += intervals[id], id++) {
558
+ let note = new Note(Note.getDiatonicClass(diatonicId), getAccidental(chromaticId, diatonicId));
559
+ if (Math.abs(note.accidental) >= 2) {
560
+ throw new MusicError3(MusicErrorType3.KeySignature, "Key signature contains double accidental.");
561
+ }
562
+ this.naturalScaleNotes[id] = note;
563
+ this.accidentalByDiatonicClass[note.diatonicClass] = note.accidental;
564
+ }
565
+ let sharps = this.naturalScaleNotes.filter((n) => n.accidental > 0).sort((a, b) => {
566
+ let ai = _KeySignature.OrderOfSharps.indexOf(a.noteLetter);
567
+ let bi = _KeySignature.OrderOfSharps.indexOf(b.noteLetter);
568
+ if (ai === -1 || bi === -1) {
569
+ throw new MusicError3(MusicErrorType3.KeySignature, "Unexpected note in key signature.");
570
+ }
571
+ return ai - bi;
572
+ });
573
+ let flats = this.naturalScaleNotes.filter((n) => n.accidental < 0).sort((a, b) => {
574
+ let ai = _KeySignature.OrderOfFlats.indexOf(a.noteLetter);
575
+ let bi = _KeySignature.OrderOfFlats.indexOf(b.noteLetter);
576
+ if (ai === -1 || bi === -1) {
577
+ throw new MusicError3(MusicErrorType3.KeySignature, "Unexpected note in key signature.");
578
+ }
579
+ return ai - bi;
580
+ });
581
+ if (sharps.length !== 0 && flats.length !== 0) {
582
+ throw new MusicError3(MusicErrorType3.KeySignature, "Key Signature has both sharps and flats.");
583
+ }
584
+ this.orderedAccidentedNotes = flats.length > 0 ? flats : sharps;
585
+ }
586
+ /**
587
+ * Get accidental type sharps, flats, or natural (without accidentals).
588
+ * @returns - Accidental type.
589
+ */
590
+ getAccidentalType() {
591
+ if (this.orderedAccidentedNotes.length === 0) {
592
+ return 0 /* Natural */;
593
+ } else if (this.orderedAccidentedNotes[0].accidental < 0) {
594
+ return 1 /* Flats */;
595
+ } else {
596
+ return 2 /* Sharps */;
597
+ }
598
+ }
599
+ /**
600
+ * Get natural scale notes of this key signature, natural scale has 7 steps (e.g. Major scale: W, W, H, W, W, W, H).
601
+ * @returns - Array of notes.
602
+ */
603
+ getNaturalScaleNotes() {
604
+ return this.naturalScaleNotes;
605
+ }
606
+ /**
607
+ * Get accidental for given diatonic id.
608
+ * @param diatonicId - Diatonic id.
609
+ * @returns - Accidental -2, -1, 0, 1 or 2.
610
+ */
611
+ getAccidental(diatonicId) {
612
+ var _a;
613
+ return (_a = this.accidentalByDiatonicClass[Note.getDiatonicClass(diatonicId)]) != null ? _a : 0;
614
+ }
615
+ /**
616
+ * Get number of accidentals this key signature has.
617
+ * @returns - Number of accidentals.
618
+ */
619
+ getNumAccidentals() {
620
+ return this.orderedAccidentedNotes.length;
621
+ }
622
+ /**
623
+ * Get accidental notes in correct order.
624
+ * @returns - Array of accidental notes.
625
+ */
626
+ getOrderedAccidentalNotes() {
627
+ return this.orderedAccidentedNotes;
628
+ }
629
+ /**
630
+ * Get note of key signature by degree value.
631
+ * @param degree - Degree number in range [1, 7] or string e.g "b5" or "#4".
632
+ * @returns - Note.
633
+ */
634
+ getNoteByDegree(degree) {
635
+ let { deg, acc } = parseDegree(degree);
636
+ if (acc === 0) {
637
+ return this.naturalScaleNotes[(deg - 1) % 7];
638
+ } else {
639
+ let note = this.naturalScaleNotes[(deg - 1) % 7];
640
+ return new Note(note.diatonicId, note.accidental + acc);
641
+ }
642
+ }
643
+ /**
644
+ * Test equality of given key signatures.
645
+ * @param a - Key signature a.
646
+ * @param b - Key signature b.
647
+ * @returns - True/false.
648
+ */
649
+ static equals(a, b) {
650
+ if (a == null && b == null) {
651
+ return true;
652
+ } else if (a == null || b == null) {
653
+ return false;
654
+ } else {
655
+ return a === b || a.tonic === b.tonic && a.mode === b.mode;
656
+ }
657
+ }
658
+ };
659
+ __publicField(_KeySignature, "OrderOfSharps", "FCGDAEB");
660
+ __publicField(_KeySignature, "OrderOfFlats", "BEADGCF");
661
+ var KeySignature = _KeySignature;
662
+
663
+ // src/theory/interval.ts
664
+ import { MusicError as MusicError4, MusicErrorType as MusicErrorType4 } from "web-music-score/core";
665
+ var IntervalQualityAbbrMap = new UniMap([
666
+ ["Major", "M"],
667
+ ["minor", "m"],
668
+ ["Perfect", "P"],
669
+ ["diminished", "d"],
670
+ ["Augmented", "A"],
671
+ ["doubly diminished", "dd"],
672
+ ["Doubly Augmented", "AA"]
673
+ ]);
674
+ var IntervalQualities = {
675
+ "1": {
676
+ // Unison
677
+ "0": "Perfect",
678
+ "1": "Augmented",
679
+ "-1": "diminished",
680
+ "2": "Doubly Augmented",
681
+ "-2": "doubly diminished"
682
+ },
683
+ "2": {
684
+ // Second
685
+ "2": "Major",
686
+ "1": "minor",
687
+ "3": "Augmented",
688
+ "0": "diminished",
689
+ "4": "Doubly Augmented",
690
+ "-1": "doubly diminished"
691
+ },
692
+ "3": {
693
+ // Third
694
+ "4": "Major",
695
+ "3": "minor",
696
+ "5": "Augmented",
697
+ "2": "diminished",
698
+ "6": "Doubly Augmented",
699
+ "1": "doubly diminished"
700
+ },
701
+ "4": {
702
+ // Fourth
703
+ "5": "Perfect",
704
+ "6": "Augmented",
705
+ "4": "diminished",
706
+ "7": "Doubly Augmented",
707
+ "3": "doubly diminished"
708
+ },
709
+ "5": {
710
+ // Fifth
711
+ "7": "Perfect",
712
+ "8": "Augmented",
713
+ "6": "diminished",
714
+ "9": "Doubly Augmented",
715
+ "5": "doubly diminished"
716
+ },
717
+ "6": {
718
+ // Sixth
719
+ "9": "Major",
720
+ "8": "minor",
721
+ "10": "Augmented",
722
+ "7": "diminished",
723
+ "11": "Doubly Augmented",
724
+ "6": "doubly diminished"
725
+ },
726
+ "7": {
727
+ // Seventh
728
+ "11": "Major",
729
+ "10": "minor",
730
+ "12": "Augmented",
731
+ "9": "diminished",
732
+ "13": "Doubly Augmented",
733
+ "8": "doubly diminished"
734
+ },
735
+ "8": {
736
+ // Octave
737
+ "12": "Perfect",
738
+ "13": "Augmented",
739
+ "11": "diminished",
740
+ "14": "Doubly Augmented",
741
+ "10": "doubly diminished"
742
+ }
743
+ };
744
+ function getIntervalQuality(diatonicInterval, chromaticInterval) {
745
+ while (diatonicInterval < 1) {
746
+ diatonicInterval += 7;
747
+ chromaticInterval += 12;
748
+ }
749
+ while (diatonicInterval > 8) {
750
+ diatonicInterval -= 7;
751
+ chromaticInterval -= 12;
752
+ }
753
+ const qualities = IntervalQualities[diatonicInterval];
754
+ if (qualities) {
755
+ return qualities[chromaticInterval];
756
+ } else {
757
+ return void 0;
758
+ }
759
+ }
760
+ function validateIntervalQuality(q) {
761
+ if (!(q === "Perfect" || q === "Major" || q === "minor" || q === "Augmented" || q === "diminished" || q === "Doubly Augmented" || q === "doubly diminished")) {
762
+ throw new MusicError4(MusicErrorType4.InvalidArg, `Invalid interval quality: ${q}`);
763
+ } else {
764
+ return q;
765
+ }
766
+ }
767
+ function formatQuantity(q) {
768
+ if (!guard_exports.isIntegerGte(q, 1)) {
769
+ throw new MusicError4(MusicErrorType4.InvalidArg, `Invalid interval quantity: ${q}`);
770
+ } else {
771
+ return utils_exports.Math.toOrdinalNumber(q);
772
+ }
773
+ }
774
+ var InvalidIntervalException = class extends Error {
775
+ constructor(msg) {
776
+ super(msg);
777
+ this.msg = msg;
778
+ this.name = "InvalidInterval";
779
+ }
780
+ };
781
+ var Interval = class _Interval {
782
+ constructor(note1, note2) {
783
+ this.note1 = note1;
784
+ this.note2 = note2;
785
+ /** Interval direction. */
786
+ __publicField(this, "direction");
787
+ /** Number of semitones. */
788
+ __publicField(this, "semitones");
789
+ /** Interval quantity. */
790
+ __publicField(this, "quantity");
791
+ /** Interval quality. */
792
+ __publicField(this, "quality");
793
+ if (note2.diatonicId >= note1.diatonicId) {
794
+ this.direction = note2.diatonicId === note1.diatonicId ? "Unison" : "Ascending";
795
+ this.quantity = note2.diatonicId - note1.diatonicId + 1;
796
+ this.semitones = note2.chromaticId - note1.chromaticId;
797
+ } else {
798
+ this.direction = "Descending";
799
+ this.quantity = note1.diatonicId - note2.diatonicId + 1;
800
+ this.semitones = note1.chromaticId - note2.chromaticId;
801
+ }
802
+ let quality = getIntervalQuality(this.quantity, this.semitones);
803
+ if (quality) {
804
+ this.quality = quality;
805
+ } else {
806
+ throw new InvalidIntervalException("Unknown interval quality");
807
+ }
808
+ }
809
+ /**
810
+ * Get interval between given two notes.
811
+ * @param note1 - First note.
812
+ * @param note2 - Second note.
813
+ * @returns - Interval if valid, or undefined.
814
+ */
815
+ static get(note1, note2) {
816
+ try {
817
+ return new _Interval(note1, note2);
818
+ } catch (err) {
819
+ if (err instanceof InvalidIntervalException) {
820
+ return void 0;
821
+ } else {
822
+ throw err;
823
+ }
824
+ }
825
+ }
826
+ /**
827
+ * Get string presentation of interval (e.g. "Descending Major 2").
828
+ * @returns - Interval string.
829
+ */
830
+ toString() {
831
+ let direction = this.direction === "Unison" ? "" : this.direction + " ";
832
+ let quality = this.quality + " ";
833
+ let quantity = this.direction === "Unison" ? "Unison" : formatQuantity(this.quantity);
834
+ return direction + quality + quantity;
835
+ }
836
+ /**
837
+ * Get abbrevated string presentation of interval (e.g. "↓M2").
838
+ * @returns - Interval abbrevated string.
839
+ */
840
+ toAbbrString() {
841
+ let direction = this.direction === "Descending" ? "\u2193" : "";
842
+ let quality = IntervalQualityAbbrMap.getOrDefault(this.quality, "?");
843
+ let quantity = this.quantity;
844
+ return direction + quality + quantity;
845
+ }
846
+ };
847
+
848
+ // src/theory/scale.ts
849
+ import { MusicError as MusicError5, MusicErrorType as MusicErrorType5 } from "web-music-score/core";
850
+ function getNaturalDiatonicId(chromaticId) {
851
+ let diatonicClass = Note.getDiatonicClass("CCDDEFFGGAAB"[Note.getChromaticClass(chromaticId)]);
852
+ let octave = Note.getOctaveFromChromaticId(chromaticId);
853
+ return Note.getDiatonicIdInOctave(diatonicClass, octave);
854
+ }
855
+ var FullTonicList = [
856
+ "Cb",
857
+ "C",
858
+ "C#",
859
+ "Db",
860
+ "D",
861
+ "D#",
862
+ "Eb",
863
+ "E",
864
+ "E#",
865
+ "Fb",
866
+ "F",
867
+ "F#",
868
+ "Gb",
869
+ "G",
870
+ "G#",
871
+ "Ab",
872
+ "A",
873
+ "A#",
874
+ "Bb",
875
+ "B",
876
+ "B#"
877
+ ];
878
+ var ScaleType = /* @__PURE__ */ ((ScaleType2) => {
879
+ ScaleType2["Major"] = "Major";
880
+ ScaleType2["NaturalMinor"] = "Natural Minor";
881
+ ScaleType2["HarmonicMinor"] = "Harmonic Minor";
882
+ ScaleType2["Ionian"] = "Ionian";
883
+ ScaleType2["Dorian"] = "Dorian";
884
+ ScaleType2["Phrygian"] = "Phrygian";
885
+ ScaleType2["Lydian"] = "Lydian";
886
+ ScaleType2["Mixolydian"] = "Mixolydian";
887
+ ScaleType2["Aeolian"] = "Aeolian";
888
+ ScaleType2["Locrian"] = "Locrian";
889
+ ScaleType2["MajorPentatonic"] = "Major Pentatonic";
890
+ ScaleType2["MinorPentatonic"] = "Minor Pentatonic";
891
+ ScaleType2["MajorHexatonicBlues"] = "Major Hexatonic Blues";
892
+ ScaleType2["MinorHexatonicBlues"] = "Minor Hexatonic Blues";
893
+ ScaleType2["HeptatonicBlues"] = "Heptatonic Blues";
894
+ return ScaleType2;
895
+ })(ScaleType || {});
896
+ function getMode(scaleType) {
897
+ switch (scaleType) {
898
+ case "Major" /* Major */:
899
+ return 1;
900
+ case "Natural Minor" /* NaturalMinor */:
901
+ return 6;
902
+ case "Harmonic Minor" /* HarmonicMinor */:
903
+ return 6;
904
+ case "Ionian" /* Ionian */:
905
+ return 1;
906
+ case "Dorian" /* Dorian */:
907
+ return 2;
908
+ case "Phrygian" /* Phrygian */:
909
+ return 3;
910
+ case "Lydian" /* Lydian */:
911
+ return 4;
912
+ case "Mixolydian" /* Mixolydian */:
913
+ return 5;
914
+ case "Aeolian" /* Aeolian */:
915
+ return 6;
916
+ case "Locrian" /* Locrian */:
917
+ return 7;
918
+ case "Major Pentatonic" /* MajorPentatonic */:
919
+ return 1;
920
+ case "Minor Pentatonic" /* MinorPentatonic */:
921
+ return 6;
922
+ case "Major Hexatonic Blues" /* MajorHexatonicBlues */:
923
+ return 1;
924
+ case "Minor Hexatonic Blues" /* MinorHexatonicBlues */:
925
+ return 6;
926
+ case "Heptatonic Blues" /* HeptatonicBlues */:
927
+ return 1;
928
+ default:
929
+ throw new MusicError5(MusicErrorType5.Scale, `Invalid scaleType: ${scaleType}`);
930
+ }
931
+ }
932
+ var Scale = class extends KeySignature {
933
+ /**
934
+ * Create nev scale object instance.
935
+ * @param tonic - Tonic (e.g. "C" in "C Major").
936
+ * @param scaleType - Scale typo ("e.g. "Major" in "C Major").
937
+ */
938
+ constructor(tonic, scaleType) {
939
+ super(tonic, getMode(scaleType));
940
+ this.tonic = tonic;
941
+ this.scaleType = scaleType;
942
+ /** Degrees of scale notes. */
943
+ __publicField(this, "scaleDegrees");
944
+ /** Scale notes. */
945
+ __publicField(this, "scaleNotes");
946
+ /** Degrees (or undefined) of chromatic classes. */
947
+ __publicField(this, "chromaticClassDegree");
948
+ __publicField(this, "preferredChromaticIdNoteCache", new SignedIndexArray());
949
+ switch (scaleType) {
950
+ case "Harmonic Minor" /* HarmonicMinor */:
951
+ this.scaleDegrees = [1, 2, 3, 4, 5, 6, "#7"];
952
+ break;
953
+ case "Major Pentatonic" /* MajorPentatonic */:
954
+ this.scaleDegrees = [1, 2, 3, 5, 6];
955
+ break;
956
+ case "Minor Pentatonic" /* MinorPentatonic */:
957
+ this.scaleDegrees = [1, 3, 4, 5, 7];
958
+ break;
959
+ case "Major Hexatonic Blues" /* MajorHexatonicBlues */:
960
+ this.scaleDegrees = [1, 2, "b3", 3, 5, 6];
961
+ break;
962
+ case "Minor Hexatonic Blues" /* MinorHexatonicBlues */:
963
+ this.scaleDegrees = [1, 3, 4, "b5", 5, 7];
964
+ break;
965
+ case "Heptatonic Blues" /* HeptatonicBlues */:
966
+ this.scaleDegrees = [1, 2, "b3", 4, "b5", 6, "b7"];
967
+ break;
968
+ default:
969
+ this.scaleDegrees = [1, 2, 3, 4, 5, 6, 7];
970
+ break;
971
+ }
972
+ this.scaleNotes = this.scaleDegrees.map((d) => this.getNoteByDegree(d));
973
+ this.chromaticClassDegree = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((chromaticClass) => {
974
+ let id = this.scaleNotes.findIndex((scaleNote) => scaleNote.chromaticClass === chromaticClass);
975
+ return id >= 0 ? this.scaleDegrees[id] : void 0;
976
+ });
977
+ }
978
+ /**
979
+ * Compare if two scales equals.
980
+ * @param a - Scale a.
981
+ * @param b - Scale b.
982
+ * @returns - Boolean equality of given scales.
983
+ */
984
+ static equals(a, b) {
985
+ if (a == null && b == null) {
986
+ return true;
987
+ } else if (a == null || b == null) {
988
+ return false;
989
+ } else {
990
+ return a === b || a.getScaleName() === b.getScaleName();
991
+ }
992
+ }
993
+ /**
994
+ * Get scale name.
995
+ * @param symbolSet - Symbol set to format scale name in ascii or unicode.
996
+ * @returns - Scale name string.
997
+ */
998
+ getScaleName(symbolSet) {
999
+ switch (symbolSet) {
1000
+ case 1 /* Unicode */:
1001
+ return Note.getScientificNoteName(this.tonic, symbolSet) + " " + this.scaleType;
1002
+ default:
1003
+ return this.tonic + " " + this.scaleType;
1004
+ }
1005
+ }
1006
+ /**
1007
+ * Get scale notes.
1008
+ * @param bottomNote - Computed scale notes begin no lower than this note.
1009
+ * @param numOctaves - How many octaves?
1010
+ * @returns - Array of scale notes.
1011
+ */
1012
+ getScaleNotes(bottomNote, numOctaves) {
1013
+ if (!guard_exports.isIntegerGte(numOctaves, 1)) {
1014
+ throw new MusicError5(MusicErrorType5.Scale, `Invalid numOctaves: ${numOctaves}`);
1015
+ }
1016
+ let scaleNoteList = [];
1017
+ for (let o = 1; o <= numOctaves; o++) {
1018
+ scaleNoteList = [...scaleNoteList, ...this.scaleNotes];
1019
+ }
1020
+ scaleNoteList.push(this.scaleNotes[0]);
1021
+ let diatonicId = Note.getNote(bottomNote).diatonicId;
1022
+ return scaleNoteList.map((note) => {
1023
+ diatonicId = Note.findNextDiatonicIdAbove(note.diatonicId, diatonicId, false);
1024
+ return new Note(diatonicId, note.accidental);
1025
+ });
1026
+ }
1027
+ /**
1028
+ * Get scale overview (e.g. "C - D - E - F - G - A - B" for "C Major" scale).
1029
+ * @returns - Scale overview string.
1030
+ */
1031
+ getScaleOverview() {
1032
+ return this.getScaleNotes("C4", 1).map((note) => note.formatOmitOctave(1 /* Unicode */)).join(" - ");
1033
+ }
1034
+ /**
1035
+ * Get scale steps, array of 1 (half step) and 2 (whole step), (e.g. [2, 2, 1, 2, 2, 2, 1] for Major scale).
1036
+ * @returns - Array of scale steps.
1037
+ */
1038
+ getScaleSteps() {
1039
+ let chromaticIds = this.getScaleNotes("C4", 1).map((note) => note.chromaticId);
1040
+ let steps = [];
1041
+ for (let i = 0; i < chromaticIds.length - 1; i++) {
1042
+ steps.push(utils_exports.Math.mod(chromaticIds[i + 1] - chromaticIds[i], 12));
1043
+ }
1044
+ return steps;
1045
+ }
1046
+ /**
1047
+ * 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).
1048
+ * @returns - Array of scale steps string presentation.
1049
+ */
1050
+ getScaleStringSteps() {
1051
+ return this.getScaleSteps().map((step) => step === 1 ? "H" : step === 2 ? "W" : step.toString() + "H");
1052
+ }
1053
+ /**
1054
+ * Test if given note is scale note.
1055
+ * @param note - Note to test.
1056
+ * @returns - True/false.
1057
+ */
1058
+ isScaleNote(note) {
1059
+ return this.chromaticClassDegree[note.chromaticClass] !== void 0;
1060
+ }
1061
+ /**
1062
+ * Test if given note is scale root note.
1063
+ * @param note - Note to test.
1064
+ * @returns - True/false.
1065
+ */
1066
+ isScaleRootNote(note) {
1067
+ return String(this.chromaticClassDegree[note.chromaticClass]) === "1";
1068
+ }
1069
+ /**
1070
+ * Get interval value between scale root note and given note.
1071
+ * @param note - Note.
1072
+ * @returns - Interval.
1073
+ */
1074
+ getIntervalFromRootNote(note) {
1075
+ let rootNote = this.getScaleNotes("C0", 1)[0];
1076
+ while (note.chromaticId >= rootNote.chromaticId + 12) {
1077
+ note = new Note(note.diatonicClass, note.accidental, note.octave - 1);
1078
+ }
1079
+ if (note.chromaticId < rootNote.chromaticId) {
1080
+ throw new MusicError5(MusicErrorType5.Scale, `Note is below rootNote.`);
1081
+ }
1082
+ let interval = Interval.get(rootNote, note);
1083
+ if (interval === void 0) {
1084
+ throw new MusicError5(MusicErrorType5.Scale, `Interval is undefined.`);
1085
+ } else {
1086
+ return interval;
1087
+ }
1088
+ }
1089
+ // Can it be negative case?
1090
+ /**
1091
+ * Get preferred chromatic note from given chromatic id.
1092
+ * @param chromaticId - Chromatic id.
1093
+ * @returns - Note.
1094
+ */
1095
+ getPreferredChromaticNote(chromaticId) {
1096
+ Note.validateChromaticId(chromaticId);
1097
+ let note = this.preferredChromaticIdNoteCache.get(chromaticId);
1098
+ if (note) {
1099
+ return note;
1100
+ }
1101
+ let octave = Note.getOctaveFromChromaticId(chromaticId);
1102
+ let scaleNotes = this.scaleNotes.map((accNote) => {
1103
+ if (accNote.noteLetter === "C" && accNote.accidental < 0) {
1104
+ return new Note(accNote.diatonicClass, accNote.accidental, octave + 1);
1105
+ } else if (accNote.noteLetter === "B" && accNote.accidental > 0) {
1106
+ return new Note(accNote.diatonicClass, accNote.accidental, octave - 1);
1107
+ } else {
1108
+ return new Note(accNote.diatonicClass, accNote.accidental, octave);
1109
+ }
1110
+ });
1111
+ note = scaleNotes.find((note2) => Note.getChromaticClass(chromaticId) === note2.chromaticClass);
1112
+ if (note) {
1113
+ this.preferredChromaticIdNoteCache.set(chromaticId, note);
1114
+ return note;
1115
+ }
1116
+ let diatonicIdMid = getNaturalDiatonicId(chromaticId);
1117
+ let diatonicIdStart = diatonicIdMid - 2;
1118
+ let diatonicIdEnd = diatonicIdMid + 2;
1119
+ let preferFlat = this.getAccidentalType() === 1 /* Flats */;
1120
+ let preferredAccs = preferFlat ? [0, -1, 1, -2, 2] : [0, 1, -1, 2, -2];
1121
+ for (let ai = 0; ai < preferredAccs.length; ai++) {
1122
+ let acc = preferredAccs[ai];
1123
+ for (let diatonicId = Math.max(0, diatonicIdStart); diatonicId <= diatonicIdEnd; diatonicId++) {
1124
+ note = new Note(diatonicId, acc);
1125
+ if (chromaticId === note.chromaticId) {
1126
+ this.preferredChromaticIdNoteCache.set(chromaticId, note);
1127
+ return note;
1128
+ }
1129
+ }
1130
+ }
1131
+ note = Note.getChromaticNote(chromaticId);
1132
+ this.preferredChromaticIdNoteCache.set(chromaticId, note);
1133
+ return note;
1134
+ }
1135
+ };
1136
+ var ScaleFactory = class {
1137
+ /**
1138
+ * Create new scale factory object instance.
1139
+ * @param type - Scale type.
1140
+ */
1141
+ constructor(type) {
1142
+ this.type = type;
1143
+ __publicField(this, "tonicList", []);
1144
+ __publicField(this, "scaleMap", /* @__PURE__ */ new Map());
1145
+ let naturalScales = [];
1146
+ let sharpScales = [];
1147
+ let flatScales = [];
1148
+ FullTonicList.forEach((tonic) => {
1149
+ try {
1150
+ let scale = new Scale(tonic, this.type);
1151
+ switch (scale.getAccidentalType()) {
1152
+ case 0 /* Natural */:
1153
+ naturalScales.push(scale);
1154
+ break;
1155
+ case 2 /* Sharps */:
1156
+ if (tonic.endsWith("b")) {
1157
+ return;
1158
+ } else {
1159
+ sharpScales.push(scale);
1160
+ break;
1161
+ }
1162
+ case 1 /* Flats */:
1163
+ if (tonic.endsWith("#")) {
1164
+ return;
1165
+ } else {
1166
+ flatScales.push(scale);
1167
+ break;
1168
+ }
1169
+ }
1170
+ this.scaleMap.set(tonic, scale);
1171
+ } catch (err) {
1172
+ }
1173
+ });
1174
+ if (naturalScales.length === 0) {
1175
+ throw new MusicError5(MusicErrorType5.Scale, `Expected natural scale.`);
1176
+ }
1177
+ const SortByAccidentalCountFunc = (a, b) => a.getNumAccidentals() - b.getNumAccidentals();
1178
+ this.tonicList = [
1179
+ ...naturalScales.sort(SortByAccidentalCountFunc).map((scale) => scale.tonic),
1180
+ "- Sharps -",
1181
+ ...sharpScales.sort(SortByAccidentalCountFunc).map((scale) => scale.tonic),
1182
+ "- Flats -",
1183
+ ...flatScales.sort(SortByAccidentalCountFunc).map((scale) => scale.tonic)
1184
+ ];
1185
+ }
1186
+ /**
1187
+ * Get list of tonics (e.g. "C", "C#", ... for Major scale type).
1188
+ * @returns - Array of tonics.
1189
+ */
1190
+ getTonicList() {
1191
+ return this.tonicList;
1192
+ }
1193
+ /**
1194
+ * Get default tonic.
1195
+ * @returns - Default tonic.
1196
+ */
1197
+ getDefaultTonic() {
1198
+ return this.tonicList[0];
1199
+ }
1200
+ /**
1201
+ * Get scale type.
1202
+ * @returns - SCale type.
1203
+ */
1204
+ getType() {
1205
+ return this.type;
1206
+ }
1207
+ /**
1208
+ * Get scale by given tonic.
1209
+ * @param tonic - Tonic (e.g. "C").
1210
+ * @returns - Scale.
1211
+ */
1212
+ getScale(tonic) {
1213
+ let scale = this.scaleMap.get(tonic);
1214
+ if (!scale) {
1215
+ throw new MusicError5(MusicErrorType5.Scale, `Invalid scale: ${tonic} ${this.type}`);
1216
+ } else {
1217
+ return scale;
1218
+ }
1219
+ }
1220
+ /**
1221
+ * Test if this scale factory has scale for given tonic value.
1222
+ * @param tonic - Tonic.
1223
+ * @returns - True/false.
1224
+ */
1225
+ hasScale(tonic) {
1226
+ return this.scaleMap.get(tonic) !== void 0;
1227
+ }
1228
+ };
1229
+ var ScaleFactoryList = [
1230
+ new ScaleFactory("Major" /* Major */),
1231
+ new ScaleFactory("Natural Minor" /* NaturalMinor */),
1232
+ new ScaleFactory("Harmonic Minor" /* HarmonicMinor */),
1233
+ "- Modes -",
1234
+ new ScaleFactory("Ionian" /* Ionian */),
1235
+ new ScaleFactory("Dorian" /* Dorian */),
1236
+ new ScaleFactory("Phrygian" /* Phrygian */),
1237
+ new ScaleFactory("Lydian" /* Lydian */),
1238
+ new ScaleFactory("Mixolydian" /* Mixolydian */),
1239
+ new ScaleFactory("Aeolian" /* Aeolian */),
1240
+ new ScaleFactory("Locrian" /* Locrian */),
1241
+ "- Pentatonic -",
1242
+ new ScaleFactory("Major Pentatonic" /* MajorPentatonic */),
1243
+ new ScaleFactory("Minor Pentatonic" /* MinorPentatonic */),
1244
+ "- Blues -",
1245
+ new ScaleFactory("Major Hexatonic Blues" /* MajorHexatonicBlues */),
1246
+ new ScaleFactory("Minor Hexatonic Blues" /* MinorHexatonicBlues */),
1247
+ new ScaleFactory("Heptatonic Blues" /* HeptatonicBlues */)
1248
+ ];
1249
+ function getScaleFactoryList() {
1250
+ return ScaleFactoryList;
1251
+ }
1252
+ var ScaleFactoryMap = new UniMap();
1253
+ ScaleFactoryList.forEach((factory) => {
1254
+ if (factory instanceof ScaleFactory) {
1255
+ ScaleFactoryMap.set(factory.getType(), factory);
1256
+ }
1257
+ });
1258
+ function getScaleFactory(scaleType) {
1259
+ let f = ScaleFactoryMap.get(scaleType);
1260
+ if (!f) {
1261
+ throw new MusicError5(MusicErrorType5.Scale, `Invalid scaleType: ${scaleType}`);
1262
+ } else {
1263
+ return f;
1264
+ }
1265
+ }
1266
+ function validateScaleType(scaleType) {
1267
+ if (guard_exports.isEnumValue(scaleType, ScaleType)) {
1268
+ return scaleType;
1269
+ } else {
1270
+ throw new MusicError5(MusicErrorType5.Scale, `Invalid scaleType: "${scaleType}"`);
1271
+ }
1272
+ }
1273
+ function getScale(arg0, arg1) {
1274
+ let tonic;
1275
+ let scaleType;
1276
+ if (arg1 !== void 0) {
1277
+ tonic = arg0;
1278
+ scaleType = validateScaleType(arg1);
1279
+ } else {
1280
+ tonic = arg0.split(" ")[0];
1281
+ scaleType = validateScaleType(arg0.substring(tonic.length + 1));
1282
+ }
1283
+ return getScaleFactory(scaleType).getScale(tonic);
1284
+ }
1285
+ var DefaultScale = getScale("C", "Major" /* Major */);
1286
+ function getDefaultScale() {
1287
+ return DefaultScale;
1288
+ }
1289
+
1290
+ // src/theory/chord.ts
1291
+ import { MusicError as MusicError6, MusicErrorType as MusicErrorType6 } from "web-music-score/core";
1292
+ var isEqualNote = (n1, n2) => n1.chromaticClass === n2.chromaticClass;
1293
+ var OkayRootNoteList = [
1294
+ "C",
1295
+ "C#",
1296
+ "Db",
1297
+ // Same id, first one always gets found
1298
+ "D",
1299
+ "Eb",
1300
+ "E",
1301
+ "F",
1302
+ "F#",
1303
+ "Gb",
1304
+ "G",
1305
+ "Ab",
1306
+ "A",
1307
+ "Bb",
1308
+ "B",
1309
+ "Cb"
1310
+ ].map((noteName) => Note.getNote(noteName + "0"));
1311
+ var okayRootNoteCache = new IndexArray();
1312
+ function getOkayRootNote(wantedRootNote) {
1313
+ return okayRootNoteCache.getOrCreate(wantedRootNote.chromaticClass, () => {
1314
+ let rootNote = OkayRootNoteList.find((note) => isEqualNote(note, wantedRootNote));
1315
+ if (!rootNote) {
1316
+ throw new MusicError6(MusicErrorType6.InvalidArg, `Invalid chord root note: ${wantedRootNote.formatOmitOctave(1 /* Unicode */)}`);
1317
+ }
1318
+ return rootNote;
1319
+ });
1320
+ }
1321
+ function getChordNoteByDegree(chordRootNote, degree) {
1322
+ let chordRootNoteStr = chordRootNote.formatOmitOctave(0 /* Ascii */);
1323
+ let ks = getScale(chordRootNoteStr, "Major" /* Major */);
1324
+ return ks.getNoteByDegree(degree);
1325
+ }
1326
+ function removeNoteDuplicates(notes) {
1327
+ return utils_exports.Arr.removeDuplicates(notes, isEqualNote);
1328
+ }
1329
+ var ChordInfoList = [
1330
+ // Power chord
1331
+ { name: "5", degrees: [1, 5] },
1332
+ // Triads
1333
+ { name: "", degrees: [1, 3, 5] },
1334
+ { name: "m", degrees: [1, "b3", 5] },
1335
+ { name: "dim", degrees: [1, "b3", "b5"] },
1336
+ { name: "aug", degrees: [1, 3, "#5"] },
1337
+ { name: "sus2", degrees: [1, 2, 5] },
1338
+ { name: "sus4", degrees: [1, 4, 5] },
1339
+ // Four-part chords
1340
+ { name: "6", degrees: [1, 3, 5, 6] },
1341
+ { name: "m6", degrees: [1, "b3", 5, 6] },
1342
+ { name: "m(maj7)", degrees: [1, "b3", 5, 7] },
1343
+ { name: "7", degrees: [1, 3, 5, "b7"] },
1344
+ { name: "7\u266D5", degrees: [1, 3, "b5", "b7"] },
1345
+ { name: "7\u266F5", degrees: [1, 3, "#5", "b7"] },
1346
+ { name: "7sus2", degrees: [1, 2, 5, "b7"] },
1347
+ { name: "7sus4", degrees: [1, 4, 5, "b7"] },
1348
+ { name: "dim7", degrees: [1, "b3", "b5", "bb7"] },
1349
+ { name: "maj7", degrees: [1, 3, 5, 7] },
1350
+ { name: "maj7\u266D5", degrees: [1, 3, "b5", 7] },
1351
+ { name: "maj7\u266F5", degrees: [1, 3, "#5", 7] },
1352
+ { name: "m7", degrees: [1, "b3", 5, "b7"] },
1353
+ { name: "m7\u266D5", degrees: [1, "b3", "b5", "b7"] },
1354
+ { name: "m7\u266F5", degrees: [1, "b3", "#5", "b7"] },
1355
+ // Five-part chords
1356
+ { name: "9", degrees: [1, 3, 5, "b7", 9] },
1357
+ { name: "m9", degrees: [1, "b3", 5, "b7", 9] },
1358
+ { name: "7\u266F9", degrees: [1, 3, 5, "b7", "#9"] },
1359
+ { name: "7\u266D9", degrees: [1, 3, 5, "b7", "b9"] },
1360
+ // Six-part chords
1361
+ { name: "11", degrees: [1, 3, 5, "b7", 9, 11] },
1362
+ { name: "m11", degrees: [1, "b3", 5, "b7", 9, 11] },
1363
+ // Seven-part chords
1364
+ { name: "13", degrees: [1, 3, 5, "b7", 9, 11, 13] },
1365
+ { name: "m13", degrees: [1, "b3", 5, "b7", 9, 11, 13] }
1366
+ ];
1367
+ function canOmitDegree(chordInfo, degree) {
1368
+ if (chordInfo.degrees.every((c) => c !== degree)) {
1369
+ return true;
1370
+ }
1371
+ return chordInfo.degrees.length >= 4 && degree === 5 || // Four (and higher) part chord can exclude 5th degree
1372
+ chordInfo.degrees.length >= 6 && degree === 9 || // Six (and higher) part chord can exclude 9th degree
1373
+ chordInfo.degrees.length >= 7 && degree === 11;
1374
+ }
1375
+ var InvalidChordException = class extends Error {
1376
+ constructor(msg) {
1377
+ super(msg);
1378
+ this.msg = msg;
1379
+ this.name = "InvalidChordException";
1380
+ }
1381
+ };
1382
+ var Chord = class _Chord {
1383
+ constructor(chordInfo, chordNotes, rootNote, bassNote) {
1384
+ this.chordInfo = chordInfo;
1385
+ this.rootNote = rootNote;
1386
+ /** Chord name. */
1387
+ __publicField(this, "name");
1388
+ /** Notes of this chord. */
1389
+ __publicField(this, "notes");
1390
+ /** Notes that are omitted in this chord (partial chord). */
1391
+ __publicField(this, "omitNotes");
1392
+ /** Bass note if not chord root note (e.g. "B" in "C/B"). */
1393
+ __publicField(this, "slashBassNote");
1394
+ this.name = chordInfo.name;
1395
+ let notesLeft = chordNotes.slice();
1396
+ let outOfChordBass = !notesLeft.some((note) => isEqualNote(note, bassNote));
1397
+ if (outOfChordBass) {
1398
+ notesLeft.push(bassNote);
1399
+ }
1400
+ this.notes = new Array(this.chordInfo.degrees.length);
1401
+ this.omitNotes = new Array(this.chordInfo.degrees.length);
1402
+ for (let i = 0; i < chordInfo.degrees.length; i++) {
1403
+ let degree = chordInfo.degrees[i];
1404
+ let degreeNote = this.notes[i] = getChordNoteByDegree(this.rootNote, degree);
1405
+ let noteIndex = notesLeft.findIndex((note) => isEqualNote(note, degreeNote));
1406
+ if (noteIndex >= 0) {
1407
+ this.omitNotes[i] = false;
1408
+ notesLeft.splice(noteIndex, 1);
1409
+ } else if (canOmitDegree(chordInfo, degree)) {
1410
+ this.omitNotes[i] = true;
1411
+ } else {
1412
+ throw new InvalidChordException("Missing chord note!");
1413
+ }
1414
+ if (isEqualNote(bassNote, degreeNote)) {
1415
+ bassNote = degreeNote;
1416
+ }
1417
+ }
1418
+ if (notesLeft.length > 0) {
1419
+ if (notesLeft.every((note) => isEqualNote(note, bassNote))) {
1420
+ this.slashBassNote = bassNote;
1421
+ } else {
1422
+ throw new InvalidChordException("Got some extra notes that are not bass note!");
1423
+ }
1424
+ } else {
1425
+ this.slashBassNote = isEqualNote(bassNote, this.rootNote) ? void 0 : bassNote;
1426
+ }
1427
+ if (chordInfo.name === "5" && this.slashBassNote) {
1428
+ throw new InvalidChordException("Power chord no bass note allowed!");
1429
+ }
1430
+ }
1431
+ /**
1432
+ * Get all chords that can be made up with given notes.
1433
+ * @param notes - Array of notes.
1434
+ * @returns - Array of chords.
1435
+ */
1436
+ static getChords(notes) {
1437
+ let chords = [];
1438
+ let chordNotes = Note.sort(notes);
1439
+ let bassNote = chordNotes[0];
1440
+ let uniqueNotes = removeNoteDuplicates(chordNotes);
1441
+ for (let inversion = 0; inversion < uniqueNotes.length; inversion++) {
1442
+ let rootNote = getOkayRootNote(uniqueNotes[inversion]);
1443
+ ChordInfoList.forEach((chordInfo) => {
1444
+ try {
1445
+ let newChord = new _Chord(chordInfo, uniqueNotes, rootNote, bassNote);
1446
+ let replaceIndex = chords.findIndex((chord) => chord.replaceWith(newChord));
1447
+ if (replaceIndex >= 0) {
1448
+ chords[replaceIndex] = newChord;
1449
+ } else {
1450
+ chords.push(newChord);
1451
+ }
1452
+ } catch (err) {
1453
+ if (!(err instanceof InvalidChordException)) {
1454
+ throw err;
1455
+ }
1456
+ }
1457
+ });
1458
+ }
1459
+ return chords;
1460
+ }
1461
+ replaceWith(arg) {
1462
+ if (arg.notes.length < this.notes.length) {
1463
+ return false;
1464
+ } else if (arg.slashBassNote && this.slashBassNote && !isEqualNote(arg.slashBassNote, this.slashBassNote) || arg.slashBassNote && !this.slashBassNote || !arg.slashBassNote && this.slashBassNote) {
1465
+ return false;
1466
+ }
1467
+ for (let i = 0; i < this.notes.length; i++) {
1468
+ if (!isEqualNote(arg.notes[i], this.notes[i]) || arg.omitNotes[i] !== this.omitNotes[i]) {
1469
+ return false;
1470
+ }
1471
+ }
1472
+ return true;
1473
+ }
1474
+ /**
1475
+ * Get chord string presentation.
1476
+ * @returns Chord string presentation (e.g. "C/B").
1477
+ */
1478
+ toString() {
1479
+ let symbolSet = 1 /* Unicode */;
1480
+ let rootNoteStr = this.rootNote.formatOmitOctave(symbolSet);
1481
+ let slashBassStr = this.slashBassNote ? "/" + this.slashBassNote.formatOmitOctave(symbolSet) : "";
1482
+ return rootNoteStr + this.name + slashBassStr;
1483
+ }
1484
+ /**
1485
+ * Get degree notation string (e.g. "E - 1(C) - 3(E) - 5(G)").
1486
+ * @returns Degree notation string.
1487
+ */
1488
+ getDegreeNotationString() {
1489
+ let symbolSet = 1 /* Unicode */;
1490
+ let bassNoteStr = this.slashBassNote ? this.slashBassNote.formatOmitOctave(symbolSet) + " - " : "";
1491
+ let degreeNoteStr = this.omitNotes.map((omitNote, i) => {
1492
+ return this.getDegreeStr(i) + "(" + (omitNote ? "-" : this.getNoteStr(i)) + ")";
1493
+ }).join(" - ");
1494
+ return bassNoteStr + degreeNoteStr;
1495
+ }
1496
+ /**
1497
+ * Get string presentation of omitted degrees (e.g. "Omits 5(G), 9(D)").
1498
+ * @returns - String presentation of omitted degrees.
1499
+ */
1500
+ getOmittedDegreesString() {
1501
+ let omittedStrList = this.omitNotes.map((omit, i) => {
1502
+ return omit ? this.getDegreeStr(i) + "(" + this.getNoteStr(i) + ")" : void 0;
1503
+ }).filter((str) => str !== void 0);
1504
+ return omittedStrList.length > 0 ? "Omits " + omittedStrList.join(", ") : "";
1505
+ }
1506
+ /**
1507
+ * Degree index for given degree index (e.g. "3").
1508
+ * @param i - Chord degree index (e.g. 0 is first note degree of chord).
1509
+ * @returns Degree string.
1510
+ */
1511
+ getDegreeStr(i) {
1512
+ return Note.replaceAccidentalSymbols("" + this.chordInfo.degrees[i], 1 /* Unicode */);
1513
+ }
1514
+ /**
1515
+ * Get note name for given degree index (e.g. "E").
1516
+ * @param i - Chord degree index (e.g. 0 is first note degree of chord).
1517
+ * @returns - Note name string.
1518
+ */
1519
+ getNoteStr(i) {
1520
+ return this.notes[i].formatOmitOctave(1 /* Unicode */);
1521
+ }
1522
+ };
1523
+
1524
+ // src/theory/assets/tunings.json
1525
+ var tunings_default = {
1526
+ list: [
1527
+ {
1528
+ name: "Standard",
1529
+ strings: [
1530
+ "E2",
1531
+ "A2",
1532
+ "D3",
1533
+ "G3",
1534
+ "B3",
1535
+ "E4"
1536
+ ]
1537
+ },
1538
+ {
1539
+ name: "B Standard",
1540
+ strings: [
1541
+ "B1",
1542
+ "E2",
1543
+ "A2",
1544
+ "D3",
1545
+ "F#3",
1546
+ "B3"
1547
+ ]
1548
+ },
1549
+ {
1550
+ name: "C Standard",
1551
+ strings: [
1552
+ "C2",
1553
+ "F2",
1554
+ "A#2",
1555
+ "D#3",
1556
+ "G3",
1557
+ "C4"
1558
+ ]
1559
+ },
1560
+ {
1561
+ name: "D Modal",
1562
+ strings: [
1563
+ "D2",
1564
+ "A2",
1565
+ "D3",
1566
+ "G3",
1567
+ "A3",
1568
+ "D4"
1569
+ ]
1570
+ },
1571
+ {
1572
+ name: "D Standard",
1573
+ strings: [
1574
+ "D2",
1575
+ "G2",
1576
+ "C3",
1577
+ "F3",
1578
+ "A3",
1579
+ "D4"
1580
+ ]
1581
+ },
1582
+ {
1583
+ name: "D# Standard",
1584
+ strings: [
1585
+ "D#2",
1586
+ "G#2",
1587
+ "C#3",
1588
+ "F#3",
1589
+ "A#3",
1590
+ "D#4"
1591
+ ]
1592
+ },
1593
+ {
1594
+ name: "Double Drop D",
1595
+ strings: [
1596
+ "D2",
1597
+ "A2",
1598
+ "D3",
1599
+ "G3",
1600
+ "B3",
1601
+ "D4"
1602
+ ]
1603
+ },
1604
+ {
1605
+ name: "Drop B",
1606
+ strings: [
1607
+ "B1",
1608
+ "F#2",
1609
+ "B2",
1610
+ "E3",
1611
+ "G#3",
1612
+ "C#4"
1613
+ ]
1614
+ },
1615
+ {
1616
+ name: "Drop C",
1617
+ strings: [
1618
+ "C2",
1619
+ "G2",
1620
+ "C3",
1621
+ "F3",
1622
+ "A3",
1623
+ "D4"
1624
+ ]
1625
+ },
1626
+ {
1627
+ name: "Drop D",
1628
+ strings: [
1629
+ "D2",
1630
+ "A2",
1631
+ "D3",
1632
+ "G3",
1633
+ "B3",
1634
+ "E4"
1635
+ ]
1636
+ },
1637
+ {
1638
+ name: "Low C",
1639
+ strings: [
1640
+ "C2",
1641
+ "G2",
1642
+ "D3",
1643
+ "G3",
1644
+ "A3",
1645
+ "D4"
1646
+ ]
1647
+ },
1648
+ {
1649
+ name: "New Standard",
1650
+ strings: [
1651
+ "C2",
1652
+ "G2",
1653
+ "D3",
1654
+ "A3",
1655
+ "E4",
1656
+ "G4"
1657
+ ]
1658
+ },
1659
+ {
1660
+ name: "Open A",
1661
+ strings: [
1662
+ "E2",
1663
+ "A2",
1664
+ "C#3",
1665
+ "E3",
1666
+ "A3",
1667
+ "E4"
1668
+ ]
1669
+ },
1670
+ {
1671
+ name: "Open A Minor",
1672
+ strings: [
1673
+ "E2",
1674
+ "A2",
1675
+ "E3",
1676
+ "A3",
1677
+ "C4",
1678
+ "E4"
1679
+ ]
1680
+ },
1681
+ {
1682
+ name: "Open C",
1683
+ strings: [
1684
+ "C2",
1685
+ "G2",
1686
+ "C3",
1687
+ "G3",
1688
+ "C4",
1689
+ "E4"
1690
+ ]
1691
+ },
1692
+ {
1693
+ name: "Open C Minor",
1694
+ strings: [
1695
+ "C2",
1696
+ "G2",
1697
+ "C3",
1698
+ "G3",
1699
+ "C4",
1700
+ "D#4"
1701
+ ]
1702
+ },
1703
+ {
1704
+ name: "Open D",
1705
+ strings: [
1706
+ "D2",
1707
+ "A2",
1708
+ "D3",
1709
+ "F#3",
1710
+ "A3",
1711
+ "D4"
1712
+ ]
1713
+ },
1714
+ {
1715
+ name: "Open D Minor",
1716
+ strings: [
1717
+ "D2",
1718
+ "A2",
1719
+ "D3",
1720
+ "F3",
1721
+ "A3",
1722
+ "D4"
1723
+ ]
1724
+ },
1725
+ {
1726
+ name: "Open E",
1727
+ strings: [
1728
+ "E2",
1729
+ "B2",
1730
+ "E3",
1731
+ "G#3",
1732
+ "B3",
1733
+ "E4"
1734
+ ]
1735
+ },
1736
+ {
1737
+ name: "Open E Minor",
1738
+ strings: [
1739
+ "E2",
1740
+ "B2",
1741
+ "E3",
1742
+ "G3",
1743
+ "B3",
1744
+ "E4"
1745
+ ]
1746
+ },
1747
+ {
1748
+ name: "Open G",
1749
+ strings: [
1750
+ "D2",
1751
+ "G2",
1752
+ "D3",
1753
+ "G3",
1754
+ "B3",
1755
+ "D4"
1756
+ ]
1757
+ },
1758
+ {
1759
+ name: "Open G Minor",
1760
+ strings: [
1761
+ "D2",
1762
+ "G2",
1763
+ "D3",
1764
+ "G3",
1765
+ "A#3",
1766
+ "D4"
1767
+ ]
1768
+ }
1769
+ ]
1770
+ };
1771
+
1772
+ // src/theory/guitar.ts
1773
+ import { MusicError as MusicError7, MusicErrorType as MusicErrorType7 } from "web-music-score/core";
1774
+ var TuningNameList = tunings_default.list.map((data) => data.name);
1775
+ var DefaultTuningName = TuningNameList[0];
1776
+ function validateTuningName(tuningName) {
1777
+ if (TuningNameList.indexOf(tuningName) < 0) {
1778
+ throw new MusicError7(MusicErrorType7.InvalidArg, `Invalid tuning name: ${tuningName}`);
1779
+ } else {
1780
+ return tuningName;
1781
+ }
1782
+ }
1783
+ var TuningStringsCache = new LRUCache(100);
1784
+ function getTuningStrings(tuningName) {
1785
+ let tuningStrings = TuningStringsCache.get(tuningName);
1786
+ if (!tuningStrings) {
1787
+ let tuningData = tunings_default.list.find((data) => data.name === tuningName);
1788
+ if (!tuningData) {
1789
+ throw new MusicError7(MusicErrorType7.InvalidArg, `Invalid tuningName: ${tuningName}`);
1790
+ }
1791
+ tuningStrings = tuningData.strings.slice().reverse().map((noteName) => Note.getNote(noteName));
1792
+ if (!guard_exports.isIntegerEq(tuningStrings.length, 6)) {
1793
+ throw new MusicError7(MusicErrorType7.Unknown, `Tuning has ${tuningStrings.length} strings.`);
1794
+ }
1795
+ TuningStringsCache.set(tuningName, tuningStrings);
1796
+ }
1797
+ return tuningStrings;
1798
+ }
1799
+
1800
+ // src/theory/time-signature.ts
1801
+ import { MusicError as MusicError8, MusicErrorType as MusicErrorType8 } from "web-music-score/core";
1802
+ var TimeSignatures = /* @__PURE__ */ ((TimeSignatures2) => {
1803
+ TimeSignatures2["_2_4"] = "2/4";
1804
+ TimeSignatures2["_3_4"] = "3/4";
1805
+ TimeSignatures2["_4_4"] = "4/4";
1806
+ TimeSignatures2["_3_8"] = "3/8";
1807
+ TimeSignatures2["_5_8"] = "5/8";
1808
+ TimeSignatures2["_6_8"] = "6/8";
1809
+ TimeSignatures2["_7_8"] = "7/8";
1810
+ TimeSignatures2["_9_8"] = "9/8";
1811
+ TimeSignatures2["_12_8"] = "12/8";
1812
+ return TimeSignatures2;
1813
+ })(TimeSignatures || {});
1814
+ var BeamGrouping = /* @__PURE__ */ ((BeamGrouping2) => {
1815
+ BeamGrouping2["_2_3"] = "2-3";
1816
+ BeamGrouping2["_3_2"] = "3-2";
1817
+ BeamGrouping2["_2_2_3"] = "2-2-3";
1818
+ BeamGrouping2["_3_2_2"] = "3-2-2";
1819
+ return BeamGrouping2;
1820
+ })(BeamGrouping || {});
1821
+ var TimeSignature = class {
1822
+ constructor(...args) {
1823
+ /** Number of beats in measure, upper value (e.g. "3" in "3/4"). */
1824
+ __publicField(this, "beatCount");
1825
+ /** Beat size of time signature, lower value (e.g. "4" in "3/4"). */
1826
+ __publicField(this, "beatSize");
1827
+ /** Beat length. */
1828
+ __publicField(this, "beatLength");
1829
+ /** Number of ticks in measure. */
1830
+ __publicField(this, "measureTicks");
1831
+ /** Beam groups (e.g. [[2], [2]] or [[2, 2], [2, 2]] (first try as [[4], [4]])). */
1832
+ __publicField(this, "beamGroupSizes", []);
1833
+ let beamGrouping;
1834
+ if (guard_exports.isEnumValue(args[0], TimeSignatures)) {
1835
+ let parts = args[0].split("/");
1836
+ this.beatCount = +parts[0];
1837
+ this.beatSize = +parts[1];
1838
+ if (guard_exports.isEnumValue(args[1], BeamGrouping)) {
1839
+ beamGrouping = args[1];
1840
+ }
1841
+ } else if (guard_exports.isIntegerGte(args[0], 2) && guard_exports.isIntegerGte(args[1], 2)) {
1842
+ this.beatCount = args[0];
1843
+ this.beatSize = args[1];
1844
+ if (guard_exports.isEnumValue(args[2], BeamGrouping)) {
1845
+ beamGrouping = args[2];
1846
+ }
1847
+ } else {
1848
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid args: ${args}`);
1849
+ }
1850
+ if (!guard_exports.isIntegerGte(this.beatCount, 1)) {
1851
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid beatCount: ${this.beatCount}`);
1852
+ } else if (!guard_exports.isIntegerGte(this.beatSize, 1)) {
1853
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid beatSize: ${this.beatSize}`);
1854
+ }
1855
+ let { noteLength, ticks } = NoteLengthProps.create(this.beatSize);
1856
+ this.beatLength = noteLength;
1857
+ this.measureTicks = this.beatCount * ticks;
1858
+ if (this.is(2, 4)) {
1859
+ this.beamGroupSizes = [[2], [2]];
1860
+ } else if (this.is(3, 4)) {
1861
+ this.beamGroupSizes = [[2], [2], [2]];
1862
+ } else if (this.is(4, 4)) {
1863
+ this.beamGroupSizes = [[2, 2], [2, 2]];
1864
+ } else if (this.is(3, 8)) {
1865
+ this.beamGroupSizes = [[3]];
1866
+ } else if (this.is(5, 8)) {
1867
+ if (!guard_exports.isUndefined(beamGrouping) && beamGrouping !== "2-3" /* _2_3 */ && beamGrouping !== "3-2" /* _3_2 */) {
1868
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid beam grouping "${beamGrouping}" for time signature "${this.toString()}".`);
1869
+ } else {
1870
+ this.beamGroupSizes = beamGrouping === "3-2" /* _3_2 */ ? [[3], [2]] : [[2], [3]];
1871
+ beamGrouping = void 0;
1872
+ }
1873
+ } else if (this.is(6, 8)) {
1874
+ this.beamGroupSizes = [[3], [3]];
1875
+ } else if (this.is(7, 8)) {
1876
+ if (!guard_exports.isUndefined(beamGrouping) && beamGrouping !== "2-2-3" /* _2_2_3 */ && beamGrouping !== "3-2-2" /* _3_2_2 */) {
1877
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid beam grouping "${beamGrouping}" for time signature "${this.toString()}".`);
1878
+ } else {
1879
+ this.beamGroupSizes = beamGrouping === "3-2-2" /* _3_2_2 */ ? [[3], [2], [2]] : [[2], [2], [3]];
1880
+ beamGrouping = void 0;
1881
+ }
1882
+ } else if (this.is(9, 8)) {
1883
+ this.beamGroupSizes = [[3], [3], [3]];
1884
+ } else if (this.is(12, 8)) {
1885
+ this.beamGroupSizes = [[3], [3], [3], [3]];
1886
+ }
1887
+ if (this.beamGroupSizes.length === 0) {
1888
+ throw new MusicError8(MusicErrorType8.Timesignature, `Unimplemented time signature "${this.toString()}".`);
1889
+ } else if (beamGrouping !== void 0) {
1890
+ throw new MusicError8(MusicErrorType8.Timesignature, `Invalid beam grouping "${beamGrouping}" for time signature "${this.toString()}".`);
1891
+ }
1892
+ }
1893
+ /**
1894
+ * Test whether this time signature has given beat count and size.
1895
+ * @param beatCount - Beat count.
1896
+ * @param beatSize - Beat size.
1897
+ * @returns - Boolean whether this time signature match given beat count and size.
1898
+ */
1899
+ is(beatCount, beatSize) {
1900
+ return this.beatCount === beatCount && this.beatSize === beatSize;
1901
+ }
1902
+ /**
1903
+ * Get string representation of this time signature (e.g. "3/4").
1904
+ * @returns - String representation.
1905
+ */
1906
+ toString() {
1907
+ return this.beatCount + "/" + this.beatSize;
1908
+ }
1909
+ };
1910
+ var defaultTimeSignature;
1911
+ function getDefaultTimeSignature() {
1912
+ if (!defaultTimeSignature) {
1913
+ defaultTimeSignature = new TimeSignature(4, 4);
1914
+ }
1915
+ return defaultTimeSignature;
1916
+ }
1917
+
1918
+ // src/theory/tempo.ts
1919
+ var defaultTempo;
1920
+ function getDefaultTempo() {
1921
+ if (!defaultTempo) {
1922
+ defaultTempo = { beatsPerMinute: 120, options: { beatLength: "4n" /* Quarter */, dotCount: 0 } };
1923
+ }
1924
+ return defaultTempo;
1925
+ }
1926
+ function getTempoString(tempo) {
1927
+ return RhythmProps.get(tempo.options.beatLength, tempo.options.dotCount).toString() + "=" + tempo.beatsPerMinute;
1928
+ }
1929
+ function alterTempoSpeed(tempo, speed) {
1930
+ return {
1931
+ beatsPerMinute: tempo.beatsPerMinute * speed,
1932
+ options: { beatLength: tempo.options.beatLength, dotCount: tempo.options.dotCount }
1933
+ };
1934
+ }
1935
+
1936
+ // src/theory/index.ts
1937
+ import { init as initCore } from "web-music-score/core";
1938
+ initCore();
1939
+ export {
1940
+ AccidentalType,
1941
+ BeamGrouping,
1942
+ Chord,
1943
+ DefaultGuitarNoteLabel,
1944
+ DefaultPitchNotation,
1945
+ DefaultTuningName,
1946
+ GuitarNoteLabel,
1947
+ GuitarNoteLabelList,
1948
+ Interval,
1949
+ KeySignature,
1950
+ Mode,
1951
+ Note,
1952
+ NoteLength,
1953
+ NoteLengthProps,
1954
+ PitchNotation,
1955
+ PitchNotationList,
1956
+ RhythmProps,
1957
+ Scale,
1958
+ ScaleFactory,
1959
+ ScaleType,
1960
+ SymbolSet,
1961
+ TimeSignature,
1962
+ TimeSignatures,
1963
+ TuningNameList,
1964
+ Tuplet,
1965
+ alterTempoSpeed,
1966
+ getDefaultKeySignature,
1967
+ getDefaultScale,
1968
+ getDefaultTempo,
1969
+ getDefaultTimeSignature,
1970
+ getPitchNotationName,
1971
+ getScale,
1972
+ getScaleFactory,
1973
+ getScaleFactoryList,
1974
+ getTempoString,
1975
+ getTuningStrings,
1976
+ isNoteLength,
1977
+ isTupletRatio,
1978
+ validateGuitarNoteLabel,
1979
+ validateIntervalQuality,
1980
+ validateNoteLength,
1981
+ validatePitchNotation,
1982
+ validateScaleType,
1983
+ validateTuningName,
1984
+ validateTupletRatio
1985
+ };
1986
+ //# sourceMappingURL=index.mjs.map