@libraz/libcantus 0.9.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.md +61 -270
  2. package/README_ja.md +61 -214
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +200 -353
  6. package/dist/analyze/index.d.ts +200 -353
  7. package/dist/analyze/index.js +56 -9
  8. package/dist/chunk-3JXCW6DX.cjs +3220 -0
  9. package/dist/chunk-3JXCW6DX.cjs.map +1 -0
  10. package/dist/chunk-4A2RMUCC.js +7647 -0
  11. package/dist/chunk-4A2RMUCC.js.map +1 -0
  12. package/dist/chunk-6XPTBE7Y.cjs +49 -0
  13. package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
  14. package/dist/chunk-AFM2ULJH.cjs +5121 -0
  15. package/dist/chunk-AFM2ULJH.cjs.map +1 -0
  16. package/dist/chunk-E3F4AHAH.cjs +690 -0
  17. package/dist/chunk-E3F4AHAH.cjs.map +1 -0
  18. package/dist/{chunk-2PTFGVD7.js → chunk-ELCTZUJJ.js} +250 -132
  19. package/dist/chunk-ELCTZUJJ.js.map +1 -0
  20. package/dist/chunk-FVSI36UV.js +690 -0
  21. package/dist/chunk-FVSI36UV.js.map +1 -0
  22. package/dist/chunk-GEDTJHAC.cjs +1 -0
  23. package/dist/chunk-GEDTJHAC.cjs.map +1 -0
  24. package/dist/chunk-GOZG57ER.js +3220 -0
  25. package/dist/chunk-GOZG57ER.js.map +1 -0
  26. package/dist/chunk-H3VORETI.js +2243 -0
  27. package/dist/chunk-H3VORETI.js.map +1 -0
  28. package/dist/chunk-H42UHPYW.js +1577 -0
  29. package/dist/chunk-H42UHPYW.js.map +1 -0
  30. package/dist/chunk-HFPS7JFD.js +1 -0
  31. package/dist/chunk-INHVXP6Z.js +3053 -0
  32. package/dist/chunk-INHVXP6Z.js.map +1 -0
  33. package/dist/chunk-K4ZJUGF4.cjs +379 -0
  34. package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
  35. package/dist/chunk-KNJEKLKV.js +49 -0
  36. package/dist/chunk-KNJEKLKV.js.map +1 -0
  37. package/dist/chunk-M4CWTJWJ.cjs +7647 -0
  38. package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
  39. package/dist/chunk-MXO72PLY.cjs +211 -0
  40. package/dist/chunk-MXO72PLY.cjs.map +1 -0
  41. package/dist/chunk-NLEU4QSE.cjs +360 -0
  42. package/dist/chunk-NLEU4QSE.cjs.map +1 -0
  43. package/dist/chunk-OE6WK2Y2.cjs +1577 -0
  44. package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
  45. package/dist/chunk-Q2WGKJNN.cjs +1064 -0
  46. package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
  47. package/dist/chunk-QYQQ5J6E.js +1 -0
  48. package/dist/chunk-QYQQ5J6E.js.map +1 -0
  49. package/dist/chunk-RHIJ3S4I.cjs +1 -0
  50. package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
  51. package/dist/chunk-TTCLJX6D.js +211 -0
  52. package/dist/chunk-TTCLJX6D.js.map +1 -0
  53. package/dist/chunk-UADYAYAT.cjs +3053 -0
  54. package/dist/chunk-UADYAYAT.cjs.map +1 -0
  55. package/dist/chunk-VOHZB45S.js +5121 -0
  56. package/dist/chunk-VOHZB45S.js.map +1 -0
  57. package/dist/chunk-VTI6FOLB.js +1064 -0
  58. package/dist/chunk-VTI6FOLB.js.map +1 -0
  59. package/dist/chunk-WWURE4XT.cjs +2243 -0
  60. package/dist/chunk-WWURE4XT.cjs.map +1 -0
  61. package/dist/chunk-YH3BIKHH.js +360 -0
  62. package/dist/chunk-YH3BIKHH.js.map +1 -0
  63. package/dist/core/index.cjs +93 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +87 -346
  66. package/dist/core/index.d.ts +87 -346
  67. package/dist/core/index.js +94 -7
  68. package/dist/generate/index.cjs +89 -10
  69. package/dist/generate/index.cjs.map +1 -1
  70. package/dist/generate/index.d.cts +436 -1094
  71. package/dist/generate/index.d.ts +436 -1094
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-Cfx8xgH0.d.ts → index-B4o55NDr.d.cts} +13 -9
  75. package/dist/index-B9MNZboQ.d.cts +770 -0
  76. package/dist/index-B9MNZboQ.d.ts +770 -0
  77. package/dist/index-BGFW-8P2.d.cts +2307 -0
  78. package/dist/index-BO6xE9YU.d.cts +1804 -0
  79. package/dist/index-BkGmlyI3.d.cts +470 -0
  80. package/dist/index-CCNJRG-P.d.cts +1095 -0
  81. package/dist/index-CGpRv5sl.d.cts +643 -0
  82. package/dist/index-C_8o--VE.d.cts +1242 -0
  83. package/dist/index-CgHqxdps.d.ts +2307 -0
  84. package/dist/index-Chbst3Jx.d.ts +1343 -0
  85. package/dist/index-Cj49F892.d.ts +470 -0
  86. package/dist/{index-x2vcsduE.d.cts → index-CunYKLiu.d.ts} +13 -9
  87. package/dist/index-D57QRbVn.d.ts +1804 -0
  88. package/dist/index-DInQqT8Q.d.cts +66 -0
  89. package/dist/index-DInQqT8Q.d.ts +66 -0
  90. package/dist/index-Dzr13q3d.d.cts +1343 -0
  91. package/dist/index-G1uwGGWQ.d.ts +643 -0
  92. package/dist/index-szBv3496.d.ts +1242 -0
  93. package/dist/index.cjs +348 -15
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +17 -15
  96. package/dist/index.d.ts +17 -15
  97. package/dist/index.js +397 -64
  98. package/dist/masks-DYbE485J.d.cts +445 -0
  99. package/dist/masks-DYbE485J.d.ts +445 -0
  100. package/dist/model/index.cjs +39 -7
  101. package/dist/model/index.cjs.map +1 -1
  102. package/dist/model/index.d.cts +4659 -249
  103. package/dist/model/index.d.ts +4659 -249
  104. package/dist/model/index.js +40 -8
  105. package/dist/positional-Cptlj-xs.d.ts +287 -0
  106. package/dist/positional-DvH_sd8u.d.cts +287 -0
  107. package/dist/theory/index.cjs +101 -7
  108. package/dist/theory/index.cjs.map +1 -1
  109. package/dist/theory/index.d.cts +511 -171
  110. package/dist/theory/index.d.ts +511 -171
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +6 -4
  113. package/dist/chunk-2A2QMA74.cjs +0 -261
  114. package/dist/chunk-2A2QMA74.cjs.map +0 -1
  115. package/dist/chunk-2PTFGVD7.js.map +0 -1
  116. package/dist/chunk-357ZDPVU.cjs +0 -162
  117. package/dist/chunk-357ZDPVU.cjs.map +0 -1
  118. package/dist/chunk-3KX76RLG.js +0 -1548
  119. package/dist/chunk-3KX76RLG.js.map +0 -1
  120. package/dist/chunk-4VZ5MZAM.cjs +0 -889
  121. package/dist/chunk-4VZ5MZAM.cjs.map +0 -1
  122. package/dist/chunk-7EONYVI5.js +0 -1204
  123. package/dist/chunk-7EONYVI5.js.map +0 -1
  124. package/dist/chunk-7JEP3MFY.js +0 -162
  125. package/dist/chunk-7JEP3MFY.js.map +0 -1
  126. package/dist/chunk-AID75RN3.js +0 -3176
  127. package/dist/chunk-AID75RN3.js.map +0 -1
  128. package/dist/chunk-BSLTLGM3.cjs +0 -497
  129. package/dist/chunk-BSLTLGM3.cjs.map +0 -1
  130. package/dist/chunk-CU6VG2YM.cjs +0 -109
  131. package/dist/chunk-CU6VG2YM.cjs.map +0 -1
  132. package/dist/chunk-DZMHIGI4.js +0 -337
  133. package/dist/chunk-DZMHIGI4.js.map +0 -1
  134. package/dist/chunk-EDOTEABK.js +0 -109
  135. package/dist/chunk-EDOTEABK.js.map +0 -1
  136. package/dist/chunk-HN2OEUN2.js +0 -33
  137. package/dist/chunk-HN2OEUN2.js.map +0 -1
  138. package/dist/chunk-KBTTEX7Q.js +0 -497
  139. package/dist/chunk-KBTTEX7Q.js.map +0 -1
  140. package/dist/chunk-KQN2ZYND.cjs +0 -724
  141. package/dist/chunk-KQN2ZYND.cjs.map +0 -1
  142. package/dist/chunk-MPTVJIVG.cjs +0 -3176
  143. package/dist/chunk-MPTVJIVG.cjs.map +0 -1
  144. package/dist/chunk-MTMZTMWM.cjs +0 -53
  145. package/dist/chunk-MTMZTMWM.cjs.map +0 -1
  146. package/dist/chunk-N6IHQUOX.js +0 -1
  147. package/dist/chunk-NNB67XA2.js +0 -724
  148. package/dist/chunk-NNB67XA2.js.map +0 -1
  149. package/dist/chunk-OL3IHHSJ.cjs +0 -1204
  150. package/dist/chunk-OL3IHHSJ.cjs.map +0 -1
  151. package/dist/chunk-PI3UXRWK.cjs +0 -528
  152. package/dist/chunk-PI3UXRWK.cjs.map +0 -1
  153. package/dist/chunk-R2VVFAIN.js +0 -528
  154. package/dist/chunk-R2VVFAIN.js.map +0 -1
  155. package/dist/chunk-T64C7ZD7.cjs +0 -1
  156. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  157. package/dist/chunk-WFDXOLUB.cjs +0 -337
  158. package/dist/chunk-WFDXOLUB.cjs.map +0 -1
  159. package/dist/chunk-WTAN4ACG.cjs +0 -1548
  160. package/dist/chunk-WTAN4ACG.cjs.map +0 -1
  161. package/dist/chunk-YFJXS3AU.cjs +0 -33
  162. package/dist/chunk-YFJXS3AU.cjs.map +0 -1
  163. package/dist/chunk-YUE6TJ4M.js +0 -889
  164. package/dist/chunk-YUE6TJ4M.js.map +0 -1
  165. package/dist/chunk-ZGLJM4A4.js +0 -53
  166. package/dist/chunk-ZGLJM4A4.js.map +0 -1
  167. package/dist/function-CxixTB02.d.cts +0 -299
  168. package/dist/function-DTCt675z.d.ts +0 -299
  169. package/dist/index-B2Ulxxpo.d.cts +0 -144
  170. package/dist/index-BkYJ0xMq.d.cts +0 -152
  171. package/dist/index-BuaB-ypt.d.ts +0 -144
  172. package/dist/index-CAN7v-kw.d.ts +0 -199
  173. package/dist/index-COw1Ky9g.d.ts +0 -242
  174. package/dist/index-DRtIhlJj.d.ts +0 -152
  175. package/dist/index-DXd9JikN.d.cts +0 -246
  176. package/dist/index-DXd9JikN.d.ts +0 -246
  177. package/dist/index-alrRninU.d.cts +0 -199
  178. package/dist/index-oTQbYYIs.d.cts +0 -242
  179. package/dist/index-tR9jTn26.d.cts +0 -275
  180. package/dist/index-tR9jTn26.d.ts +0 -275
  181. package/dist/styled-Bl0ZK28h.d.ts +0 -543
  182. package/dist/styled-Crv6SA4S.d.cts +0 -543
  183. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  184. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  185. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -0,0 +1,1064 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/core/errors/index.ts
2
+ function parseFailure(error) {
3
+ if (isLibcantusError(error)) {
4
+ return { ok: false, error };
5
+ }
6
+ throw error;
7
+ }
8
+ function unwrapParse(result) {
9
+ if (result.ok) {
10
+ return result.value;
11
+ }
12
+ throw result.error;
13
+ }
14
+ var InvalidInputError = (_class = class extends RangeError {constructor(...args) { super(...args); _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
15
+ __init() {this.code = "INVALID_INPUT"}
16
+ __init2() {this.name = "InvalidInputError"}
17
+ }, _class);
18
+ var NoSolutionError = (_class2 = class extends Error {
19
+ __init3() {this.code = "NO_SOLUTION"}
20
+ __init4() {this.name = "NoSolutionError"}
21
+ /**
22
+ * Where in a sequence the failure occurred, when the operation was working
23
+ * through one — the chord index of a progression, say.
24
+ */
25
+
26
+ constructor(message, options = {}) {
27
+ super(message);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);;
28
+ this.at = options.at;
29
+ }
30
+ }, _class2);
31
+ var BudgetExceededError = (_class3 = class extends RangeError {constructor(...args2) { super(...args2); _class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this); }
32
+ __init5() {this.code = "BUDGET_EXCEEDED"}
33
+ __init6() {this.name = "BudgetExceededError"}
34
+ }, _class3);
35
+ function isLibcantusError(value) {
36
+ if (typeof value !== "object" || value === null || !("code" in value)) {
37
+ return false;
38
+ }
39
+ const code = value.code;
40
+ return code === "INVALID_INPUT" || code === "NO_SOLUTION" || code === "BUDGET_EXCEEDED";
41
+ }
42
+
43
+ // src/core/meter/internal.ts
44
+ var EPS = 1e-9;
45
+ function unitBeatsOf(ts) {
46
+ return 4 / ts.denominator;
47
+ }
48
+ function isCompoundNumerator(numerator) {
49
+ return numerator % 3 === 0 && numerator > 3;
50
+ }
51
+ function groupingSumOf(ts) {
52
+ const grouping = ts.grouping;
53
+ if (grouping === void 0) {
54
+ return void 0;
55
+ }
56
+ let sum = 0;
57
+ for (const entry of grouping) {
58
+ sum += entry;
59
+ }
60
+ return sum;
61
+ }
62
+ function isAdditiveReading(ts) {
63
+ const grouping = ts.grouping;
64
+ const sum = groupingSumOf(ts);
65
+ if (grouping === void 0 || sum === void 0 || sum === 0) {
66
+ return false;
67
+ }
68
+ if (!isCompoundNumerator(ts.numerator) || sum !== ts.numerator) {
69
+ return false;
70
+ }
71
+ return !grouping.every((entry) => entry === 3);
72
+ }
73
+ function pulseCountOf(ts) {
74
+ if (isAdditiveReading(ts)) {
75
+ return ts.numerator;
76
+ }
77
+ return isCompoundNumerator(ts.numerator) ? ts.numerator / 3 : ts.numerator;
78
+ }
79
+ function pulseBeatsOf(ts) {
80
+ return ts.numerator * unitBeatsOf(ts) / pulseCountOf(ts);
81
+ }
82
+ function pulseGroupingOf(ts) {
83
+ const grouping = ts.grouping;
84
+ if (grouping === void 0) {
85
+ return void 0;
86
+ }
87
+ const pulses = pulseCountOf(ts);
88
+ if (groupingSumOf(ts) === pulses) {
89
+ return grouping;
90
+ }
91
+ return grouping.map((entry) => entry / 3);
92
+ }
93
+ function barBeatsOf(ts) {
94
+ return ts.numerator * unitBeatsOf(ts);
95
+ }
96
+ function isMeterMap(meter) {
97
+ return Array.isArray(meter);
98
+ }
99
+ function entryIndexOf(map, beat) {
100
+ let index = 0;
101
+ for (let i = 1; i < map.length; i += 1) {
102
+ if ((_nullishCoalesce(_optionalChain([map, 'access', _ => _[i], 'optionalAccess', _2 => _2.startBeat]), () => ( Number.POSITIVE_INFINITY))) > beat + EPS) {
103
+ break;
104
+ }
105
+ index = i;
106
+ }
107
+ return index;
108
+ }
109
+ function barsBeforeEntry(map, index) {
110
+ let bars = 0;
111
+ for (let i = 0; i < index; i += 1) {
112
+ const entry = map[i];
113
+ const next = map[i + 1];
114
+ if (entry === void 0 || next === void 0) {
115
+ break;
116
+ }
117
+ bars += Math.max(1, Math.ceil((next.startBeat - entry.startBeat) / barBeatsOf(entry.ts) - EPS));
118
+ }
119
+ return bars;
120
+ }
121
+ function barStartOf(map, beat) {
122
+ const entry = map[entryIndexOf(map, beat)];
123
+ if (entry === void 0) {
124
+ return 0;
125
+ }
126
+ const barLen = barBeatsOf(entry.ts);
127
+ return entry.startBeat + Math.floor((beat - entry.startBeat) / barLen + EPS) * barLen;
128
+ }
129
+ function barLengthOf(map, beat) {
130
+ const index = entryIndexOf(map, beat);
131
+ const entry = map[index];
132
+ if (entry === void 0) {
133
+ return 0;
134
+ }
135
+ const full = barBeatsOf(entry.ts);
136
+ const next = _optionalChain([map, 'access', _3 => _3[index + 1], 'optionalAccess', _4 => _4.startBeat]);
137
+ return next === void 0 ? full : Math.min(full, next - barStartOf(map, beat));
138
+ }
139
+ function barIndexOf(map, beat) {
140
+ const index = entryIndexOf(map, beat);
141
+ const entry = map[index];
142
+ if (entry === void 0) {
143
+ return 0;
144
+ }
145
+ const barLen = barBeatsOf(entry.ts);
146
+ return barsBeforeEntry(map, index) + Math.floor((beat - entry.startBeat) / barLen + EPS);
147
+ }
148
+ function beatOfBarIndex(map, barIndex) {
149
+ let base = 0;
150
+ for (let i = 0; i < map.length; i += 1) {
151
+ const entry = map[i];
152
+ if (entry === void 0) {
153
+ continue;
154
+ }
155
+ const barLen = barBeatsOf(entry.ts);
156
+ const next = map[i + 1];
157
+ if (next === void 0) {
158
+ return entry.startBeat + (barIndex - base) * barLen;
159
+ }
160
+ const bars = Math.max(1, Math.ceil((next.startBeat - entry.startBeat) / barLen - EPS));
161
+ if (barIndex < base + bars) {
162
+ return entry.startBeat + (barIndex - base) * barLen;
163
+ }
164
+ base += bars;
165
+ }
166
+ return 0;
167
+ }
168
+
169
+ // src/core/validation/index.ts
170
+ var DEFAULT_GENERATION_BUDGET = 1e6;
171
+ function assertFiniteNumber(value, name) {
172
+ if (!Number.isFinite(value)) {
173
+ throw new InvalidInputError(`${name} must be finite; received ${value}`);
174
+ }
175
+ return value;
176
+ }
177
+ function assertInteger(value, name, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
178
+ assertFiniteNumber(value, name);
179
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
180
+ throw new InvalidInputError(
181
+ `${name} must be an integer in [${min}, ${max}]; received ${value}`
182
+ );
183
+ }
184
+ return value;
185
+ }
186
+ function assertMidiPitch(value, name = "MIDI pitch") {
187
+ return assertInteger(value, name, 0, 127);
188
+ }
189
+ function clampToMidi(value, name = "MIDI pitch") {
190
+ assertInteger(value, name);
191
+ return Math.min(127, Math.max(0, value));
192
+ }
193
+ function assertDegree(value, name = "degree") {
194
+ return assertInteger(value, name, 1, 1e3);
195
+ }
196
+ function assertFiniteSemitones(value, name = "semitones") {
197
+ return assertFiniteNumber(value, name);
198
+ }
199
+ function dropSilentNotes(events) {
200
+ return events.filter((event) => event !== void 0 && event.durationBeat > 0);
201
+ }
202
+ var soundingNotesOnly = dropSilentNotes;
203
+ function assertOneOf(value, allowed, name) {
204
+ if (typeof value !== "string" || !allowed.includes(value)) {
205
+ throw new InvalidInputError(
206
+ `${name} must be one of ${allowed.join(", ")}; received ${JSON.stringify(value)}`
207
+ );
208
+ }
209
+ return value;
210
+ }
211
+ function assertPositiveInt(value, name, max = DEFAULT_GENERATION_BUDGET) {
212
+ return assertInteger(value, name, 1, max);
213
+ }
214
+ function assertRange(value, min, max, name) {
215
+ assertFiniteNumber(value, name);
216
+ if (value < min || value > max) {
217
+ throw new InvalidInputError(`${name} must be in [${min}, ${max}]; received ${value}`);
218
+ }
219
+ return value;
220
+ }
221
+ function assertGenerationBudget(estimated, name, limit = DEFAULT_GENERATION_BUDGET) {
222
+ const cap = _nullishCoalesce(limit, () => ( DEFAULT_GENERATION_BUDGET));
223
+ assertFiniteNumber(estimated, name);
224
+ assertPositiveInt(cap, `${name} limit`, Number.MAX_SAFE_INTEGER);
225
+ if (estimated < 0) {
226
+ throw new InvalidInputError(`${name} must not be negative; received ${estimated}`);
227
+ }
228
+ if (estimated > cap) {
229
+ throw new BudgetExceededError(
230
+ `${name} exceeds the generation budget ${cap}; received ${estimated}`
231
+ );
232
+ }
233
+ return estimated;
234
+ }
235
+ function assertTimeSignature(ts, name = "time signature") {
236
+ assertPositiveInt(ts.numerator, `${name}.numerator`);
237
+ assertPositiveInt(ts.denominator, `${name}.denominator`);
238
+ if (ts.grouping !== void 0) {
239
+ if (ts.grouping.length === 0) {
240
+ throw new InvalidInputError(`${name}.grouping must not be empty`);
241
+ }
242
+ let sum = 0;
243
+ for (let index = 0; index < ts.grouping.length; index += 1) {
244
+ sum += assertPositiveInt(_nullishCoalesce(ts.grouping[index], () => ( Number.NaN)), `${name}.grouping[${index}]`);
245
+ }
246
+ const pulses = isCompoundNumerator(ts.numerator) ? ts.numerator / 3 : ts.numerator;
247
+ if (sum !== pulses && sum !== ts.numerator) {
248
+ const accepted = pulses === ts.numerator ? `${pulses}` : `${pulses} (pulses) or ${ts.numerator} (units)`;
249
+ throw new InvalidInputError(`${name}.grouping must sum to ${accepted}; received ${sum}`);
250
+ }
251
+ }
252
+ return ts;
253
+ }
254
+ function assertMeterMap(meters, name = "meters") {
255
+ if (!Array.isArray(meters)) {
256
+ throw new InvalidInputError(`${name} must be an array; received ${typeof meters}`);
257
+ }
258
+ if (meters.length === 0) {
259
+ throw new InvalidInputError(`${name} must not be empty`);
260
+ }
261
+ let previous = Number.NEGATIVE_INFINITY;
262
+ for (let index = 0; index < meters.length; index += 1) {
263
+ const entry = meters[index];
264
+ if (entry === void 0) {
265
+ throw new InvalidInputError(`${name}[${index}] must be a meter change; received undefined`);
266
+ }
267
+ assertFiniteNumber(entry.startBeat, `${name}[${index}].startBeat`);
268
+ if (index > 0 && entry.startBeat <= previous) {
269
+ throw new InvalidInputError(
270
+ `${name}[${index}].startBeat must be greater than ${previous}; received ${entry.startBeat}`
271
+ );
272
+ }
273
+ previous = entry.startBeat;
274
+ assertTimeSignature(entry.ts, `${name}[${index}].ts`);
275
+ }
276
+ return meters;
277
+ }
278
+ function assertNoteEvent(event, name = "note event", options = {}) {
279
+ assertMidiPitch(event.pitch, `${name}.pitch`);
280
+ const minStartBeat = _nullishCoalesce(options.minStartBeat, () => ( -Number.MAX_SAFE_INTEGER));
281
+ assertFiniteNumber(minStartBeat, "minStartBeat");
282
+ assertRange(event.startBeat, minStartBeat, Number.MAX_SAFE_INTEGER, `${name}.startBeat`);
283
+ assertFiniteNumber(event.durationBeat, `${name}.durationBeat`);
284
+ if (!options.allowNonPositiveDuration && event.durationBeat <= 0) {
285
+ throw new InvalidInputError(
286
+ `${name}.durationBeat must be positive; received ${event.durationBeat}`
287
+ );
288
+ }
289
+ assertRange(
290
+ event.durationBeat,
291
+ -Number.MAX_SAFE_INTEGER,
292
+ Number.MAX_SAFE_INTEGER,
293
+ `${name}.durationBeat`
294
+ );
295
+ if (event.velocity !== void 0) {
296
+ assertRange(event.velocity, 0, 127, `${name}.velocity`);
297
+ }
298
+ return event;
299
+ }
300
+ function assertNoteEvents(events, name = "note events", options = {}) {
301
+ if (!Array.isArray(events)) {
302
+ throw new InvalidInputError(`${name} must be an array; received ${typeof events}`);
303
+ }
304
+ assertGenerationBudget(events.length, `${name} count`, options.budget);
305
+ for (let index = 0; index < events.length; index += 1) {
306
+ const event = events[index];
307
+ if (event === void 0) {
308
+ throw new InvalidInputError(`${name}[${index}] must be a note event; received undefined`);
309
+ }
310
+ assertNoteEvent(event, `${name}[${index}]`, options);
311
+ }
312
+ return events;
313
+ }
314
+
315
+ // src/core/pitch/naming.ts
316
+ var NOTE_NAME_SYSTEMS = [
317
+ "english",
318
+ "german",
319
+ "japanese",
320
+ "italian",
321
+ "fixedDo"
322
+ ];
323
+ var DETECTION_ORDER = ["english", "german", "japanese", "italian"];
324
+ function tableOf(system) {
325
+ return system === "fixedDo" ? "italian" : system;
326
+ }
327
+ var ENGLISH_LETTERS = ["C", "D", "E", "F", "G", "A", "B"];
328
+ var GERMAN_LETTERS = ["c", "d", "e", "f", "g", "a", "h"];
329
+ var JAPANESE_LETTERS = ["\u30CF", "\u30CB", "\u30DB", "\u30D8", "\u30C8", "\u30A4", "\u30ED"];
330
+ var ITALIAN_LETTERS = ["do", "re", "mi", "fa", "sol", "la", "si"];
331
+ var GERMAN_ELIDING_LETTERS = [2, 5];
332
+ var A_LETTER = 5;
333
+ var B_LETTER = 6;
334
+ var MODE_WORDS = {
335
+ english: { major: "major", minor: "minor" },
336
+ german: { major: "dur", minor: "moll" },
337
+ japanese: { major: "\u9577\u8ABF", minor: "\u77ED\u8ABF" },
338
+ italian: { major: "maggiore", minor: "minore" }
339
+ };
340
+ var JAPANESE_SHARP = "\u5B30";
341
+ var JAPANESE_FLAT = "\u5909";
342
+ var JAPANESE_DOUBLE = "\u91CD";
343
+ var ITALIAN_SHARP = "diesis";
344
+ var ITALIAN_FLAT = "bemolle";
345
+ var ITALIAN_DOUBLE = "doppio";
346
+ function normalize(text) {
347
+ return text.trim().replace(/\s+/g, " ");
348
+ }
349
+ function assertSystem(system) {
350
+ return assertOneOf(system, NOTE_NAME_SYSTEMS, "system");
351
+ }
352
+ var ENGLISH_PATTERN = /^([A-Ga-g])([#x]*|b*)(-?\d+)?$/;
353
+ function parseEnglish(text) {
354
+ const match = text.match(ENGLISH_PATTERN);
355
+ if (match === null) {
356
+ return null;
357
+ }
358
+ const glyph = (_nullishCoalesce(match[1], () => ( ""))).toUpperCase();
359
+ const letter = ENGLISH_LETTERS.indexOf(glyph);
360
+ let alter = 0;
361
+ for (const ch of _nullishCoalesce(match[2], () => ( ""))) {
362
+ if (ch === "#") {
363
+ alter += 1;
364
+ } else if (ch === "x") {
365
+ alter += 2;
366
+ } else if (ch === "b") {
367
+ alter -= 1;
368
+ }
369
+ }
370
+ const note = { letter, alter };
371
+ if (match[3] !== void 0) {
372
+ note.octave = Number.parseInt(match[3], 10);
373
+ }
374
+ return note;
375
+ }
376
+ function formatEnglish(note) {
377
+ const glyph = _nullishCoalesce(ENGLISH_LETTERS[note.letter], () => ( "C"));
378
+ if (note.alter > 0) {
379
+ return `${glyph}${"#".repeat(note.alter)}`;
380
+ }
381
+ return note.alter < 0 ? `${glyph}${"b".repeat(-note.alter)}` : glyph;
382
+ }
383
+ function germanFlats(letter, suffix) {
384
+ let rest = suffix;
385
+ if (GERMAN_ELIDING_LETTERS.includes(letter) && rest.startsWith("s")) {
386
+ rest = rest.slice(1);
387
+ } else if (rest.startsWith("es")) {
388
+ rest = rest.slice(2);
389
+ } else {
390
+ return null;
391
+ }
392
+ let flats = 1;
393
+ while (rest.length > 0) {
394
+ if (rest.startsWith("es") || letter === A_LETTER && rest.startsWith("as")) {
395
+ rest = rest.slice(2);
396
+ flats += 1;
397
+ } else {
398
+ return null;
399
+ }
400
+ }
401
+ return flats;
402
+ }
403
+ function parseGerman(text) {
404
+ const lower = text.toLowerCase();
405
+ if (lower === "b") {
406
+ return { letter: B_LETTER, alter: -1 };
407
+ }
408
+ const letter = GERMAN_LETTERS.indexOf(lower.slice(0, 1));
409
+ if (letter < 0) {
410
+ return null;
411
+ }
412
+ const suffix = lower.slice(1);
413
+ if (suffix === "") {
414
+ return { letter, alter: 0 };
415
+ }
416
+ if (/^(?:is)+$/.test(suffix)) {
417
+ return { letter, alter: suffix.length / 2 };
418
+ }
419
+ const flats = germanFlats(letter, suffix);
420
+ return flats === null ? null : { letter, alter: -flats };
421
+ }
422
+ function formatGerman(note) {
423
+ const glyph = _nullishCoalesce(GERMAN_LETTERS[note.letter], () => ( "c"));
424
+ if (note.alter >= 0) {
425
+ return `${glyph}${"is".repeat(note.alter)}`;
426
+ }
427
+ const flats = -note.alter;
428
+ if (note.letter === B_LETTER) {
429
+ return flats === 1 ? "b" : `h${"es".repeat(flats)}`;
430
+ }
431
+ const first = GERMAN_ELIDING_LETTERS.includes(note.letter) ? `${glyph}s` : `${glyph}es`;
432
+ return `${first}${"es".repeat(flats - 1)}`;
433
+ }
434
+ function parseJapanese(text) {
435
+ let rest = text;
436
+ let alter = 0;
437
+ let direction = 0;
438
+ while (rest.length > 0) {
439
+ const doubled = rest.startsWith(JAPANESE_DOUBLE);
440
+ const mark = doubled ? rest.slice(1, 2) : rest.slice(0, 1);
441
+ const step = mark === JAPANESE_SHARP ? 1 : mark === JAPANESE_FLAT ? -1 : 0;
442
+ if (step === 0) {
443
+ break;
444
+ }
445
+ if (direction !== 0 && direction !== step) {
446
+ return null;
447
+ }
448
+ direction = step;
449
+ alter += doubled ? step * 2 : step;
450
+ rest = rest.slice(doubled ? 2 : 1);
451
+ }
452
+ const letter = JAPANESE_LETTERS.indexOf(rest);
453
+ return letter < 0 ? null : { letter, alter };
454
+ }
455
+ function formatJapanese(note) {
456
+ const glyph = _nullishCoalesce(JAPANESE_LETTERS[note.letter], () => ( "\u30CF"));
457
+ const marks = Math.abs(note.alter);
458
+ if (marks === 0) {
459
+ return glyph;
460
+ }
461
+ const mark = note.alter > 0 ? JAPANESE_SHARP : JAPANESE_FLAT;
462
+ return `${marks === 2 ? `${JAPANESE_DOUBLE}${mark}` : mark.repeat(marks)}${glyph}`;
463
+ }
464
+ function parseItalian(text) {
465
+ const words = text.toLowerCase().split(" ");
466
+ const letter = ITALIAN_LETTERS.indexOf(words[0]);
467
+ if (letter < 0) {
468
+ return null;
469
+ }
470
+ let alter = 0;
471
+ let direction = 0;
472
+ let doubled = false;
473
+ for (const word of words.slice(1)) {
474
+ if (word === ITALIAN_DOUBLE) {
475
+ if (doubled) {
476
+ return null;
477
+ }
478
+ doubled = true;
479
+ continue;
480
+ }
481
+ const step = word === ITALIAN_SHARP ? 1 : word === ITALIAN_FLAT ? -1 : 0;
482
+ if (step === 0 || direction !== 0 && direction !== step) {
483
+ return null;
484
+ }
485
+ direction = step;
486
+ alter += doubled ? step * 2 : step;
487
+ doubled = false;
488
+ }
489
+ return doubled ? null : { letter, alter };
490
+ }
491
+ function formatItalian(note) {
492
+ const glyph = _nullishCoalesce(ITALIAN_LETTERS[note.letter], () => ( "do"));
493
+ const marks = Math.abs(note.alter);
494
+ if (marks === 0) {
495
+ return glyph;
496
+ }
497
+ const word = note.alter > 0 ? ITALIAN_SHARP : ITALIAN_FLAT;
498
+ const accidental = marks === 2 ? `${ITALIAN_DOUBLE} ${word}` : Array.from({ length: marks }, () => word).join(" ");
499
+ return `${glyph} ${accidental}`;
500
+ }
501
+ function splitOctave(text) {
502
+ const match = text.match(/^(.*?)(-?\d+)$/);
503
+ if (match === null) {
504
+ return { name: text, octave: void 0 };
505
+ }
506
+ return { name: _nullishCoalesce(match[1], () => ( "")), octave: Number.parseInt(_nullishCoalesce(match[2], () => ( "0")), 10) };
507
+ }
508
+ function parseIn(text, system) {
509
+ const table = tableOf(system);
510
+ if (table === "english") {
511
+ return parseEnglish(text);
512
+ }
513
+ const { name, octave } = splitOctave(text);
514
+ const parser = table === "german" ? parseGerman : table === "japanese" ? parseJapanese : parseItalian;
515
+ const note = parser(name);
516
+ if (note === null) {
517
+ return null;
518
+ }
519
+ return octave === void 0 ? note : { ...note, octave };
520
+ }
521
+ function splitModeWord(text) {
522
+ const lower = text.toLowerCase();
523
+ let best;
524
+ for (const system of DETECTION_ORDER) {
525
+ for (const mode of ["major", "minor"]) {
526
+ const word = MODE_WORDS[system][mode];
527
+ if (lower.endsWith(word) && (best === void 0 || word.length > best.word.length)) {
528
+ best = { word, mode, system };
529
+ }
530
+ }
531
+ }
532
+ if (best === void 0) {
533
+ return { tonic: text, mode: void 0, system: void 0 };
534
+ }
535
+ return {
536
+ tonic: text.slice(0, text.length - best.word.length).replace(/[\s-]+$/, ""),
537
+ mode: best.mode,
538
+ system: best.system
539
+ };
540
+ }
541
+ function resolveSystem(text) {
542
+ const { tonic, system: wordSystem } = splitModeWord(text);
543
+ const accepted = DETECTION_ORDER.filter((system) => parseIn(tonic, system) !== null);
544
+ const first = accepted[0];
545
+ if (first === void 0) {
546
+ return null;
547
+ }
548
+ if (wordSystem === void 0) {
549
+ return first;
550
+ }
551
+ if (!accepted.includes(wordSystem)) {
552
+ throw new InvalidInputError(
553
+ `${JSON.stringify(text)} mixes note-name systems: ${JSON.stringify(tonic)} is ${accepted.join(" or ")} but its mode word is ${wordSystem}`
554
+ );
555
+ }
556
+ return wordSystem;
557
+ }
558
+ function detectNoteNameSystem(text) {
559
+ if (typeof text !== "string") {
560
+ throw new InvalidInputError(`name must be a string; received ${typeof text}`);
561
+ }
562
+ const system = resolveSystem(normalize(text));
563
+ if (system === null) {
564
+ throw new InvalidInputError(`no note-name system reads ${JSON.stringify(text)}`);
565
+ }
566
+ return system;
567
+ }
568
+ function readNoteName(text, opts) {
569
+ const name = normalize(text);
570
+ const system = _optionalChain([opts, 'optionalAccess', _5 => _5.system]) === void 0 ? resolveSystem(name) : assertSystem(opts.system);
571
+ const note = system === null ? null : parseIn(name, system);
572
+ if (note === null) {
573
+ throw new InvalidInputError(`Invalid note: ${text}`);
574
+ }
575
+ return note;
576
+ }
577
+ function writeNoteName(note, opts) {
578
+ const system = _optionalChain([opts, 'optionalAccess', _6 => _6.system]) === void 0 ? "english" : assertSystem(opts.system);
579
+ const table = tableOf(system);
580
+ const name = table === "german" ? formatGerman(note) : table === "japanese" ? formatJapanese(note) : table === "italian" ? formatItalian(note) : formatEnglish(note);
581
+ return note.octave === void 0 ? name : `${name}${note.octave}`;
582
+ }
583
+ function impliedMode(system, tonic) {
584
+ return tableOf(system) === "german" && /^[a-h]/.test(tonic) ? "minor" : "major";
585
+ }
586
+ function readKeyName(text, opts) {
587
+ const name = normalize(text);
588
+ const split = splitModeWord(name);
589
+ const system = _optionalChain([opts, 'optionalAccess', _7 => _7.system]) === void 0 ? resolveSystem(name) : assertSystem(opts.system);
590
+ if (system !== null && split.system !== void 0 && split.system !== tableOf(system)) {
591
+ throw new InvalidInputError(
592
+ `${JSON.stringify(text)} is not a ${system} key name: its mode word is ${split.system}`
593
+ );
594
+ }
595
+ const tonic = system === null ? null : parseIn(split.tonic, system);
596
+ if (tonic === null || system === null) {
597
+ throw new InvalidInputError(`Invalid key name: ${text}`);
598
+ }
599
+ if (tonic.octave !== void 0) {
600
+ throw new InvalidInputError(`a key name carries no octave; received ${JSON.stringify(text)}`);
601
+ }
602
+ return { tonic, mode: _nullishCoalesce(split.mode, () => ( impliedMode(system, split.tonic))) };
603
+ }
604
+ function writeKeyName(key, opts) {
605
+ const system = _optionalChain([opts, 'optionalAccess', _8 => _8.system]) === void 0 ? "english" : assertSystem(opts.system);
606
+ const table = tableOf(system);
607
+ const word = MODE_WORDS[table][key.mode];
608
+ const tonic = writeNoteName(key.tonic, { system });
609
+ if (table === "japanese") {
610
+ return `${tonic}${word}`;
611
+ }
612
+ if (table === "german") {
613
+ const cased = key.mode === "major" ? `${tonic.slice(0, 1).toUpperCase()}${tonic.slice(1)}` : tonic;
614
+ return `${cased} ${word}`;
615
+ }
616
+ return `${tonic} ${word}`;
617
+ }
618
+
619
+ // src/core/pitch/index.ts
620
+ var LETTER_SEMITONES = [0, 2, 4, 5, 7, 9, 11];
621
+ var MAX_ALTER = 6;
622
+ function assertNote(note, name) {
623
+ assertInteger(note.letter, `${name}.letter`, 0, 6);
624
+ assertInteger(note.alter, `${name}.alter`, -MAX_ALTER, MAX_ALTER);
625
+ if (note.octave !== void 0) {
626
+ assertInteger(note.octave, `${name}.octave`, -100, 100);
627
+ }
628
+ return note;
629
+ }
630
+ function pitchClassOf(value) {
631
+ assertFiniteNumber(value, "pitch");
632
+ return (Math.round(value) % 12 + 12) % 12;
633
+ }
634
+ var mod12 = pitchClassOf;
635
+ function diatonicLetterOf(value) {
636
+ assertFiniteNumber(value, "diatonic letter");
637
+ return (Math.round(value) % 7 + 7) % 7;
638
+ }
639
+ function naturalPitchClassOf(letter) {
640
+ return _nullishCoalesce(LETTER_SEMITONES[diatonicLetterOf(letter)], () => ( 0));
641
+ }
642
+ var mod7 = diatonicLetterOf;
643
+ function parseNote(text, opts) {
644
+ return unwrapParse(tryParseNote(text, opts));
645
+ }
646
+ function tryParseNote(text, opts) {
647
+ try {
648
+ if (typeof text !== "string") {
649
+ throw new InvalidInputError(`note must be a string; received ${typeof text}`);
650
+ }
651
+ return { ok: true, value: assertNote(readNoteName(text, opts), `note ${text}`) };
652
+ } catch (error) {
653
+ return parseFailure(error);
654
+ }
655
+ }
656
+ function formatNote(note, opts) {
657
+ assertNote(note, "note");
658
+ return writeNoteName({ letter: mod7(note.letter), alter: note.alter, octave: note.octave }, opts);
659
+ }
660
+ function parseKeyName(text, opts) {
661
+ return unwrapParse(tryParseKeyName(text, opts));
662
+ }
663
+ function tryParseKeyName(text, opts) {
664
+ try {
665
+ if (typeof text !== "string") {
666
+ throw new InvalidInputError(`key name must be a string; received ${typeof text}`);
667
+ }
668
+ const key = readKeyName(text, opts);
669
+ assertNote(key.tonic, `key ${text}`);
670
+ return { ok: true, value: key };
671
+ } catch (error) {
672
+ return parseFailure(error);
673
+ }
674
+ }
675
+ function formatKeyName(key, opts) {
676
+ assertNote(key.tonic, "key.tonic");
677
+ const mode = assertOneOf(key.mode, ["major", "minor"], "key.mode");
678
+ return writeKeyName(
679
+ { tonic: { letter: mod7(key.tonic.letter), alter: key.tonic.alter }, mode },
680
+ opts
681
+ );
682
+ }
683
+ function noteToPitchClass(note) {
684
+ assertNote(note, "note");
685
+ const natural = _nullishCoalesce(LETTER_SEMITONES[mod7(note.letter)], () => ( 0));
686
+ return mod12(natural + note.alter);
687
+ }
688
+ function noteToMidi(note) {
689
+ assertNote(note, "note");
690
+ if (note.octave === void 0) {
691
+ throw new InvalidInputError("noteToMidi requires an octave");
692
+ }
693
+ const natural = _nullishCoalesce(LETTER_SEMITONES[mod7(note.letter)], () => ( 0));
694
+ return (note.octave + 1) * 12 + natural + note.alter;
695
+ }
696
+ var SPELLINGS = {
697
+ sharp: [
698
+ [0, 0],
699
+ [0, 1],
700
+ [1, 0],
701
+ [1, 1],
702
+ [2, 0],
703
+ [3, 0],
704
+ [3, 1],
705
+ [4, 0],
706
+ [4, 1],
707
+ [5, 0],
708
+ [5, 1],
709
+ [6, 0]
710
+ ],
711
+ flat: [
712
+ [0, 0],
713
+ [1, -1],
714
+ [1, 0],
715
+ [2, -1],
716
+ [2, 0],
717
+ [3, 0],
718
+ [4, -1],
719
+ [4, 0],
720
+ [5, -1],
721
+ [5, 0],
722
+ [6, -1],
723
+ [6, 0]
724
+ ]
725
+ };
726
+ var SPELLING_NAMES = Object.keys(SPELLINGS);
727
+ function midiToNote(midi, spelling = "sharp") {
728
+ assertFiniteNumber(midi, "midi");
729
+ const side = assertOneOf(spelling, SPELLING_NAMES, "midi spelling");
730
+ const rounded = Math.round(midi);
731
+ const pc = mod12(rounded);
732
+ const octave = Math.floor(rounded / 12) - 1;
733
+ const entry = _nullishCoalesce(SPELLINGS[side][pc], () => ( [0, 0]));
734
+ return { letter: entry[0], alter: entry[1], octave };
735
+ }
736
+ var LETTER_STEPS_BY_SEMITONE = [0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6];
737
+ function transposeNote(note, semitones, opts) {
738
+ if (!Number.isFinite(semitones)) {
739
+ throw new InvalidInputError(`semitones must be a finite number; received ${semitones}`);
740
+ }
741
+ const steps = Math.round(semitones);
742
+ if (_optionalChain([opts, 'optionalAccess', _9 => _9.spelling]) !== void 0) {
743
+ return note.octave === void 0 ? bareOf(midiToNote(60 + mod12(noteToPitchClass(note) + steps), opts.spelling)) : midiToNote(noteToMidi(note) + steps, opts.spelling);
744
+ }
745
+ const direction = steps < 0 ? -1 : 1;
746
+ const magnitude = Math.abs(steps);
747
+ const octaves = Math.floor(magnitude / 12);
748
+ const withinOctave = magnitude % 12;
749
+ const letterSteps = direction * ((_nullishCoalesce(LETTER_STEPS_BY_SEMITONE[withinOctave], () => ( 0))) + 7 * octaves);
750
+ const absoluteLetter = mod7(note.letter) + letterSteps;
751
+ const letter = mod7(absoluteLetter);
752
+ const natural = _nullishCoalesce(LETTER_SEMITONES[letter], () => ( 0));
753
+ if (note.octave === void 0) {
754
+ return { letter, alter: alterFor(natural, mod12(noteToPitchClass(note) + steps)) };
755
+ }
756
+ const octave = note.octave + Math.floor(absoluteLetter / 7);
757
+ return { letter, alter: noteToMidi(note) + steps - ((octave + 1) * 12 + natural), octave };
758
+ }
759
+ function bareOf(note) {
760
+ return { letter: note.letter, alter: note.alter };
761
+ }
762
+ function alterFor(natural, pc) {
763
+ const d = mod12(pc - natural);
764
+ return d > 6 ? d - 12 : d;
765
+ }
766
+ var SIMPLE_REFERENCE = [0, 0, 2, 4, 5, 7, 9, 11];
767
+ function isPerfectNumber(simpleNumber) {
768
+ return simpleNumber === 1 || simpleNumber === 4 || simpleNumber === 5;
769
+ }
770
+ function qualityFromSpan(numberValue, semitones) {
771
+ const octaves = Math.floor((numberValue - 1) / 7);
772
+ const simple = numberValue - 7 * octaves;
773
+ const reference = (_nullishCoalesce(SIMPLE_REFERENCE[simple], () => ( 0))) + 12 * octaves;
774
+ const delta = semitones - reference;
775
+ const repeat = (glyph, count) => `${glyph}${glyph.repeat(Math.min(count, MAX_ALTER * 2) - 1)}`;
776
+ if (isPerfectNumber(simple)) {
777
+ if (delta === 0) {
778
+ return "P";
779
+ }
780
+ return delta > 0 ? repeat("A", delta) : repeat("d", -delta);
781
+ }
782
+ if (delta === 0) {
783
+ return "M";
784
+ }
785
+ if (delta === -1) {
786
+ return "m";
787
+ }
788
+ return delta > 0 ? repeat("A", delta) : repeat("d", -delta - 1);
789
+ }
790
+ var MAX_INTERVAL_NUMBER = 75;
791
+ function intervalSemitones(numberValue, quality) {
792
+ assertInteger(numberValue, "interval number", 1, MAX_INTERVAL_NUMBER);
793
+ const octaves = Math.floor((numberValue - 1) / 7);
794
+ const simple = numberValue - 7 * octaves;
795
+ const reference = (_nullishCoalesce(SIMPLE_REFERENCE[simple], () => ( 0))) + 12 * octaves;
796
+ const perfect = isPerfectNumber(simple);
797
+ if (quality === "P" || quality === "M") {
798
+ if (perfect !== (quality === "P")) {
799
+ throw new InvalidInputError(
800
+ `an interval of ${numberValue} cannot be ${quality === "P" ? "perfect" : "major"}`
801
+ );
802
+ }
803
+ return reference;
804
+ }
805
+ if (quality === "m") {
806
+ if (perfect) {
807
+ throw new InvalidInputError(
808
+ `a perfect-class interval cannot be minor; received ${numberValue}`
809
+ );
810
+ }
811
+ return reference - 1;
812
+ }
813
+ if (/^A+$/.test(quality)) {
814
+ return reference + quality.length;
815
+ }
816
+ if (/^d+$/.test(quality)) {
817
+ if (numberValue === 1) {
818
+ throw new InvalidInputError(
819
+ `a unison cannot be diminished; a unison narrowed by a semitone descends, so name it '-${"A".repeat(quality.length)}1'`
820
+ );
821
+ }
822
+ return Math.abs(reference - quality.length - (perfect ? 0 : 1));
823
+ }
824
+ throw new InvalidInputError(`unknown interval quality ${JSON.stringify(quality)}`);
825
+ }
826
+ var INTERVAL_NAME_PATTERN = /^(-?)(P|M|m|A+|d+)(\d+)$/;
827
+ function parseInterval(name) {
828
+ return unwrapParse(tryParseInterval(name));
829
+ }
830
+ function tryParseInterval(name) {
831
+ try {
832
+ const match = typeof name === "string" ? name.trim().match(INTERVAL_NAME_PATTERN) : null;
833
+ const quality = _optionalChain([match, 'optionalAccess', _10 => _10[2]]);
834
+ const numberValue = Number(_optionalChain([match, 'optionalAccess', _11 => _11[3]]));
835
+ if (quality === void 0 || !Number.isFinite(numberValue)) {
836
+ throw new InvalidInputError(
837
+ `interval must be a quality followed by a number, such as 'P5'; received ${JSON.stringify(name)}`
838
+ );
839
+ }
840
+ const span = intervalSemitones(numberValue, quality);
841
+ if (_optionalChain([match, 'optionalAccess', _12 => _12[1]]) !== "-") {
842
+ return { ok: true, value: { number: numberValue, quality, semitones: span } };
843
+ }
844
+ return {
845
+ ok: true,
846
+ value: { number: numberValue, quality, semitones: span === 0 ? 0 : -span, descending: true }
847
+ };
848
+ } catch (error) {
849
+ return parseFailure(error);
850
+ }
851
+ }
852
+ function isDescendingInterval(interval) {
853
+ if (interval.descending !== void 0) {
854
+ return interval.descending;
855
+ }
856
+ const span = Math.round(interval.semitones);
857
+ if (span < 0 && qualityFromSpan(interval.number, span) === interval.quality) {
858
+ return false;
859
+ }
860
+ return span < 0;
861
+ }
862
+ function normalizedInterval(data) {
863
+ assertInteger(data.number, "interval.number", 1);
864
+ assertFiniteNumber(data.semitones, "interval.semitones");
865
+ const expected = intervalSemitones(data.number, data.quality);
866
+ if (Math.abs(data.semitones) !== expected) {
867
+ throw new InvalidInputError(
868
+ `${data.quality}${data.number} spans ${expected} semitones; received ${data.semitones}`
869
+ );
870
+ }
871
+ const normalized = {
872
+ number: data.number,
873
+ quality: data.quality,
874
+ semitones: data.semitones
875
+ };
876
+ if (isDescendingInterval(data)) {
877
+ normalized.descending = true;
878
+ }
879
+ return normalized;
880
+ }
881
+ function toSpelledInterval(value) {
882
+ if (typeof value === "string") {
883
+ return parseInterval(value);
884
+ }
885
+ if (typeof value === "object" && value !== null) {
886
+ const data = "toJSON" in value && typeof value.toJSON === "function" ? value.toJSON() : value;
887
+ return normalizedInterval(data);
888
+ }
889
+ throw new InvalidInputError(
890
+ `interval must be a name or spelled interval data; received ${typeof value}`
891
+ );
892
+ }
893
+ function toNoteData(value) {
894
+ if (typeof value === "string") {
895
+ return parseNote(value);
896
+ }
897
+ if (typeof value === "number") {
898
+ return midiToNote(value);
899
+ }
900
+ if (typeof value === "object" && value !== null) {
901
+ const data = "toJSON" in value && typeof value.toJSON === "function" ? value.toJSON() : value;
902
+ const note = assertNote(data, "note");
903
+ return note.octave === void 0 ? { letter: note.letter, alter: note.alter } : { letter: note.letter, alter: note.alter, octave: note.octave };
904
+ }
905
+ throw new InvalidInputError(
906
+ `note must be a name, a MIDI number, or spelled note data; received ${typeof value}`
907
+ );
908
+ }
909
+ function transposeByInterval(note, interval) {
910
+ assertNote(note, "note");
911
+ assertInteger(interval.number, "interval.number", 1);
912
+ assertFiniteNumber(interval.semitones, "interval.semitones");
913
+ const letterSteps = (interval.number - 1) * (isDescendingInterval(interval) ? -1 : 1);
914
+ const absoluteLetter = mod7(note.letter) + letterSteps;
915
+ const letter = mod7(absoluteLetter);
916
+ const natural = _nullishCoalesce(LETTER_SEMITONES[letter], () => ( 0));
917
+ const steps = Math.round(interval.semitones);
918
+ if (note.octave === void 0) {
919
+ return { letter, alter: alterFor(natural, mod12(noteToPitchClass(note) + steps)) };
920
+ }
921
+ const octave = note.octave + Math.floor(absoluteLetter / 7);
922
+ return { letter, alter: noteToMidi(note) + steps - ((octave + 1) * 12 + natural), octave };
923
+ }
924
+ function diatonicIndex(note) {
925
+ return mod7(note.letter) + 7 * (_nullishCoalesce(note.octave, () => ( 0)));
926
+ }
927
+ function spelledInterval(a, b) {
928
+ assertNote(a, "a");
929
+ assertNote(b, "b");
930
+ if (a.octave === void 0 !== (b.octave === void 0)) {
931
+ throw new InvalidInputError(
932
+ "spelledInterval needs both notes to carry an octave or neither to; received one of each"
933
+ );
934
+ }
935
+ const octaved = a.octave !== void 0 && b.octave !== void 0;
936
+ let letterSteps;
937
+ let semitones;
938
+ if (octaved) {
939
+ letterSteps = diatonicIndex(b) - diatonicIndex(a);
940
+ semitones = noteToMidi(b) - noteToMidi(a);
941
+ } else {
942
+ letterSteps = mod7(b.letter - a.letter);
943
+ let rawSemis = mod12(noteToPitchClass(b) - noteToPitchClass(a));
944
+ const reference = _nullishCoalesce(SIMPLE_REFERENCE[letterSteps + 1], () => ( 0));
945
+ if (reference - rawSemis > 6) {
946
+ rawSemis += 12;
947
+ } else if (rawSemis - reference > 6) {
948
+ rawSemis -= 12;
949
+ }
950
+ semitones = rawSemis;
951
+ }
952
+ const absSteps = Math.abs(letterSteps);
953
+ const number = absSteps + 1;
954
+ const directedSpan = letterSteps === 0 ? Math.abs(semitones) : letterSteps > 0 ? semitones : -semitones;
955
+ const interval = {
956
+ number,
957
+ quality: qualityFromSpan(number, directedSpan),
958
+ semitones
959
+ };
960
+ if (letterSteps < 0 || letterSteps === 0 && semitones < 0) {
961
+ interval.descending = true;
962
+ }
963
+ return interval;
964
+ }
965
+
966
+ // src/core/interval/index.ts
967
+ var ConsonanceClass = /* @__PURE__ */ ((ConsonanceClass2) => {
968
+ ConsonanceClass2[ConsonanceClass2["PerfectConsonance"] = 0] = "PerfectConsonance";
969
+ ConsonanceClass2[ConsonanceClass2["ImperfectConsonance"] = 1] = "ImperfectConsonance";
970
+ ConsonanceClass2[ConsonanceClass2["Dissonance"] = 2] = "Dissonance";
971
+ return ConsonanceClass2;
972
+ })(ConsonanceClass || {});
973
+ function simpleInterval(semitones) {
974
+ return pitchClassOf(Math.abs(semitones));
975
+ }
976
+ function classifyInterval(semitones, twoVoice = true) {
977
+ const pc = simpleInterval(semitones);
978
+ if (pc === 0 || pc === 7) {
979
+ return 0 /* PerfectConsonance */;
980
+ }
981
+ if (pc === 3 || pc === 4 || pc === 8 || pc === 9) {
982
+ return 1 /* ImperfectConsonance */;
983
+ }
984
+ if (pc === 5) {
985
+ return twoVoice ? 2 /* Dissonance */ : 1 /* ImperfectConsonance */;
986
+ }
987
+ return 2 /* Dissonance */;
988
+ }
989
+ function isPerfectInterval(semitones) {
990
+ const pc = simpleInterval(semitones);
991
+ return pc === 0 || pc === 7;
992
+ }
993
+ function isConsonantInterval(semitones, twoVoice = true) {
994
+ const pc = simpleInterval(semitones);
995
+ if (pc === 5) {
996
+ return !twoVoice;
997
+ }
998
+ return pc === 0 || pc === 3 || pc === 4 || pc === 7 || pc === 8 || pc === 9;
999
+ }
1000
+
1001
+
1002
+
1003
+
1004
+
1005
+
1006
+
1007
+
1008
+
1009
+
1010
+
1011
+
1012
+
1013
+
1014
+
1015
+
1016
+
1017
+
1018
+
1019
+
1020
+
1021
+
1022
+
1023
+
1024
+
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+
1040
+
1041
+
1042
+
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+
1056
+
1057
+
1058
+
1059
+
1060
+
1061
+
1062
+
1063
+ exports.parseFailure = parseFailure; exports.unwrapParse = unwrapParse; exports.InvalidInputError = InvalidInputError; exports.NoSolutionError = NoSolutionError; exports.BudgetExceededError = BudgetExceededError; exports.isLibcantusError = isLibcantusError; exports.isCompoundNumerator = isCompoundNumerator; exports.groupingSumOf = groupingSumOf; exports.isAdditiveReading = isAdditiveReading; exports.pulseCountOf = pulseCountOf; exports.pulseBeatsOf = pulseBeatsOf; exports.pulseGroupingOf = pulseGroupingOf; exports.barBeatsOf = barBeatsOf; exports.isMeterMap = isMeterMap; exports.entryIndexOf = entryIndexOf; exports.barStartOf = barStartOf; exports.barLengthOf = barLengthOf; exports.barIndexOf = barIndexOf; exports.beatOfBarIndex = beatOfBarIndex; exports.DEFAULT_GENERATION_BUDGET = DEFAULT_GENERATION_BUDGET; exports.assertFiniteNumber = assertFiniteNumber; exports.assertInteger = assertInteger; exports.assertMidiPitch = assertMidiPitch; exports.clampToMidi = clampToMidi; exports.assertDegree = assertDegree; exports.assertFiniteSemitones = assertFiniteSemitones; exports.dropSilentNotes = dropSilentNotes; exports.soundingNotesOnly = soundingNotesOnly; exports.assertOneOf = assertOneOf; exports.assertPositiveInt = assertPositiveInt; exports.assertRange = assertRange; exports.assertGenerationBudget = assertGenerationBudget; exports.assertTimeSignature = assertTimeSignature; exports.assertMeterMap = assertMeterMap; exports.assertNoteEvent = assertNoteEvent; exports.assertNoteEvents = assertNoteEvents; exports.detectNoteNameSystem = detectNoteNameSystem; exports.pitchClassOf = pitchClassOf; exports.diatonicLetterOf = diatonicLetterOf; exports.naturalPitchClassOf = naturalPitchClassOf; exports.parseNote = parseNote; exports.tryParseNote = tryParseNote; exports.formatNote = formatNote; exports.parseKeyName = parseKeyName; exports.tryParseKeyName = tryParseKeyName; exports.formatKeyName = formatKeyName; exports.noteToPitchClass = noteToPitchClass; exports.noteToMidi = noteToMidi; exports.midiToNote = midiToNote; exports.transposeNote = transposeNote; exports.intervalSemitones = intervalSemitones; exports.parseInterval = parseInterval; exports.tryParseInterval = tryParseInterval; exports.toSpelledInterval = toSpelledInterval; exports.toNoteData = toNoteData; exports.transposeByInterval = transposeByInterval; exports.spelledInterval = spelledInterval; exports.ConsonanceClass = ConsonanceClass; exports.classifyInterval = classifyInterval; exports.isPerfectInterval = isPerfectInterval; exports.isConsonantInterval = isConsonantInterval;
1064
+ //# sourceMappingURL=chunk-Q2WGKJNN.cjs.map