@libraz/libcantus 1.0.1 → 1.1.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 (142) hide show
  1. package/README.md +27 -65
  2. package/README_ja.md +28 -66
  3. package/dist/analyze/index.cjs +11 -7
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +79 -144
  6. package/dist/analyze/index.d.ts +79 -144
  7. package/dist/analyze/index.js +20 -16
  8. package/dist/{chunk-TTCLJX6D.js → chunk-57WB2EDF.js} +10 -8
  9. package/dist/chunk-57WB2EDF.js.map +1 -0
  10. package/dist/chunk-5JJ6EHEZ.cjs +457 -0
  11. package/dist/chunk-5JJ6EHEZ.cjs.map +1 -0
  12. package/dist/{chunk-AFM2ULJH.cjs → chunk-5KW7DNCJ.cjs} +1567 -1067
  13. package/dist/chunk-5KW7DNCJ.cjs.map +1 -0
  14. package/dist/chunk-7GOKQJTQ.js +145 -0
  15. package/dist/chunk-7GOKQJTQ.js.map +1 -0
  16. package/dist/{chunk-H3VORETI.js → chunk-AWEMD747.js} +487 -592
  17. package/dist/chunk-AWEMD747.js.map +1 -0
  18. package/dist/{chunk-PKFNUHDY.js → chunk-DKICR7AN.js} +1216 -611
  19. package/dist/chunk-DKICR7AN.js.map +1 -0
  20. package/dist/{chunk-INHVXP6Z.js → chunk-FPBZFNCD.js} +1159 -415
  21. package/dist/chunk-FPBZFNCD.js.map +1 -0
  22. package/dist/{chunk-6XH6QZYQ.cjs → chunk-GDKE6RQW.cjs} +1453 -848
  23. package/dist/chunk-GDKE6RQW.cjs.map +1 -0
  24. package/dist/chunk-H66ARFU5.js +363 -0
  25. package/dist/chunk-H66ARFU5.js.map +1 -0
  26. package/dist/{chunk-YH3BIKHH.js → chunk-HOHFYGKW.js} +146 -49
  27. package/dist/chunk-HOHFYGKW.js.map +1 -0
  28. package/dist/chunk-HSY2VQXZ.js +2272 -0
  29. package/dist/chunk-HSY2VQXZ.js.map +1 -0
  30. package/dist/{chunk-E3F4AHAH.cjs → chunk-J772DL3B.cjs} +221 -134
  31. package/dist/chunk-J772DL3B.cjs.map +1 -0
  32. package/dist/chunk-K6OZXIR6.js +1 -0
  33. package/dist/{chunk-VTI6FOLB.js → chunk-PQL7E5F6.js} +709 -133
  34. package/dist/chunk-PQL7E5F6.js.map +1 -0
  35. package/dist/chunk-Q6R3QAZD.cjs +3329 -0
  36. package/dist/chunk-Q6R3QAZD.cjs.map +1 -0
  37. package/dist/{chunk-MXO72PLY.cjs → chunk-QCXYKSZS.cjs} +21 -19
  38. package/dist/chunk-QCXYKSZS.cjs.map +1 -0
  39. package/dist/{chunk-VOHZB45S.js → chunk-R5VUXQMP.js} +1321 -821
  40. package/dist/chunk-R5VUXQMP.js.map +1 -0
  41. package/dist/chunk-R7B5N2JK.cjs +363 -0
  42. package/dist/chunk-R7B5N2JK.cjs.map +1 -0
  43. package/dist/{chunk-WWURE4XT.cjs → chunk-RVS4G4MR.cjs} +624 -729
  44. package/dist/chunk-RVS4G4MR.cjs.map +1 -0
  45. package/dist/chunk-TT6PKMPR.cjs +145 -0
  46. package/dist/chunk-TT6PKMPR.cjs.map +1 -0
  47. package/dist/chunk-UGLFDG7L.cjs +1 -0
  48. package/dist/chunk-UGLFDG7L.cjs.map +1 -0
  49. package/dist/chunk-VLE5R7PF.js +3329 -0
  50. package/dist/chunk-VLE5R7PF.js.map +1 -0
  51. package/dist/{chunk-FVSI36UV.js → chunk-X6RJAHII.js} +197 -110
  52. package/dist/chunk-X6RJAHII.js.map +1 -0
  53. package/dist/{chunk-Q2WGKJNN.cjs → chunk-XKSO2V5I.cjs} +699 -123
  54. package/dist/chunk-XKSO2V5I.cjs.map +1 -0
  55. package/dist/chunk-XVMJO3QF.cjs +2272 -0
  56. package/dist/chunk-XVMJO3QF.cjs.map +1 -0
  57. package/dist/{chunk-UADYAYAT.cjs → chunk-YPB3A3EY.cjs} +1309 -565
  58. package/dist/chunk-YPB3A3EY.cjs.map +1 -0
  59. package/dist/{index-C_8o--VE.d.cts → coerce-CJlgULrQ.d.cts} +210 -199
  60. package/dist/{index-szBv3496.d.ts → coerce-wwdELNYS.d.ts} +210 -199
  61. package/dist/core/index.cjs +9 -5
  62. package/dist/core/index.cjs.map +1 -1
  63. package/dist/core/index.d.cts +122 -108
  64. package/dist/core/index.d.ts +122 -108
  65. package/dist/core/index.js +32 -28
  66. package/dist/generate/index.cjs +8 -8
  67. package/dist/generate/index.d.cts +168 -84
  68. package/dist/generate/index.d.ts +168 -84
  69. package/dist/generate/index.js +7 -7
  70. package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
  71. package/dist/{index-Dzr13q3d.d.cts → index-B3WCj_Hm.d.cts} +135 -58
  72. package/dist/{index-BO6xE9YU.d.cts → index-BUtIf2go.d.ts} +460 -254
  73. package/dist/{index-9wcMhG73.d.ts → index-BaoKVVhE.d.cts} +293 -56
  74. package/dist/{index-Chbst3Jx.d.ts → index-BpWOhkkE.d.ts} +135 -58
  75. package/dist/{index-CgHqxdps.d.ts → index-BzLNkmA7.d.cts} +196 -114
  76. package/dist/{index-CunYKLiu.d.ts → index-CBCP84rv.d.cts} +1 -1
  77. package/dist/{index-BkGmlyI3.d.cts → index-Ci3xyows.d.ts} +60 -23
  78. package/dist/{index-CCNJRG-P.d.cts → index-CnHQgqNh.d.ts} +293 -56
  79. package/dist/{index-B4o55NDr.d.cts → index-CnIdP4EZ.d.ts} +1 -1
  80. package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
  81. package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
  82. package/dist/{index-D57QRbVn.d.ts → index-DTsKaPaC.d.cts} +460 -254
  83. package/dist/{index-Cj49F892.d.ts → index-D_hzxD8w.d.cts} +60 -23
  84. package/dist/{index-BGFW-8P2.d.cts → index-DpC-mDlV.d.ts} +196 -114
  85. package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
  86. package/dist/index.cjs +42 -14
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.d.cts +14 -14
  89. package/dist/index.d.ts +14 -14
  90. package/dist/index.js +82 -54
  91. package/dist/{masks-DYbE485J.d.ts → masks-C8TWLFIi.d.cts} +49 -2
  92. package/dist/{masks-DYbE485J.d.cts → masks-O73Rhj5d.d.ts} +49 -2
  93. package/dist/model/index.cjs +19 -12
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +502 -181
  96. package/dist/model/index.d.ts +502 -181
  97. package/dist/model/index.js +18 -11
  98. package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
  99. package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
  100. package/dist/theory/index.cjs +35 -6
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +257 -62
  103. package/dist/theory/index.d.ts +257 -62
  104. package/dist/theory/index.js +41 -12
  105. package/package.json +6 -5
  106. package/dist/chunk-2LCPM3EG.js +0 -3224
  107. package/dist/chunk-2LCPM3EG.js.map +0 -1
  108. package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
  109. package/dist/chunk-6XPTBE7Y.cjs +0 -49
  110. package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
  111. package/dist/chunk-AFM2ULJH.cjs.map +0 -1
  112. package/dist/chunk-B6XNKWZV.cjs +0 -3224
  113. package/dist/chunk-B6XNKWZV.cjs.map +0 -1
  114. package/dist/chunk-E3F4AHAH.cjs.map +0 -1
  115. package/dist/chunk-ELCTZUJJ.js +0 -379
  116. package/dist/chunk-ELCTZUJJ.js.map +0 -1
  117. package/dist/chunk-FVSI36UV.js.map +0 -1
  118. package/dist/chunk-H3VORETI.js.map +0 -1
  119. package/dist/chunk-H42UHPYW.js +0 -1577
  120. package/dist/chunk-H42UHPYW.js.map +0 -1
  121. package/dist/chunk-HFPS7JFD.js +0 -1
  122. package/dist/chunk-INHVXP6Z.js.map +0 -1
  123. package/dist/chunk-K4ZJUGF4.cjs +0 -379
  124. package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
  125. package/dist/chunk-KNJEKLKV.js +0 -49
  126. package/dist/chunk-KNJEKLKV.js.map +0 -1
  127. package/dist/chunk-MXO72PLY.cjs.map +0 -1
  128. package/dist/chunk-NLEU4QSE.cjs +0 -360
  129. package/dist/chunk-NLEU4QSE.cjs.map +0 -1
  130. package/dist/chunk-OE6WK2Y2.cjs +0 -1577
  131. package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
  132. package/dist/chunk-PKFNUHDY.js.map +0 -1
  133. package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
  134. package/dist/chunk-RHIJ3S4I.cjs +0 -1
  135. package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
  136. package/dist/chunk-TTCLJX6D.js.map +0 -1
  137. package/dist/chunk-UADYAYAT.cjs.map +0 -1
  138. package/dist/chunk-VOHZB45S.js.map +0 -1
  139. package/dist/chunk-VTI6FOLB.js.map +0 -1
  140. package/dist/chunk-WWURE4XT.cjs.map +0 -1
  141. package/dist/chunk-YH3BIKHH.js.map +0 -1
  142. /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
@@ -26,7 +26,6 @@
26
26
 
27
27
 
28
28
 
29
- var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
30
29
 
31
30
 
32
31
 
@@ -34,6 +33,7 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
34
33
 
35
34
 
36
35
 
36
+ var _chunkYPB3A3EYcjs = require('./chunk-YPB3A3EY.cjs');
37
37
 
38
38
 
39
39
 
@@ -44,7 +44,18 @@ var _chunkUADYAYATcjs = require('./chunk-UADYAYAT.cjs');
44
44
 
45
45
 
46
46
 
47
- var _chunkQ2WGKJNNcjs = require('./chunk-Q2WGKJNN.cjs');
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+ var _chunkXKSO2V5Icjs = require('./chunk-XKSO2V5I.cjs');
48
59
 
49
60
  // src/theory/transposition/index.ts
50
61
  var TRANSPOSING_INSTRUMENTS = Object.freeze({
@@ -74,18 +85,22 @@ var TRANSPOSING_INSTRUMENTS = Object.freeze({
74
85
  contrabassoon: "-P8",
75
86
  guitar: "-P8"
76
87
  });
88
+ var PROFILE_NAME_ALIASES = Object.freeze({
89
+ "guitar (drop D)": "guitar"
90
+ });
77
91
  function instrumentTransposition(instrument) {
78
92
  if (typeof instrument !== "string") {
79
- return _chunkQ2WGKJNNcjs.toSpelledInterval.call(void 0, instrument);
93
+ return _chunkXKSO2V5Icjs.toSpelledInterval.call(void 0, instrument);
80
94
  }
81
- if (Object.hasOwn(TRANSPOSING_INSTRUMENTS, instrument)) {
82
- return _chunkQ2WGKJNNcjs.parseInterval.call(void 0, TRANSPOSING_INSTRUMENTS[instrument]);
95
+ const name = Object.hasOwn(PROFILE_NAME_ALIASES, instrument) ? PROFILE_NAME_ALIASES[instrument] : instrument;
96
+ if (Object.hasOwn(TRANSPOSING_INSTRUMENTS, name)) {
97
+ return _chunkXKSO2V5Icjs.parseInterval.call(void 0, TRANSPOSING_INSTRUMENTS[name]);
83
98
  }
84
99
  try {
85
- return _chunkQ2WGKJNNcjs.parseInterval.call(void 0, instrument);
100
+ return _chunkXKSO2V5Icjs.parseInterval.call(void 0, instrument);
86
101
  } catch (error) {
87
- if (error instanceof _chunkQ2WGKJNNcjs.InvalidInputError) {
88
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
102
+ if (error instanceof _chunkXKSO2V5Icjs.InvalidInputError) {
103
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
89
104
  `unknown transposing instrument ${JSON.stringify(instrument)}; pass a name from TRANSPOSING_INSTRUMENTS or an interval such as '-M2'`
90
105
  );
91
106
  }
@@ -93,332 +108,24 @@ function instrumentTransposition(instrument) {
93
108
  }
94
109
  }
95
110
  function reversedInterval(step) {
96
- const reversed = {
111
+ return {
97
112
  number: step.number,
98
113
  quality: step.quality,
99
114
  // Negating a zero span yields -0, which compares unequal to 0 under Object.is.
100
- semitones: step.semitones === 0 ? 0 : -step.semitones
115
+ semitones: step.semitones === 0 ? 0 : -step.semitones,
116
+ descending: !step.descending
101
117
  };
102
- if (!(_nullishCoalesce(step.descending, () => ( step.semitones < 0)))) {
103
- reversed.descending = true;
104
- }
105
- return reversed;
106
118
  }
107
119
  function toSoundingPitch(note, instrument) {
108
- return _chunkQ2WGKJNNcjs.transposeByInterval.call(void 0, _chunkQ2WGKJNNcjs.toNoteData.call(void 0, note), instrumentTransposition(instrument));
120
+ return _chunkXKSO2V5Icjs.transposeByInterval.call(void 0, _chunkXKSO2V5Icjs.toNoteData.call(void 0, note), instrumentTransposition(instrument));
109
121
  }
110
122
  function toWrittenPitch(note, instrument) {
111
- return _chunkQ2WGKJNNcjs.transposeByInterval.call(void 0,
112
- _chunkQ2WGKJNNcjs.toNoteData.call(void 0, note),
123
+ return _chunkXKSO2V5Icjs.transposeByInterval.call(void 0,
124
+ _chunkXKSO2V5Icjs.toNoteData.call(void 0, note),
113
125
  reversedInterval(instrumentTransposition(instrument))
114
126
  );
115
127
  }
116
128
 
117
- // src/theory/chordscale/index.ts
118
- function popcount12(mask) {
119
- let count = 0;
120
- for (let n = 0; n < 12; n += 1) {
121
- count += mask >> n & 1;
122
- }
123
- return count;
124
- }
125
- function maskHasPitchClass(mask, scaleRootPc, pc) {
126
- const offset = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - scaleRootPc);
127
- return (mask >> offset & 1) === 1;
128
- }
129
- function scaleMatchesChord(chordPcs, scaleMask, scaleRootPc) {
130
- for (const pc of chordPcs) {
131
- if (!maskHasPitchClass(scaleMask, scaleRootPc, pc)) {
132
- return false;
133
- }
134
- }
135
- return true;
136
- }
137
- var ALIASED_SCALE_NAMES = /* @__PURE__ */ new Set(["major", "naturalMinor"]);
138
- var HEPTATONIC_SIZE = 7;
139
- var SCALE_PREFERENCE = [
140
- "ionian",
141
- "major",
142
- "mixolydian",
143
- "dorian",
144
- "lydian",
145
- "aeolian",
146
- "naturalMinor",
147
- "phrygian",
148
- "locrian"
149
- ];
150
- var IDIOMATIC_SCALES = {
151
- aug: ["wholeTone"],
152
- m7b5: ["locrian"],
153
- // The diminished scale played over a diminished seventh is the whole-half
154
- // octatonic. Its half-whole rotation belongs to the dominant it colours: over
155
- // a diminished seventh every one of its non-chord tones lands a semitone
156
- // above a chord tone, so it offers no tension at all.
157
- dim7: ["octatonicWholeHalf"],
158
- minMaj7: ["melodicMinor"]
159
- };
160
- function conventionalRank(name) {
161
- const index = SCALE_PREFERENCE.indexOf(name);
162
- return index < 0 ? SCALE_PREFERENCE.length : index;
163
- }
164
- function idiomaticRank(chord, name) {
165
- const choices = IDIOMATIC_SCALES[chord.quality];
166
- if (choices === void 0) {
167
- return 0;
168
- }
169
- const index = choices.indexOf(name);
170
- return index < 0 ? choices.length + 1 : index;
171
- }
172
- function isAlteredDominant(chord, scaleName) {
173
- return chord.quality === "dom7" && scaleName === "altered";
174
- }
175
- function chordScales(chord) {
176
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
177
- const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
178
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
179
- const ranked = [];
180
- const seenMasks = /* @__PURE__ */ new Set();
181
- for (const name of Object.keys(_chunkUADYAYATcjs.NAMED_SCALES)) {
182
- if (name === "chromatic" || ALIASED_SCALE_NAMES.has(name)) {
183
- continue;
184
- }
185
- const mask = _chunkUADYAYATcjs.namedScaleMask.call(void 0, name);
186
- if (mask === void 0 || seenMasks.has(mask)) {
187
- continue;
188
- }
189
- seenMasks.add(mask);
190
- if (scaleMatchesChord(chordPcs, mask, rootPc) || isAlteredDominant(data, name)) {
191
- const size = popcount12(mask);
192
- ranked.push({
193
- name,
194
- extra: size - chordPcs.length,
195
- size,
196
- avoid: avoidNotes(data, name).length
197
- });
198
- }
199
- }
200
- if (ranked.length === 0) {
201
- if (_chunkUADYAYATcjs.NAMED_SCALES.chromatic !== void 0) {
202
- return [{ name: "chromatic", rootPc }];
203
- }
204
- return [];
205
- }
206
- const preferHeptatonic = chordPcs.length <= 3;
207
- ranked.sort((a, b) => {
208
- const idiomaticA = idiomaticRank(data, a.name);
209
- const idiomaticB = idiomaticRank(data, b.name);
210
- if (idiomaticA !== idiomaticB) {
211
- return idiomaticA - idiomaticB;
212
- }
213
- if (preferHeptatonic && a.size === HEPTATONIC_SIZE !== (b.size === HEPTATONIC_SIZE)) {
214
- return a.size === HEPTATONIC_SIZE ? -1 : 1;
215
- }
216
- if (a.extra !== b.extra) {
217
- return a.extra - b.extra;
218
- }
219
- const rankA = conventionalRank(a.name);
220
- const rankB = conventionalRank(b.name);
221
- if (rankA !== rankB) {
222
- return rankA - rankB;
223
- }
224
- if (a.avoid !== b.avoid) {
225
- return a.avoid - b.avoid;
226
- }
227
- return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
228
- });
229
- return ranked.map((entry) => ({ name: entry.name, rootPc }));
230
- }
231
- function suspendedTone(chordPcs, rootPc) {
232
- const sounds = (semitones) => chordPcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones));
233
- if (sounds(3) || sounds(4)) {
234
- return void 0;
235
- }
236
- if (sounds(5)) {
237
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 5);
238
- }
239
- if (sounds(2)) {
240
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 2);
241
- }
242
- return void 0;
243
- }
244
- function avoidNotes(chord, scaleName, opts = {}) {
245
- const mask = _chunkUADYAYATcjs.requireScaleMask.call(void 0, scaleName);
246
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
247
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
248
- const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
249
- const alteredDominant = isAlteredDominant(data, scaleName);
250
- if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
251
- return [];
252
- }
253
- const chordSet = new Set(chordPcs);
254
- const suspended = suspendedTone(chordPcs, rootPc);
255
- const displacedThird = suspended === void 0 ? void 0 : _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, suspended - 1);
256
- const melodic = opts.use === "melodic";
257
- const semitoneAboveRoot = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 1);
258
- const avoid = [];
259
- for (let pc = 0; pc < 12; pc += 1) {
260
- if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
261
- continue;
262
- }
263
- if (melodic && pc !== semitoneAboveRoot) {
264
- continue;
265
- }
266
- if (!alteredDominant && (chordSet.has(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - 1)) || pc === displacedThird)) {
267
- avoid.push(pc);
268
- }
269
- }
270
- return avoid.sort((a, b) => a - b);
271
- }
272
- var MINOR_RESOLUTION_TENSIONS = [1, 3, 8];
273
- function resolvesToMinorTonic(chord, chordPcs, target) {
274
- if (target === void 0) {
275
- return false;
276
- }
277
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc);
278
- const sounds = (semitones) => chordPcs.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones));
279
- if (!sounds(4) || !sounds(10)) {
280
- return false;
281
- }
282
- const targetRootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, target.rootPc);
283
- if (targetRootPc !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + 5)) {
284
- return false;
285
- }
286
- return _chunkUADYAYATcjs.chordPitchClasses.call(void 0, target).includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, targetRootPc + 3));
287
- }
288
- function availableTensions(chord, scaleName, opts = {}) {
289
- const mask = _chunkUADYAYATcjs.requireScaleMask.call(void 0, scaleName);
290
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
291
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, data.rootPc);
292
- const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, data);
293
- const alteredDominant = isAlteredDominant(data, scaleName);
294
- if (!scaleMatchesChord(chordPcs, mask, rootPc) && !alteredDominant) {
295
- return [];
296
- }
297
- const chordSet = new Set(chordPcs);
298
- const avoidSet = alteredDominant ? /* @__PURE__ */ new Set() : new Set(avoidNotes(data, scaleName));
299
- const functional = resolvesToMinorTonic(
300
- data,
301
- chordPcs,
302
- opts.resolvesTo === void 0 ? void 0 : _chunkUADYAYATcjs.toChordData.call(void 0, opts.resolvesTo)
303
- ) ? new Set(MINOR_RESOLUTION_TENSIONS.map((semitones) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + semitones))) : void 0;
304
- const tensions = [];
305
- for (let pc = 0; pc < 12; pc += 1) {
306
- if (!maskHasPitchClass(mask, rootPc, pc) || chordSet.has(pc)) {
307
- continue;
308
- }
309
- if (avoidSet.has(pc) && _optionalChain([functional, 'optionalAccess', _2 => _2.has, 'call', _3 => _3(pc)]) !== true) {
310
- continue;
311
- }
312
- tensions.push(pc);
313
- }
314
- return tensions.sort((a, b) => a - b);
315
- }
316
- function chordScaleReport(chord, limit) {
317
- if (limit !== void 0) {
318
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, limit, "chord-scale report limit");
319
- }
320
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
321
- const matches = chordScales(data);
322
- const chosen = limit === void 0 ? matches : matches.slice(0, limit);
323
- return chosen.map((match) => {
324
- const harmonic = avoidNotes(data, match.name);
325
- const melodic = new Set(avoidNotes(data, match.name, { use: "melodic" }));
326
- return {
327
- name: match.name,
328
- rootPc: match.rootPc,
329
- avoid: harmonic.filter((pc) => melodic.has(pc)),
330
- passing: harmonic.filter((pc) => !melodic.has(pc)),
331
- tensions: availableTensions(data, match.name)
332
- };
333
- });
334
- }
335
- var RANK_PENALTY = 0.01;
336
- function scalePitchClassSet(name, rootPc) {
337
- const mask = _chunkUADYAYATcjs.namedScaleMask.call(void 0, name);
338
- const pcs = /* @__PURE__ */ new Set();
339
- if (mask === void 0) {
340
- return pcs;
341
- }
342
- for (let n = 0; n < 12; n += 1) {
343
- if ((mask >> n & 1) === 1) {
344
- pcs.add(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, rootPc + n));
345
- }
346
- }
347
- return pcs;
348
- }
349
- function symmetricDifferenceSize(a, b) {
350
- let count = 0;
351
- for (const pc of a) {
352
- if (!b.has(pc)) {
353
- count += 1;
354
- }
355
- }
356
- for (const pc of b) {
357
- if (!a.has(pc)) {
358
- count += 1;
359
- }
360
- }
361
- return count;
362
- }
363
- function scalesForChanges(chords) {
364
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, chords.length, "chord-scale changes");
365
- if (chords.length === 0) {
366
- return [];
367
- }
368
- const candidateLists = chords.map((chord) => chordScales(chord));
369
- const pcSets = candidateLists.map(
370
- (candidates) => candidates.map((match) => scalePitchClassSet(match.name, match.rootPc))
371
- );
372
- const first = _nullishCoalesce(candidateLists[0], () => ( []));
373
- const dp = [first.map((_, j) => j * RANK_PENALTY)];
374
- const back = [first.map(() => -1)];
375
- for (let i = 1; i < candidateLists.length; i += 1) {
376
- const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
377
- const prevCosts = _nullishCoalesce(dp[i - 1], () => ( []));
378
- const prevSets = _nullishCoalesce(pcSets[i - 1], () => ( []));
379
- const curSets = _nullishCoalesce(pcSets[i], () => ( []));
380
- const stageCosts = [];
381
- const stageBack = [];
382
- for (let j = 0; j < candidates.length; j += 1) {
383
- let bestCost = Number.POSITIVE_INFINITY;
384
- let bestPrev = -1;
385
- for (let k = 0; k < prevCosts.length; k += 1) {
386
- const transition = symmetricDifferenceSize(
387
- _nullishCoalesce(prevSets[k], () => ( /* @__PURE__ */ new Set())),
388
- _nullishCoalesce(curSets[j], () => ( /* @__PURE__ */ new Set()))
389
- );
390
- const cost = (_nullishCoalesce(prevCosts[k], () => ( 0))) + transition;
391
- if (cost < bestCost) {
392
- bestCost = cost;
393
- bestPrev = k;
394
- }
395
- }
396
- stageCosts.push(bestCost + j * RANK_PENALTY);
397
- stageBack.push(bestPrev);
398
- }
399
- dp.push(stageCosts);
400
- back.push(stageBack);
401
- }
402
- const lastCosts = _nullishCoalesce(dp[dp.length - 1], () => ( []));
403
- let bestFinal = 0;
404
- for (let j = 1; j < lastCosts.length; j += 1) {
405
- if ((_nullishCoalesce(lastCosts[j], () => ( Number.POSITIVE_INFINITY))) < (_nullishCoalesce(lastCosts[bestFinal], () => ( 0)))) {
406
- bestFinal = j;
407
- }
408
- }
409
- const chosenIndices = new Array(candidateLists.length);
410
- let current = bestFinal;
411
- for (let i = candidateLists.length - 1; i >= 0; i -= 1) {
412
- chosenIndices[i] = current;
413
- current = _nullishCoalesce(_optionalChain([back, 'access', _4 => _4[i], 'optionalAccess', _5 => _5[current]]), () => ( -1));
414
- }
415
- return chords.map((chord, i) => {
416
- const candidates = _nullishCoalesce(candidateLists[i], () => ( []));
417
- const scale = _nullishCoalesce(candidates[_nullishCoalesce(chosenIndices[i], () => ( 0))], () => ( { name: "chromatic", rootPc: chord.rootPc }));
418
- return { chord, scale };
419
- });
420
- }
421
-
422
129
  // src/theory/figured-bass/index.ts
423
130
  var MIN_FIGURE_NUMBER = 2;
424
131
  var MAX_FIGURE_NUMBER = 9;
@@ -433,7 +140,7 @@ var DOUBLE_ACCIDENTALS = {
433
140
  var SINGLE_ACCIDENTALS = {
434
141
  "#": "sharp",
435
142
  "\u266F": "sharp",
436
- "+": "sharp",
143
+ "+": "raised",
437
144
  b: "flat",
438
145
  "\u266D": "flat",
439
146
  n: "natural",
@@ -441,31 +148,28 @@ var SINGLE_ACCIDENTALS = {
441
148
  };
442
149
  var FIGURE_SEPARATORS = /* @__PURE__ */ new Set([" ", " ", ",", "/"]);
443
150
  var MOTION_MARKS = /* @__PURE__ */ new Set(["-", "\u2013"]);
444
- var ACCIDENTAL_GLYPHS = {
445
- 1: "#",
446
- 2: "##",
447
- "-1": "b",
448
- "-2": "bb"
449
- };
151
+ var PRINTED_ACCIDENTALS = [
152
+ ["natural", "n", 0],
153
+ ["sharp", "#", 1],
154
+ ["doubleSharp", "##", 2],
155
+ ["flat", "b", -1],
156
+ ["doubleFlat", "bb", -2]
157
+ ];
158
+ var PRINTED_ALTERS = new Map(
159
+ PRINTED_ACCIDENTALS.map(([accidental, , alter]) => [accidental, alter])
160
+ );
161
+ var ACCIDENTAL_GLYPHS = new Map(
162
+ PRINTED_ACCIDENTALS.map(([, glyph, alter]) => [alter, glyph])
163
+ );
450
164
  function invalidFigures(figures, detail) {
451
- return new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`figured bass ${JSON.stringify(figures)} ${detail}`);
165
+ return new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, figures)} ${detail}`);
452
166
  }
453
167
  function alteredBy(diatonicAlter, accidental) {
454
- switch (accidental) {
455
- case "sharp":
456
- return diatonicAlter + 1;
457
- case "doubleSharp":
458
- return diatonicAlter + 2;
459
- case "flat":
460
- return diatonicAlter - 1;
461
- case "doubleFlat":
462
- return diatonicAlter - 2;
463
- // A natural sign names the natural note, whatever the key would have given.
464
- case "natural":
465
- return 0;
466
- default:
467
- return diatonicAlter;
168
+ const printed = PRINTED_ALTERS.get(accidental);
169
+ if (printed !== void 0) {
170
+ return printed;
468
171
  }
172
+ return accidental === "raised" ? diatonicAlter + 1 : diatonicAlter;
469
173
  }
470
174
  function readFigure(text, start) {
471
175
  let index = start;
@@ -564,8 +268,8 @@ var FIGURE_ABBREVIATIONS = /* @__PURE__ */ new Map([
564
268
  ]);
565
269
  function tertianQualityIndex() {
566
270
  const index = /* @__PURE__ */ new Map();
567
- for (const quality of _chunkUADYAYATcjs.chordQualities.call(void 0, )) {
568
- const { intervals } = _chunkUADYAYATcjs.makeChord.call(void 0, 0, quality);
271
+ for (const quality of _chunkYPB3A3EYcjs.chordQualities.call(void 0, )) {
272
+ const { intervals } = _chunkYPB3A3EYcjs.makeChord.call(void 0, 0, quality);
569
273
  const template = intervals.join(",");
570
274
  if (intervals.length >= 3 && intervals.length <= MAX_STACK_SIZE && !index.has(template)) {
571
275
  index.set(template, quality);
@@ -575,22 +279,22 @@ function tertianQualityIndex() {
575
279
  }
576
280
  var TERTIAN_QUALITIES = tertianQualityIndex();
577
281
  function keyLetterAlters(key) {
578
- const size = _chunkUADYAYATcjs.scaleTonesInDegreeOrder.call(void 0, key).length;
282
+ const size = _chunkYPB3A3EYcjs.scaleTonesInDegreeOrder.call(void 0, key.scale).length;
579
283
  if (size !== 7) {
580
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`figured bass requires a heptatonic key (received ${size} tones)`);
284
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass requires a heptatonic key (received ${size} tones)`);
581
285
  }
582
286
  const alters = [];
583
- for (const note of _chunkUADYAYATcjs.spellScale.call(void 0, _chunkUADYAYATcjs.spelledKeyOf.call(void 0, key).tonic, key)) {
584
- alters[_chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, note.letter)] = note.alter;
287
+ for (const note of _chunkYPB3A3EYcjs.spellScale.call(void 0, key.tonic, key.scale)) {
288
+ alters[_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, note.letter)] = note.alter;
585
289
  }
586
290
  return alters;
587
291
  }
588
292
  function noteAbove(bass, figure, alters) {
589
- const letter = _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, bass.letter + figure.number - 1);
293
+ const letter = _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, bass.letter + figure.number - 1);
590
294
  return { letter, alter: alteredBy(_nullishCoalesce(alters[letter], () => ( 0)), figure.accidental) };
591
295
  }
592
296
  function bareNote(note) {
593
- return { letter: _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, note.letter), alter: note.alter };
297
+ return { letter: _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, note.letter), alter: note.alter };
594
298
  }
595
299
  function realizeSonority(bass, resolved, text, alters) {
596
300
  const numbers = [...new Set(resolved.map((figure) => figure.number))].sort((a, b) => a - b);
@@ -604,36 +308,35 @@ function realizeSonority(bass, resolved, text, alters) {
604
308
  );
605
309
  const tones = [bass, ...notes];
606
310
  const root = _nullishCoalesce(tones[[1, ...sonority.intervals].indexOf(sonority.rootInterval)], () => ( bass));
607
- const rootPc = _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, root);
311
+ const rootPc = _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, root);
608
312
  const stack = [];
609
313
  for (let step = 0; step < tones.length; step += 1) {
610
- const tone = tones.find((candidate) => _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, candidate.letter - root.letter) === step * 2);
314
+ const tone = tones.find((candidate) => _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, candidate.letter - root.letter) === step * 2);
611
315
  if (tone === void 0) {
612
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
613
- `figured bass ${JSON.stringify(text)} sounds ${_chunkUADYAYATcjs.noteNames.call(void 0, tones).join(" ")}, which does not stack in thirds`
316
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
317
+ `figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, text)} sounds ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")}, which does not stack in thirds`
614
318
  );
615
319
  }
616
- stack.push(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, tone) - rootPc));
320
+ stack.push(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, tone) - rootPc));
617
321
  }
618
322
  const quality = TERTIAN_QUALITIES.get(stack.join(","));
619
323
  if (quality === void 0) {
620
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
621
- `figured bass ${JSON.stringify(text)} sounds ${_chunkUADYAYATcjs.noteNames.call(void 0, tones).join(" ")}, which no chord quality names`
324
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
325
+ `figured bass ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, text)} sounds ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")}, which no chord quality names`
622
326
  );
623
327
  }
624
- const chord = _chunkUADYAYATcjs.makeChord.call(void 0, rootPc, quality, _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, bass));
328
+ const chord = _chunkYPB3A3EYcjs.makeChord.call(void 0, rootPc, quality, _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bass));
625
329
  chord.rootSpelling = { letter: root.letter, alter: root.alter };
626
330
  chord.bassSpelling = { letter: bass.letter, alter: bass.alter };
627
331
  return { chord, notes };
628
332
  }
629
333
  function figuredBassRealization(bass, figures, key) {
630
334
  if (typeof figures !== "string") {
631
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`figured bass must be a string; received ${typeof figures}`);
335
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`figured bass must be a string; received ${typeof figures}`);
632
336
  }
633
- const bassData = _chunkQ2WGKJNNcjs.toNoteData.call(void 0, bass);
634
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
635
- const alters = keyLetterAlters(scale);
636
- _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, bassData);
337
+ const bassData = _chunkXKSO2V5Icjs.toNoteData.call(void 0, bass);
338
+ const alters = keyLetterAlters(_chunkYPB3A3EYcjs.resolveKey.call(void 0, key));
339
+ _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bassData);
637
340
  const bassNote = bareNote(bassData);
638
341
  const text = figures.trim();
639
342
  const { figures: written, motions } = parseFigures(text);
@@ -654,90 +357,170 @@ function realizeFiguredBass(bass, figures, key) {
654
357
  return figuredBassRealization(bass, figures, key).chord;
655
358
  }
656
359
  function figuredBassOf(chord, key) {
657
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
658
- const scale = _chunkUADYAYATcjs.toKeyScale.call(void 0, key);
659
- const alters = keyLetterAlters(scale);
660
- const tones = _chunkUADYAYATcjs.spellChord.call(void 0, data, _chunkUADYAYATcjs.spelledKeyOf.call(void 0, scale).tonic, scale);
661
- const bassPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, _nullishCoalesce(data.bassPc, () => ( data.rootPc)));
662
- const bass = tones.find((tone) => _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, tone) === bassPc);
360
+ const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
361
+ const resolved = _chunkYPB3A3EYcjs.resolveKey.call(void 0, key);
362
+ const alters = keyLetterAlters(resolved);
363
+ const tones = _chunkYPB3A3EYcjs.spellChord.call(void 0, data, resolved.tonic, resolved.scale);
364
+ const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(data.bassPc, () => ( data.rootPc)));
365
+ const bass = tones.find((tone) => _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, tone) === bassPc);
663
366
  if (bass === void 0) {
664
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
367
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
665
368
  "figured bass measures intervals above a chord tone, and this chord's bass is not one of its tones"
666
369
  );
667
370
  }
668
371
  const written = /* @__PURE__ */ new Map();
669
372
  for (const tone of tones) {
670
373
  if (tone !== bass) {
671
- written.set(_chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, tone.letter - bass.letter) + 1, tone);
374
+ written.set(_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, tone.letter - bass.letter) + 1, tone);
672
375
  }
673
376
  }
674
377
  const digits = FIGURE_ABBREVIATIONS.get([...written.keys()].sort((a, b) => a - b).join(","));
675
378
  if (digits === void 0) {
676
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
677
- `no figured bass names ${_chunkUADYAYATcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkUADYAYATcjs.noteNames.call(void 0, [bass])[0]}`
379
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
380
+ `no figured bass names ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkYPB3A3EYcjs.noteNames.call(void 0, [bass])[0]}`
678
381
  );
679
382
  }
680
383
  const accidentals = /* @__PURE__ */ new Map();
681
384
  for (const [number, tone] of written) {
682
- const alteration = tone.alter - (_nullishCoalesce(alters[_chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, tone.letter)], () => ( 0)));
683
- if (alteration === 0) {
385
+ if (tone.alter === (_nullishCoalesce(alters[_chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, tone.letter)], () => ( 0)))) {
684
386
  continue;
685
387
  }
686
- const glyph = ACCIDENTAL_GLYPHS[alteration];
388
+ const glyph = ACCIDENTAL_GLYPHS.get(tone.alter);
687
389
  if (glyph === void 0) {
688
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
689
- `no figure names an interval altered by ${alteration} semitones from the key`
690
- );
390
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(`no accidental writes a note altered by ${tone.alter} semitones`);
691
391
  }
692
392
  accidentals.set(number, glyph);
693
393
  }
694
394
  const figures = [.../* @__PURE__ */ new Set([...digits, ...accidentals.keys()])].sort((a, b) => b - a).map((number) => `${_nullishCoalesce(accidentals.get(number), () => ( ""))}${number}`).join("");
695
- assertRealizesAs(bass, figures, scale, tones);
395
+ assertRealizesAs(bass, figures, resolved, tones);
696
396
  return figures;
697
397
  }
698
398
  function pitchClassesOf(notes) {
699
- return [...new Set(notes.map(_chunkQ2WGKJNNcjs.noteToPitchClass))].sort((a, b) => a - b);
399
+ return [...new Set(notes.map(_chunkXKSO2V5Icjs.noteToPitchClass))].sort((a, b) => a - b);
700
400
  }
701
401
  function assertRealizesAs(bass, figures, key, tones) {
702
- const noFigures = (detail) => new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
703
- `no figured bass names ${_chunkUADYAYATcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkUADYAYATcjs.noteNames.call(void 0, [bass])[0]}: the figures ${JSON.stringify(figures)} ${detail}`
402
+ const noFigures = (detail) => new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
403
+ `no figured bass names ${_chunkYPB3A3EYcjs.noteNames.call(void 0, tones).join(" ")} over ${_chunkYPB3A3EYcjs.noteNames.call(void 0, [bass])[0]}: the figures ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, figures)} ${detail}`
704
404
  );
705
405
  let realized;
706
406
  try {
707
407
  realized = figuredBassRealization(bass, figures, key);
708
408
  } catch (error) {
709
- throw error instanceof _chunkQ2WGKJNNcjs.NoSolutionError ? noFigures("read back as no chord at all") : error;
409
+ throw error instanceof _chunkXKSO2V5Icjs.NoSolutionError ? noFigures("read back as no chord at all") : error;
710
410
  }
711
411
  const written = pitchClassesOf(realized.notes);
712
412
  const expected = pitchClassesOf(tones);
713
413
  if (written.length !== expected.length || written.some((pc, index) => pc !== expected[index])) {
714
- throw noFigures(`sound ${_chunkUADYAYATcjs.noteNames.call(void 0, realized.notes).join(" ")} instead`);
414
+ throw noFigures(`sound ${_chunkYPB3A3EYcjs.noteNames.call(void 0, realized.notes).join(" ")} instead`);
715
415
  }
716
416
  }
717
417
 
718
- // src/theory/voicing/tendency.ts
719
- function leadingTonePcOf(key) {
720
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc - 1);
418
+ // src/theory/partwriting/cross-relation.ts
419
+ function resolvesAsAppliedDominant(chord, next, key) {
420
+ if (next === void 0 || !_chunkYPB3A3EYcjs.hasDominantSonority.call(void 0, chord)) {
421
+ return false;
422
+ }
423
+ const target = _chunkYPB3A3EYcjs.appliedDominantTarget.call(void 0, chord.rootPc + 5, key);
424
+ return target !== null && _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, next.rootPc) === target.rootPc;
721
425
  }
722
- var LEADING_TONE_QUALITIES = /* @__PURE__ */ new Set(["dim", "dim7", "m7b5"]);
723
- function isFunctioningLeadingTone(chord, key) {
724
- const role = _chunkUADYAYATcjs.chordToneRole.call(void 0, leadingTonePcOf(key), chord);
725
- if (role === "third") {
726
- return _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc) === _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc + 7);
426
+ function soundsAugmentedSixth(moment, key) {
427
+ const bass = moment.notes[0];
428
+ if (bass === void 0 || _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, bass) !== _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc + 8)) {
429
+ return false;
727
430
  }
728
- if (role === "root") {
729
- return LEADING_TONE_QUALITIES.has(chord.quality);
431
+ return moment.notes.some((note, voice) => {
432
+ if (voice === 0) {
433
+ return false;
434
+ }
435
+ const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, bass, note);
436
+ return (interval.number - 1) % 7 + 1 === 6 && _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, interval.semitones) === 10;
437
+ });
438
+ }
439
+ function isChromaticHarmony(moment, next, key) {
440
+ return resolvesAsAppliedDominant(moment.chord, next, key) || _chunkYPB3A3EYcjs.isNeapolitanChordOf.call(void 0, moment.chord, key) || soundsAugmentedSixth(moment, key);
441
+ }
442
+ function approachedByStep(before, moment, voice) {
443
+ const earlier = _optionalChain([before, 'optionalAccess', _2 => _2[voice]]);
444
+ const later = moment[voice];
445
+ if (earlier === void 0 || later === void 0) {
446
+ return false;
730
447
  }
731
- return false;
448
+ return _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later).number === 2;
732
449
  }
733
- function seventhPcOf(chord) {
734
- for (const interval of chord.intervals) {
735
- const pc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + interval);
736
- if (_chunkUADYAYATcjs.chordToneRole.call(void 0, pc, chord) === "seventh") {
737
- return pc;
450
+ function isOuterVoice(voice, notes) {
451
+ return voice === 0 || voice === notes.length - 1;
452
+ }
453
+ function crossRelations(from, to, key, context = {}) {
454
+ const found = [];
455
+ const chromatic = isChromaticHarmony(from, to.chord, key) || isChromaticHarmony(to, context.after, key);
456
+ if (chromatic) {
457
+ return found;
458
+ }
459
+ const reported = /* @__PURE__ */ new Set();
460
+ for (let first = 0; first < from.notes.length; first += 1) {
461
+ for (let second = 0; second < to.notes.length; second += 1) {
462
+ if (first === second) {
463
+ continue;
464
+ }
465
+ const earlier = from.notes[first];
466
+ const later = to.notes[second];
467
+ if (earlier === void 0 || later === void 0) {
468
+ continue;
469
+ }
470
+ const letter = _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, earlier.letter);
471
+ if (letter !== _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, later.letter) || earlier.alter === later.alter) {
472
+ continue;
473
+ }
474
+ if (approachedByStep(context.before, from.notes, first) || approachedByStep(from.notes, to.notes, second) || !isOuterVoice(first, from.notes) && !isOuterVoice(second, to.notes)) {
475
+ continue;
476
+ }
477
+ const pair = `${letter}:${Math.min(first, second)}:${Math.max(first, second)}`;
478
+ if (reported.has(pair)) {
479
+ continue;
480
+ }
481
+ reported.add(pair);
482
+ found.push({ earlierVoice: first, laterVoice: second, earlier, later });
738
483
  }
739
484
  }
740
- return void 0;
485
+ return found;
486
+ }
487
+
488
+ // src/theory/voicing/tendency.ts
489
+ function spellingTable(keyLike, chord) {
490
+ const { tonic, scale: key } = _chunkYPB3A3EYcjs.resolveKey.call(void 0, keyLike);
491
+ const notes = [];
492
+ const bases = [];
493
+ for (let pc = 0; pc < 12; pc += 1) {
494
+ const note = chord === void 0 ? _chunkYPB3A3EYcjs.spellPitchClass.call(void 0, pc, tonic, key) : _chunkYPB3A3EYcjs.spellPitchClass.call(void 0, pc, tonic, key, { chord });
495
+ notes.push(note);
496
+ bases.push(_chunkXKSO2V5Icjs.noteToMidi.call(void 0, { letter: note.letter, alter: note.alter, octave: 0 }));
497
+ }
498
+ return { notes, bases };
499
+ }
500
+ function spelledAt(table, pitch) {
501
+ const pc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch);
502
+ const note = table.notes[pc];
503
+ const base = table.bases[pc];
504
+ if (note === void 0 || base === void 0) {
505
+ return void 0;
506
+ }
507
+ return { letter: note.letter, alter: note.alter, octave: (pitch - base) / 12 };
508
+ }
509
+ function movesByAugmentedInterval(from, fromPitch, to, toPitch) {
510
+ const earlier = spelledAt(from, fromPitch);
511
+ const later = spelledAt(to, toPitch);
512
+ if (earlier === void 0 || later === void 0) {
513
+ return false;
514
+ }
515
+ return _chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, earlier, later);
516
+ }
517
+ function movesByDescendingStep(from, fromPitch, to, toPitch) {
518
+ const earlier = spelledAt(from, fromPitch);
519
+ const later = spelledAt(to, toPitch);
520
+ if (earlier === void 0 || later === void 0) {
521
+ return false;
522
+ }
523
+ return _chunkYPB3A3EYcjs.isDescendingStep.call(void 0, _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later));
741
524
  }
742
525
 
743
526
  // src/theory/voicing/internal.ts
@@ -750,8 +533,11 @@ var UNISON_PENALTY = 100;
750
533
  var LEADING_TONE_DOUBLING_PENALTY = 200;
751
534
  var RESOLUTION_PENALTY = 200;
752
535
  var DEFAULT_MAX_CANDIDATES = 4e3;
536
+ var MAX_LOOKAHEAD_PAIRS = 1e6;
753
537
  var SEARCH_NODES_PER_CANDIDATE = 16;
754
- var HIDDEN_PERFECT_PENALTY = 6;
538
+ var HIDDEN_PERFECT_PENALTY = 100;
539
+ var CROSS_RELATION_PENALTY = HIDDEN_PERFECT_PENALTY;
540
+ var AUGMENTED_MELODIC_PENALTY = VIOLATION_PENALTY;
755
541
  var INITIAL_CANDIDATE_CAPACITY = 512;
756
542
  function createCandidateBuffer() {
757
543
  return { pitches: new Int32Array(0), count: 0, voices: 0 };
@@ -759,7 +545,7 @@ function createCandidateBuffer() {
759
545
  function pitchesForPc(pc, range) {
760
546
  const result = [];
761
547
  for (let midi = Math.ceil(range.min); midi <= range.max; midi += 1) {
762
- if (_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, midi) === pc) {
548
+ if (_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, midi) === pc) {
763
549
  result.push(midi);
764
550
  }
765
551
  }
@@ -771,14 +557,14 @@ function pitchesForPc(pc, range) {
771
557
  });
772
558
  }
773
559
  function enumerateVoicings(chord, ranges, maxSpacing, maxCandidates = DEFAULT_MAX_CANDIDATES, into = createCandidateBuffer()) {
774
- const chordPcs = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, chord);
775
- const bassPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
560
+ const chordPcs = _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, chord);
561
+ const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
776
562
  const byVoice = ranges.map(
777
563
  (range, voice) => (voice === 0 ? [bassPc] : chordPcs).flatMap((pc) => pitchesForPc(pc, range))
778
564
  );
779
565
  for (let voice = 0; voice < byVoice.length; voice += 1) {
780
566
  if ((_nullishCoalesce(byVoice[voice], () => ( []))).length === 0) {
781
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)("no voicing satisfies the given ranges");
567
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)("no voicing satisfies the given ranges");
782
568
  }
783
569
  }
784
570
  const voices = ranges.length;
@@ -827,17 +613,17 @@ function enumerateVoicings(chord, ranges, maxSpacing, maxCandidates = DEFAULT_MA
827
613
  return into;
828
614
  }
829
615
  function structuralTables(chord, key) {
830
- const tones = _chunkUADYAYATcjs.chordPitchClasses.call(void 0, chord);
616
+ const tones = _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, chord);
831
617
  const missing = tones.map(
832
- (pc) => _chunkUADYAYATcjs.chordToneRole.call(void 0, pc, chord) === "fifth" ? MISSING_FIFTH_PENALTY : MISSING_TONE_PENALTY
618
+ (pc) => _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord) === "fifth" ? MISSING_FIFTH_PENALTY : MISSING_TONE_PENALTY
833
619
  );
834
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc);
835
- const leadingTonePc = key !== void 0 && isFunctioningLeadingTone(chord, key) ? leadingTonePcOf(key) : -1;
620
+ const rootPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc);
621
+ const leadingTonePc = key !== void 0 && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, chord, key) ? _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key) : -1;
836
622
  const doubling = new Float64Array(12);
837
623
  for (let pc = 0; pc < 12; pc += 1) {
838
624
  if (pc === leadingTonePc) {
839
625
  doubling[pc] = LEADING_TONE_DOUBLING_PENALTY;
840
- } else if (pc !== rootPc && _chunkUADYAYATcjs.chordToneRole.call(void 0, pc, chord) !== "fifth") {
626
+ } else if (pc !== rootPc && _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord) !== "fifth") {
841
627
  doubling[pc] = POOR_DOUBLING_PENALTY;
842
628
  }
843
629
  }
@@ -852,7 +638,7 @@ function structuralPenalty(tables, pitches, offset, voices) {
852
638
  if (pitch === void 0) {
853
639
  continue;
854
640
  }
855
- const pc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pitch);
641
+ const pc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch);
856
642
  PITCH_CLASS_COUNTS[pc] = (_nullishCoalesce(PITCH_CLASS_COUNTS[pc], () => ( 0))) + 1;
857
643
  if (index > 0 && pitch === pitches[offset + index - 1]) {
858
644
  penalty += UNISON_PENALTY;
@@ -874,18 +660,19 @@ function structuralPenalty(tables, pitches, offset, voices) {
874
660
  }
875
661
  function resolutionTables(prevChord, nextChord, key) {
876
662
  const nextHas = new Uint8Array(12);
877
- for (const pc of _chunkUADYAYATcjs.chordPitchClasses.call(void 0, nextChord)) {
663
+ for (const pc of _chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, nextChord)) {
878
664
  nextHas[pc] = 1;
879
665
  }
880
666
  return {
881
- seventhPc: _nullishCoalesce(seventhPcOf(prevChord), () => ( -1)),
882
- leadingTonePc: key !== void 0 && isFunctioningLeadingTone(prevChord, key) ? leadingTonePcOf(key) : -1,
883
- tonicPc: key === void 0 ? -1 : _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc),
884
- nextHas
667
+ seventhPc: _nullishCoalesce(_chunkYPB3A3EYcjs.seventhPcOf.call(void 0, prevChord), () => ( -1)),
668
+ leadingTonePc: key !== void 0 && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, prevChord, key) ? _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key) : -1,
669
+ tonicPc: key === void 0 ? -1 : _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc),
670
+ nextHas,
671
+ nextChord
885
672
  };
886
673
  }
887
- function resolutionViolations(tables, prev, prevOffset, cur, curOffset, voices) {
888
- const { seventhPc, leadingTonePc, tonicPc, nextHas } = tables;
674
+ function resolutionViolations(tables, spelling, prev, prevOffset, cur, curOffset, voices) {
675
+ const { seventhPc, leadingTonePc, tonicPc, nextHas, nextChord } = tables;
889
676
  let count = 0;
890
677
  for (let voice = 0; voice < voices; voice += 1) {
891
678
  const from = prev[prevOffset + voice];
@@ -893,12 +680,14 @@ function resolutionViolations(tables, prev, prevOffset, cur, curOffset, voices)
893
680
  if (from === void 0 || to === void 0) {
894
681
  continue;
895
682
  }
896
- const fromPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, from);
683
+ const fromPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, from);
897
684
  const motion = to - from;
898
- if (fromPc === seventhPc && nextHas[fromPc] === 0 && motion !== -1 && motion !== -2) {
685
+ const resolved = spelling === void 0 ? motion === -1 || motion === -2 : movesByDescendingStep(spelling.from, from, spelling.to, to);
686
+ if (fromPc === seventhPc && nextHas[fromPc] === 0 && !resolved) {
899
687
  count += 1;
900
688
  }
901
- if (fromPc === leadingTonePc && tonicPc >= 0 && nextHas[tonicPc] === 1 && nextHas[fromPc] === 0 && motion !== 1) {
689
+ const inner = voice > 0 && voice < voices - 1;
690
+ if (fromPc === leadingTonePc && tonicPc >= 0 && nextHas[tonicPc] === 1 && nextHas[fromPc] === 0 && motion !== 1 && !(inner && _chunkYPB3A3EYcjs.isFrustratedLeadingTone.call(void 0, from, to, nextChord))) {
902
691
  count += 1;
903
692
  }
904
693
  }
@@ -914,16 +703,76 @@ function leadingCost(prev, prevOffset, cur, curOffset, voices) {
914
703
  }
915
704
  total += Math.abs(b - a);
916
705
  }
917
- if (voices >= 2) {
918
- const bassPrev = prev[prevOffset];
919
- const bassCur = cur[curOffset];
920
- const sopPrev = prev[prevOffset + voices - 1];
921
- const sopCur = cur[curOffset + voices - 1];
922
- if (bassPrev !== void 0 && bassCur !== void 0 && sopPrev !== void 0 && sopCur !== void 0 && _chunkUADYAYATcjs.createsHiddenParallelPerfect.call(void 0, bassPrev, bassCur, sopPrev, sopCur)) {
923
- total += HIDDEN_PERFECT_PENALTY;
706
+ return total;
707
+ }
708
+ function createsOuterHiddenPerfect(prev, prevOffset, cur, curOffset, voices) {
709
+ if (voices < 2) {
710
+ return false;
711
+ }
712
+ const bassPrev = prev[prevOffset];
713
+ const bassCur = cur[curOffset];
714
+ const sopPrev = prev[prevOffset + voices - 1];
715
+ const sopCur = cur[curOffset + voices - 1];
716
+ if (bassPrev === void 0 || bassCur === void 0 || sopPrev === void 0 || sopCur === void 0) {
717
+ return false;
718
+ }
719
+ return _chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, sopPrev, sopCur, bassPrev, bassCur);
720
+ }
721
+ function augmentedMelodicCount(spelling, prev, prevOffset, cur, curOffset, voices) {
722
+ let count = 0;
723
+ for (let voice = 0; voice < voices; voice += 1) {
724
+ const from = prev[prevOffset + voice];
725
+ const to = cur[curOffset + voice];
726
+ if (from === void 0 || to === void 0) {
727
+ continue;
728
+ }
729
+ if (movesByAugmentedInterval(spelling.from, from, spelling.to, to)) {
730
+ count += 1;
924
731
  }
925
732
  }
926
- return total;
733
+ return count;
734
+ }
735
+ function spellRow(table, pitches, offset, voices) {
736
+ const notes = [];
737
+ for (let voice = 0; voice < voices; voice += 1) {
738
+ const pitch = pitches[offset + voice];
739
+ const note = pitch === void 0 ? void 0 : spelledAt(table, pitch);
740
+ if (note !== void 0) {
741
+ notes.push(note);
742
+ }
743
+ }
744
+ return notes;
745
+ }
746
+ function contradictsALetter(spelling, prev, prevOffset, cur, curOffset, voices) {
747
+ for (let voice = 0; voice < voices; voice += 1) {
748
+ const from = prev[prevOffset + voice];
749
+ const earlier = from === void 0 ? void 0 : spelling.from.notes[_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, from)];
750
+ if (earlier === void 0) {
751
+ continue;
752
+ }
753
+ for (let other = 0; other < voices; other += 1) {
754
+ if (other === voice) {
755
+ continue;
756
+ }
757
+ const to = cur[curOffset + other];
758
+ const later = to === void 0 ? void 0 : spelling.to.notes[_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, to)];
759
+ if (later !== void 0 && earlier.alter !== later.alter && _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, earlier.letter) === _chunkXKSO2V5Icjs.diatonicLetterOf.call(void 0, later.letter)) {
760
+ return true;
761
+ }
762
+ }
763
+ }
764
+ return false;
765
+ }
766
+ function crossRelationCount(spelling, tables, prev, prevOffset, cur, curOffset, voices) {
767
+ if (!contradictsALetter(spelling, prev, prevOffset, cur, curOffset, voices)) {
768
+ return 0;
769
+ }
770
+ return crossRelations(
771
+ { notes: spellRow(spelling.from, prev, prevOffset, voices), chord: tables.fromChord },
772
+ { notes: spellRow(spelling.to, cur, curOffset, voices), chord: tables.toChord },
773
+ tables.key,
774
+ { after: tables.afterChord }
775
+ ).length;
927
776
  }
928
777
  function violationCount(prev, prevOffset, cur, curOffset, voices) {
929
778
  let count = 0;
@@ -936,11 +785,11 @@ function violationCount(prev, prevOffset, cur, curOffset, voices) {
936
785
  if (prevLower === void 0 || prevUpper === void 0 || curLower === void 0 || curUpper === void 0) {
937
786
  continue;
938
787
  }
939
- if (_chunkUADYAYATcjs.createsParallelPerfect.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
788
+ if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
940
789
  count += 1;
941
790
  }
942
791
  if (upper === lower + 1) {
943
- if (_chunkUADYAYATcjs.createsVoiceOverlap.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
792
+ if (_chunkYPB3A3EYcjs.createsVoiceOverlap.call(void 0, prevUpper, curUpper, prevLower, curLower)) {
944
793
  count += 1;
945
794
  }
946
795
  }
@@ -948,6 +797,24 @@ function violationCount(prev, prevOffset, cur, curOffset, voices) {
948
797
  }
949
798
  return count;
950
799
  }
800
+ function moveScoring(prevChord, chord, key, nextChord) {
801
+ return {
802
+ structure: structuralTables(chord, _optionalChain([key, 'optionalAccess', _3 => _3.scale])),
803
+ resolution: prevChord === void 0 ? void 0 : resolutionTables(prevChord, chord, _optionalChain([key, 'optionalAccess', _4 => _4.scale])),
804
+ spelling: key === void 0 ? void 0 : { from: spellingTable(key, prevChord), to: spellingTable(key, chord) },
805
+ crossRelation: prevChord === void 0 || key === void 0 ? void 0 : { fromChord: prevChord, toChord: chord, afterChord: nextChord, key: key.scale }
806
+ };
807
+ }
808
+ function violationWeight(scoring, prev, prevOffset, cur, curOffset, voices) {
809
+ const { resolution, spelling, crossRelation } = scoring;
810
+ const unresolved = resolution === void 0 ? 0 : resolutionViolations(resolution, spelling, prev, prevOffset, cur, curOffset, voices);
811
+ const augmented = spelling === void 0 ? 0 : augmentedMelodicCount(spelling, prev, prevOffset, cur, curOffset, voices);
812
+ const contradictions = spelling === void 0 || crossRelation === void 0 ? 0 : crossRelationCount(spelling, crossRelation, prev, prevOffset, cur, curOffset, voices);
813
+ return VIOLATION_PENALTY * violationCount(prev, prevOffset, cur, curOffset, voices) + RESOLUTION_PENALTY * unresolved + AUGMENTED_MELODIC_PENALTY * augmented + CROSS_RELATION_PENALTY * contradictions + (createsOuterHiddenPerfect(prev, prevOffset, cur, curOffset, voices) ? HIDDEN_PERFECT_PENALTY : 0);
814
+ }
815
+ function moveScore(scoring, prev, prevOffset, cur, curOffset, voices) {
816
+ return structuralPenalty(scoring.structure, cur, curOffset, voices) + leadingCost(prev, prevOffset, cur, curOffset, voices) + violationWeight(scoring, prev, prevOffset, cur, curOffset, voices);
817
+ }
951
818
 
952
819
  // src/theory/voicing/satb.ts
953
820
  var SATB_RANGES = Object.freeze([
@@ -960,18 +827,19 @@ var DERIVED_LOW = 40;
960
827
  var DERIVED_HIGH = 79;
961
828
  var DERIVED_SPAN = 19;
962
829
  function resolveRanges(opts) {
963
- if (_optionalChain([opts, 'optionalAccess', _6 => _6.ranges]) !== void 0) {
830
+ _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
831
+ if (_optionalChain([opts, 'optionalAccess', _5 => _5.ranges]) !== void 0) {
964
832
  if (opts.ranges.length === 0) {
965
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("ranges must contain at least one voice range");
833
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("ranges must contain at least one voice range");
966
834
  }
967
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, opts.ranges.length, "voice ranges", 128);
835
+ _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, opts.ranges.length, "voice ranges", 128);
968
836
  return opts.ranges.map((range, index) => {
969
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, range.min, `ranges[${index}].min`);
970
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, range.max, `ranges[${index}].max`);
837
+ _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, range.min, `ranges[${index}].min`);
838
+ _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, range.max, `ranges[${index}].max`);
971
839
  if (range.min > range.max) {
972
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`ranges[${index}].min must not exceed max`);
840
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`ranges[${index}].min must not exceed max`);
973
841
  }
974
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0,
842
+ _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0,
975
843
  Math.floor(range.max) - Math.ceil(range.min) + 1,
976
844
  `ranges[${index}] span`,
977
845
  4096
@@ -979,8 +847,8 @@ function resolveRanges(opts) {
979
847
  return { ...range };
980
848
  });
981
849
  }
982
- const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _7 => _7.voices]), () => ( 4));
983
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, voices, "voices", 128);
850
+ const voices = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _6 => _6.voices]), () => ( 4));
851
+ _chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, voices, "voices", 128);
984
852
  if (voices === 4) {
985
853
  return SATB_RANGES.map((range) => ({ ...range }));
986
854
  }
@@ -997,59 +865,63 @@ function resolveRanges(opts) {
997
865
  return ranges;
998
866
  }
999
867
  function resolveMaxSpacing(opts) {
1000
- const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _8 => _8.maxSpacing]), () => ( DEFAULT_MAX_SPACING));
1001
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, maxSpacing, "maxSpacing");
868
+ const maxSpacing = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _7 => _7.maxSpacing]), () => ( DEFAULT_MAX_SPACING));
869
+ _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, maxSpacing, "maxSpacing");
1002
870
  if (maxSpacing < 0) {
1003
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("maxSpacing must be non-negative");
871
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("maxSpacing must be non-negative");
1004
872
  }
1005
873
  return maxSpacing;
1006
874
  }
1007
- function resolveKey(opts) {
1008
- return _optionalChain([opts, 'optionalAccess', _9 => _9.key]) === void 0 ? void 0 : _chunkUADYAYATcjs.toKeyScale.call(void 0, opts.key);
875
+ function resolvedKeyOf(opts) {
876
+ return _optionalChain([opts, 'optionalAccess', _8 => _8.key]) === void 0 ? void 0 : _chunkYPB3A3EYcjs.resolveKey.call(void 0, opts.key);
1009
877
  }
1010
878
  function resolvePreviousChord(opts) {
1011
- return _optionalChain([opts, 'optionalAccess', _10 => _10.previousChord]) === void 0 ? void 0 : _chunkUADYAYATcjs.toChordData.call(void 0, opts.previousChord);
879
+ return _optionalChain([opts, 'optionalAccess', _9 => _9.previousChord]) === void 0 ? void 0 : _chunkYPB3A3EYcjs.toChordData.call(void 0, opts.previousChord);
1012
880
  }
1013
881
  function resolveMaxCandidates(opts) {
1014
- if (_optionalChain([opts, 'optionalAccess', _11 => _11.maxCandidates]) === void 0) return void 0;
1015
- _chunkQ2WGKJNNcjs.assertPositiveInt.call(void 0, opts.maxCandidates, "maxCandidates", 1e6);
882
+ if (_optionalChain([opts, 'optionalAccess', _10 => _10.maxCandidates]) === void 0) return void 0;
883
+ _chunkXKSO2V5Icjs.assertPositiveInt.call(void 0, opts.maxCandidates, "maxCandidates", 1e6);
1016
884
  return opts.maxCandidates;
1017
885
  }
886
+ function centeringPenalty(ranges, pitches, offset, voices) {
887
+ let penalty = 0;
888
+ for (let voice = 0; voice < voices; voice += 1) {
889
+ const pitch = pitches[offset + voice];
890
+ const range = ranges[voice];
891
+ if (pitch === void 0 || range === void 0) {
892
+ continue;
893
+ }
894
+ penalty += Math.abs(pitch - (range.min + range.max) / 2);
895
+ }
896
+ return penalty;
897
+ }
1018
898
  function voiceChord(chord, opts) {
1019
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
899
+ const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
1020
900
  const ranges = resolveRanges(opts);
1021
901
  const maxSpacing = resolveMaxSpacing(opts);
1022
902
  const candidates = enumerateVoicings(data, ranges, maxSpacing, resolveMaxCandidates(opts));
1023
- const tables = structuralTables(data, resolveKey(opts));
903
+ const { structure } = moveScoring(void 0, data, resolvedKeyOf(opts));
1024
904
  const { pitches, voices } = candidates;
1025
905
  let bestOffset = -1;
1026
906
  let bestScore = Number.POSITIVE_INFINITY;
1027
907
  for (let index = 0; index < candidates.count; index += 1) {
1028
908
  const offset = index * voices;
1029
- let score = structuralPenalty(tables, pitches, offset, voices);
1030
- for (let i = 0; i < voices; i += 1) {
1031
- const pitch = pitches[offset + i];
1032
- const range = ranges[i];
1033
- if (pitch === void 0 || range === void 0) {
1034
- continue;
1035
- }
1036
- score += Math.abs(pitch - (range.min + range.max) / 2);
1037
- }
909
+ const score = structuralPenalty(structure, pitches, offset, voices) + centeringPenalty(ranges, pitches, offset, voices);
1038
910
  if (score < bestScore) {
1039
911
  bestScore = score;
1040
912
  bestOffset = offset;
1041
913
  }
1042
914
  }
1043
915
  if (bestOffset < 0) {
1044
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
1045
- `no voicing satisfies the given ranges for ${_chunkUADYAYATcjs.formatChordSymbol.call(void 0, chord)}`
916
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
917
+ `no voicing satisfies the given ranges for ${_chunkYPB3A3EYcjs.formatChordSymbol.call(void 0, chord)}`
1046
918
  );
1047
919
  }
1048
920
  return [...pitches.subarray(bestOffset, bestOffset + voices)];
1049
921
  }
1050
922
  function noSolutionAt(index, chord) {
1051
- return new (0, _chunkQ2WGKJNNcjs.NoSolutionError)(
1052
- `no voicing satisfies the given ranges for ${_chunkUADYAYATcjs.formatChordSymbol.call(void 0, chord)} at index ${index}`,
923
+ return new (0, _chunkXKSO2V5Icjs.NoSolutionError)(
924
+ `no voicing satisfies the given ranges for ${_chunkYPB3A3EYcjs.formatChordSymbol.call(void 0, chord)} at index ${index}`,
1053
925
  { at: index }
1054
926
  );
1055
927
  }
@@ -1057,48 +929,72 @@ function locate(index, chord, work) {
1057
929
  try {
1058
930
  return work();
1059
931
  } catch (error) {
1060
- if (error instanceof _chunkQ2WGKJNNcjs.NoSolutionError) {
932
+ if (error instanceof _chunkXKSO2V5Icjs.NoSolutionError) {
1061
933
  throw noSolutionAt(index, chord);
1062
934
  }
1063
935
  throw error;
1064
936
  }
1065
937
  }
938
+ function successorWeight(next, cur, curOffset, voices) {
939
+ const { candidates, scoring } = next;
940
+ const { pitches } = candidates;
941
+ let bestScore = Number.POSITIVE_INFINITY;
942
+ let bestWeight = 0;
943
+ for (let candidate = 0; candidate < candidates.count; candidate += 1) {
944
+ const offset = candidate * voices;
945
+ const weight = violationWeight(scoring, cur, curOffset, pitches, offset, voices);
946
+ const score = weight + structuralPenalty(scoring.structure, pitches, offset, voices) + leadingCost(cur, curOffset, pitches, offset, voices);
947
+ if (score < bestScore) {
948
+ bestScore = score;
949
+ bestWeight = weight;
950
+ }
951
+ }
952
+ return bestWeight;
953
+ }
1066
954
  function voiceProgression(chords, opts) {
1067
- _chunkQ2WGKJNNcjs.assertGenerationBudget.call(void 0, chords.length, "voiced progression chords", _optionalChain([opts, 'optionalAccess', _12 => _12.budget]));
1068
- const data = chords.map((chord) => _chunkUADYAYATcjs.toChordData.call(void 0, chord));
1069
- const key = resolveKey(opts);
955
+ const progression = _chunkXKSO2V5Icjs.assertArray.call(void 0, chords, "chords");
956
+ _chunkXKSO2V5Icjs.assertGenerationBudget.call(void 0, progression.length, "voiced progression chords", _optionalChain([opts, 'optionalAccess', _11 => _11.budget]));
957
+ const data = progression.map((chord) => _chunkYPB3A3EYcjs.toChordData.call(void 0, chord));
958
+ const key = resolvedKeyOf(opts);
1070
959
  const ranges = resolveRanges(opts);
1071
960
  const maxSpacing = resolveMaxSpacing(opts);
1072
961
  const maxCandidates = resolveMaxCandidates(opts);
1073
962
  const result = [];
1074
- const buffer = createCandidateBuffer();
963
+ const first = data[0];
964
+ if (first === void 0) {
965
+ return result;
966
+ }
967
+ let current = locate(
968
+ 0,
969
+ first,
970
+ () => enumerateVoicings(first, ranges, maxSpacing, maxCandidates, createCandidateBuffer())
971
+ );
972
+ let spare = createCandidateBuffer();
973
+ let scoring = moveScoring(void 0, first, key);
1075
974
  let prev;
1076
- let prevChord;
1077
- for (let index = 0; index < data.length; index += 1) {
1078
- const chord = data[index];
1079
- if (chord === void 0) {
1080
- continue;
1081
- }
1082
- if (prev === void 0) {
1083
- prev = locate(index, chord, () => voiceChord(chord, opts));
1084
- prevChord = chord;
1085
- result.push(prev);
1086
- continue;
1087
- }
1088
- const candidates = locate(
1089
- index,
1090
- chord,
1091
- () => enumerateVoicings(chord, ranges, maxSpacing, maxCandidates, buffer)
1092
- );
1093
- const { pitches, voices } = candidates;
1094
- const structure = structuralTables(chord, key);
1095
- const resolution = prevChord === void 0 ? void 0 : resolutionTables(prevChord, chord, key);
975
+ for (const [index, chord] of data.entries()) {
976
+ const nextChord = data[index + 1];
977
+ const lookahead = nextChord === void 0 ? void 0 : {
978
+ candidates: locate(
979
+ index + 1,
980
+ nextChord,
981
+ () => enumerateVoicings(nextChord, ranges, maxSpacing, maxCandidates, spare)
982
+ ),
983
+ // The chord after the one being reached goes in too: it is what
984
+ // says whether the chromatic tone the next chord introduces is the
985
+ // one an applied dominant is written with.
986
+ scoring: moveScoring(chord, nextChord, key, data[index + 2])
987
+ };
988
+ const { pitches, voices, count } = current;
989
+ const weighed = lookahead !== void 0 && count * lookahead.candidates.count <= MAX_LOOKAHEAD_PAIRS ? lookahead : void 0;
1096
990
  let bestOffset = -1;
1097
991
  let bestScore = Number.POSITIVE_INFINITY;
1098
- for (let candidate = 0; candidate < candidates.count; candidate += 1) {
992
+ for (let candidate = 0; candidate < count; candidate += 1) {
1099
993
  const offset = candidate * voices;
1100
- const unresolved = resolution === void 0 ? 0 : resolutionViolations(resolution, prev, 0, pitches, offset, voices);
1101
- const score = structuralPenalty(structure, pitches, offset, voices) + leadingCost(prev, 0, pitches, offset, voices) + VIOLATION_PENALTY * violationCount(prev, 0, pitches, offset, voices) + RESOLUTION_PENALTY * unresolved;
994
+ let score = prev === void 0 ? structuralPenalty(scoring.structure, pitches, offset, voices) + centeringPenalty(ranges, pitches, offset, voices) : moveScore(scoring, prev, 0, pitches, offset, voices);
995
+ if (weighed !== void 0) {
996
+ score += successorWeight(weighed, pitches, offset, voices);
997
+ }
1102
998
  if (score < bestScore) {
1103
999
  bestScore = score;
1104
1000
  bestOffset = offset;
@@ -1110,28 +1006,41 @@ function voiceProgression(chords, opts) {
1110
1006
  const best = [...pitches.subarray(bestOffset, bestOffset + voices)];
1111
1007
  result.push(best);
1112
1008
  prev = best;
1113
- prevChord = chord;
1009
+ if (lookahead !== void 0) {
1010
+ spare = current;
1011
+ current = lookahead.candidates;
1012
+ scoring = lookahead.scoring;
1013
+ }
1114
1014
  }
1115
1015
  return result;
1116
1016
  }
1117
1017
 
1118
1018
  // src/theory/voicing/leading.ts
1019
+ function assertVoicing(pitches, name) {
1020
+ const voices = _chunkXKSO2V5Icjs.assertArray.call(void 0, pitches, name);
1021
+ for (let index = 0; index < voices.length; index += 1) {
1022
+ _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, _nullishCoalesce(voices[index], () => ( Number.NaN)), `${name}[${index}]`);
1023
+ }
1024
+ return voices;
1025
+ }
1119
1026
  function voiceLeadingCost(from, to) {
1120
- if (from.length !== to.length) {
1027
+ const previous = assertVoicing(from, "from");
1028
+ const next = assertVoicing(to, "to");
1029
+ if (previous.length !== next.length) {
1121
1030
  return Number.POSITIVE_INFINITY;
1122
1031
  }
1123
- return leadingCost(from, 0, to, 0, from.length);
1032
+ return leadingCost(previous, 0, next, 0, previous.length);
1124
1033
  }
1125
1034
  function nextVoicing(current, chord, opts) {
1126
- const data = _chunkUADYAYATcjs.toChordData.call(void 0, chord);
1127
- const derived = _optionalChain([opts, 'optionalAccess', _13 => _13.ranges]) === void 0 && _optionalChain([opts, 'optionalAccess', _14 => _14.voices]) === void 0;
1035
+ _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
1036
+ assertVoicing(current, "current");
1037
+ const data = _chunkYPB3A3EYcjs.toChordData.call(void 0, chord);
1038
+ const derived = _optionalChain([opts, 'optionalAccess', _12 => _12.ranges]) === void 0 && _optionalChain([opts, 'optionalAccess', _13 => _13.voices]) === void 0;
1128
1039
  if (derived) {
1129
1040
  if (current.length === 0) {
1130
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("current must contain at least one pitch");
1131
- }
1132
- for (let index = 0; index < current.length; index += 1) {
1133
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
1041
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("current must contain at least one pitch");
1134
1042
  }
1043
+ assertVoicing(current, "current");
1135
1044
  }
1136
1045
  const ranges = derived ? resolveRanges({
1137
1046
  ranges: current.map((pitch) => {
@@ -1139,11 +1048,9 @@ function nextVoicing(current, chord, opts) {
1139
1048
  return { min: Math.max(0, centre - 12), max: Math.min(127, centre + 12) };
1140
1049
  })
1141
1050
  }) : resolveRanges(opts);
1142
- for (let index = 0; index < current.length; index += 1) {
1143
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, _nullishCoalesce(current[index], () => ( Number.NaN)), `current[${index}]`);
1144
- }
1051
+ assertVoicing(current, "current");
1145
1052
  if (current.length < ranges.length) {
1146
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1053
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
1147
1054
  `current has ${current.length} voices but the requested ranges require ${ranges.length}`
1148
1055
  );
1149
1056
  }
@@ -1153,29 +1060,33 @@ function nextVoicing(current, chord, opts) {
1153
1060
  });
1154
1061
  const maxSpacing = resolveMaxSpacing(opts);
1155
1062
  const candidates = enumerateVoicings(data, ranges, maxSpacing, resolveMaxCandidates(opts));
1156
- const key = resolveKey(opts);
1157
- const previousChord = resolvePreviousChord(opts);
1158
- const structure = structuralTables(data, key);
1159
- const resolution = previousChord === void 0 ? void 0 : resolutionTables(previousChord, data, key);
1063
+ const key = resolvedKeyOf(opts);
1064
+ const scoring = moveScoring(resolvePreviousChord(opts), data, key);
1160
1065
  const { pitches, voices } = candidates;
1161
1066
  let bestOffset = -1;
1162
1067
  let bestScore = Number.POSITIVE_INFINITY;
1163
1068
  for (let candidate = 0; candidate < candidates.count; candidate += 1) {
1164
1069
  const offset = candidate * voices;
1165
- const score = structuralPenalty(structure, pitches, offset, voices) + leadingCost(source, 0, pitches, offset, voices) + VIOLATION_PENALTY * violationCount(source, 0, pitches, offset, voices) + RESOLUTION_PENALTY * (resolution === void 0 ? 0 : resolutionViolations(resolution, source, 0, pitches, offset, voices));
1070
+ const score = moveScore(scoring, source, 0, pitches, offset, voices);
1166
1071
  if (score < bestScore) {
1167
1072
  bestScore = score;
1168
1073
  bestOffset = offset;
1169
1074
  }
1170
1075
  }
1171
1076
  if (bestOffset < 0) {
1172
- throw new (0, _chunkQ2WGKJNNcjs.NoSolutionError)("no voicing satisfies the given ranges");
1077
+ throw new (0, _chunkXKSO2V5Icjs.NoSolutionError)("no voicing satisfies the given ranges");
1173
1078
  }
1174
1079
  return [...pitches.subarray(bestOffset, bestOffset + voices)];
1175
1080
  }
1176
1081
 
1177
1082
  // src/theory/voicing/styled.ts
1178
1083
  var DEFAULT_STYLE_OCTAVE = 4;
1084
+ function dropPositionFromTop(style) {
1085
+ if (style === "drop2") {
1086
+ return 2;
1087
+ }
1088
+ return style === "drop3" ? 3 : 0;
1089
+ }
1179
1090
  function dedupePcs(pcs) {
1180
1091
  const seen = /* @__PURE__ */ new Set();
1181
1092
  const result = [];
@@ -1188,7 +1099,7 @@ function dedupePcs(pcs) {
1188
1099
  return result;
1189
1100
  }
1190
1101
  function lowestPitchAtOrAbove(pc, floor) {
1191
- return floor + ((pc - _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, floor)) % 12 + 12) % 12;
1102
+ return floor + ((pc - _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, floor)) % 12 + 12) % 12;
1192
1103
  }
1193
1104
  function nearestPc(target, pcs) {
1194
1105
  let best = _nullishCoalesce(pcs[0], () => ( target));
@@ -1202,26 +1113,29 @@ function nearestPc(target, pcs) {
1202
1113
  }
1203
1114
  return best;
1204
1115
  }
1205
- function voiceChordStyled(chord, opts) {
1206
- const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _15 => _15.style]), () => ( "close"));
1207
- const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _16 => _16.octave]), () => ( DEFAULT_STYLE_OCTAVE));
1208
- _chunkQ2WGKJNNcjs.assertInteger.call(void 0, octave, "styled voicing octave", -10, 10);
1209
- if (_optionalChain([opts, 'optionalAccess', _17 => _17.topNote]) !== void 0) {
1210
- _chunkQ2WGKJNNcjs.assertFiniteNumber.call(void 0, opts.topNote, "styled voicing topNote");
1116
+ function voiceChordStyled(given, opts) {
1117
+ _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
1118
+ const chord = _chunkYPB3A3EYcjs.toChordData.call(void 0, given);
1119
+ const style = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _14 => _14.style]), () => ( "close"));
1120
+ const octave = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _15 => _15.octave]), () => ( DEFAULT_STYLE_OCTAVE));
1121
+ _chunkXKSO2V5Icjs.assertInteger.call(void 0, octave, "styled voicing octave", -10, 10);
1122
+ if (_optionalChain([opts, 'optionalAccess', _16 => _16.topNote]) !== void 0) {
1123
+ _chunkXKSO2V5Icjs.assertFiniteNumber.call(void 0, opts.topNote, "styled voicing topNote");
1211
1124
  }
1212
1125
  const base = 12 * (octave + 1);
1213
- const rootPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc);
1214
- const bassPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
1215
- const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess', _18 => _18.rootless]) === true;
1216
- let order = dedupePcs(chord.intervals.map((interval) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + interval)));
1126
+ const rootPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc);
1127
+ const bassPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, _nullishCoalesce(chord.bassPc, () => ( chord.rootPc)));
1128
+ const omitRoot = style === "rootless" || _optionalChain([opts, 'optionalAccess', _17 => _17.rootless]) === true;
1129
+ let order = dedupePcs(chord.intervals.map((interval) => _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, chord.rootPc + interval)));
1217
1130
  const bassIndex = order.indexOf(bassPc);
1131
+ const bassIsChordTone = bassIndex >= 0;
1218
1132
  if (bassIndex > 0) {
1219
1133
  order = [...order.slice(bassIndex), ...order.slice(0, bassIndex)];
1220
1134
  } else if (bassIndex < 0) {
1221
1135
  order = [bassPc, ...order];
1222
1136
  }
1223
1137
  if (style === "shell") {
1224
- const roles = order.map((pc) => _chunkUADYAYATcjs.chordToneRole.call(void 0, pc, chord));
1138
+ const roles = order.map((pc) => _chunkYPB3A3EYcjs.chordToneRole.call(void 0, pc, chord));
1225
1139
  const guide = roles.includes("seventh") ? "seventh" : roles.includes("sixth") ? "sixth" : "fifth";
1226
1140
  order = order.filter((_pc, i) => {
1227
1141
  const role = roles[i];
@@ -1234,18 +1148,27 @@ function voiceChordStyled(chord, opts) {
1234
1148
  if (order.length === 0) {
1235
1149
  return [];
1236
1150
  }
1237
- if (_optionalChain([opts, 'optionalAccess', _19 => _19.topNote]) !== void 0) {
1238
- const target = order.includes(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, opts.topNote)) ? _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, opts.topNote) : nearestPc(_chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, opts.topNote), order);
1151
+ if (_optionalChain([opts, 'optionalAccess', _18 => _18.topNote]) !== void 0) {
1152
+ const target = order.includes(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote)) ? _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote) : nearestPc(_chunkXKSO2V5Icjs.pitchClassOf.call(void 0, opts.topNote), order);
1239
1153
  const targetIndex = order.indexOf(target);
1240
1154
  order = [...order.slice(targetIndex + 1), ...order.slice(0, targetIndex + 1)];
1241
1155
  }
1156
+ const dropFromTop = dropPositionFromTop(style);
1157
+ const dropsToNamedBass = dropFromTop > 0 && bassIsChordTone && chord.bassPc !== void 0 && order.length > dropFromTop;
1158
+ if (dropsToNamedBass) {
1159
+ const bassAt = order.indexOf(bassPc);
1160
+ if (bassAt >= 0) {
1161
+ const shift = ((bassAt - (order.length - dropFromTop)) % order.length + order.length) % order.length;
1162
+ order = [...order.slice(shift), ...order.slice(0, shift)];
1163
+ }
1164
+ }
1242
1165
  const stack = [];
1243
1166
  let prev;
1244
1167
  for (const pc of order) {
1245
1168
  if (prev === void 0) {
1246
1169
  prev = lowestPitchAtOrAbove(pc, base);
1247
1170
  } else {
1248
- let delta = ((pc - _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, prev)) % 12 + 12) % 12;
1171
+ let delta = ((pc - _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, prev)) % 12 + 12) % 12;
1249
1172
  if (delta === 0) {
1250
1173
  delta = 12;
1251
1174
  }
@@ -1253,13 +1176,13 @@ function voiceChordStyled(chord, opts) {
1253
1176
  }
1254
1177
  stack.push(prev);
1255
1178
  }
1256
- if (style === "drop2" && stack.length >= 2) {
1257
- stack[stack.length - 2] = (_nullishCoalesce(stack[stack.length - 2], () => ( 0))) - 12;
1258
- } else if (style === "drop3" && stack.length >= 3) {
1259
- stack[stack.length - 3] = (_nullishCoalesce(stack[stack.length - 3], () => ( 0))) - 12;
1179
+ const stackedFloor = bassIsChordTone ? 0 : 1;
1180
+ if (dropFromTop > 0 && stack.length - dropFromTop > stackedFloor) {
1181
+ const dropIndex = stack.length - dropFromTop;
1182
+ stack[dropIndex] = (_nullishCoalesce(stack[dropIndex], () => ( 0))) - 12;
1260
1183
  }
1261
1184
  if (chord.bassPc !== void 0) {
1262
- const bassIndexInStack = stack.findIndex((pitch) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pitch) === bassPc);
1185
+ const bassIndexInStack = stack.findIndex((pitch) => _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, pitch) === bassPc);
1263
1186
  if (bassIndexInStack >= 0) {
1264
1187
  const otherLowest = Math.min(
1265
1188
  ...stack.filter((_pitch, index) => index !== bassIndexInStack),
@@ -1274,7 +1197,7 @@ function voiceChordStyled(chord, opts) {
1274
1197
  const lowest = _nullishCoalesce(voicing[0], () => ( 0));
1275
1198
  const highest = _nullishCoalesce(voicing[voicing.length - 1], () => ( 0));
1276
1199
  if (lowest < 0 || highest > 127) {
1277
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1200
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
1278
1201
  `styled voicing at octave ${octave} spans MIDI ${lowest}..${highest}, outside 0..127`
1279
1202
  );
1280
1203
  }
@@ -1290,17 +1213,26 @@ var INTERVAL_WORDS = [
1290
1213
  "fifth",
1291
1214
  "sixth",
1292
1215
  "seventh",
1293
- "octave"
1216
+ "octave",
1217
+ "ninth",
1218
+ "tenth",
1219
+ "eleventh",
1220
+ "twelfth",
1221
+ "thirteenth",
1222
+ "fourteenth",
1223
+ "fifteenth"
1294
1224
  ];
1295
1225
  function intervalWord(numberValue) {
1296
- return _nullishCoalesce(INTERVAL_WORDS[numberValue - 1], () => ( "interval"));
1226
+ return _nullishCoalesce(INTERVAL_WORDS[numberValue - 1], () => ( `${numberValue}th`));
1227
+ }
1228
+ function intervalArticle(word) {
1229
+ return /^[aeiou]/.test(word) ? "an" : "a";
1297
1230
  }
1298
1231
  function violation(kind, voices, fromIndex, toIndex, rationale) {
1299
1232
  return { kind, voices, fromIndex, toIndex, rationale };
1300
1233
  }
1301
1234
 
1302
1235
  // src/theory/partwriting/species.ts
1303
- var EPS = 1e-9;
1304
1236
  var SPECIES_RATIO = { 1: 1, 2: 2, 3: 4, 4: 2, 5: 0 };
1305
1237
  var SPECIES_NUMBERS = Object.keys(SPECIES_RATIO).map(Number);
1306
1238
  var MIN_FLORID_VALUE = 0.125;
@@ -1308,7 +1240,7 @@ function meanPitch(notes) {
1308
1240
  if (notes.length === 0) {
1309
1241
  return 0;
1310
1242
  }
1311
- return notes.reduce((sum, note) => sum + _chunkQ2WGKJNNcjs.noteToMidi.call(void 0, note), 0) / notes.length;
1243
+ return notes.reduce((sum, note) => sum + _chunkXKSO2V5Icjs.noteToMidi.call(void 0, note), 0) / notes.length;
1312
1244
  }
1313
1245
  function impliedDurations(count, measures, ratio) {
1314
1246
  const full = ratio * Math.max(measures - 1, 0);
@@ -1324,21 +1256,22 @@ function direction(interval) {
1324
1256
  function sameNote(a, b) {
1325
1257
  return a.letter === b.letter && a.alter === b.alter && a.octave === b.octave;
1326
1258
  }
1327
- function isPassedThrough(entries, position) {
1259
+ function isPassedThrough(entries, position, species) {
1328
1260
  const previous = entries[position - 1];
1329
1261
  const current = entries[position];
1330
1262
  const next = entries[position + 1];
1331
1263
  if (previous === void 0 || current === void 0 || next === void 0) {
1332
1264
  return false;
1333
1265
  }
1334
- const incoming = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, previous.note, current.note);
1335
- const outgoing = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, current.note, next.note);
1266
+ const incoming = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, previous.note, current.note);
1267
+ const outgoing = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, current.note, next.note);
1336
1268
  if (!isStep(incoming) || !isStep(outgoing)) {
1337
1269
  return false;
1338
1270
  }
1339
- const passing = direction(incoming) === direction(outgoing);
1340
- const neighbour = sameNote(previous.note, next.note);
1341
- return passing || neighbour;
1271
+ if (direction(incoming) === direction(outgoing)) {
1272
+ return true;
1273
+ }
1274
+ return sameNote(previous.note, next.note) && (species === 3 || species === 5);
1342
1275
  }
1343
1276
  function isThird(interval) {
1344
1277
  return interval.number === 3;
@@ -1362,9 +1295,9 @@ function isCambiata(entries, start) {
1362
1295
  if (first.consonance === 2 /* Dissonance */ || third.consonance === 2 /* Dissonance */) {
1363
1296
  return false;
1364
1297
  }
1365
- const approach = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, first.note, second.note);
1366
- const leap = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, second.note, third.note);
1367
- const answer = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, third.note, fourth.note);
1298
+ const approach = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.note, second.note);
1299
+ const leap = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, second.note, third.note);
1300
+ const answer = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, third.note, fourth.note);
1368
1301
  return isStep(approach) && direction(approach) < 0 && isThird(leap) && direction(leap) < 0 && isStep(answer) && direction(answer) > 0;
1369
1302
  }
1370
1303
  function isDoubleNeighbour(entries, start) {
@@ -1376,9 +1309,9 @@ function isDoubleNeighbour(entries, start) {
1376
1309
  if (first.consonance === 2 /* Dissonance */ || !sameNote(first.note, fourth.note)) {
1377
1310
  return false;
1378
1311
  }
1379
- const away = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, first.note, second.note);
1380
- const across = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, second.note, third.note);
1381
- const back = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, third.note, fourth.note);
1312
+ const away = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.note, second.note);
1313
+ const across = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, second.note, third.note);
1314
+ const back = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, third.note, fourth.note);
1382
1315
  return isStep(away) && isThird(across) && isStep(back) && direction(away) !== 0 && direction(across) === -direction(away);
1383
1316
  }
1384
1317
  function isFiguredDissonance(entries, position, species) {
@@ -1399,7 +1332,7 @@ function licenceFor(species, downbeat) {
1399
1332
  }
1400
1333
  return downbeat ? "none" : "passing";
1401
1334
  }
1402
- function verticalViolations(entries, position, species, counterpointAbove) {
1335
+ function verticalViolations(entries, position, species, counterpointAbove, crossed) {
1403
1336
  const entry = entries[position];
1404
1337
  if (entry === void 0) {
1405
1338
  return [];
@@ -1407,7 +1340,8 @@ function verticalViolations(entries, position, species, counterpointAbove) {
1407
1340
  const found = [];
1408
1341
  const upper = counterpointAbove ? entry.note : entry.against;
1409
1342
  const lower = counterpointAbove ? entry.against : entry.note;
1410
- if (_chunkUADYAYATcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
1343
+ if (!crossed.has(entry.index) && _chunkYPB3A3EYcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
1344
+ crossed.add(entry.index);
1411
1345
  found.push(
1412
1346
  violation(
1413
1347
  "voiceCrossing",
@@ -1422,7 +1356,7 @@ function verticalViolations(entries, position, species, counterpointAbove) {
1422
1356
  return found;
1423
1357
  }
1424
1358
  const licence = licenceFor(species, entry.downbeat);
1425
- if (licence === "passing" && (isPassedThrough(entries, position) || isFiguredDissonance(entries, position, species))) {
1359
+ if (licence === "passing" && (isPassedThrough(entries, position, species) || isFiguredDissonance(entries, position, species))) {
1426
1360
  return found;
1427
1361
  }
1428
1362
  if (licence === "suspension") {
@@ -1459,7 +1393,7 @@ function suspensionViolations(entries, position) {
1459
1393
  )
1460
1394
  ];
1461
1395
  }
1462
- const resolution = next === void 0 ? void 0 : _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, entry.note, next.note);
1396
+ const resolution = next === void 0 ? void 0 : _chunkXKSO2V5Icjs.spelledInterval.call(void 0, entry.note, next.note);
1463
1397
  if (next === void 0 || resolution === void 0 || !isStep(resolution) || direction(resolution) >= 0) {
1464
1398
  return [
1465
1399
  violation(
@@ -1471,11 +1405,22 @@ function suspensionViolations(entries, position) {
1471
1405
  )
1472
1406
  ];
1473
1407
  }
1408
+ if (next.consonance === 2 /* Dissonance */) {
1409
+ return [
1410
+ violation(
1411
+ "unresolvedSuspension",
1412
+ [0, 1],
1413
+ entry.index,
1414
+ next.index,
1415
+ `The suspension falls onto a ${next.interval.quality}${next.interval.number}, which is no resolution`
1416
+ )
1417
+ ];
1418
+ }
1474
1419
  return [];
1475
1420
  }
1476
1421
  function melodicViolations(from, to) {
1477
- const interval = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, from.note, to.note);
1478
- if (_chunkUADYAYATcjs.isAugmentedMelodicInterval.call(void 0, from.note, to.note)) {
1422
+ const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, from.note, to.note);
1423
+ if (_chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, from.note, to.note)) {
1479
1424
  return [
1480
1425
  violation(
1481
1426
  "augmentedMelodicInterval",
@@ -1486,7 +1431,7 @@ function melodicViolations(from, to) {
1486
1431
  )
1487
1432
  ];
1488
1433
  }
1489
- if (_chunkUADYAYATcjs.isForbiddenMelodicLeap.call(void 0, from.note, to.note)) {
1434
+ if (_chunkYPB3A3EYcjs.isForbiddenMelodicLeap.call(void 0, from.note, to.note)) {
1490
1435
  return [
1491
1436
  violation(
1492
1437
  "illegalLeap",
@@ -1514,19 +1459,19 @@ function melodicMoves(entries) {
1514
1459
  if (from === void 0 || to === void 0) {
1515
1460
  continue;
1516
1461
  }
1517
- moves.push({ from, to, interval: _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, from.note, to.note) });
1462
+ moves.push({ from, to, interval: _chunkXKSO2V5Icjs.spelledInterval.call(void 0, from.note, to.note) });
1518
1463
  }
1519
1464
  return moves;
1520
1465
  }
1521
1466
  function climaxViolations(entries) {
1522
1467
  const body = entries.slice(0, -1);
1523
1468
  const peak = body.reduce(
1524
- (high, entry) => Math.max(high, _chunkQ2WGKJNNcjs.noteToMidi.call(void 0, entry.note)),
1469
+ (high, entry) => Math.max(high, _chunkXKSO2V5Icjs.noteToMidi.call(void 0, entry.note)),
1525
1470
  Number.NEGATIVE_INFINITY
1526
1471
  );
1527
1472
  const arrivals = body.filter((entry, position) => {
1528
1473
  const previous = body[position - 1];
1529
- return _chunkQ2WGKJNNcjs.noteToMidi.call(void 0, entry.note) === peak && (previous === void 0 || _chunkQ2WGKJNNcjs.noteToMidi.call(void 0, previous.note) !== peak);
1474
+ return _chunkXKSO2V5Icjs.noteToMidi.call(void 0, entry.note) === peak && (previous === void 0 || _chunkXKSO2V5Icjs.noteToMidi.call(void 0, previous.note) !== peak);
1530
1475
  });
1531
1476
  const first = arrivals[0];
1532
1477
  const second = arrivals[1];
@@ -1607,7 +1552,7 @@ function leapAnswerViolations(moves) {
1607
1552
  [1],
1608
1553
  leap.from.index,
1609
1554
  answer.to.index,
1610
- `The leap of ${word === "octave" ? "an" : "a"} ${word} is not answered by a step the other way`
1555
+ `The leap of ${intervalArticle(word)} ${word} is not answered by a step the other way`
1611
1556
  )
1612
1557
  );
1613
1558
  }
@@ -1620,7 +1565,7 @@ function tritoneOutlineViolations(entries) {
1620
1565
  const first = run[0];
1621
1566
  const last = run[run.length - 1];
1622
1567
  if (first !== void 0 && last !== void 0 && run.length > 1) {
1623
- const outline = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, first.from.note, last.to.note);
1568
+ const outline = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, first.from.note, last.to.note);
1624
1569
  if (Math.abs(outline.semitones) === TRITONE_SEMITONES) {
1625
1570
  found.push(
1626
1571
  violation(
@@ -1663,7 +1608,7 @@ function repeatedNoteViolations(moves, closingMeasure) {
1663
1608
  }
1664
1609
  function melodicShapeViolations(entries, species) {
1665
1610
  const moves = melodicMoves(entries);
1666
- const closingMeasure = _nullishCoalesce(_optionalChain([entries, 'access', _20 => _20[entries.length - 1], 'optionalAccess', _21 => _21.againstIndex]), () => ( 0));
1611
+ const closingMeasure = _nullishCoalesce(_optionalChain([entries, 'access', _19 => _19[entries.length - 1], 'optionalAccess', _20 => _20.againstIndex]), () => ( 0));
1667
1612
  const found = [
1668
1613
  ...climaxViolations(entries),
1669
1614
  ...leapChainViolations(moves),
@@ -1673,14 +1618,19 @@ function melodicShapeViolations(entries, species) {
1673
1618
  ];
1674
1619
  return found.sort((left, right) => left.fromIndex - right.fromIndex);
1675
1620
  }
1621
+ function inTimeOrder(violations) {
1622
+ return [...violations].sort(
1623
+ (left, right) => left.fromIndex - right.fromIndex || left.toIndex - right.toIndex
1624
+ );
1625
+ }
1676
1626
  function motionViolations(from, to, counterpointAbove) {
1677
1627
  const found = [];
1678
1628
  const upperPrev = counterpointAbove ? from.note : from.against;
1679
1629
  const upperCur = counterpointAbove ? to.note : to.against;
1680
1630
  const lowerPrev = counterpointAbove ? from.against : from.note;
1681
1631
  const lowerCur = counterpointAbove ? to.against : to.note;
1682
- if (_chunkUADYAYATcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1683
- const fifth = _chunkUADYAYATcjs.simpleIntervalNumber.call(void 0, to.interval.number) === 5;
1632
+ if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1633
+ const fifth = _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, to.interval.number) === 5;
1684
1634
  found.push(
1685
1635
  violation(
1686
1636
  fifth ? "parallelFifth" : "parallelOctave",
@@ -1691,18 +1641,18 @@ function motionViolations(from, to, counterpointAbove) {
1691
1641
  )
1692
1642
  );
1693
1643
  }
1694
- if (_chunkUADYAYATcjs.createsHiddenParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1644
+ if (_chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur, "twoVoice")) {
1695
1645
  found.push(
1696
1646
  violation(
1697
1647
  "hiddenPerfect",
1698
1648
  [0, 1],
1699
1649
  from.index,
1700
1650
  to.index,
1701
- "The voices leap into a perfect interval by similar motion"
1651
+ "The voices move into a perfect interval by similar motion"
1702
1652
  )
1703
1653
  );
1704
1654
  }
1705
- if (_chunkUADYAYATcjs.createsBattuta.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1655
+ if (_chunkYPB3A3EYcjs.createsBattuta.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1706
1656
  found.push(
1707
1657
  violation(
1708
1658
  "battuta",
@@ -1731,7 +1681,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
1731
1681
  if (first === void 0 || last === void 0) {
1732
1682
  return found;
1733
1683
  }
1734
- if (_chunkUADYAYATcjs.classifySpelledInterval.call(void 0, first.interval) !== 0 /* PerfectConsonance */) {
1684
+ if (_chunkYPB3A3EYcjs.classifySpelledInterval.call(void 0, first.interval) !== 0 /* PerfectConsonance */) {
1735
1685
  found.push(
1736
1686
  violation(
1737
1687
  "missingCadence",
@@ -1742,7 +1692,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
1742
1692
  )
1743
1693
  );
1744
1694
  }
1745
- if (_chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, last.note) !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, mode.rootPc)) {
1695
+ if (_chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, last.note) !== _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, mode.rootPc)) {
1746
1696
  found.push(
1747
1697
  violation(
1748
1698
  "missingCadence",
@@ -1754,7 +1704,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
1754
1704
  );
1755
1705
  }
1756
1706
  const closing = last.interval;
1757
- if (closing.quality !== "P" || _chunkUADYAYATcjs.simpleIntervalNumber.call(void 0, closing.number) !== 1) {
1707
+ if (closing.quality !== "P" || _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, closing.number) !== 1) {
1758
1708
  found.push(
1759
1709
  violation(
1760
1710
  "missingCadence",
@@ -1771,9 +1721,9 @@ function cadenceViolations(entries, mode, counterpointAbove) {
1771
1721
  return found;
1772
1722
  }
1773
1723
  const arrival = _nullishCoalesce(entries.find((entry) => entry.againstIndex === last.againstIndex), () => ( last));
1774
- const approach = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, penultimate.note, arrival.note);
1724
+ const approach = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, penultimate.note, arrival.note);
1775
1725
  const expected = counterpointAbove ? { quality: "M", number: 6 } : { quality: "m", number: 3 };
1776
- if (penultimate.interval.quality !== expected.quality || _chunkUADYAYATcjs.simpleIntervalNumber.call(void 0, penultimate.interval.number) !== expected.number || !isStep(approach)) {
1726
+ if (penultimate.interval.quality !== expected.quality || _chunkYPB3A3EYcjs.simpleIntervalNumber.call(void 0, penultimate.interval.number) !== expected.number || !isStep(approach)) {
1777
1727
  found.push(
1778
1728
  violation(
1779
1729
  "missingCadence",
@@ -1787,7 +1737,7 @@ function cadenceViolations(entries, mode, counterpointAbove) {
1787
1737
  return found;
1788
1738
  }
1789
1739
  function resolveDurations(counterpoint, measures, species, opts) {
1790
- const given = _optionalChain([opts, 'optionalAccess', _22 => _22.durations]);
1740
+ const given = _optionalChain([opts, 'optionalAccess', _21 => _21.durations]);
1791
1741
  if (given !== void 0) {
1792
1742
  if (given.length !== counterpoint.length) {
1793
1743
  return {
@@ -1796,20 +1746,20 @@ function resolveDurations(counterpoint, measures, species, opts) {
1796
1746
  }
1797
1747
  for (const duration of given) {
1798
1748
  const units = duration / MIN_FLORID_VALUE;
1799
- if (!(duration > 0) || duration > 1 || Math.abs(units - Math.round(units)) > EPS) {
1749
+ if (!(duration > 0) || duration > 1 || Math.abs(units - Math.round(units)) > _chunkXKSO2V5Icjs.BEAT_EPS) {
1800
1750
  return {
1801
1751
  problem: `A note lasts ${duration} of a measure, which this style does not write`
1802
1752
  };
1803
1753
  }
1804
1754
  }
1805
1755
  const total = given.reduce((sum, duration) => sum + duration, 0);
1806
- if (Math.abs(total - measures) > EPS) {
1756
+ if (Math.abs(total - measures) > _chunkXKSO2V5Icjs.BEAT_EPS) {
1807
1757
  return { problem: `The counterpoint fills ${total} of the ${measures} measures` };
1808
1758
  }
1809
1759
  return { durations: [...given] };
1810
1760
  }
1811
1761
  if (species === 5) {
1812
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1762
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
1813
1763
  "checkSpecies needs durations for the fifth species, whose note values are not fixed"
1814
1764
  );
1815
1765
  }
@@ -1831,23 +1781,32 @@ function buildEntries(cantusFirmus, counterpoint, durations, counterpointAbove)
1831
1781
  if (note === void 0) {
1832
1782
  continue;
1833
1783
  }
1834
- const againstIndex = Math.min(Math.floor(onset + EPS), cantusFirmus.length - 1);
1835
- const against = cantusFirmus[againstIndex];
1836
- if (against === void 0) {
1837
- continue;
1784
+ const struckOver = Math.min(Math.floor(onset + _chunkXKSO2V5Icjs.BEAT_EPS), cantusFirmus.length - 1);
1785
+ const lastOver = Math.min(
1786
+ Math.max(Math.ceil(onset + duration - _chunkXKSO2V5Icjs.BEAT_EPS) - 1, struckOver),
1787
+ cantusFirmus.length - 1
1788
+ );
1789
+ for (let againstIndex = struckOver; againstIndex <= lastOver; againstIndex += 1) {
1790
+ const against = cantusFirmus[againstIndex];
1791
+ if (against === void 0) {
1792
+ continue;
1793
+ }
1794
+ const held = againstIndex > struckOver;
1795
+ const begins = held ? againstIndex : onset;
1796
+ const interval = counterpointAbove ? _chunkXKSO2V5Icjs.spelledInterval.call(void 0, against, note) : _chunkXKSO2V5Icjs.spelledInterval.call(void 0, note, against);
1797
+ entries.push({
1798
+ note,
1799
+ index,
1800
+ onset: begins,
1801
+ duration: onset + duration - begins,
1802
+ held,
1803
+ against,
1804
+ againstIndex,
1805
+ downbeat: Math.abs(begins - Math.round(begins)) < _chunkXKSO2V5Icjs.BEAT_EPS,
1806
+ interval,
1807
+ consonance: _chunkYPB3A3EYcjs.classifySpelledInterval.call(void 0, interval)
1808
+ });
1838
1809
  }
1839
- const interval = counterpointAbove ? _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, against, note) : _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, note, against);
1840
- entries.push({
1841
- note,
1842
- index,
1843
- onset,
1844
- duration,
1845
- against,
1846
- againstIndex,
1847
- downbeat: Math.abs(onset - Math.round(onset)) < EPS,
1848
- interval,
1849
- consonance: _chunkUADYAYATcjs.classifySpelledInterval.call(void 0, interval)
1850
- });
1851
1810
  onset += duration;
1852
1811
  }
1853
1812
  return entries;
@@ -1866,40 +1825,50 @@ function motionPairs(entries) {
1866
1825
  }
1867
1826
  return pairs;
1868
1827
  }
1869
- function checkSpecies(cantusFirmus, counterpoint, species, mode, opts) {
1828
+ function checkSpecies(cantusFirmus, counterpoint, species, modeLike, opts) {
1829
+ _chunkXKSO2V5Icjs.assertArray.call(void 0, cantusFirmus, "cantus firmus");
1830
+ _chunkXKSO2V5Icjs.assertArray.call(void 0, counterpoint, "counterpoint");
1831
+ const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
1832
+ const mode = _chunkYPB3A3EYcjs.toKeyScale.call(void 0, modeLike);
1870
1833
  if (!SPECIES_NUMBERS.includes(species)) {
1871
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
1872
- `species must be one of ${SPECIES_NUMBERS.join(", ")}; received ${JSON.stringify(species)}`
1834
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
1835
+ `species must be one of ${SPECIES_NUMBERS.join(", ")}; received ${_chunkXKSO2V5Icjs.describeRejected.call(void 0, species)}`
1873
1836
  );
1874
1837
  }
1875
1838
  if (cantusFirmus.length === 0 || counterpoint.length === 0) {
1876
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)("checkSpecies needs both a cantus firmus and a counterpoint");
1839
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)("checkSpecies needs both a cantus firmus and a counterpoint");
1877
1840
  }
1878
- const resolved = resolveDurations(counterpoint, cantusFirmus.length, species, opts);
1841
+ const resolved = resolveDurations(counterpoint, cantusFirmus.length, species, asked);
1879
1842
  if ("problem" in resolved) {
1880
- return [violation("wrongRhythmicRatio", [1], 0, counterpoint.length - 1, resolved.problem)];
1843
+ return inTimeOrder([
1844
+ violation("wrongRhythmicRatio", [1], 0, counterpoint.length - 1, resolved.problem)
1845
+ ]);
1881
1846
  }
1882
- const counterpointAbove = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _23 => _23.counterpointAbove]), () => ( meanPitch(counterpoint) >= meanPitch(cantusFirmus)));
1847
+ const counterpointAbove = _nullishCoalesce(asked.counterpointAbove, () => ( meanPitch(counterpoint) >= meanPitch(cantusFirmus)));
1883
1848
  const entries = buildEntries(cantusFirmus, counterpoint, resolved.durations, counterpointAbove);
1849
+ const struck = entries.filter((entry) => !entry.held);
1884
1850
  const violations = [];
1851
+ const crossed = /* @__PURE__ */ new Set();
1885
1852
  for (let position = 0; position < entries.length; position += 1) {
1886
- violations.push(...verticalViolations(entries, position, species, counterpointAbove));
1887
- const previous = entries[position - 1];
1888
- const current = entries[position];
1853
+ violations.push(...verticalViolations(entries, position, species, counterpointAbove, crossed));
1854
+ }
1855
+ for (let position = 1; position < struck.length; position += 1) {
1856
+ const previous = struck[position - 1];
1857
+ const current = struck[position];
1889
1858
  if (previous !== void 0 && current !== void 0) {
1890
1859
  violations.push(...melodicViolations(previous, current));
1891
1860
  }
1892
1861
  }
1893
- violations.push(...melodicShapeViolations(entries, species));
1894
- for (const [from, to] of motionPairs(entries)) {
1895
- const previous = entries[from];
1896
- const current = entries[to];
1862
+ violations.push(...melodicShapeViolations(struck, species));
1863
+ for (const [from, to] of motionPairs(struck)) {
1864
+ const previous = struck[from];
1865
+ const current = struck[to];
1897
1866
  if (previous !== void 0 && current !== void 0) {
1898
1867
  violations.push(...motionViolations(previous, current, counterpointAbove));
1899
1868
  }
1900
1869
  }
1901
- violations.push(...cadenceViolations(entries, mode, counterpointAbove));
1902
- return violations;
1870
+ violations.push(...cadenceViolations(struck, mode, counterpointAbove));
1871
+ return inTimeOrder(violations);
1903
1872
  }
1904
1873
 
1905
1874
  // src/theory/partwriting/index.ts
@@ -1908,12 +1877,9 @@ function bare(note) {
1908
1877
  }
1909
1878
  function midiOf(note, index, voice) {
1910
1879
  if (note.octave === void 0) {
1911
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(`voicings[${index}][${voice}] must carry an octave`);
1880
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(`voicings[${index}][${voice}] must carry an octave`);
1912
1881
  }
1913
- return _chunkQ2WGKJNNcjs.noteToMidi.call(void 0, note);
1914
- }
1915
- function isDescendingStep(interval) {
1916
- return interval.number === 2 && (interval.semitones === -1 || interval.semitones === -2);
1882
+ return _chunkXKSO2V5Icjs.noteToMidi.call(void 0, note);
1917
1883
  }
1918
1884
  function verticalViolations2(moment, ranges, maxSpacing) {
1919
1885
  const found = [];
@@ -1924,7 +1890,7 @@ function verticalViolations2(moment, ranges, maxSpacing) {
1924
1890
  if (upper === void 0 || lower === void 0) {
1925
1891
  continue;
1926
1892
  }
1927
- if (_chunkUADYAYATcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
1893
+ if (_chunkYPB3A3EYcjs.createsVoiceCrossing.call(void 0, upper, lower)) {
1928
1894
  found.push(
1929
1895
  violation(
1930
1896
  "voiceCrossing",
@@ -1935,7 +1901,7 @@ function verticalViolations2(moment, ranges, maxSpacing) {
1935
1901
  )
1936
1902
  );
1937
1903
  }
1938
- if (voice >= 2 && _chunkUADYAYATcjs.exceedsSpacing.call(void 0, upper, lower, maxSpacing)) {
1904
+ if (voice >= 2 && _chunkYPB3A3EYcjs.exceedsSpacing.call(void 0, upper, lower, maxSpacing)) {
1939
1905
  found.push(
1940
1906
  violation(
1941
1907
  "spacing",
@@ -1975,8 +1941,8 @@ function pairViolations(transition) {
1975
1941
  if (upperPrev === void 0 || upperCur === void 0 || lowerPrev === void 0 || lowerCur === void 0) {
1976
1942
  continue;
1977
1943
  }
1978
- if (_chunkUADYAYATcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1979
- const fifth = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, upperCur - lowerCur) === 7;
1944
+ if (_chunkYPB3A3EYcjs.createsParallelPerfect.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1945
+ const fifth = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, upperCur - lowerCur) === 7;
1980
1946
  found.push(
1981
1947
  violation(
1982
1948
  fifth ? "parallelFifth" : "parallelOctave",
@@ -1987,7 +1953,7 @@ function pairViolations(transition) {
1987
1953
  )
1988
1954
  );
1989
1955
  }
1990
- if (upper === lower + 1 && _chunkUADYAYATcjs.createsVoiceOverlap.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1956
+ if (upper === lower + 1 && _chunkYPB3A3EYcjs.createsVoiceOverlap.call(void 0, upperPrev, upperCur, lowerPrev, lowerCur)) {
1991
1957
  found.push(
1992
1958
  violation(
1993
1959
  "overlap",
@@ -2004,110 +1970,38 @@ function pairViolations(transition) {
2004
1970
  const bassCur = to.pitches[0];
2005
1971
  const topPrev = from.pitches[voices - 1];
2006
1972
  const topCur = to.pitches[voices - 1];
2007
- if (voices >= 2 && bassPrev !== void 0 && bassCur !== void 0 && topPrev !== void 0 && topCur !== void 0 && _chunkUADYAYATcjs.createsHiddenParallelPerfect.call(void 0, topPrev, topCur, bassPrev, bassCur)) {
1973
+ if (voices >= 2 && bassPrev !== void 0 && bassCur !== void 0 && topPrev !== void 0 && topCur !== void 0 && _chunkYPB3A3EYcjs.createsHiddenParallelPerfect.call(void 0, topPrev, topCur, bassPrev, bassCur)) {
2008
1974
  found.push(
2009
1975
  violation(
2010
1976
  "hiddenPerfect",
2011
1977
  [0, voices - 1],
2012
1978
  from.index,
2013
1979
  to.index,
2014
- "The outer voices leap into a perfect interval by similar motion"
1980
+ "The outer voices reach a perfect interval by similar motion, the upper one by leap"
2015
1981
  )
2016
1982
  );
2017
1983
  }
2018
1984
  return found;
2019
1985
  }
2020
- function inKey(pc, key) {
2021
- return (key.modeMask12 >> _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, pc - key.rootPc) & 1) === 1;
2022
- }
2023
- function isDominantSonority(chord) {
2024
- const has = (semitones) => chord.intervals.some((interval) => _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, interval) === semitones);
2025
- return chord.quality === "maj" || has(4) && has(10);
2026
- }
2027
- function isAppliedDominant(chord, next, key) {
2028
- if (next === void 0 || !isDominantSonority(chord)) {
2029
- return false;
2030
- }
2031
- const target = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc + 5);
2032
- return target !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc) && inKey(target, key) && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, next.rootPc) === target;
2033
- }
2034
- function isNeapolitan(chord, key) {
2035
- return chord.quality === "maj" && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, chord.rootPc - key.rootPc) === 1;
2036
- }
2037
- function soundsAugmentedSixth(moment, key) {
2038
- const bass = moment.notes[0];
2039
- if (bass === void 0 || _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, bass) !== _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc + 8)) {
2040
- return false;
2041
- }
2042
- return moment.notes.some((note, voice) => {
2043
- if (voice === 0) {
2044
- return false;
2045
- }
2046
- const interval = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, bass, note);
2047
- return (interval.number - 1) % 7 + 1 === 6 && _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, interval.semitones) === 10;
2048
- });
2049
- }
2050
- function isChromaticHarmony(moment, next, key) {
2051
- return isAppliedDominant(moment.chord, next, key) || isNeapolitan(moment.chord, key) || soundsAugmentedSixth(moment, key);
2052
- }
2053
- function approachedByStep(before, moment, voice) {
2054
- const earlier = _optionalChain([before, 'optionalAccess', _24 => _24.notes, 'access', _25 => _25[voice]]);
2055
- const later = moment.notes[voice];
2056
- if (earlier === void 0 || later === void 0) {
2057
- return false;
2058
- }
2059
- return _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, earlier, later).number === 2;
2060
- }
2061
- function isOuterVoice(voice, moment) {
2062
- return voice === 0 || voice === moment.notes.length - 1;
2063
- }
2064
1986
  function crossRelationViolations(transition) {
2065
- const found = [];
2066
1987
  const { from, to, key, before, after } = transition;
2067
- const chromatic = isChromaticHarmony(from, to.chord, key) || isChromaticHarmony(to, _optionalChain([after, 'optionalAccess', _26 => _26.chord]), key);
2068
- const reported = /* @__PURE__ */ new Set();
2069
- for (let first = 0; first < from.notes.length; first += 1) {
2070
- for (let second = 0; second < to.notes.length; second += 1) {
2071
- if (first === second) {
2072
- continue;
2073
- }
2074
- const earlier = from.notes[first];
2075
- const later = to.notes[second];
2076
- if (earlier === void 0 || later === void 0) {
2077
- continue;
2078
- }
2079
- const letter = _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, earlier.letter);
2080
- if (letter !== _chunkQ2WGKJNNcjs.diatonicLetterOf.call(void 0, later.letter) || earlier.alter === later.alter) {
2081
- continue;
2082
- }
2083
- if (chromatic || approachedByStep(before, from, first) || approachedByStep(from, to, second) || !isOuterVoice(first, from) && !isOuterVoice(second, to)) {
2084
- continue;
2085
- }
2086
- const pair = `${letter}:${Math.min(first, second)}:${Math.max(first, second)}`;
2087
- if (reported.has(pair)) {
2088
- continue;
2089
- }
2090
- reported.add(pair);
2091
- found.push(
2092
- violation(
2093
- "crossRelation",
2094
- [first, second],
2095
- from.index,
2096
- to.index,
2097
- `${_chunkQ2WGKJNNcjs.formatNote.call(void 0, bare(earlier))} is contradicted by ${_chunkQ2WGKJNNcjs.formatNote.call(void 0, bare(later))} in another voice`
2098
- )
2099
- );
2100
- }
2101
- }
2102
- return found;
1988
+ return crossRelations(from, to, key, { before: _optionalChain([before, 'optionalAccess', _22 => _22.notes]), after: _optionalChain([after, 'optionalAccess', _23 => _23.chord]) }).map(
1989
+ (clash) => violation(
1990
+ "crossRelation",
1991
+ [clash.earlierVoice, clash.laterVoice],
1992
+ from.index,
1993
+ to.index,
1994
+ `${_chunkXKSO2V5Icjs.formatNote.call(void 0, bare(clash.earlier))} is contradicted by ${_chunkXKSO2V5Icjs.formatNote.call(void 0, bare(clash.later))} in another voice`
1995
+ )
1996
+ );
2103
1997
  }
2104
1998
  function melodicViolations2(transition) {
2105
1999
  const found = [];
2106
2000
  const { from, to, key } = transition;
2107
- const seventhPc = seventhPcOf(from.chord);
2108
- const nextPcs = new Set(_chunkUADYAYATcjs.chordPitchClasses.call(void 0, to.chord));
2109
- const leadingTonePc = leadingTonePcOf(key);
2110
- const tonicPc = _chunkQ2WGKJNNcjs.pitchClassOf.call(void 0, key.rootPc);
2001
+ const seventhPc = _chunkYPB3A3EYcjs.seventhPcOf.call(void 0, from.chord);
2002
+ const nextPcs = new Set(_chunkYPB3A3EYcjs.chordPitchClasses.call(void 0, to.chord));
2003
+ const leadingTonePc = _chunkYPB3A3EYcjs.leadingTonePcOf.call(void 0, key);
2004
+ const tonicPc = _chunkXKSO2V5Icjs.pitchClassOf.call(void 0, key.rootPc);
2111
2005
  const voices = Math.min(from.notes.length, to.notes.length);
2112
2006
  for (let voice = 0; voice < voices; voice += 1) {
2113
2007
  const earlier = from.notes[voice];
@@ -2117,8 +2011,8 @@ function melodicViolations2(transition) {
2117
2011
  if (earlier === void 0 || later === void 0 || earlierPitch === void 0 || laterPitch === void 0) {
2118
2012
  continue;
2119
2013
  }
2120
- const interval = _chunkQ2WGKJNNcjs.spelledInterval.call(void 0, earlier, later);
2121
- if (_chunkUADYAYATcjs.isAugmentedMelodicInterval.call(void 0, earlier, later)) {
2014
+ const interval = _chunkXKSO2V5Icjs.spelledInterval.call(void 0, earlier, later);
2015
+ if (_chunkYPB3A3EYcjs.isAugmentedMelodicInterval.call(void 0, earlier, later)) {
2122
2016
  found.push(
2123
2017
  violation(
2124
2018
  "augmentedMelodicInterval",
@@ -2129,8 +2023,8 @@ function melodicViolations2(transition) {
2129
2023
  )
2130
2024
  );
2131
2025
  }
2132
- const earlierPc = _chunkQ2WGKJNNcjs.noteToPitchClass.call(void 0, earlier);
2133
- if (earlierPc === seventhPc && !nextPcs.has(earlierPc) && !isDescendingStep(interval)) {
2026
+ const earlierPc = _chunkXKSO2V5Icjs.noteToPitchClass.call(void 0, earlier);
2027
+ if (earlierPc === seventhPc && !nextPcs.has(earlierPc) && !_chunkYPB3A3EYcjs.isDescendingStep.call(void 0, interval)) {
2134
2028
  found.push(
2135
2029
  violation(
2136
2030
  "unresolvedSeventh",
@@ -2141,7 +2035,8 @@ function melodicViolations2(transition) {
2141
2035
  )
2142
2036
  );
2143
2037
  }
2144
- if (earlierPc === leadingTonePc && isFunctioningLeadingTone(from.chord, key) && nextPcs.has(tonicPc) && !nextPcs.has(earlierPc) && !_chunkUADYAYATcjs.isLeadingToneResolution.call(void 0, earlierPitch, laterPitch, key)) {
2038
+ const inner = !isOuterVoice(voice, from.notes) && !isOuterVoice(voice, to.notes);
2039
+ if (earlierPc === leadingTonePc && _chunkYPB3A3EYcjs.isFunctioningLeadingTone.call(void 0, from.chord, key) && nextPcs.has(tonicPc) && !nextPcs.has(earlierPc) && !_chunkYPB3A3EYcjs.isLeadingToneResolution.call(void 0, earlierPitch, laterPitch, key) && !(inner && _chunkYPB3A3EYcjs.isFrustratedLeadingTone.call(void 0, earlierPitch, laterPitch, to.chord))) {
2145
2040
  found.push(
2146
2041
  violation(
2147
2042
  "unresolvedLeadingTone",
@@ -2156,24 +2051,30 @@ function melodicViolations2(transition) {
2156
2051
  return found;
2157
2052
  }
2158
2053
  function spellVoicing(voicing, chord, key) {
2159
- const { tonic } = _chunkUADYAYATcjs.spelledKeyOf.call(void 0, key);
2160
- return voicing.map((pitch) => _chunkUADYAYATcjs.spellPitch.call(void 0, pitch, tonic, key, { chordRoot: chord.rootPc }));
2161
- }
2162
- function checkPartWriting(voicings, chords, key, opts) {
2054
+ _chunkXKSO2V5Icjs.assertArray.call(void 0, voicing, "voicing");
2055
+ _chunkYPB3A3EYcjs.assertChord.call(void 0, chord);
2056
+ const { tonic, scale } = _chunkYPB3A3EYcjs.resolveKey.call(void 0, key);
2057
+ return voicing.map((pitch) => _chunkYPB3A3EYcjs.spellPitch.call(void 0, pitch, tonic, scale, { chord }));
2058
+ }
2059
+ function checkPartWriting(voicings, chords, keyLike, opts) {
2060
+ _chunkXKSO2V5Icjs.assertArray.call(void 0, voicings, "voicings");
2061
+ _chunkXKSO2V5Icjs.assertArray.call(void 0, chords, "chords");
2062
+ const asked = _chunkXKSO2V5Icjs.assertOptions.call(void 0, opts, "opts");
2063
+ const key = _chunkYPB3A3EYcjs.toKeyScale.call(void 0, keyLike);
2163
2064
  if (voicings.length !== chords.length) {
2164
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
2065
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
2165
2066
  `checkPartWriting needs one chord per voicing; received ${voicings.length} voicings and ${chords.length} chords`
2166
2067
  );
2167
2068
  }
2168
- const maxSpacing = resolveMaxSpacing(opts);
2069
+ const maxSpacing = resolveMaxSpacing(asked);
2169
2070
  const voiceCount = voicings.reduce((widest, voicing) => Math.max(widest, voicing.length), 0);
2170
2071
  let ranges;
2171
- if (_optionalChain([opts, 'optionalAccess', _27 => _27.ranges]) === void 0) {
2072
+ if (asked.ranges === void 0) {
2172
2073
  ranges = voiceCount === 4 ? SATB_RANGES : void 0;
2173
2074
  } else {
2174
- ranges = resolveRanges(opts);
2075
+ ranges = resolveRanges(asked);
2175
2076
  if (ranges.length < voiceCount) {
2176
- throw new (0, _chunkQ2WGKJNNcjs.InvalidInputError)(
2077
+ throw new (0, _chunkXKSO2V5Icjs.InvalidInputError)(
2177
2078
  `checkPartWriting needs one range per voice; received ${ranges.length} ranges for ${voiceCount} voices`
2178
2079
  );
2179
2080
  }
@@ -2233,11 +2134,5 @@ function checkPartWriting(voicings, chords, key, opts) {
2233
2134
 
2234
2135
 
2235
2136
 
2236
-
2237
-
2238
-
2239
-
2240
-
2241
-
2242
- exports.TRANSPOSING_INSTRUMENTS = TRANSPOSING_INSTRUMENTS; exports.instrumentTransposition = instrumentTransposition; exports.toSoundingPitch = toSoundingPitch; exports.toWrittenPitch = toWrittenPitch; exports.scaleMatchesChord = scaleMatchesChord; exports.chordScales = chordScales; exports.avoidNotes = avoidNotes; exports.availableTensions = availableTensions; exports.chordScaleReport = chordScaleReport; exports.scalesForChanges = scalesForChanges; exports.figuredBassRealization = figuredBassRealization; exports.realizeFiguredBass = realizeFiguredBass; exports.figuredBassOf = figuredBassOf; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled; exports.checkSpecies = checkSpecies; exports.spellVoicing = spellVoicing; exports.checkPartWriting = checkPartWriting;
2243
- //# sourceMappingURL=chunk-WWURE4XT.cjs.map
2137
+ exports.TRANSPOSING_INSTRUMENTS = TRANSPOSING_INSTRUMENTS; exports.instrumentTransposition = instrumentTransposition; exports.toSoundingPitch = toSoundingPitch; exports.toWrittenPitch = toWrittenPitch; exports.figuredBassRealization = figuredBassRealization; exports.realizeFiguredBass = realizeFiguredBass; exports.figuredBassOf = figuredBassOf; exports.SATB_RANGES = SATB_RANGES; exports.voiceChord = voiceChord; exports.voiceProgression = voiceProgression; exports.voiceLeadingCost = voiceLeadingCost; exports.nextVoicing = nextVoicing; exports.voiceChordStyled = voiceChordStyled; exports.checkSpecies = checkSpecies; exports.spellVoicing = spellVoicing; exports.checkPartWriting = checkPartWriting;
2138
+ //# sourceMappingURL=chunk-RVS4G4MR.cjs.map