@libraz/libcantus 0.9.4 → 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 (186) hide show
  1. package/README.md +61 -261
  2. package/README_ja.md +115 -0
  3. package/dist/analyze/index.cjs +54 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +213 -456
  6. package/dist/analyze/index.d.ts +213 -456
  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-ELCTZUJJ.js +379 -0
  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 +103 -6
  64. package/dist/core/index.cjs.map +1 -1
  65. package/dist/core/index.d.cts +96 -345
  66. package/dist/core/index.d.ts +96 -345
  67. package/dist/core/index.js +104 -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 +437 -1095
  71. package/dist/generate/index.d.ts +437 -1095
  72. package/dist/generate/index.js +94 -15
  73. package/dist/index-9wcMhG73.d.ts +1095 -0
  74. package/dist/{index-D6b6na69.d.cts → 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-tSQh-xwY.d.ts → 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 +358 -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 +407 -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 +4685 -243
  103. package/dist/model/index.d.ts +4685 -243
  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 +513 -173
  110. package/dist/theory/index.d.ts +513 -173
  111. package/dist/theory/index.js +118 -24
  112. package/package.json +16 -11
  113. package/dist/chunk-36N73KZ3.cjs +0 -246
  114. package/dist/chunk-36N73KZ3.cjs.map +0 -1
  115. package/dist/chunk-4HLLZPXT.cjs +0 -3077
  116. package/dist/chunk-4HLLZPXT.cjs.map +0 -1
  117. package/dist/chunk-7MGZ7D62.cjs +0 -498
  118. package/dist/chunk-7MGZ7D62.cjs.map +0 -1
  119. package/dist/chunk-7NKDPF74.js +0 -3077
  120. package/dist/chunk-7NKDPF74.js.map +0 -1
  121. package/dist/chunk-CAN76NCU.cjs +0 -679
  122. package/dist/chunk-CAN76NCU.cjs.map +0 -1
  123. package/dist/chunk-D2FAJBYL.js +0 -479
  124. package/dist/chunk-D2FAJBYL.js.map +0 -1
  125. package/dist/chunk-EHVEBWM5.js +0 -1449
  126. package/dist/chunk-EHVEBWM5.js.map +0 -1
  127. package/dist/chunk-FCANXBAT.cjs +0 -30
  128. package/dist/chunk-FCANXBAT.cjs.map +0 -1
  129. package/dist/chunk-FNDWXRRI.cjs +0 -479
  130. package/dist/chunk-FNDWXRRI.cjs.map +0 -1
  131. package/dist/chunk-GQSGS2N4.js +0 -173
  132. package/dist/chunk-GQSGS2N4.js.map +0 -1
  133. package/dist/chunk-HKGYCQ34.js +0 -53
  134. package/dist/chunk-HKGYCQ34.js.map +0 -1
  135. package/dist/chunk-I24MV65U.js +0 -106
  136. package/dist/chunk-I24MV65U.js.map +0 -1
  137. package/dist/chunk-KMHOICIY.js +0 -679
  138. package/dist/chunk-KMHOICIY.js.map +0 -1
  139. package/dist/chunk-LW53AHQU.cjs +0 -1117
  140. package/dist/chunk-LW53AHQU.cjs.map +0 -1
  141. package/dist/chunk-N6IHQUOX.js +0 -1
  142. package/dist/chunk-NKIBQ6PT.js +0 -30
  143. package/dist/chunk-NKIBQ6PT.js.map +0 -1
  144. package/dist/chunk-NP4NA2FB.cjs +0 -106
  145. package/dist/chunk-NP4NA2FB.cjs.map +0 -1
  146. package/dist/chunk-NVE34YGZ.cjs +0 -1449
  147. package/dist/chunk-NVE34YGZ.cjs.map +0 -1
  148. package/dist/chunk-NYYWMV2C.js +0 -327
  149. package/dist/chunk-NYYWMV2C.js.map +0 -1
  150. package/dist/chunk-Q2XUP227.js +0 -845
  151. package/dist/chunk-Q2XUP227.js.map +0 -1
  152. package/dist/chunk-Q6OP4U43.cjs +0 -327
  153. package/dist/chunk-Q6OP4U43.cjs.map +0 -1
  154. package/dist/chunk-RC6GBMCD.cjs +0 -845
  155. package/dist/chunk-RC6GBMCD.cjs.map +0 -1
  156. package/dist/chunk-RK35RIN7.js +0 -1117
  157. package/dist/chunk-RK35RIN7.js.map +0 -1
  158. package/dist/chunk-RUMU3UA6.cjs +0 -53
  159. package/dist/chunk-RUMU3UA6.cjs.map +0 -1
  160. package/dist/chunk-T64C7ZD7.cjs +0 -1
  161. package/dist/chunk-T64C7ZD7.cjs.map +0 -1
  162. package/dist/chunk-XEBG3G53.js +0 -498
  163. package/dist/chunk-XEBG3G53.js.map +0 -1
  164. package/dist/chunk-YLEMGEFH.js +0 -246
  165. package/dist/chunk-YLEMGEFH.js.map +0 -1
  166. package/dist/chunk-ZEJR4TKL.cjs +0 -173
  167. package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
  168. package/dist/function-BtiZb9i9.d.ts +0 -208
  169. package/dist/function-DNugAOrt.d.cts +0 -208
  170. package/dist/index-6VR8-kit.d.ts +0 -143
  171. package/dist/index-70p168VM.d.ts +0 -192
  172. package/dist/index-Be74mcon.d.cts +0 -152
  173. package/dist/index-BtXT6UQ0.d.cts +0 -192
  174. package/dist/index-C5UBbQ4G.d.ts +0 -152
  175. package/dist/index-C83mZI1P.d.ts +0 -242
  176. package/dist/index-DXd9JikN.d.cts +0 -246
  177. package/dist/index-DXd9JikN.d.ts +0 -246
  178. package/dist/index-OG65ocOr.d.cts +0 -272
  179. package/dist/index-OG65ocOr.d.ts +0 -272
  180. package/dist/index-tJhkmQgE.d.cts +0 -143
  181. package/dist/index-y_ak9_Hi.d.cts +0 -242
  182. package/dist/styled-DIRkk9Uz.d.ts +0 -506
  183. package/dist/styled-DJbkJQfj.d.cts +0 -506
  184. package/dist/types-BN6_Q9Kl.d.cts +0 -39
  185. package/dist/types-BN6_Q9Kl.d.ts +0 -39
  186. /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
@@ -1,1117 +0,0 @@
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; }
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
- var _chunkFNDWXRRIcjs = require('./chunk-FNDWXRRI.cjs');
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
- var _chunk7MGZ7D62cjs = require('./chunk-7MGZ7D62.cjs');
28
-
29
- // src/theory/chordscale/index.ts
30
- function popcount12(mask) {
31
- let count = 0;
32
- for (let n = 0; n < 12; n += 1) {
33
- count += mask >> n & 1;
34
- }
35
- return count;
36
- }
37
- function maskHasPitchClass(mask, scaleRootPc, pc) {
38
- const offset = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc - scaleRootPc);
39
- return (mask >> offset & 1) === 1;
40
- }
41
- function scaleMatchesChord(chordPcs, scaleMask, scaleRootPc) {
42
- for (const pc of chordPcs) {
43
- if (!maskHasPitchClass(scaleMask, scaleRootPc, pc)) {
44
- return false;
45
- }
46
- }
47
- return true;
48
- }
49
- var ALIASED_SCALE_NAMES = /* @__PURE__ */ new Set(["major", "naturalMinor"]);
50
- var HEPTATONIC_SIZE = 7;
51
- var SCALE_PREFERENCE = [
52
- "ionian",
53
- "major",
54
- "mixolydian",
55
- "dorian",
56
- "lydian",
57
- "aeolian",
58
- "naturalMinor",
59
- "phrygian",
60
- "locrian"
61
- ];
62
- function conventionalRank(name) {
63
- const index = SCALE_PREFERENCE.indexOf(name);
64
- return index < 0 ? SCALE_PREFERENCE.length : index;
65
- }
66
- function chordScales(chord) {
67
- const chordPcs = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
68
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
69
- const ranked = [];
70
- const seenMasks = /* @__PURE__ */ new Set();
71
- for (const name of Object.keys(_chunkFNDWXRRIcjs.NAMED_SCALES)) {
72
- if (name === "chromatic" || ALIASED_SCALE_NAMES.has(name)) {
73
- continue;
74
- }
75
- const mask = _chunkFNDWXRRIcjs.namedScaleMask.call(void 0, name);
76
- if (mask === void 0 || seenMasks.has(mask)) {
77
- continue;
78
- }
79
- seenMasks.add(mask);
80
- if (scaleMatchesChord(chordPcs, mask, rootPc)) {
81
- const size = popcount12(mask);
82
- ranked.push({ name, extra: size - chordPcs.length, size });
83
- }
84
- }
85
- if (ranked.length === 0) {
86
- if (_chunkFNDWXRRIcjs.NAMED_SCALES.chromatic !== void 0) {
87
- return [{ name: "chromatic", rootPc }];
88
- }
89
- return [];
90
- }
91
- const preferHeptatonic = chordPcs.length <= 3;
92
- ranked.sort((a, b) => {
93
- if (preferHeptatonic && a.size === HEPTATONIC_SIZE !== (b.size === HEPTATONIC_SIZE)) {
94
- return a.size === HEPTATONIC_SIZE ? -1 : 1;
95
- }
96
- if (a.extra !== b.extra) {
97
- return a.extra - b.extra;
98
- }
99
- if (a.size !== b.size) {
100
- return a.size - b.size;
101
- }
102
- const rankA = conventionalRank(a.name);
103
- const rankB = conventionalRank(b.name);
104
- if (rankA !== rankB) {
105
- return rankA - rankB;
106
- }
107
- return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
108
- });
109
- return ranked.map((entry) => ({ name: entry.name, rootPc }));
110
- }
111
- function avoidNotes(chord, scaleName) {
112
- const mask = _chunkFNDWXRRIcjs.requireScaleMask.call(void 0, scaleName);
113
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
114
- const chordPcs = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
115
- if (!scaleMatchesChord(chordPcs, mask, rootPc)) {
116
- return [];
117
- }
118
- const chordSet = new Set(chordPcs);
119
- const avoid = [];
120
- for (let pc = 0; pc < 12; pc += 1) {
121
- if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
122
- continue;
123
- }
124
- if (chordSet.has(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc - 1))) {
125
- avoid.push(pc);
126
- }
127
- }
128
- return avoid.sort((a, b) => a - b);
129
- }
130
- function availableTensions(chord, scaleName) {
131
- const mask = _chunkFNDWXRRIcjs.requireScaleMask.call(void 0, scaleName);
132
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
133
- const chordPcs = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
134
- if (!scaleMatchesChord(chordPcs, mask, rootPc)) {
135
- return [];
136
- }
137
- const chordSet = new Set(chordPcs);
138
- const avoidSet = new Set(avoidNotes(chord, scaleName));
139
- const tensions = [];
140
- for (let pc = 0; pc < 12; pc += 1) {
141
- if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc) || avoidSet.has(pc)) {
142
- continue;
143
- }
144
- tensions.push(pc);
145
- }
146
- return tensions.sort((a, b) => a - b);
147
- }
148
- function chordScaleReport(chord, limit) {
149
- const matches = chordScales(chord);
150
- const chosen = limit === void 0 ? matches : matches.slice(0, Math.max(0, limit));
151
- return chosen.map((match) => ({
152
- name: match.name,
153
- rootPc: match.rootPc,
154
- avoid: avoidNotes(chord, match.name),
155
- tensions: availableTensions(chord, match.name)
156
- }));
157
- }
158
- var RANK_PENALTY = 0.01;
159
- function scalePitchClassSet(name, rootPc) {
160
- const mask = _chunkFNDWXRRIcjs.namedScaleMask.call(void 0, name);
161
- const pcs = /* @__PURE__ */ new Set();
162
- if (mask === void 0) {
163
- return pcs;
164
- }
165
- for (let n = 0; n < 12; n += 1) {
166
- if ((mask >> n & 1) === 1) {
167
- pcs.add(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rootPc + n));
168
- }
169
- }
170
- return pcs;
171
- }
172
- function symmetricDifferenceSize(a, b) {
173
- let count = 0;
174
- for (const pc of a) {
175
- if (!b.has(pc)) {
176
- count += 1;
177
- }
178
- }
179
- for (const pc of b) {
180
- if (!a.has(pc)) {
181
- count += 1;
182
- }
183
- }
184
- return count;
185
- }
186
- function scalesForChanges(chords) {
187
- if (chords.length === 0) {
188
- return [];
189
- }
190
- const candidateLists = chords.map((chord) => chordScales(chord));
191
- const pcSets = candidateLists.map(
192
- (candidates) => candidates.map((match) => scalePitchClassSet(match.name, match.rootPc))
193
- );
194
- const first = _nullishCoalesce(candidateLists[0], () => ( []));
195
- const dp = [first.map((_, j) => j * RANK_PENALTY)];
196
- const back = [first.map(() => -1)];
197
- for (let i = 1; i < candidateLists.length; i += 1) {
198
- const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
199
- const prevCosts = _nullishCoalesce(dp[i - 1], () => ( []));
200
- const prevSets = _nullishCoalesce(pcSets[i - 1], () => ( []));
201
- const curSets = _nullishCoalesce(pcSets[i], () => ( []));
202
- const stageCosts = [];
203
- const stageBack = [];
204
- for (let j = 0; j < candidates.length; j += 1) {
205
- let bestCost = Number.POSITIVE_INFINITY;
206
- let bestPrev = -1;
207
- for (let k = 0; k < prevCosts.length; k += 1) {
208
- const transition = symmetricDifferenceSize(
209
- _nullishCoalesce(prevSets[k], () => ( /* @__PURE__ */ new Set())),
210
- _nullishCoalesce(curSets[j], () => ( /* @__PURE__ */ new Set()))
211
- );
212
- const cost = (_nullishCoalesce(prevCosts[k], () => ( 0))) + transition;
213
- if (cost < bestCost) {
214
- bestCost = cost;
215
- bestPrev = k;
216
- }
217
- }
218
- stageCosts.push(bestCost + j * RANK_PENALTY);
219
- stageBack.push(bestPrev);
220
- }
221
- dp.push(stageCosts);
222
- back.push(stageBack);
223
- }
224
- const lastCosts = _nullishCoalesce(dp[dp.length - 1], () => ( []));
225
- let bestFinal = 0;
226
- for (let j = 1; j < lastCosts.length; j += 1) {
227
- if ((_nullishCoalesce(lastCosts[j], () => ( Number.POSITIVE_INFINITY))) < (_nullishCoalesce(lastCosts[bestFinal], () => ( 0)))) {
228
- bestFinal = j;
229
- }
230
- }
231
- const chosenIndices = new Array(candidateLists.length);
232
- let current = bestFinal;
233
- for (let i = candidateLists.length - 1; i >= 0; i -= 1) {
234
- chosenIndices[i] = current;
235
- current = _nullishCoalesce(_optionalChain([back, 'access', _2 => _2[i], 'optionalAccess', _3 => _3[current]]), () => ( -1));
236
- }
237
- return chords.map((chord, i) => {
238
- const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
239
- const scale = _nullishCoalesce(candidates[_nullishCoalesce(chosenIndices[i], () => ( 0))], () => ( { name: "chromatic", rootPc: chord.rootPc }));
240
- return { chord, scale };
241
- });
242
- }
243
-
244
- // src/theory/spelling/index.ts
245
- var CHROMATIC_SPELLING = {
246
- 1: 1,
247
- // b2
248
- 2: 1,
249
- // M2
250
- 3: 2,
251
- // b3
252
- 4: 2,
253
- // M3
254
- 5: 3,
255
- // P4
256
- 6: 3,
257
- // #4
258
- 7: 4,
259
- // P5
260
- 8: 5,
261
- // b6
262
- 9: 5,
263
- // #6 (raised sixth of a minor key)
264
- 10: 6,
265
- // b7
266
- 11: 6
267
- // #7 (raised leading tone of a minor key)
268
- };
269
- var CHROMATIC_SPELLING_FLAT = {
270
- ...CHROMATIC_SPELLING,
271
- 6: 4
272
- // b5
273
- };
274
- var CHROMATIC_SPELLING_SHARP = {
275
- 1: 0,
276
- // #1
277
- 2: 1,
278
- // M2
279
- 3: 1,
280
- // #2
281
- 4: 2,
282
- // M3
283
- 5: 3,
284
- // P4
285
- 6: 3,
286
- // #4
287
- 7: 4,
288
- // P5
289
- 8: 4,
290
- // #5
291
- 9: 5,
292
- // M6
293
- 10: 5,
294
- // #6
295
- 11: 6
296
- // M7
297
- };
298
- function alterFor(letter, pc) {
299
- let d = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc - _chunk7MGZ7D62cjs.naturalPitchClassOf.call(void 0, letter));
300
- if (d > 6) {
301
- d -= 12;
302
- }
303
- return d;
304
- }
305
- function letterFor(tonic, degreeOffset, pc) {
306
- if (degreeOffset === void 0) {
307
- return void 0;
308
- }
309
- const letter = _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, tonic.letter + degreeOffset);
310
- return { letter, alter: alterFor(letter, pc) };
311
- }
312
- function pickSpelling(flat, sharp, preferFlat) {
313
- if (flat === void 0 || sharp === void 0) {
314
- return _nullishCoalesce(flat, () => ( sharp));
315
- }
316
- if (Math.abs(flat.alter) !== Math.abs(sharp.alter)) {
317
- return Math.abs(flat.alter) < Math.abs(sharp.alter) ? flat : sharp;
318
- }
319
- return preferFlat ? flat : sharp;
320
- }
321
- function isHeptatonic(key) {
322
- return _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key).length === 7;
323
- }
324
- function hasMinorThird(key) {
325
- const tones = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key);
326
- const root = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
327
- return tones.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, root + 3)) && !tones.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, root + 4));
328
- }
329
- function spellPitchClass(pc, tonic, key) {
330
- const tonicPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _chunk7MGZ7D62cjs.naturalPitchClassOf.call(void 0, tonic.letter) + tonic.alter);
331
- const offset = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc - tonicPc);
332
- if (offset === 0) {
333
- return { letter: _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, tonic.letter), alter: tonic.alter };
334
- }
335
- const heptatonic = isHeptatonic(key);
336
- if (heptatonic) {
337
- const degree = _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key).indexOf(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc));
338
- if (degree >= 0) {
339
- const letter = _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, tonic.letter + degree);
340
- return { letter, alter: alterFor(letter, pc) };
341
- }
342
- const degreeOffset = CHROMATIC_SPELLING[offset];
343
- if (degreeOffset !== void 0) {
344
- const letter = _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, tonic.letter + degreeOffset);
345
- return { letter, alter: alterFor(letter, pc) };
346
- }
347
- } else {
348
- const flat = letterFor(tonic, CHROMATIC_SPELLING_FLAT[offset], pc);
349
- const sharp = letterFor(tonic, CHROMATIC_SPELLING_SHARP[offset], pc);
350
- const best = pickSpelling(flat, sharp, tonic.alter < 0 || hasMinorThird(key));
351
- if (best !== void 0) {
352
- return best;
353
- }
354
- }
355
- const belowLetter = _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, _nullishCoalesce([0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6][_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pc)], () => ( 0)));
356
- return { letter: belowLetter, alter: alterFor(belowLetter, pc) };
357
- }
358
- function spellScale(tonic, key) {
359
- return _chunkFNDWXRRIcjs.scaleTonesInDegreeOrder.call(void 0, key).map((pc) => spellPitchClass(pc, tonic, key));
360
- }
361
- function spellPitchClasses(pcs, tonic, key) {
362
- return pcs.map((pc) => spellPitchClass(pc, tonic, key));
363
- }
364
- function chordLetterOffset(interval, chord) {
365
- switch (interval) {
366
- case 0:
367
- case 12:
368
- return 0;
369
- case 1:
370
- case 2:
371
- case 13:
372
- case 14:
373
- return 1;
374
- case 3:
375
- case 4:
376
- case 16:
377
- return 2;
378
- case 15:
379
- return chord.intervals.some((i) => i === 3 || i === 4) ? 1 : 2;
380
- case 5:
381
- case 17:
382
- case 18:
383
- return 3;
384
- case 6:
385
- case 7:
386
- case 8:
387
- case 19:
388
- return 4;
389
- case 9:
390
- return chord.quality === "dim7" ? 6 : 5;
391
- case 20:
392
- case 21:
393
- return 5;
394
- case 10:
395
- case 11:
396
- case 22:
397
- case 23:
398
- return 6;
399
- default: {
400
- const octaves = Math.floor(Math.max(0, interval) / 12);
401
- const simple = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, interval);
402
- const simpleOffset = _nullishCoalesce([0, 1, 1, 2, 2, 3, 4, 4, 4, 5, 6, 6][simple], () => ( 0));
403
- return octaves * 7 + simpleOffset;
404
- }
405
- }
406
- }
407
- function spellChordFromRoot(chord, root) {
408
- return chord.intervals.map((interval) => {
409
- const letter = _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, root.letter + chordLetterOffset(interval, chord));
410
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + interval);
411
- return { letter, alter: alterFor(letter, pc) };
412
- });
413
- }
414
- function spellChord(chord, tonic, key) {
415
- return spellChordFromRoot(chord, chordRootSpelling(chord, tonic, key));
416
- }
417
- function chordRootSpelling(chord, tonic, key) {
418
- const hint = chord.rootSpelling;
419
- if (hint !== void 0 && _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _chunk7MGZ7D62cjs.naturalPitchClassOf.call(void 0, hint.letter) + hint.alter) === _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc)) {
420
- return { letter: _chunk7MGZ7D62cjs.diatonicLetterOf.call(void 0, hint.letter), alter: hint.alter };
421
- }
422
- return spellPitchClass(chord.rootPc, tonic, key);
423
- }
424
- function spellPitch(pitch, tonic, key) {
425
- const rounded = Math.round(pitch);
426
- const spelled = spellPitchClass(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, rounded), tonic, key);
427
- const octave = (rounded - _chunk7MGZ7D62cjs.naturalPitchClassOf.call(void 0, spelled.letter) - spelled.alter) / 12 - 1;
428
- return { letter: spelled.letter, alter: spelled.alter, octave };
429
- }
430
- function noteNames(notes) {
431
- return notes.map(_chunk7MGZ7D62cjs.formatNote);
432
- }
433
-
434
- // src/theory/symbol/index.ts
435
- var ROOT_RE = /^[A-Ga-g](?:#{1,2}|b{1,2})?/;
436
- var BASS_RE = /^[A-Ga-g](?:#{1,2}|b{1,2})?$/;
437
- var QUALITY_MAP = {
438
- "": "maj",
439
- m: "min",
440
- min: "min",
441
- "-": "min",
442
- dim: "dim",
443
- "\xB0": "dim",
444
- aug: "aug",
445
- "+": "aug",
446
- maj7: "maj7",
447
- M7: "maj7",
448
- \u0394: "maj7",
449
- \u03947: "maj7",
450
- m7: "min7",
451
- min7: "min7",
452
- "-7": "min7",
453
- "7": "dom7",
454
- dim7: "dim7",
455
- "\xB07": "dim7",
456
- m7b5: "m7b5",
457
- \u00F8: "m7b5",
458
- \u00F87: "m7b5",
459
- min7b5: "m7b5",
460
- mMaj7: "minMaj7",
461
- mM7: "minMaj7",
462
- minMaj7: "minMaj7",
463
- aug7: "aug7",
464
- "+7": "aug7",
465
- "7#5": "aug7",
466
- augMaj7: "augMaj7",
467
- augM7: "augMaj7",
468
- "+maj7": "augMaj7",
469
- "+M7": "augMaj7",
470
- majb5: "majb5",
471
- "maj(b5)": "majb5",
472
- "(b5)": "majb5",
473
- "6": "6",
474
- m6: "min6",
475
- min6: "min6",
476
- "6/9": "6/9",
477
- "69": "6/9",
478
- sus2: "sus2",
479
- sus: "sus4",
480
- sus4: "sus4",
481
- add9: "add9",
482
- add11: "add11",
483
- maj9: "maj9",
484
- M9: "maj9",
485
- m9: "min9",
486
- min9: "min9",
487
- "9": "dom9",
488
- "7b9": "7b9",
489
- "7#9": "7#9",
490
- "7#11": "7#11",
491
- "7b13": "7b13",
492
- "11": "11",
493
- "13": "13",
494
- "5": "5",
495
- "7sus4": "7sus4",
496
- "7sus": "7sus4",
497
- "9sus4": "11",
498
- "9sus": "11",
499
- "7b5": "7b5",
500
- "7alt": "7alt",
501
- alt: "7alt",
502
- "13b9": "13b9",
503
- maj13: "maj13",
504
- M13: "maj13",
505
- "maj7#11": "maj7#11",
506
- "M7#11": "maj7#11",
507
- m11: "min11",
508
- min11: "min11",
509
- m13: "min13",
510
- min13: "min13",
511
- madd9: "minAdd9",
512
- "min(add9)": "minAdd9",
513
- "m(add9)": "minAdd9",
514
- "6add9": "6/9",
515
- "m6/9": "min6/9",
516
- m69: "min6/9",
517
- "min6/9": "min6/9",
518
- // ASCII stand-ins for the degree and half-diminished glyphs, which most
519
- // lead sheets and chord-chart text files use instead of the typographic ones.
520
- o: "dim",
521
- o7: "dim7",
522
- h: "m7b5",
523
- h7: "m7b5"
524
- };
525
- var CANONICAL_SUFFIX = {
526
- maj: "",
527
- min: "m",
528
- dim: "dim",
529
- aug: "aug",
530
- maj7: "maj7",
531
- min7: "m7",
532
- dom7: "7",
533
- dim7: "dim7",
534
- m7b5: "m7b5",
535
- minMaj7: "mMaj7",
536
- aug7: "aug7",
537
- augMaj7: "augMaj7",
538
- majb5: "maj(b5)",
539
- "6": "6",
540
- min6: "m6",
541
- "6/9": "6/9",
542
- sus2: "sus2",
543
- sus4: "sus4",
544
- add9: "add9",
545
- add11: "add11",
546
- maj9: "maj9",
547
- min9: "m9",
548
- dom9: "9",
549
- "7b9": "7b9",
550
- "7#9": "7#9",
551
- "7#11": "7#11",
552
- "7b13": "7b13",
553
- "11": "11",
554
- "13": "13",
555
- "5": "5",
556
- "7sus4": "7sus4",
557
- "7b5": "7b5",
558
- "7alt": "7alt",
559
- "13b9": "13b9",
560
- maj13: "maj13",
561
- "maj7#11": "maj7#11",
562
- min11: "m11",
563
- min13: "m13",
564
- minAdd9: "madd9",
565
- "min6/9": "m6/9"
566
- };
567
- function knownQuality(suffix) {
568
- return Object.hasOwn(QUALITY_MAP, suffix) ? QUALITY_MAP[suffix] : void 0;
569
- }
570
- function makeSpelledChord(root, quality, bass) {
571
- const chord = _chunkFNDWXRRIcjs.makeChord.call(void 0,
572
- _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, root),
573
- quality,
574
- bass === void 0 ? void 0 : _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, bass)
575
- );
576
- chord.rootSpelling = { letter: root.letter, alter: root.alter };
577
- if (bass !== void 0) {
578
- chord.bassSpelling = { letter: bass.letter, alter: bass.alter };
579
- }
580
- return chord;
581
- }
582
- function parseChordSymbol(text) {
583
- const trimmed = text.trim();
584
- const rootMatch = ROOT_RE.exec(trimmed);
585
- if (!rootMatch) {
586
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Invalid chord symbol: ${text}`);
587
- }
588
- const rootToken = rootMatch[0];
589
- const rootNote = _chunk7MGZ7D62cjs.parseNote.call(void 0, rootToken);
590
- const rest = trimmed.slice(rootToken.length);
591
- const slashIndex = rest.lastIndexOf("/");
592
- if (slashIndex !== -1) {
593
- const before = rest.slice(0, slashIndex);
594
- const after = rest.slice(slashIndex + 1);
595
- const beforeQuality = knownQuality(before);
596
- if (beforeQuality !== void 0 && BASS_RE.test(after)) {
597
- return makeSpelledChord(rootNote, beforeQuality, _chunk7MGZ7D62cjs.parseNote.call(void 0, after));
598
- }
599
- }
600
- const quality = knownQuality(rest);
601
- if (quality === void 0) {
602
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Unrecognized chord quality: ${text}`);
603
- }
604
- return makeSpelledChord(rootNote, quality);
605
- }
606
- function pitchClassName(pc, hint, flats, inheritFlats) {
607
- if (flats === void 0 && hint !== void 0 && _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, hint) === pc) {
608
- return _chunk7MGZ7D62cjs.formatNote.call(void 0, hint);
609
- }
610
- const note = _chunk7MGZ7D62cjs.midiToNote.call(void 0, 60 + pc, _nullishCoalesce(flats, () => ( inheritFlats)) ? "flat" : "sharp");
611
- return _chunk7MGZ7D62cjs.formatNote.call(void 0, { letter: note.letter, alter: note.alter });
612
- }
613
- function formatChordSymbol(chord, opts) {
614
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, chord.rootPc, "chord rootPc");
615
- if (chord.bassPc !== void 0) {
616
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, chord.bassPc, "chord bassPc");
617
- }
618
- if (!Object.hasOwn(CANONICAL_SUFFIX, chord.quality)) {
619
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`Unknown chord quality: ${String(chord.quality)}`);
620
- }
621
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
622
- const rootHint = chord.rootSpelling;
623
- const rootName = pitchClassName(rootPc, rootHint, _optionalChain([opts, 'optionalAccess', _4 => _4.flats]));
624
- const suffix = CANONICAL_SUFFIX[chord.quality];
625
- let symbol = `${rootName}${suffix}`;
626
- if (chord.bassPc !== void 0 && _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.bassPc) !== rootPc) {
627
- const inheritFlats = rootHint !== void 0 && _chunk7MGZ7D62cjs.noteToPitchClass.call(void 0, rootHint) === rootPc ? rootHint.alter < 0 : void 0;
628
- symbol += `/${pitchClassName(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.bassPc), chord.bassSpelling, _optionalChain([opts, 'optionalAccess', _5 => _5.flats]), inheritFlats)}`;
629
- }
630
- return symbol;
631
- }
632
- function transposeChordSymbol(text, semitones, opts) {
633
- const chord = parseChordSymbol(text);
634
- const transposed = { ...chord, rootPc: _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + semitones) };
635
- if (chord.bassPc !== void 0) {
636
- transposed.bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.bassPc + semitones);
637
- }
638
- return formatChordSymbol(transposed, opts);
639
- }
640
-
641
- // src/theory/voicing/internal.ts
642
- var DEFAULT_MAX_SPACING = 12;
643
- var VIOLATION_PENALTY = 1e3;
644
- var MISSING_TONE_PENALTY = 500;
645
- var MISSING_FIFTH_PENALTY = 20;
646
- var POOR_DOUBLING_PENALTY = 4;
647
- var LEADING_TONE_DOUBLING_PENALTY = 200;
648
- var RESOLUTION_PENALTY = 40;
649
- var MAX_CANDIDATES = 4e3;
650
- var MAX_SEARCH_NODES = MAX_CANDIDATES * 16;
651
- function pitchesForPc(pc, range) {
652
- const result = [];
653
- for (let midi = Math.ceil(range.min); midi <= range.max; midi += 1) {
654
- if (_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, midi) === pc) {
655
- result.push(midi);
656
- }
657
- }
658
- const center = (range.min + range.max) / 2;
659
- return result.sort((a, b) => {
660
- const da = Math.abs(a - center);
661
- const db = Math.abs(b - center);
662
- return da === db ? a - b : da - db;
663
- });
664
- }
665
- function enumerateVoicings(chord, ranges, maxSpacing) {
666
- const chordPcs = _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord);
667
- const bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
668
- const byVoice = ranges.map(
669
- (range, voice) => (voice === 0 ? [bassPc] : chordPcs).flatMap((pc) => pitchesForPc(pc, range))
670
- );
671
- for (let voice = 0; voice < byVoice.length; voice += 1) {
672
- if ((_nullishCoalesce(byVoice[voice], () => ( []))).length === 0) {
673
- throw new (0, _chunk7MGZ7D62cjs.NoSolutionError)("no voicing satisfies the given ranges");
674
- }
675
- }
676
- const results = [];
677
- const current = [];
678
- let nodes = 0;
679
- const build = (voice) => {
680
- nodes += 1;
681
- if (results.length >= MAX_CANDIDATES || nodes > MAX_SEARCH_NODES) {
682
- return;
683
- }
684
- if (voice === ranges.length) {
685
- results.push([...current]);
686
- return;
687
- }
688
- const prev = current[voice - 1];
689
- for (const pitch of _nullishCoalesce(byVoice[voice], () => ( []))) {
690
- if (prev !== void 0) {
691
- if (pitch < prev) {
692
- continue;
693
- }
694
- const spacingLimit = voice === 1 ? maxSpacing + 12 : maxSpacing;
695
- if (pitch - prev > spacingLimit) {
696
- continue;
697
- }
698
- }
699
- current.push(pitch);
700
- build(voice + 1);
701
- current.pop();
702
- }
703
- };
704
- build(0);
705
- return results;
706
- }
707
- function structuralPenalty(pitches, chord, key) {
708
- const counts = /* @__PURE__ */ new Map();
709
- for (const pitch of pitches) {
710
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch);
711
- counts.set(pc, (_nullishCoalesce(counts.get(pc), () => ( 0))) + 1);
712
- }
713
- let penalty = 0;
714
- for (const pc of _chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, chord)) {
715
- if (!counts.has(pc)) {
716
- penalty += _chunkFNDWXRRIcjs.chordToneRole.call(void 0, pc, chord) === "fifth" ? MISSING_FIFTH_PENALTY : MISSING_TONE_PENALTY;
717
- }
718
- }
719
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
720
- const leadingTonePc = key === void 0 ? void 0 : _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc - 1);
721
- for (const [pc, count] of counts) {
722
- if (count <= 1) {
723
- continue;
724
- }
725
- if (pc === leadingTonePc) {
726
- penalty += (count - 1) * LEADING_TONE_DOUBLING_PENALTY;
727
- continue;
728
- }
729
- if (pc !== rootPc && _chunkFNDWXRRIcjs.chordToneRole.call(void 0, pc, chord) !== "fifth") {
730
- penalty += (count - 1) * POOR_DOUBLING_PENALTY;
731
- }
732
- }
733
- return penalty;
734
- }
735
- function seventhPcOf(chord) {
736
- for (const interval of chord.intervals) {
737
- const pc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + interval);
738
- if (_chunkFNDWXRRIcjs.chordToneRole.call(void 0, pc, chord) === "seventh") {
739
- return pc;
740
- }
741
- }
742
- return void 0;
743
- }
744
- function resolutionViolations(prev, cur, prevChord, nextChord, key) {
745
- const seventhPc = seventhPcOf(prevChord);
746
- const leadingTonePc = key === void 0 ? void 0 : _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc - 1);
747
- const tonicPc = key === void 0 ? void 0 : _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, key.rootPc);
748
- const nextPcs = new Set(_chunkFNDWXRRIcjs.chordPitchClasses.call(void 0, nextChord));
749
- let count = 0;
750
- for (let voice = 0; voice < prev.length && voice < cur.length; voice += 1) {
751
- const from = prev[voice];
752
- const to = cur[voice];
753
- if (from === void 0 || to === void 0) {
754
- continue;
755
- }
756
- const fromPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, from);
757
- const motion = to - from;
758
- if (fromPc === seventhPc && !nextPcs.has(fromPc) && motion !== -1 && motion !== -2) {
759
- count += 1;
760
- }
761
- if (fromPc === leadingTonePc && tonicPc !== void 0 && nextPcs.has(tonicPc) && !nextPcs.has(fromPc) && motion !== 1) {
762
- count += 1;
763
- }
764
- }
765
- return count;
766
- }
767
- function violationCount(prev, cur) {
768
- let count = 0;
769
- for (let lower = 0; lower < cur.length; lower += 1) {
770
- for (let upper = lower + 1; upper < cur.length; upper += 1) {
771
- const prevLower = prev[lower];
772
- const prevUpper = prev[upper];
773
- const curLower = cur[lower];
774
- const curUpper = cur[upper];
775
- if (prevLower === void 0 || prevUpper === void 0 || curLower === void 0 || curUpper === void 0) {
776
- continue;
777
- }
778
- if (_chunkFNDWXRRIcjs.createsParallelPerfect.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
779
- count += 1;
780
- }
781
- if (upper === lower + 1) {
782
- if (_chunkFNDWXRRIcjs.createsVoiceOverlap.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
783
- count += 1;
784
- }
785
- }
786
- }
787
- }
788
- return count;
789
- }
790
-
791
- // src/theory/voicing/leading.ts
792
- var HIDDEN_PERFECT_PENALTY = 6;
793
- function voiceLeadingCost(from, to) {
794
- if (from.length !== to.length) {
795
- return Number.POSITIVE_INFINITY;
796
- }
797
- let total = 0;
798
- for (let i = 0; i < from.length; i += 1) {
799
- const a = from[i];
800
- const b = to[i];
801
- if (a === void 0 || b === void 0) {
802
- continue;
803
- }
804
- total += Math.abs(b - a);
805
- }
806
- if (from.length >= 2) {
807
- const bassPrev = from[0];
808
- const bassCur = to[0];
809
- const sopPrev = from[from.length - 1];
810
- const sopCur = to[to.length - 1];
811
- if (bassPrev !== void 0 && bassCur !== void 0 && sopPrev !== void 0 && sopCur !== void 0 && _chunkFNDWXRRIcjs.createsHiddenParallelPerfect.call(void 0, bassPrev, bassCur, sopPrev, sopCur)) {
812
- total += HIDDEN_PERFECT_PENALTY;
813
- }
814
- }
815
- return total;
816
- }
817
- function nextVoicing(current, chord, opts) {
818
- const derived = _optionalChain([opts, 'optionalAccess', _6 => _6.ranges]) === void 0 && _optionalChain([opts, 'optionalAccess', _7 => _7.voices]) === void 0;
819
- if (derived) {
820
- if (current.length === 0) {
821
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("current must contain at least one pitch");
822
- }
823
- for (let index = 0; index < current.length; index += 1) {
824
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
825
- }
826
- }
827
- const ranges = derived ? current.map((pitch) => {
828
- const centre = Math.min(127, Math.max(0, pitch));
829
- return { min: Math.max(0, centre - 12), max: Math.min(127, centre + 12) };
830
- }) : resolveRanges(opts);
831
- const maxSpacing = resolveMaxSpacing(opts);
832
- const candidates = enumerateVoicings(chord, ranges, maxSpacing);
833
- let best;
834
- let bestScore = Number.POSITIVE_INFINITY;
835
- for (const candidate of candidates) {
836
- const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _8 => _8.key])) + voiceLeadingCost(current, candidate) + VIOLATION_PENALTY * violationCount(current, candidate);
837
- if (score < bestScore) {
838
- bestScore = score;
839
- best = candidate;
840
- }
841
- }
842
- if (best === void 0) {
843
- throw new (0, _chunk7MGZ7D62cjs.NoSolutionError)("no voicing satisfies the given ranges");
844
- }
845
- return best;
846
- }
847
-
848
- // src/theory/voicing/satb.ts
849
- var SATB_RANGES = Object.freeze([
850
- Object.freeze({ min: 40, max: 60 }),
851
- Object.freeze({ min: 48, max: 67 }),
852
- Object.freeze({ min: 55, max: 74 }),
853
- Object.freeze({ min: 60, max: 79 })
854
- ]);
855
- var DERIVED_LOW = 40;
856
- var DERIVED_HIGH = 79;
857
- var DERIVED_SPAN = 19;
858
- function resolveRanges(opts) {
859
- if (_optionalChain([opts, 'optionalAccess', _9 => _9.ranges]) !== void 0) {
860
- if (opts.ranges.length === 0) {
861
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("ranges must contain at least one voice range");
862
- }
863
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, opts.ranges.length, "voice ranges", 128);
864
- return opts.ranges.map((range, index) => {
865
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, range.min, `ranges[${index}].min`);
866
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, range.max, `ranges[${index}].max`);
867
- if (range.min > range.max) {
868
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(`ranges[${index}].min must not exceed max`);
869
- }
870
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0,
871
- Math.floor(range.max) - Math.ceil(range.min) + 1,
872
- `ranges[${index}] span`,
873
- 4096
874
- );
875
- return { ...range };
876
- });
877
- }
878
- const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _10 => _10.voices]), () => ( 4));
879
- _chunk7MGZ7D62cjs.assertPositiveInt.call(void 0, voices, "voices", 128);
880
- if (voices === 4) {
881
- return SATB_RANGES.map((range) => ({ ...range }));
882
- }
883
- if (voices === 1) {
884
- return [{ min: DERIVED_LOW, max: DERIVED_HIGH }];
885
- }
886
- const ranges = [];
887
- for (let i = 0; i < voices; i += 1) {
888
- const min = Math.round(
889
- DERIVED_LOW + i * (DERIVED_HIGH - DERIVED_SPAN - DERIVED_LOW) / (voices - 1)
890
- );
891
- ranges.push({ min, max: min + DERIVED_SPAN });
892
- }
893
- return ranges;
894
- }
895
- function resolveMaxSpacing(opts) {
896
- const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _11 => _11.maxSpacing]), () => ( DEFAULT_MAX_SPACING));
897
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, maxSpacing, "maxSpacing");
898
- if (maxSpacing < 0) {
899
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)("maxSpacing must be non-negative");
900
- }
901
- return maxSpacing;
902
- }
903
- function voiceChord(chord, opts) {
904
- const ranges = resolveRanges(opts);
905
- const maxSpacing = resolveMaxSpacing(opts);
906
- const candidates = enumerateVoicings(chord, ranges, maxSpacing);
907
- let best;
908
- let bestScore = Number.POSITIVE_INFINITY;
909
- for (const candidate of candidates) {
910
- let score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _12 => _12.key]));
911
- for (let i = 0; i < candidate.length; i += 1) {
912
- const pitch = candidate[i];
913
- const range = ranges[i];
914
- if (pitch === void 0 || range === void 0) {
915
- continue;
916
- }
917
- score += Math.abs(pitch - (range.min + range.max) / 2);
918
- }
919
- if (score < bestScore) {
920
- bestScore = score;
921
- best = candidate;
922
- }
923
- }
924
- if (best === void 0) {
925
- throw new (0, _chunk7MGZ7D62cjs.NoSolutionError)(
926
- `no voicing satisfies the given ranges for ${formatChordSymbol(chord)}`
927
- );
928
- }
929
- return best;
930
- }
931
- function noSolutionAt(index, chord) {
932
- return new (0, _chunk7MGZ7D62cjs.NoSolutionError)(
933
- `no voicing satisfies the given ranges for ${formatChordSymbol(chord)} at index ${index}`,
934
- { at: index }
935
- );
936
- }
937
- function locate(index, chord, work) {
938
- try {
939
- return work();
940
- } catch (error) {
941
- if (error instanceof _chunk7MGZ7D62cjs.NoSolutionError) {
942
- throw noSolutionAt(index, chord);
943
- }
944
- throw error;
945
- }
946
- }
947
- function voiceProgression(chords, opts) {
948
- _chunk7MGZ7D62cjs.assertGenerationBudget.call(void 0, chords.length, "voiced progression chords", _optionalChain([opts, 'optionalAccess', _13 => _13.budget]));
949
- const ranges = resolveRanges(opts);
950
- const maxSpacing = resolveMaxSpacing(opts);
951
- const result = [];
952
- let prev;
953
- let prevChord;
954
- for (let index = 0; index < chords.length; index += 1) {
955
- const chord = chords[index];
956
- if (chord === void 0) {
957
- continue;
958
- }
959
- if (prev === void 0) {
960
- prev = locate(index, chord, () => voiceChord(chord, opts));
961
- prevChord = chord;
962
- result.push(prev);
963
- continue;
964
- }
965
- const candidates = locate(index, chord, () => enumerateVoicings(chord, ranges, maxSpacing));
966
- let best;
967
- let bestScore = Number.POSITIVE_INFINITY;
968
- for (const candidate of candidates) {
969
- const unresolved = prevChord === void 0 ? 0 : resolutionViolations(prev, candidate, prevChord, chord, _optionalChain([opts, 'optionalAccess', _14 => _14.key]));
970
- const score = structuralPenalty(candidate, chord, _optionalChain([opts, 'optionalAccess', _15 => _15.key])) + voiceLeadingCost(prev, candidate) + VIOLATION_PENALTY * violationCount(prev, candidate) + RESOLUTION_PENALTY * unresolved;
971
- if (score < bestScore) {
972
- bestScore = score;
973
- best = candidate;
974
- }
975
- }
976
- if (best === void 0) {
977
- throw noSolutionAt(index, chord);
978
- }
979
- result.push(best);
980
- prev = best;
981
- prevChord = chord;
982
- }
983
- return result;
984
- }
985
-
986
- // src/theory/voicing/styled.ts
987
- var DEFAULT_STYLE_OCTAVE = 4;
988
- function dedupePcs(pcs) {
989
- const seen = /* @__PURE__ */ new Set();
990
- const result = [];
991
- for (const pc of pcs) {
992
- if (!seen.has(pc)) {
993
- seen.add(pc);
994
- result.push(pc);
995
- }
996
- }
997
- return result;
998
- }
999
- function lowestPitchAtOrAbove(pc, floor) {
1000
- return floor + ((pc - _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, floor)) % 12 + 12) % 12;
1001
- }
1002
- function nearestPc(target, pcs) {
1003
- let best = _nullishCoalesce(pcs[0], () => ( target));
1004
- let bestDist = 12;
1005
- for (const pc of pcs) {
1006
- const dist = Math.min(((pc - target) % 12 + 12) % 12, ((target - pc) % 12 + 12) % 12);
1007
- if (dist < bestDist) {
1008
- bestDist = dist;
1009
- best = pc;
1010
- }
1011
- }
1012
- return best;
1013
- }
1014
- function voiceChordStyled(chord, opts) {
1015
- const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _16 => _16.style]), () => ( "close"));
1016
- const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _17 => _17.octave]), () => ( DEFAULT_STYLE_OCTAVE));
1017
- _chunk7MGZ7D62cjs.assertInteger.call(void 0, octave, "styled voicing octave", -10, 10);
1018
- if (_optionalChain([opts, 'optionalAccess', _18 => _18.topNote]) !== void 0) {
1019
- _chunk7MGZ7D62cjs.assertFiniteNumber.call(void 0, opts.topNote, "styled voicing topNote");
1020
- }
1021
- const base = 12 * (octave + 1);
1022
- const rootPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc);
1023
- const bassPc = _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
1024
- const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess', _19 => _19.rootless]) === true;
1025
- let order = dedupePcs(chord.intervals.map((interval) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, chord.rootPc + interval)));
1026
- const bassIndex = order.indexOf(bassPc);
1027
- if (bassIndex > 0) {
1028
- order = [...order.slice(bassIndex), ...order.slice(0, bassIndex)];
1029
- } else if (bassIndex < 0) {
1030
- order = [bassPc, ...order];
1031
- }
1032
- if (style === "shell") {
1033
- const roles = order.map((pc) => _chunkFNDWXRRIcjs.chordToneRole.call(void 0, pc, chord));
1034
- const guide = roles.includes("seventh") ? "seventh" : roles.includes("sixth") ? "sixth" : "fifth";
1035
- order = order.filter((_pc, i) => {
1036
- const role = roles[i];
1037
- return _pc === bassPc || role === "root" || role === "third" || role === guide;
1038
- });
1039
- }
1040
- if (omitRoot) {
1041
- order = order.filter((pc) => pc !== rootPc || chord.bassPc !== void 0 && pc === bassPc);
1042
- }
1043
- if (order.length === 0) {
1044
- return [];
1045
- }
1046
- if (_optionalChain([opts, 'optionalAccess', _20 => _20.topNote]) !== void 0) {
1047
- const target = order.includes(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, opts.topNote)) ? _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, opts.topNote) : nearestPc(_chunk7MGZ7D62cjs.pitchClassOf.call(void 0, opts.topNote), order);
1048
- const targetIndex = order.indexOf(target);
1049
- order = [...order.slice(targetIndex + 1), ...order.slice(0, targetIndex + 1)];
1050
- }
1051
- const stack = [];
1052
- let prev;
1053
- for (const pc of order) {
1054
- if (prev === void 0) {
1055
- prev = lowestPitchAtOrAbove(pc, base);
1056
- } else {
1057
- let delta = ((pc - _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, prev)) % 12 + 12) % 12;
1058
- if (delta === 0) {
1059
- delta = 12;
1060
- }
1061
- prev += delta;
1062
- }
1063
- stack.push(prev);
1064
- }
1065
- if (style === "drop2" && stack.length >= 2) {
1066
- stack[stack.length - 2] = (_nullishCoalesce(stack[stack.length - 2], () => ( 0))) - 12;
1067
- } else if (style === "drop3" && stack.length >= 3) {
1068
- stack[stack.length - 3] = (_nullishCoalesce(stack[stack.length - 3], () => ( 0))) - 12;
1069
- }
1070
- if (chord.bassPc !== void 0) {
1071
- const bassIndexInStack = stack.findIndex((pitch) => _chunk7MGZ7D62cjs.pitchClassOf.call(void 0, pitch) === bassPc);
1072
- if (bassIndexInStack >= 0) {
1073
- const otherLowest = Math.min(
1074
- ...stack.filter((_pitch, index) => index !== bassIndexInStack),
1075
- Number.POSITIVE_INFINITY
1076
- );
1077
- while ((_nullishCoalesce(stack[bassIndexInStack], () => ( 0))) >= otherLowest) {
1078
- stack[bassIndexInStack] = (_nullishCoalesce(stack[bassIndexInStack], () => ( 0))) - 12;
1079
- }
1080
- }
1081
- }
1082
- const voicing = stack.sort((a, b) => a - b);
1083
- const lowest = _nullishCoalesce(voicing[0], () => ( 0));
1084
- const highest = _nullishCoalesce(voicing[voicing.length - 1], () => ( 0));
1085
- if (lowest < 0 || highest > 127) {
1086
- throw new (0, _chunk7MGZ7D62cjs.InvalidInputError)(
1087
- `styled voicing at octave ${octave} spans MIDI ${lowest}..${highest}, outside 0..127`
1088
- );
1089
- }
1090
- return voicing;
1091
- }
1092
-
1093
-
1094
-
1095
-
1096
-
1097
-
1098
-
1099
-
1100
-
1101
-
1102
-
1103
-
1104
-
1105
-
1106
-
1107
-
1108
-
1109
-
1110
-
1111
-
1112
-
1113
-
1114
-
1115
-
1116
- exports.scaleMatchesChord = scaleMatchesChord; exports.chordScales = chordScales; exports.avoidNotes = avoidNotes; exports.availableTensions = availableTensions; exports.chordScaleReport = chordScaleReport; exports.scalesForChanges = scalesForChanges; exports.spellPitchClass = spellPitchClass; exports.spellScale = spellScale; exports.spellPitchClasses = spellPitchClasses; exports.spellChordFromRoot = spellChordFromRoot; exports.spellChord = spellChord; exports.spellPitch = spellPitch; exports.noteNames = noteNames; exports.parseChordSymbol = parseChordSymbol; exports.formatChordSymbol = formatChordSymbol; exports.transposeChordSymbol = transposeChordSymbol; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled;
1117
- //# sourceMappingURL=chunk-LW53AHQU.cjs.map